Coverage Report

Created: 2026-11-12 08:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/gif/giflib/egif_lib.c
Line
Count
Source
1
/******************************************************************************
2
3
egif_lib.c - GIF encoding
4
5
The functions here and in dgif_lib.c are partitioned carefully so that
6
if you only require one of read and write capability, only one of these
7
two modules will be linked.  Preserve this property!
8
9
*****************************************************************************/
10
// SPDX-License-Identifier: MIT
11
// SPDX-FileCopyrightText: Copyright (C) Eric S. Raymond <esr@thyrsus.com>
12
13
#include <fcntl.h>
14
#include <stdint.h>
15
#include <stdio.h>
16
#include <stdlib.h>
17
#include <string.h>
18
19
#ifdef _WIN32
20
#include <io.h>
21
#else
22
#include <sys/types.h>
23
#include <unistd.h>
24
#endif /* _WIN32 */
25
#include <sys/stat.h>
26
27
#include "gif_lib.h"
28
#include "gif_lib_private.h"
29
30
/* Masks given codes to BitsPerPixel, to make sure all codes are in range: */
31
/*@+charint@*/
32
static const GifPixelType CodeMask[] = {0x00, 0x01, 0x03, 0x07, 0x0f,
33
                                        0x1f, 0x3f, 0x7f, 0xff};
34
/*@-charint@*/
35
36
static int EGifPutWord(int Word, GifFileType *GifFile);
37
static int EGifSetupCompress(GifFileType *GifFile);
38
static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line,
39
                            const int LineLen);
40
static int EGifCompressOutput(GifFileType *GifFile, int Code);
41
static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c);
42
43
/* extract bytes from an unsigned word */
44
0
#define LOBYTE(x) ((x)&0xff)
45
0
#define HIBYTE(x) (((x) >> 8) & 0xff)
46
47
#ifndef S_IREAD
48
#define S_IREAD S_IRUSR
49
#endif
50
51
#ifndef S_IWRITE
52
#define S_IWRITE S_IWUSR
53
#endif
54
/******************************************************************************
55
 Open a new GIF file for write, specified by name. If TestExistance then
56
 if the file exists this routines fails (returns NULL).
57
 Returns a dynamically allocated GifFileType pointer which serves as the GIF
58
 info record. The Error member is cleared if successful.
59
******************************************************************************/
60
GifFileType *EGifOpenFileName(const char *FileName, const bool TestExistence,
61
0
                              int *Error) {
62
63
0
  int FileHandle;
64
0
  GifFileType *GifFile;
65
66
0
  if (TestExistence) {
67
0
    FileHandle = open(FileName, O_WRONLY | O_CREAT | O_EXCL,
68
0
                      S_IREAD | S_IWRITE);
69
0
  } else {
70
0
    FileHandle = open(FileName, O_WRONLY | O_CREAT | O_TRUNC,
71
0
                      S_IREAD | S_IWRITE);
72
0
  }
73
74
0
  if (FileHandle == -1) {
75
0
    if (Error != NULL) {
76
0
      *Error = E_GIF_ERR_OPEN_FAILED;
77
0
    }
78
0
    return NULL;
79
0
  }
80
0
  GifFile = EGifOpenFileHandle(FileHandle, Error);
81
0
  if (GifFile == (GifFileType *)NULL) {
82
0
    (void)close(FileHandle);
83
0
  }
84
0
  return GifFile;
85
0
}
86
87
/******************************************************************************
88
 Update a new GIF file, given its file handle, which must be opened for
89
 write in binary mode.
90
 Returns dynamically allocated a GifFileType pointer which serves as the GIF
91
 info record.
92
 Only fails on a memory allocation error.
93
******************************************************************************/
94
0
GifFileType *EGifOpenFileHandle(const int FileHandle, int *Error) {
95
0
  GifFileType *GifFile;
96
0
  GifFilePrivateType *Private;
97
0
  FILE *f;
98
99
0
  GifFile = (GifFileType *)malloc(sizeof(GifFileType));
100
0
  if (GifFile == NULL) {
101
0
    return NULL;
102
0
  }
103
104
0
  memset(GifFile, '\0', sizeof(GifFileType));
105
106
0
  Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
107
0
  if (Private == NULL) {
108
0
    free(GifFile);
109
0
    if (Error != NULL) {
110
0
      *Error = E_GIF_ERR_NOT_ENOUGH_MEM;
111
0
    }
112
0
    return NULL;
113
0
  }
114
0
  /*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType));
115
0
  if ((Private->HashTable = _InitHashTable()) == NULL) {
116
0
    free(GifFile);
117
0
    free(Private);
118
0
    if (Error != NULL) {
119
0
      *Error = E_GIF_ERR_NOT_ENOUGH_MEM;
120
0
    }
121
0
    return NULL;
122
0
  }
123
124
#ifdef _WIN32
125
  _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */
126
#endif                                  /* _WIN32 */
127
128
0
  f = fdopen(FileHandle, "wb"); /* Make it into a stream: */
129
130
0
  GifFile->Private = (void *)Private;
131
0
  Private->FileHandle = FileHandle;
132
0
  Private->File = f;
133
0
  Private->FileState = FILE_STATE_WRITE;
134
0
  Private->gif89 = false;
135
136
0
  Private->Write = (OutputFunc)0;   /* No user write routine (MRB) */
137
0
  GifFile->UserData = (void *)NULL; /* No user write handle (MRB) */
138
139
0
  GifFile->Error = 0;
140
141
0
  return GifFile;
142
0
}
143
144
/******************************************************************************
145
 Output constructor that takes user supplied output function.
146
 Basically just a copy of EGifOpenFileHandle. (MRB)
147
******************************************************************************/
148
0
GifFileType *EGifOpen(void *userData, OutputFunc writeFunc, int *Error) {
149
0
  GifFileType *GifFile;
150
0
  GifFilePrivateType *Private;
151
152
0
  GifFile = (GifFileType *)malloc(sizeof(GifFileType));
153
0
  if (GifFile == NULL) {
154
0
    if (Error != NULL) {
155
0
      *Error = E_GIF_ERR_NOT_ENOUGH_MEM;
156
0
    }
157
0
    return NULL;
158
0
  }
159
160
0
  memset(GifFile, '\0', sizeof(GifFileType));
161
162
0
  Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
163
0
  if (Private == NULL) {
164
0
    free(GifFile);
165
0
    if (Error != NULL) {
166
0
      *Error = E_GIF_ERR_NOT_ENOUGH_MEM;
167
0
    }
168
0
    return NULL;
169
0
  }
170
171
0
  memset(Private, '\0', sizeof(GifFilePrivateType));
172
173
0
  Private->HashTable = _InitHashTable();
174
0
  if (Private->HashTable == NULL) {
175
0
    free(GifFile);
176
0
    free(Private);
177
0
    if (Error != NULL) {
178
0
      *Error = E_GIF_ERR_NOT_ENOUGH_MEM;
179
0
    }
180
0
    return NULL;
181
0
  }
182
183
0
  GifFile->Private = (void *)Private;
184
0
  Private->FileHandle = 0;
185
0
  Private->File = (FILE *)0;
186
0
  Private->FileState = FILE_STATE_WRITE;
187
188
0
  Private->Write = writeFunc;   /* User write routine (MRB) */
189
0
  GifFile->UserData = userData; /* User write handle (MRB) */
190
191
0
  Private->gif89 = false; /* initially, write GIF87 */
192
193
0
  GifFile->Error = 0;
194
195
0
  return GifFile;
196
0
}
197
198
/******************************************************************************
199
 Routine to compute the GIF version that will be written on output.
200
******************************************************************************/
201
0
const char *EGifGetGifVersion(GifFileType *GifFile) {
202
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
203
0
  int i, j;
204
205
  /*
206
   * Bulletproofing - always write GIF89 if we need to.
207
   * Note, we don't clear the gif89 flag here because
208
   * users of the sequential API might have called EGifSetGifVersion()
209
   * in order to set that flag.
210
   */
211
0
  for (i = 0; i < GifFile->ImageCount; i++) {
212
0
    for (j = 0; j < GifFile->SavedImages[i].ExtensionBlockCount;
213
0
         j++) {
214
0
      int function =
215
0
          GifFile->SavedImages[i].ExtensionBlocks[j].Function;
216
217
0
      if (function == COMMENT_EXT_FUNC_CODE ||
218
0
          function == GRAPHICS_EXT_FUNC_CODE ||
219
0
          function == PLAINTEXT_EXT_FUNC_CODE ||
220
0
          function == APPLICATION_EXT_FUNC_CODE) {
221
0
        Private->gif89 = true;
222
0
      }
223
0
    }
224
0
  }
225
0
  for (i = 0; i < GifFile->ExtensionBlockCount; i++) {
226
0
    int function = GifFile->ExtensionBlocks[i].Function;
227
228
0
    if (function == COMMENT_EXT_FUNC_CODE ||
229
0
        function == GRAPHICS_EXT_FUNC_CODE ||
230
0
        function == PLAINTEXT_EXT_FUNC_CODE ||
231
0
        function == APPLICATION_EXT_FUNC_CODE) {
232
0
      Private->gif89 = true;
233
0
    }
234
0
  }
235
236
0
  if (Private->gif89) {
237
0
    return GIF89_STAMP;
238
0
  } else {
239
0
    return GIF87_STAMP;
240
0
  }
241
0
}
242
243
/******************************************************************************
244
 Set the GIF version. In the extremely unlikely event that there is ever
245
 another version, replace the bool argument with an enum in which the
246
 GIF87 value is 0 (numerically the same as bool false) and the GIF89 value
247
 is 1 (numerically the same as bool true).  That way we'll even preserve
248
 object-file compatibility!
249
******************************************************************************/
250
0
void EGifSetGifVersion(GifFileType *GifFile, const bool gif89) {
251
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
252
253
0
  Private->gif89 = gif89;
254
0
}
255
256
/******************************************************************************
257
 All writes to the GIF should go through this.
258
******************************************************************************/
259
static int InternalWrite(GifFileType *GifFileOut, const unsigned char *buf,
260
0
                         size_t len) {
261
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFileOut->Private;
262
0
  if (Private->Write) {
263
0
    return Private->Write(GifFileOut, buf, len);
264
0
  } else {
265
0
    return fwrite(buf, 1, len, Private->File);
266
0
  }
267
0
}
268
269
/******************************************************************************
270
 This routine should be called before any other EGif calls, immediately
271
 following the GIF file opening.
272
******************************************************************************/
273
int EGifPutScreenDesc(GifFileType *GifFile, const int Width, const int Height,
274
                      const int ColorRes, const int BackGround,
275
0
                      const ColorMapObject *ColorMap) {
276
0
  GifByteType Buf[3];
277
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
278
0
  const char *write_version;
279
0
  GifFile->SColorMap = NULL;
280
281
0
  if (Private->FileState & FILE_STATE_SCREEN) {
282
    /* If already has screen descriptor - something is wrong! */
283
0
    GifFile->Error = E_GIF_ERR_HAS_SCRN_DSCR;
284
0
    return GIF_ERROR;
285
0
  }
286
0
  if (!IS_WRITEABLE(Private)) {
287
    /* This file was NOT open for writing: */
288
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
289
0
    return GIF_ERROR;
290
0
  }
291
292
0
  write_version = EGifGetGifVersion(GifFile);
293
294
  /* First write the version prefix into the file. */
295
0
  if (InternalWrite(GifFile, (unsigned char *)write_version,
296
0
                    strlen(write_version)) != strlen(write_version)) {
297
0
    GifFile->Error = E_GIF_ERR_WRITE_FAILED;
298
0
    return GIF_ERROR;
299
0
  }
300
301
0
  GifFile->SWidth = Width;
302
0
  GifFile->SHeight = Height;
303
0
  GifFile->SColorResolution = ColorRes;
304
0
  GifFile->SBackGroundColor = BackGround;
305
0
  if (ColorMap) {
306
0
    GifFile->SColorMap =
307
0
        GifMakeMapObject(ColorMap->ColorCount, ColorMap->Colors);
308
0
    if (GifFile->SColorMap == NULL) {
309
0
      GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM;
310
0
      return GIF_ERROR;
311
0
    }
312
0
  } else {
313
0
    GifFile->SColorMap = NULL;
314
0
  }
315
316
  /*
317
   * Put the logical screen descriptor into the file:
318
   */
319
  /* Logical Screen Descriptor: Dimensions */
320
0
  (void)EGifPutWord(Width, GifFile);
321
0
  (void)EGifPutWord(Height, GifFile);
322
323
  /* Logical Screen Descriptor: Packed Fields */
324
  /* Note: We have actual size of the color table default to the largest
325
   * possible size (7+1 == 8 bits) because the decoder can use it to
326
   * decide how to display the files.
327
   */
328
0
  Buf[0] =
329
0
      (ColorMap ? 0x80 : 0x00) | /* Yes/no global colormap */
330
0
      ((ColorRes - 1) << 4) | /* Bits allocated to each primary color */
331
0
      (ColorMap ? ColorMap->BitsPerPixel - 1
332
0
                : 0x07); /* Actual size of the
333
                            color table. */
334
0
  if (ColorMap != NULL && ColorMap->SortFlag) {
335
0
    Buf[0] |= 0x08;
336
0
  }
337
0
  Buf[1] =
338
0
      BackGround; /* Index into the ColorTable for background color */
339
0
  Buf[2] = GifFile->AspectByte; /* Pixel Aspect Ratio */
340
0
  InternalWrite(GifFile, Buf, 3);
341
342
  /* If we have Global color map - dump it also: */
343
0
  if (ColorMap != NULL) {
344
0
    int i;
345
0
    for (i = 0; i < ColorMap->ColorCount; i++) {
346
      /* Put the ColorMap out also: */
347
0
      Buf[0] = ColorMap->Colors[i].Red;
348
0
      Buf[1] = ColorMap->Colors[i].Green;
349
0
      Buf[2] = ColorMap->Colors[i].Blue;
350
0
      if (InternalWrite(GifFile, Buf, 3) != 3) {
351
0
        GifFile->Error = E_GIF_ERR_WRITE_FAILED;
352
0
        return GIF_ERROR;
353
0
      }
354
0
    }
355
0
  }
356
357
  /* Mark this file as has screen descriptor, and no pixel written yet: */
358
0
  Private->FileState |= FILE_STATE_SCREEN;
359
360
0
  return GIF_OK;
361
0
}
362
363
/******************************************************************************
364
 This routine should be called before any attempt to dump an image - any
365
 call to any of the pixel dump routines.
366
******************************************************************************/
367
int EGifPutImageDesc(GifFileType *GifFile, const int Left, const int Top,
368
                     const int Width, const int Height, const bool Interlace,
369
0
                     const ColorMapObject *ColorMap) {
370
0
  GifByteType Buf[3];
371
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
372
373
0
  if (Private->FileState & FILE_STATE_IMAGE &&
374
0
      Private->PixelCount > 0xffff0000UL) {
375
    /* If already has active image descriptor - something is wrong!
376
     */
377
0
    GifFile->Error = E_GIF_ERR_HAS_IMAG_DSCR;
378
0
    return GIF_ERROR;
379
0
  }
380
0
  if (!IS_WRITEABLE(Private)) {
381
    /* This file was NOT open for writing: */
382
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
383
0
    return GIF_ERROR;
384
0
  }
385
0
  GifFile->Image.Left = Left;
386
0
  GifFile->Image.Top = Top;
387
0
  GifFile->Image.Width = Width;
388
0
  GifFile->Image.Height = Height;
389
0
  GifFile->Image.Interlace = Interlace;
390
0
  if (ColorMap != GifFile->Image.ColorMap) {
391
0
    if (ColorMap) {
392
0
      if (GifFile->Image.ColorMap != NULL) {
393
0
        GifFreeMapObject(GifFile->Image.ColorMap);
394
0
        GifFile->Image.ColorMap = NULL;
395
0
      }
396
0
      GifFile->Image.ColorMap = GifMakeMapObject(
397
0
          ColorMap->ColorCount, ColorMap->Colors);
398
0
      if (GifFile->Image.ColorMap == NULL) {
399
0
        GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM;
400
0
        return GIF_ERROR;
401
0
      }
402
0
    } else {
403
0
      GifFile->Image.ColorMap = NULL;
404
0
    }
405
0
  }
406
407
  /* Put the image descriptor into the file: */
408
0
  Buf[0] = DESCRIPTOR_INTRODUCER; /* Image separator character. */
409
0
  InternalWrite(GifFile, Buf, 1);
410
0
  (void)EGifPutWord(Left, GifFile);
411
0
  (void)EGifPutWord(Top, GifFile);
412
0
  (void)EGifPutWord(Width, GifFile);
413
0
  (void)EGifPutWord(Height, GifFile);
414
0
  Buf[0] = (ColorMap ? 0x80 : 0x00) | (Interlace ? 0x40 : 0x00) |
415
0
           (ColorMap ? ColorMap->BitsPerPixel - 1 : 0);
416
0
  InternalWrite(GifFile, Buf, 1);
417
418
  /* If we have Global color map - dump it also: */
419
0
  if (ColorMap != NULL) {
420
0
    int i;
421
0
    for (i = 0; i < ColorMap->ColorCount; i++) {
422
      /* Put the ColorMap out also: */
423
0
      Buf[0] = ColorMap->Colors[i].Red;
424
0
      Buf[1] = ColorMap->Colors[i].Green;
425
0
      Buf[2] = ColorMap->Colors[i].Blue;
426
0
      if (InternalWrite(GifFile, Buf, 3) != 3) {
427
0
        GifFile->Error = E_GIF_ERR_WRITE_FAILED;
428
0
        return GIF_ERROR;
429
0
      }
430
0
    }
431
0
  }
432
0
  if (GifFile->SColorMap == NULL && GifFile->Image.ColorMap == NULL) {
433
0
    GifFile->Error = E_GIF_ERR_NO_COLOR_MAP;
434
0
    return GIF_ERROR;
435
0
  }
436
437
  /* Mark this file as has screen descriptor: */
438
0
  Private->FileState |= FILE_STATE_IMAGE;
439
0
  Private->PixelCount = (long)Width * (long)Height;
440
441
  /* Reset compress algorithm parameters. */
442
0
  (void)EGifSetupCompress(GifFile);
443
444
0
  return GIF_OK;
445
0
}
446
447
/******************************************************************************
448
 Put one full scanned line (Line) of length LineLen into GIF file.
449
******************************************************************************/
450
0
int EGifPutLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) {
451
0
  int i;
452
0
  GifPixelType Mask;
453
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
454
455
0
  if (!IS_WRITEABLE(Private)) {
456
    /* This file was NOT open for writing: */
457
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
458
0
    return GIF_ERROR;
459
0
  }
460
461
0
  if (!LineLen) {
462
0
    LineLen = GifFile->Image.Width;
463
0
  }
464
0
  if (Private->PixelCount < (unsigned)LineLen) {
465
0
    GifFile->Error = E_GIF_ERR_DATA_TOO_BIG;
466
0
    return GIF_ERROR;
467
0
  }
468
0
  Private->PixelCount -= LineLen;
469
470
  /* Make sure the codes are not out of bit range, as we might generate
471
   * wrong code (because of overflow when we combine them) in this case:
472
   */
473
0
  Mask = CodeMask[Private->BitsPerPixel];
474
0
  for (i = 0; i < LineLen; i++) {
475
0
    Line[i] &= Mask;
476
0
  }
477
478
0
  return EGifCompressLine(GifFile, Line, LineLen);
479
0
}
480
481
/******************************************************************************
482
 Put one pixel (Pixel) into GIF file.
483
******************************************************************************/
484
0
int EGifPutPixel(GifFileType *GifFile, GifPixelType Pixel) {
485
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
486
487
0
  if (!IS_WRITEABLE(Private)) {
488
    /* This file was NOT open for writing: */
489
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
490
0
    return GIF_ERROR;
491
0
  }
492
493
0
  if (Private->PixelCount == 0) {
494
0
    GifFile->Error = E_GIF_ERR_DATA_TOO_BIG;
495
0
    return GIF_ERROR;
496
0
  }
497
0
  --Private->PixelCount;
498
499
  /* Make sure the code is not out of bit range, as we might generate
500
   * wrong code (because of overflow when we combine them) in this case:
501
   */
502
0
  Pixel &= CodeMask[Private->BitsPerPixel];
503
504
0
  return EGifCompressLine(GifFile, &Pixel, 1);
505
0
}
506
507
/******************************************************************************
508
 Put a comment into GIF file using the GIF89 comment extension block.
509
******************************************************************************/
510
0
int EGifPutComment(GifFileType *GifFile, const char *Comment) {
511
0
  unsigned int length;
512
0
  char *buf;
513
514
0
  length = strlen(Comment);
515
0
  if (length <= 255) {
516
0
    return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, length,
517
0
                            Comment);
518
0
  } else {
519
0
    buf = (char *)Comment;
520
0
    if (EGifPutExtensionLeader(GifFile, COMMENT_EXT_FUNC_CODE) ==
521
0
        GIF_ERROR) {
522
0
      return GIF_ERROR;
523
0
    }
524
525
    /* Break the comment into 255 byte sub blocks */
526
0
    while (length > 255) {
527
0
      if (EGifPutExtensionBlock(GifFile, 255, buf) ==
528
0
          GIF_ERROR) {
529
0
        return GIF_ERROR;
530
0
      }
531
0
      buf = buf + 255;
532
0
      length -= 255;
533
0
    }
534
    /* Output any partial block and the clear code. */
535
0
    if (length > 0) {
536
0
      if (EGifPutExtensionBlock(GifFile, length, buf) ==
537
0
          GIF_ERROR) {
538
0
        return GIF_ERROR;
539
0
      }
540
0
    }
541
0
    if (EGifPutExtensionTrailer(GifFile) == GIF_ERROR) {
542
0
      return GIF_ERROR;
543
0
    }
544
0
  }
545
0
  return GIF_OK;
546
0
}
547
548
/******************************************************************************
549
 Begin an extension block (see GIF manual).  More
550
 extensions can be dumped using EGifPutExtensionBlock until
551
 EGifPutExtensionTrailer is invoked.
552
******************************************************************************/
553
0
int EGifPutExtensionLeader(GifFileType *GifFile, const int ExtCode) {
554
0
  GifByteType Buf[3];
555
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
556
557
0
  if (!IS_WRITEABLE(Private)) {
558
    /* This file was NOT open for writing: */
559
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
560
0
    return GIF_ERROR;
561
0
  }
562
563
0
  Buf[0] = EXTENSION_INTRODUCER;
564
0
  Buf[1] = ExtCode;
565
0
  InternalWrite(GifFile, Buf, 2);
566
567
0
  return GIF_OK;
568
0
}
569
570
/******************************************************************************
571
 Put extension block data (see GIF manual) into a GIF file.
572
******************************************************************************/
573
int EGifPutExtensionBlock(GifFileType *GifFile, const int ExtLen,
574
0
                          const void *Extension) {
575
0
  GifByteType Buf;
576
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
577
578
0
  if (!IS_WRITEABLE(Private)) {
579
    /* This file was NOT open for writing: */
580
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
581
0
    return GIF_ERROR;
582
0
  }
583
584
0
  Buf = ExtLen;
585
0
  InternalWrite(GifFile, &Buf, 1);
586
0
  InternalWrite(GifFile, Extension, ExtLen);
587
588
0
  return GIF_OK;
589
0
}
590
591
/******************************************************************************
592
 Put a terminating block (see GIF manual) into a GIF file.
593
******************************************************************************/
594
0
int EGifPutExtensionTrailer(GifFileType *GifFile) {
595
596
0
  GifByteType Buf;
597
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
598
599
0
  if (!IS_WRITEABLE(Private)) {
600
    /* This file was NOT open for writing: */
601
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
602
0
    return GIF_ERROR;
603
0
  }
604
605
  /* Write the block terminator */
606
0
  Buf = 0;
607
0
  InternalWrite(GifFile, &Buf, 1);
608
609
0
  return GIF_OK;
610
0
}
611
612
/******************************************************************************
613
 Put an extension block (see GIF manual) into a GIF file.
614
 Warning: This function is only useful for Extension blocks that have at
615
 most one subblock.  Extensions with more than one subblock need to use the
616
 EGifPutExtension{Leader,Block,Trailer} functions instead.
617
******************************************************************************/
618
int EGifPutExtension(GifFileType *GifFile, const int ExtCode, const int ExtLen,
619
0
                     const void *Extension) {
620
621
0
  GifByteType Buf[3];
622
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
623
624
0
  if (!IS_WRITEABLE(Private)) {
625
    /* This file was NOT open for writing: */
626
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
627
0
    return GIF_ERROR;
628
0
  }
629
630
0
  if (ExtCode == 0) {
631
0
    InternalWrite(GifFile, (GifByteType *)&ExtLen, 1);
632
0
  } else {
633
0
    Buf[0] = EXTENSION_INTRODUCER;
634
0
    Buf[1] = ExtCode; /* Extension Label */
635
0
    Buf[2] = ExtLen;  /* Extension length */
636
0
    InternalWrite(GifFile, Buf, 3);
637
0
  }
638
0
  InternalWrite(GifFile, Extension, ExtLen);
639
0
  Buf[0] = 0;
640
0
  InternalWrite(GifFile, Buf, 1);
641
642
0
  return GIF_OK;
643
0
}
644
645
/******************************************************************************
646
 Render a Graphics Control Block as raw extension data
647
******************************************************************************/
648
649
size_t EGifGCBToExtension(const GraphicsControlBlock *GCB,
650
0
                          GifByteType *GifExtension) {
651
0
  GifExtension[0] = 0;
652
0
  GifExtension[0] |=
653
0
      (GCB->TransparentColor == NO_TRANSPARENT_COLOR) ? 0x00 : 0x01;
654
0
  GifExtension[0] |= GCB->UserInputFlag ? 0x02 : 0x00;
655
0
  GifExtension[0] |= ((GCB->DisposalMode & 0x07) << 2);
656
0
  GifExtension[1] = LOBYTE(GCB->DelayTime);
657
0
  GifExtension[2] = HIBYTE(GCB->DelayTime);
658
0
  GifExtension[3] = (char)GCB->TransparentColor;
659
0
  return 4;
660
0
}
661
662
/******************************************************************************
663
 Replace the Graphics Control Block for a saved image, if it exists.
664
******************************************************************************/
665
666
int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB,
667
0
                            GifFileType *GifFile, int ImageIndex) {
668
0
  int i;
669
0
  size_t Len;
670
0
  GifByteType buf[sizeof(GraphicsControlBlock)]; /* a bit dodgy... */
671
672
0
  if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) {
673
0
    return GIF_ERROR;
674
0
  }
675
676
0
  for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount;
677
0
       i++) {
678
0
    ExtensionBlock *ep =
679
0
        &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
680
0
    if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
681
      /* A Graphics Control Block is always 4 bytes.  If
682
       * the existing block came from a malformed GIF and
683
       * is shorter, resize it so EGifGCBToExtension does
684
       * not write past the end of ep->Bytes. */
685
0
      if (ep->ByteCount != 4) {
686
0
        GifByteType *new_bytes =
687
0
            (GifByteType *)realloc(ep->Bytes, 4);
688
0
        if (new_bytes == NULL) {
689
0
          return GIF_ERROR;
690
0
        }
691
0
        ep->Bytes = new_bytes;
692
0
        ep->ByteCount = 4;
693
0
      }
694
695
0
      EGifGCBToExtension(GCB, ep->Bytes);
696
0
      return GIF_OK;
697
0
    }
698
0
  }
699
700
0
  Len = EGifGCBToExtension(GCB, (GifByteType *)buf);
701
0
  if (GifAddExtensionBlock(
702
0
          &GifFile->SavedImages[ImageIndex].ExtensionBlockCount,
703
0
          &GifFile->SavedImages[ImageIndex].ExtensionBlocks,
704
0
          GRAPHICS_EXT_FUNC_CODE, Len,
705
0
          (unsigned char *)buf) == GIF_ERROR) {
706
0
    return (GIF_ERROR);
707
0
  }
708
709
0
  return (GIF_OK);
710
0
}
711
712
/******************************************************************************
713
 Put the image code in compressed form. This routine can be called if the
714
 information needed to be piped out as is. Obviously this is much faster
715
 than decoding and encoding again. This routine should be followed by calls
716
 to EGifPutCodeNext, until NULL block is given.
717
 The block should NOT be freed by the user (not dynamically allocated).
718
******************************************************************************/
719
int EGifPutCode(GifFileType *GifFile, int CodeSize,
720
0
                const GifByteType *CodeBlock) {
721
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
722
723
0
  if (!IS_WRITEABLE(Private)) {
724
    /* This file was NOT open for writing: */
725
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
726
0
    return GIF_ERROR;
727
0
  }
728
729
  /* No need to dump code size as Compression set up does any for us: */
730
  /*
731
   * Buf = CodeSize;
732
   * if (InternalWrite(GifFile, &Buf, 1) != 1) {
733
   *      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
734
   *      return GIF_ERROR;
735
   * }
736
   */
737
738
0
  return EGifPutCodeNext(GifFile, CodeBlock);
739
0
}
740
741
/******************************************************************************
742
 Continue to put the image code in compressed form. This routine should be
743
 called with blocks of code as read via DGifGetCode/DGifGetCodeNext. If
744
 given buffer pointer is NULL, empty block is written to mark end of code.
745
******************************************************************************/
746
0
int EGifPutCodeNext(GifFileType *GifFile, const GifByteType *CodeBlock) {
747
0
  GifByteType Buf;
748
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
749
750
0
  if (CodeBlock != NULL) {
751
0
    if (InternalWrite(GifFile, CodeBlock, CodeBlock[0] + 1) !=
752
0
        (unsigned)(CodeBlock[0] + 1)) {
753
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
754
0
      return GIF_ERROR;
755
0
    }
756
0
  } else {
757
0
    Buf = 0;
758
0
    if (InternalWrite(GifFile, &Buf, 1) != 1) {
759
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
760
0
      return GIF_ERROR;
761
0
    }
762
0
    Private->PixelCount =
763
0
        0; /* And local info. indicate image read. */
764
0
  }
765
766
0
  return GIF_OK;
767
0
}
768
769
/******************************************************************************
770
 This routine should be called last, to close the GIF file.
771
******************************************************************************/
772
0
int EGifCloseFile(GifFileType *GifFile, int *ErrorCode) {
773
0
  GifByteType Buf;
774
0
  GifFilePrivateType *Private;
775
0
  FILE *File;
776
777
0
  if (GifFile == NULL) {
778
0
    return GIF_ERROR;
779
0
  }
780
781
0
  Private = (GifFilePrivateType *)GifFile->Private;
782
0
  if (Private == NULL) {
783
0
    return GIF_ERROR;
784
0
  } else if (!IS_WRITEABLE(Private)) {
785
    /* This file was NOT open for writing: */
786
0
    if (ErrorCode != NULL) {
787
0
      *ErrorCode = E_GIF_ERR_NOT_WRITEABLE;
788
0
    }
789
0
    free(GifFile);
790
0
    return GIF_ERROR;
791
0
  } else {
792
0
    File = Private->File;
793
794
0
    Buf = TERMINATOR_INTRODUCER;
795
0
    InternalWrite(GifFile, &Buf, 1);
796
797
0
    if (GifFile->Image.ColorMap) {
798
0
      GifFreeMapObject(GifFile->Image.ColorMap);
799
0
      GifFile->Image.ColorMap = NULL;
800
0
    }
801
0
    if (GifFile->SColorMap) {
802
0
      GifFreeMapObject(GifFile->SColorMap);
803
0
      GifFile->SColorMap = NULL;
804
0
    }
805
0
    if (Private->HashTable) {
806
0
      free((char *)Private->HashTable);
807
0
    }
808
0
    free((char *)Private);
809
810
0
    if (File && fclose(File) != 0) {
811
0
      if (ErrorCode != NULL) {
812
0
        *ErrorCode = E_GIF_ERR_CLOSE_FAILED;
813
0
      }
814
0
      free(GifFile);
815
0
      return GIF_ERROR;
816
0
    }
817
818
0
    free(GifFile);
819
0
    if (ErrorCode != NULL) {
820
0
      *ErrorCode = E_GIF_SUCCEEDED;
821
0
    }
822
0
  }
823
0
  return GIF_OK;
824
0
}
825
826
/******************************************************************************
827
 Put 2 bytes (a word) into the given file in little-endian order:
828
******************************************************************************/
829
0
static int EGifPutWord(int Word, GifFileType *GifFile) {
830
0
  unsigned char c[2];
831
832
0
  c[0] = LOBYTE(Word);
833
0
  c[1] = HIBYTE(Word);
834
0
  if (InternalWrite(GifFile, c, 2) == 2) {
835
0
    return GIF_OK;
836
0
  } else {
837
0
    return GIF_ERROR;
838
0
  }
839
0
}
840
841
/******************************************************************************
842
 Setup the LZ compression for this image:
843
******************************************************************************/
844
0
static int EGifSetupCompress(GifFileType *GifFile) {
845
0
  int BitsPerPixel;
846
0
  GifByteType Buf;
847
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
848
849
  /* Test and see what color map to use, and from it # bits per pixel: */
850
0
  if (GifFile->Image.ColorMap) {
851
0
    BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel;
852
0
  } else if (GifFile->SColorMap) {
853
0
    BitsPerPixel = GifFile->SColorMap->BitsPerPixel;
854
0
  } else {
855
0
    GifFile->Error = E_GIF_ERR_NO_COLOR_MAP;
856
0
    return GIF_ERROR;
857
0
  }
858
859
0
  Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel);
860
0
  InternalWrite(GifFile, &Buf, 1); /* Write the Code size to file. */
861
862
0
  Private->Buf[0] = 0; /* Nothing was output yet. */
863
0
  Private->BitsPerPixel = BitsPerPixel;
864
0
  Private->ClearCode = (1 << BitsPerPixel);
865
0
  Private->EOFCode = Private->ClearCode + 1;
866
0
  Private->RunningCode = Private->EOFCode + 1;
867
0
  Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */
868
0
  Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */
869
0
  Private->CrntCode = FIRST_CODE; /* Signal that this is first one! */
870
0
  Private->CrntShiftState = 0;    /* No information in CrntShiftDWord. */
871
0
  Private->CrntShiftDWord = 0;
872
873
  /* Clear hash table and send Clear to make sure the decoder do the same.
874
   */
875
0
  _ClearHashTable(Private->HashTable);
876
877
0
  if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) {
878
0
    GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
879
0
    return GIF_ERROR;
880
0
  }
881
0
  return GIF_OK;
882
0
}
883
884
/******************************************************************************
885
 The LZ compression routine:
886
 This version compresses the given buffer Line of length LineLen.
887
 This routine can be called a few times (one per scan line, for example), in
888
 order to complete the whole image.
889
******************************************************************************/
890
static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line,
891
0
                            const int LineLen) {
892
0
  int i = 0, CrntCode;
893
0
  GifHashTableType *HashTable;
894
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
895
896
0
  HashTable = Private->HashTable;
897
898
0
  if (Private->CrntCode == FIRST_CODE) { /* Its first time! */
899
0
    CrntCode = Line[i++];
900
0
  } else {
901
0
    CrntCode =
902
0
        Private->CrntCode; /* Get last code in compression. */
903
0
  }
904
0
  while (i < LineLen) { /* Decode LineLen items. */
905
0
    GifPixelType Pixel =
906
0
        Line[i++]; /* Get next pixel from stream. */
907
    /* Form a new unique key to search hash table for the code
908
     * combines CrntCode as Prefix string with Pixel as postfix
909
     * char.
910
     */
911
0
    int NewCode;
912
0
    unsigned long NewKey = (((uint32_t)CrntCode) << 8) + Pixel;
913
0
    if ((NewCode = _ExistsHashTable(HashTable, NewKey)) >= 0) {
914
      /* This Key is already there, or the string is old one,
915
       * so simple take new code as our CrntCode:
916
       */
917
0
      CrntCode = NewCode;
918
0
    } else {
919
      /* Put it in hash table, output the prefix code, and
920
       * make our CrntCode equal to Pixel.
921
       */
922
0
      if (EGifCompressOutput(GifFile, CrntCode) ==
923
0
          GIF_ERROR) {
924
0
        GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
925
0
        return GIF_ERROR;
926
0
      }
927
0
      CrntCode = Pixel;
928
929
      /* If however the HashTable if full, we send a clear
930
       * first and Clear the hash table.
931
       */
932
0
      if (Private->RunningCode >= LZ_MAX_CODE) {
933
        /* Time to do some clearance: */
934
0
        if (EGifCompressOutput(GifFile,
935
0
                               Private->ClearCode) ==
936
0
            GIF_ERROR) {
937
0
          GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
938
0
          return GIF_ERROR;
939
0
        }
940
0
        Private->RunningCode = Private->EOFCode + 1;
941
0
        Private->RunningBits =
942
0
            Private->BitsPerPixel + 1;
943
0
        Private->MaxCode1 = 1 << Private->RunningBits;
944
0
        _ClearHashTable(HashTable);
945
0
      } else {
946
        /* Put this unique key with its relative Code in
947
         * hash table: */
948
0
        _InsertHashTable(HashTable, NewKey,
949
0
                         Private->RunningCode++);
950
0
      }
951
0
    }
952
0
  }
953
954
  /* Preserve the current state of the compression algorithm: */
955
0
  Private->CrntCode = CrntCode;
956
957
0
  if (Private->PixelCount == 0) {
958
    /* We are done - output last Code and flush output buffers: */
959
0
    if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) {
960
0
      GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
961
0
      return GIF_ERROR;
962
0
    }
963
0
    if (EGifCompressOutput(GifFile, Private->EOFCode) ==
964
0
        GIF_ERROR) {
965
0
      GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
966
0
      return GIF_ERROR;
967
0
    }
968
0
    if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) {
969
0
      GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
970
0
      return GIF_ERROR;
971
0
    }
972
0
  }
973
974
0
  return GIF_OK;
975
0
}
976
977
/******************************************************************************
978
 The LZ compression output routine:
979
 This routine is responsible for the compression of the bit stream into
980
 8 bits (bytes) packets.
981
 Returns GIF_OK if written successfully.
982
******************************************************************************/
983
0
static int EGifCompressOutput(GifFileType *GifFile, const int Code) {
984
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
985
0
  int retval = GIF_OK;
986
987
0
  if (Code == FLUSH_OUTPUT) {
988
0
    while (Private->CrntShiftState > 0) {
989
      /* Get Rid of what is left in DWord, and flush it. */
990
0
      if (EGifBufferedOutput(GifFile, Private->Buf,
991
0
                             Private->CrntShiftDWord &
992
0
                                 0xff) == GIF_ERROR) {
993
0
        retval = GIF_ERROR;
994
0
      }
995
0
      Private->CrntShiftDWord >>= 8;
996
0
      Private->CrntShiftState -= 8;
997
0
    }
998
0
    Private->CrntShiftState = 0; /* For next time. */
999
0
    if (EGifBufferedOutput(GifFile, Private->Buf, FLUSH_OUTPUT) ==
1000
0
        GIF_ERROR) {
1001
0
      retval = GIF_ERROR;
1002
0
    }
1003
0
  } else {
1004
0
    Private->CrntShiftDWord |= ((long)Code)
1005
0
                               << Private->CrntShiftState;
1006
0
    Private->CrntShiftState += Private->RunningBits;
1007
0
    while (Private->CrntShiftState >= 8) {
1008
      /* Dump out full bytes: */
1009
0
      if (EGifBufferedOutput(GifFile, Private->Buf,
1010
0
                             Private->CrntShiftDWord &
1011
0
                                 0xff) == GIF_ERROR) {
1012
0
        retval = GIF_ERROR;
1013
0
      }
1014
0
      Private->CrntShiftDWord >>= 8;
1015
0
      Private->CrntShiftState -= 8;
1016
0
    }
1017
0
  }
1018
1019
  /* If code cannot fit into RunningBits bits, must raise its size. Note */
1020
  /* however that codes above 4095 are used for special signaling.      */
1021
0
  if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) {
1022
0
    Private->MaxCode1 = 1 << ++Private->RunningBits;
1023
0
  }
1024
1025
0
  return retval;
1026
0
}
1027
1028
/******************************************************************************
1029
 This routines buffers the given characters until 255 characters are ready
1030
 to be output. If Code is equal to -1 the buffer is flushed (EOF).
1031
 The buffer is Dumped with first byte as its size, as GIF format requires.
1032
 Returns GIF_OK if written successfully.
1033
******************************************************************************/
1034
0
static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c) {
1035
0
  if (c == FLUSH_OUTPUT) {
1036
    /* Flush everything out. */
1037
0
    if (Buf[0] != 0 && InternalWrite(GifFile, Buf, Buf[0] + 1) !=
1038
0
                           (unsigned)(Buf[0] + 1)) {
1039
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1040
0
      return GIF_ERROR;
1041
0
    }
1042
    /* Mark end of compressed data, by an empty block (see GIF doc):
1043
     */
1044
0
    Buf[0] = 0;
1045
0
    if (InternalWrite(GifFile, Buf, 1) != 1) {
1046
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1047
0
      return GIF_ERROR;
1048
0
    }
1049
0
  } else {
1050
0
    if (Buf[0] == 255) {
1051
      /* Dump out this buffer - it is full: */
1052
0
      if (InternalWrite(GifFile, Buf, Buf[0] + 1) !=
1053
0
          (unsigned)(Buf[0] + 1)) {
1054
0
        GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1055
0
        return GIF_ERROR;
1056
0
      }
1057
0
      Buf[0] = 0;
1058
0
    }
1059
0
    Buf[++Buf[0]] = c;
1060
0
  }
1061
1062
0
  return GIF_OK;
1063
0
}
1064
1065
/******************************************************************************
1066
 This routine writes to disk an in-core representation of a GIF previously
1067
 created by DGifSlurp().
1068
******************************************************************************/
1069
1070
static int EGifWriteExtensions(GifFileType *GifFileOut,
1071
                               ExtensionBlock *ExtensionBlocks,
1072
0
                               int ExtensionBlockCount) {
1073
0
  if (ExtensionBlocks) {
1074
0
    int j;
1075
1076
0
    for (j = 0; j < ExtensionBlockCount; j++) {
1077
0
      ExtensionBlock *ep = &ExtensionBlocks[j];
1078
0
      if (ep->Function != CONTINUE_EXT_FUNC_CODE) {
1079
0
        if (EGifPutExtensionLeader(GifFileOut,
1080
0
                                   ep->Function) ==
1081
0
            GIF_ERROR) {
1082
0
          return (GIF_ERROR);
1083
0
        }
1084
0
      }
1085
0
      if (EGifPutExtensionBlock(GifFileOut, ep->ByteCount,
1086
0
                                ep->Bytes) == GIF_ERROR) {
1087
0
        return (GIF_ERROR);
1088
0
      }
1089
0
      if (j == ExtensionBlockCount - 1 ||
1090
0
          (ep + 1)->Function != CONTINUE_EXT_FUNC_CODE) {
1091
0
        if (EGifPutExtensionTrailer(GifFileOut) ==
1092
0
            GIF_ERROR) {
1093
0
          return (GIF_ERROR);
1094
0
        }
1095
0
      }
1096
0
    }
1097
0
  }
1098
1099
0
  return (GIF_OK);
1100
0
}
1101
1102
0
int EGifSpew(GifFileType *GifFileOut) {
1103
0
  int i, j;
1104
1105
0
  if (EGifPutScreenDesc(GifFileOut, GifFileOut->SWidth,
1106
0
                        GifFileOut->SHeight, GifFileOut->SColorResolution,
1107
0
                        GifFileOut->SBackGroundColor,
1108
0
                        GifFileOut->SColorMap) == GIF_ERROR) {
1109
0
    return (GIF_ERROR);
1110
0
  }
1111
1112
0
  for (i = 0; i < GifFileOut->ImageCount; i++) {
1113
0
    SavedImage *sp = &GifFileOut->SavedImages[i];
1114
0
    int SavedHeight = sp->ImageDesc.Height;
1115
0
    int SavedWidth = sp->ImageDesc.Width;
1116
1117
    /* this allows us to delete images by nuking their rasters */
1118
0
    if (sp->RasterBits == NULL) {
1119
0
      continue;
1120
0
    }
1121
1122
0
    if (EGifWriteExtensions(GifFileOut, sp->ExtensionBlocks,
1123
0
                            sp->ExtensionBlockCount) == GIF_ERROR) {
1124
0
      return (GIF_ERROR);
1125
0
    }
1126
1127
0
    if (EGifPutImageDesc(GifFileOut, sp->ImageDesc.Left,
1128
0
                         sp->ImageDesc.Top, SavedWidth, SavedHeight,
1129
0
                         sp->ImageDesc.Interlace,
1130
0
                         sp->ImageDesc.ColorMap) == GIF_ERROR) {
1131
0
      return (GIF_ERROR);
1132
0
    }
1133
1134
0
    if (sp->ImageDesc.Interlace) {
1135
      /*
1136
       * The way an interlaced image should be written -
1137
       * offsets and jumps...
1138
       */
1139
0
      static const int InterlacedOffset[] = {0, 4, 2, 1};
1140
0
      static const int InterlacedJumps[] = {8, 8, 4, 2};
1141
0
      int k;
1142
      /* Need to perform 4 passes on the images: */
1143
0
      for (k = 0; k < 4; k++) {
1144
0
        for (j = InterlacedOffset[k]; j < SavedHeight;
1145
0
             j += InterlacedJumps[k]) {
1146
0
          if (EGifPutLine(
1147
0
                  GifFileOut,
1148
0
                  sp->RasterBits + j * SavedWidth,
1149
0
                  SavedWidth) == GIF_ERROR) {
1150
0
            return (GIF_ERROR);
1151
0
          }
1152
0
        }
1153
0
      }
1154
0
    } else {
1155
0
      for (j = 0; j < SavedHeight; j++) {
1156
0
        if (EGifPutLine(GifFileOut,
1157
0
                        sp->RasterBits + j * SavedWidth,
1158
0
                        SavedWidth) == GIF_ERROR) {
1159
0
          return (GIF_ERROR);
1160
0
        }
1161
0
      }
1162
0
    }
1163
0
  }
1164
1165
0
  if (EGifWriteExtensions(GifFileOut, GifFileOut->ExtensionBlocks,
1166
0
                          GifFileOut->ExtensionBlockCount) == GIF_ERROR) {
1167
0
    return (GIF_ERROR);
1168
0
  }
1169
1170
0
  if (EGifCloseFile(GifFileOut, NULL) == GIF_ERROR) {
1171
0
    return (GIF_ERROR);
1172
0
  }
1173
1174
0
  return (GIF_OK);
1175
0
}
1176
1177
/* end */