Coverage Report

Created: 2026-06-15 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/giflib-code/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
2.25k
#define LOBYTE(x) ((x)&0xff)
45
2.25k
#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
375
GifFileType *EGifOpen(void *userData, OutputFunc writeFunc, int *Error) {
149
375
  GifFileType *GifFile;
150
375
  GifFilePrivateType *Private;
151
152
375
  GifFile = (GifFileType *)malloc(sizeof(GifFileType));
153
375
  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
375
  memset(GifFile, '\0', sizeof(GifFileType));
161
162
375
  Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
163
375
  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
375
  memset(Private, '\0', sizeof(GifFilePrivateType));
172
173
375
  Private->HashTable = _InitHashTable();
174
375
  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
375
  GifFile->Private = (void *)Private;
184
375
  Private->FileHandle = 0;
185
375
  Private->File = (FILE *)0;
186
375
  Private->FileState = FILE_STATE_WRITE;
187
188
375
  Private->Write = writeFunc;   /* User write routine (MRB) */
189
375
  GifFile->UserData = userData; /* User write handle (MRB) */
190
191
375
  Private->gif89 = false; /* initially, write GIF87 */
192
193
375
  GifFile->Error = 0;
194
195
375
  return GifFile;
196
375
}
197
198
/******************************************************************************
199
 Routine to compute the GIF version that will be written on output.
200
******************************************************************************/
201
375
const char *EGifGetGifVersion(GifFileType *GifFile) {
202
375
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
203
375
  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
375
  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
375
  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
375
  if (Private->gif89) {
237
0
    return GIF89_STAMP;
238
375
  } else {
239
375
    return GIF87_STAMP;
240
375
  }
241
375
}
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
375
void EGifSetGifVersion(GifFileType *GifFile, const bool gif89) {
251
375
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
252
253
375
  Private->gif89 = gif89;
254
375
}
255
256
/******************************************************************************
257
 All writes to the GIF should go through this.
258
******************************************************************************/
259
static int InternalWrite(GifFileType *GifFileOut, const unsigned char *buf,
260
124k
                         size_t len) {
261
124k
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFileOut->Private;
262
124k
  if (Private->Write) {
263
124k
    return Private->Write(GifFileOut, buf, len);
264
124k
  } else {
265
0
    return fwrite(buf, 1, len, Private->File);
266
0
  }
267
124k
}
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
375
                      const ColorMapObject *ColorMap) {
276
375
  GifByteType Buf[3];
277
375
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
278
375
  const char *write_version;
279
375
  ColorMapObject *old_map = GifFile->SColorMap;
280
281
375
  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
375
  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
375
  write_version = EGifGetGifVersion(GifFile);
293
294
  /* First write the version prefix into the file. */
295
375
  if (InternalWrite(GifFile, (unsigned char *)write_version,
296
375
                    strlen(write_version)) != strlen(write_version)) {
297
0
    GifFile->Error = E_GIF_ERR_WRITE_FAILED;
298
0
    return GIF_ERROR;
299
0
  }
300
301
375
  GifFile->SWidth = Width;
302
375
  GifFile->SHeight = Height;
303
375
  GifFile->SColorResolution = ColorRes;
304
375
  GifFile->SBackGroundColor = BackGround;
305
375
  if (ColorMap == NULL) {
306
0
    if (old_map != NULL) {
307
0
      GifFreeMapObject(old_map);
308
0
    }
309
0
    GifFile->SColorMap = NULL;
310
375
  } else if (ColorMap == old_map) {
311
    /* Reuse existing map to avoid a redundant copy. */
312
0
    GifFile->SColorMap = old_map;
313
375
  } else {
314
375
    if (old_map != NULL) {
315
0
      GifFreeMapObject(old_map);
316
0
    }
317
375
    GifFile->SColorMap =
318
375
        GifMakeMapObject(ColorMap->ColorCount, ColorMap->Colors);
319
375
    if (GifFile->SColorMap == NULL) {
320
0
      GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM;
321
0
      return GIF_ERROR;
322
0
    }
323
375
  }
324
325
  /*
326
   * Put the logical screen descriptor into the file:
327
   */
328
  /* Logical Screen Descriptor: Dimensions */
329
375
  (void)EGifPutWord(Width, GifFile);
330
375
  (void)EGifPutWord(Height, GifFile);
331
332
  /* Logical Screen Descriptor: Packed Fields */
333
  /* Note: We have actual size of the color table default to the largest
334
   * possible size (7+1 == 8 bits) because the decoder can use it to
335
   * decide how to display the files.
336
   */
337
375
  Buf[0] =
338
375
      (ColorMap ? 0x80 : 0x00) | /* Yes/no global colormap */
339
375
      ((ColorRes - 1) << 4) | /* Bits allocated to each primary color */
340
375
      (ColorMap ? ColorMap->BitsPerPixel - 1
341
375
                : 0x07); /* Actual size of the
342
                            color table. */
343
375
  if (ColorMap != NULL && ColorMap->SortFlag) {
344
0
    Buf[0] |= 0x08;
345
0
  }
346
375
  Buf[1] =
347
375
      BackGround; /* Index into the ColorTable for background color */
348
375
  Buf[2] = GifFile->AspectByte; /* Pixel Aspect Ratio */
349
375
  InternalWrite(GifFile, Buf, 3);
350
351
  /* If we have Global color map - dump it also: */
352
375
  if (ColorMap != NULL) {
353
375
    int i;
354
96.3k
    for (i = 0; i < ColorMap->ColorCount; i++) {
355
      /* Put the ColorMap out also: */
356
96.0k
      Buf[0] = ColorMap->Colors[i].Red;
357
96.0k
      Buf[1] = ColorMap->Colors[i].Green;
358
96.0k
      Buf[2] = ColorMap->Colors[i].Blue;
359
96.0k
      if (InternalWrite(GifFile, Buf, 3) != 3) {
360
0
        GifFile->Error = E_GIF_ERR_WRITE_FAILED;
361
0
        return GIF_ERROR;
362
0
      }
363
96.0k
    }
364
375
  }
365
366
  /* Mark this file as has screen descriptor, and no pixel written yet: */
367
375
  Private->FileState |= FILE_STATE_SCREEN;
368
369
375
  return GIF_OK;
370
375
}
371
372
/******************************************************************************
373
 This routine should be called before any attempt to dump an image - any
374
 call to any of the pixel dump routines.
375
******************************************************************************/
376
int EGifPutImageDesc(GifFileType *GifFile, const int Left, const int Top,
377
                     const int Width, const int Height, const bool Interlace,
378
375
                     const ColorMapObject *ColorMap) {
379
375
  GifByteType Buf[3];
380
375
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
381
382
375
  if (Private->FileState & FILE_STATE_IMAGE &&
383
0
      Private->PixelCount > 0xffff0000UL) {
384
    /* If already has active image descriptor - something is wrong!
385
     */
386
0
    GifFile->Error = E_GIF_ERR_HAS_IMAG_DSCR;
387
0
    return GIF_ERROR;
388
0
  }
389
375
  if (!IS_WRITEABLE(Private)) {
390
    /* This file was NOT open for writing: */
391
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
392
0
    return GIF_ERROR;
393
0
  }
394
375
  GifFile->Image.Left = Left;
395
375
  GifFile->Image.Top = Top;
396
375
  GifFile->Image.Width = Width;
397
375
  GifFile->Image.Height = Height;
398
375
  GifFile->Image.Interlace = Interlace;
399
375
  if (ColorMap != GifFile->Image.ColorMap) {
400
0
    if (ColorMap) {
401
0
      if (GifFile->Image.ColorMap != NULL) {
402
0
        GifFreeMapObject(GifFile->Image.ColorMap);
403
0
        GifFile->Image.ColorMap = NULL;
404
0
      }
405
0
      GifFile->Image.ColorMap = GifMakeMapObject(
406
0
          ColorMap->ColorCount, ColorMap->Colors);
407
0
      if (GifFile->Image.ColorMap == NULL) {
408
0
        GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM;
409
0
        return GIF_ERROR;
410
0
      }
411
0
    } else {
412
0
      if (GifFile->Image.ColorMap != NULL) {
413
0
        GifFreeMapObject(GifFile->Image.ColorMap);
414
0
      }
415
0
      GifFile->Image.ColorMap = NULL;
416
0
    }
417
0
  }
418
419
  /* Put the image descriptor into the file: */
420
375
  Buf[0] = DESCRIPTOR_INTRODUCER; /* Image separator character. */
421
375
  InternalWrite(GifFile, Buf, 1);
422
375
  (void)EGifPutWord(Left, GifFile);
423
375
  (void)EGifPutWord(Top, GifFile);
424
375
  (void)EGifPutWord(Width, GifFile);
425
375
  (void)EGifPutWord(Height, GifFile);
426
375
  Buf[0] = (ColorMap ? 0x80 : 0x00) | (Interlace ? 0x40 : 0x00) |
427
375
           (ColorMap ? ColorMap->BitsPerPixel - 1 : 0);
428
375
  InternalWrite(GifFile, Buf, 1);
429
430
  /* If we have Global color map - dump it also: */
431
375
  if (ColorMap != NULL) {
432
0
    int i;
433
0
    for (i = 0; i < ColorMap->ColorCount; i++) {
434
      /* Put the ColorMap out also: */
435
0
      Buf[0] = ColorMap->Colors[i].Red;
436
0
      Buf[1] = ColorMap->Colors[i].Green;
437
0
      Buf[2] = ColorMap->Colors[i].Blue;
438
0
      if (InternalWrite(GifFile, Buf, 3) != 3) {
439
0
        GifFile->Error = E_GIF_ERR_WRITE_FAILED;
440
0
        return GIF_ERROR;
441
0
      }
442
0
    }
443
0
  }
444
375
  if (GifFile->SColorMap == NULL && GifFile->Image.ColorMap == NULL) {
445
0
    GifFile->Error = E_GIF_ERR_NO_COLOR_MAP;
446
0
    return GIF_ERROR;
447
0
  }
448
449
  /* Mark this file as has screen descriptor: */
450
375
  Private->FileState |= FILE_STATE_IMAGE;
451
375
  Private->PixelCount =
452
375
      (unsigned long)Width * (unsigned long)Height;
453
454
  /* Reset compress algorithm parameters. */
455
375
  (void)EGifSetupCompress(GifFile);
456
457
375
  return GIF_OK;
458
375
}
459
460
/******************************************************************************
461
 Put one full scanned line (Line) of length LineLen into GIF file.
462
******************************************************************************/
463
76.7k
int EGifPutLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) {
464
76.7k
  int i;
465
76.7k
  GifPixelType Mask;
466
76.7k
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
467
468
76.7k
  if (!IS_WRITEABLE(Private)) {
469
    /* This file was NOT open for writing: */
470
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
471
0
    return GIF_ERROR;
472
0
  }
473
474
76.7k
  if (!LineLen) {
475
0
    LineLen = GifFile->Image.Width;
476
0
  }
477
76.7k
  if (Private->PixelCount < (unsigned)LineLen) {
478
0
    GifFile->Error = E_GIF_ERR_DATA_TOO_BIG;
479
0
    return GIF_ERROR;
480
0
  }
481
76.7k
  Private->PixelCount -= LineLen;
482
483
  /* Make sure the codes are not out of bit range, as we might generate
484
   * wrong code (because of overflow when we combine them) in this case:
485
   */
486
76.7k
  Mask = CodeMask[Private->BitsPerPixel];
487
7.75M
  for (i = 0; i < LineLen; i++) {
488
7.67M
    Line[i] &= Mask;
489
7.67M
  }
490
491
76.7k
  return EGifCompressLine(GifFile, Line, LineLen);
492
76.7k
}
493
494
/******************************************************************************
495
 Put one pixel (Pixel) into GIF file.
496
******************************************************************************/
497
0
int EGifPutPixel(GifFileType *GifFile, GifPixelType Pixel) {
498
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
499
500
0
  if (!IS_WRITEABLE(Private)) {
501
    /* This file was NOT open for writing: */
502
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
503
0
    return GIF_ERROR;
504
0
  }
505
506
0
  if (Private->PixelCount == 0) {
507
0
    GifFile->Error = E_GIF_ERR_DATA_TOO_BIG;
508
0
    return GIF_ERROR;
509
0
  }
510
0
  --Private->PixelCount;
511
512
  /* Make sure the code is not out of bit range, as we might generate
513
   * wrong code (because of overflow when we combine them) in this case:
514
   */
515
0
  Pixel &= CodeMask[Private->BitsPerPixel];
516
517
0
  return EGifCompressLine(GifFile, &Pixel, 1);
518
0
}
519
520
/******************************************************************************
521
 Put a comment into GIF file using the GIF89 comment extension block.
522
******************************************************************************/
523
0
int EGifPutComment(GifFileType *GifFile, const char *Comment) {
524
0
  unsigned int length;
525
0
  char *buf;
526
527
0
  length = strlen(Comment);
528
0
  if (length <= 255) {
529
0
    return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, length,
530
0
                            Comment);
531
0
  } else {
532
0
    buf = (char *)Comment;
533
0
    if (EGifPutExtensionLeader(GifFile, COMMENT_EXT_FUNC_CODE) ==
534
0
        GIF_ERROR) {
535
0
      return GIF_ERROR;
536
0
    }
537
538
    /* Break the comment into 255 byte sub blocks */
539
0
    while (length > 255) {
540
0
      if (EGifPutExtensionBlock(GifFile, 255, buf) ==
541
0
          GIF_ERROR) {
542
0
        return GIF_ERROR;
543
0
      }
544
0
      buf = buf + 255;
545
0
      length -= 255;
546
0
    }
547
    /* Output any partial block and the clear code. */
548
0
    if (length > 0) {
549
0
      if (EGifPutExtensionBlock(GifFile, length, buf) ==
550
0
          GIF_ERROR) {
551
0
        return GIF_ERROR;
552
0
      }
553
0
    }
554
0
    if (EGifPutExtensionTrailer(GifFile) == GIF_ERROR) {
555
0
      return GIF_ERROR;
556
0
    }
557
0
  }
558
0
  return GIF_OK;
559
0
}
560
561
/******************************************************************************
562
 Begin an extension block (see GIF manual).  More
563
 extensions can be dumped using EGifPutExtensionBlock until
564
 EGifPutExtensionTrailer is invoked.
565
******************************************************************************/
566
0
int EGifPutExtensionLeader(GifFileType *GifFile, const int ExtCode) {
567
0
  GifByteType Buf[3];
568
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
569
570
0
  if (!IS_WRITEABLE(Private)) {
571
    /* This file was NOT open for writing: */
572
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
573
0
    return GIF_ERROR;
574
0
  }
575
576
0
  Buf[0] = EXTENSION_INTRODUCER;
577
0
  Buf[1] = ExtCode;
578
0
  InternalWrite(GifFile, Buf, 2);
579
580
0
  return GIF_OK;
581
0
}
582
583
/******************************************************************************
584
 Put extension block data (see GIF manual) into a GIF file.
585
******************************************************************************/
586
int EGifPutExtensionBlock(GifFileType *GifFile, const int ExtLen,
587
0
                          const void *Extension) {
588
0
  GifByteType Buf;
589
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
590
591
0
  if (!IS_WRITEABLE(Private)) {
592
    /* This file was NOT open for writing: */
593
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
594
0
    return GIF_ERROR;
595
0
  }
596
597
0
  Buf = ExtLen;
598
0
  InternalWrite(GifFile, &Buf, 1);
599
0
  InternalWrite(GifFile, Extension, ExtLen);
600
601
0
  return GIF_OK;
602
0
}
603
604
/******************************************************************************
605
 Put a terminating block (see GIF manual) into a GIF file.
606
******************************************************************************/
607
0
int EGifPutExtensionTrailer(GifFileType *GifFile) {
608
609
0
  GifByteType Buf;
610
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
611
612
0
  if (!IS_WRITEABLE(Private)) {
613
    /* This file was NOT open for writing: */
614
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
615
0
    return GIF_ERROR;
616
0
  }
617
618
  /* Write the block terminator */
619
0
  Buf = 0;
620
0
  InternalWrite(GifFile, &Buf, 1);
621
622
0
  return GIF_OK;
623
0
}
624
625
/******************************************************************************
626
 Put an extension block (see GIF manual) into a GIF file.
627
 Warning: This function is only useful for Extension blocks that have at
628
 most one subblock.  Extensions with more than one subblock need to use the
629
 EGifPutExtension{Leader,Block,Trailer} functions instead.
630
******************************************************************************/
631
int EGifPutExtension(GifFileType *GifFile, const int ExtCode, const int ExtLen,
632
0
                     const void *Extension) {
633
634
0
  GifByteType Buf[3];
635
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
636
637
0
  if (!IS_WRITEABLE(Private)) {
638
    /* This file was NOT open for writing: */
639
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
640
0
    return GIF_ERROR;
641
0
  }
642
643
0
  if (ExtCode == 0) {
644
0
    InternalWrite(GifFile, (GifByteType *)&ExtLen, 1);
645
0
  } else {
646
0
    Buf[0] = EXTENSION_INTRODUCER;
647
0
    Buf[1] = ExtCode; /* Extension Label */
648
0
    Buf[2] = ExtLen;  /* Extension length */
649
0
    InternalWrite(GifFile, Buf, 3);
650
0
  }
651
0
  InternalWrite(GifFile, Extension, ExtLen);
652
0
  Buf[0] = 0;
653
0
  InternalWrite(GifFile, Buf, 1);
654
655
0
  return GIF_OK;
656
0
}
657
658
/******************************************************************************
659
 Render a Graphics Control Block as raw extension data
660
******************************************************************************/
661
662
size_t EGifGCBToExtension(const GraphicsControlBlock *GCB,
663
0
                          GifByteType *GifExtension) {
664
0
  GifExtension[0] = 0;
665
0
  GifExtension[0] |=
666
0
      (GCB->TransparentColor == NO_TRANSPARENT_COLOR) ? 0x00 : 0x01;
667
0
  GifExtension[0] |= GCB->UserInputFlag ? 0x02 : 0x00;
668
0
  GifExtension[0] |= ((GCB->DisposalMode & 0x07) << 2);
669
0
  GifExtension[1] = LOBYTE(GCB->DelayTime);
670
0
  GifExtension[2] = HIBYTE(GCB->DelayTime);
671
0
  GifExtension[3] = (char)GCB->TransparentColor;
672
0
  return 4;
673
0
}
674
675
/******************************************************************************
676
 Replace the Graphics Control Block for a saved image, if it exists.
677
******************************************************************************/
678
679
int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB,
680
0
                            GifFileType *GifFile, int ImageIndex) {
681
0
  int i;
682
0
  size_t Len;
683
0
  GifByteType buf[sizeof(GraphicsControlBlock)]; /* a bit dodgy... */
684
685
0
  if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) {
686
0
    return GIF_ERROR;
687
0
  }
688
689
0
  for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount;
690
0
       i++) {
691
0
    ExtensionBlock *ep =
692
0
        &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
693
0
    if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
694
0
      if (ep->ByteCount < 4) {
695
0
        return GIF_ERROR;
696
0
      }
697
0
      EGifGCBToExtension(GCB, ep->Bytes);
698
0
      return GIF_OK;
699
0
    }
700
0
  }
701
702
0
  Len = EGifGCBToExtension(GCB, (GifByteType *)buf);
703
0
  if (GifAddExtensionBlock(
704
0
          &GifFile->SavedImages[ImageIndex].ExtensionBlockCount,
705
0
          &GifFile->SavedImages[ImageIndex].ExtensionBlocks,
706
0
          GRAPHICS_EXT_FUNC_CODE, Len,
707
0
          (unsigned char *)buf) == GIF_ERROR) {
708
0
    return (GIF_ERROR);
709
0
  }
710
711
0
  return (GIF_OK);
712
0
}
713
714
/******************************************************************************
715
 Put the image code in compressed form. This routine can be called if the
716
 information needed to be piped out as is. Obviously this is much faster
717
 than decoding and encoding again. This routine should be followed by calls
718
 to EGifPutCodeNext, until NULL block is given.
719
 The block should NOT be freed by the user (not dynamically allocated).
720
******************************************************************************/
721
int EGifPutCode(GifFileType *GifFile, int CodeSize,
722
0
                const GifByteType *CodeBlock) {
723
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
724
725
0
  if (!IS_WRITEABLE(Private)) {
726
    /* This file was NOT open for writing: */
727
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
728
0
    return GIF_ERROR;
729
0
  }
730
731
  /* No need to dump code size as Compression set up does any for us: */
732
  /*
733
   * Buf = CodeSize;
734
   * if (InternalWrite(GifFile, &Buf, 1) != 1) {
735
   *      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
736
   *      return GIF_ERROR;
737
   * }
738
   */
739
740
0
  return EGifPutCodeNext(GifFile, CodeBlock);
741
0
}
742
743
/******************************************************************************
744
 Continue to put the image code in compressed form. This routine should be
745
 called with blocks of code as read via DGifGetCode/DGifGetCodeNext. If
746
 given buffer pointer is NULL, empty block is written to mark end of code.
747
******************************************************************************/
748
0
int EGifPutCodeNext(GifFileType *GifFile, const GifByteType *CodeBlock) {
749
0
  GifByteType Buf;
750
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
751
752
0
  if (CodeBlock != NULL) {
753
0
    if (InternalWrite(GifFile, CodeBlock, CodeBlock[0] + 1) !=
754
0
        (unsigned)(CodeBlock[0] + 1)) {
755
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
756
0
      return GIF_ERROR;
757
0
    }
758
0
  } else {
759
0
    Buf = 0;
760
0
    if (InternalWrite(GifFile, &Buf, 1) != 1) {
761
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
762
0
      return GIF_ERROR;
763
0
    }
764
0
    Private->PixelCount =
765
0
        0; /* And local info. indicate image read. */
766
0
  }
767
768
0
  return GIF_OK;
769
0
}
770
771
/******************************************************************************
772
 This routine should be called last, to close the GIF file.
773
******************************************************************************/
774
375
int EGifCloseFile(GifFileType *GifFile, int *ErrorCode) {
775
375
  GifByteType Buf;
776
375
  GifFilePrivateType *Private;
777
375
  FILE *File;
778
779
375
  if (GifFile == NULL) {
780
0
    return GIF_ERROR;
781
0
  }
782
783
375
  Private = (GifFilePrivateType *)GifFile->Private;
784
375
  if (Private == NULL) {
785
0
    return GIF_ERROR;
786
375
  } else if (!IS_WRITEABLE(Private)) {
787
    /* This file was NOT open for writing: */
788
0
    if (ErrorCode != NULL) {
789
0
      *ErrorCode = E_GIF_ERR_NOT_WRITEABLE;
790
0
    }
791
0
    free(GifFile);
792
0
    return GIF_ERROR;
793
375
  } else {
794
375
    File = Private->File;
795
796
375
    Buf = TERMINATOR_INTRODUCER;
797
375
    InternalWrite(GifFile, &Buf, 1);
798
799
375
    if (GifFile->Image.ColorMap) {
800
0
      GifFreeMapObject(GifFile->Image.ColorMap);
801
0
      GifFile->Image.ColorMap = NULL;
802
0
    }
803
375
    if (GifFile->SColorMap) {
804
375
      GifFreeMapObject(GifFile->SColorMap);
805
375
      GifFile->SColorMap = NULL;
806
375
    }
807
375
    if (Private->HashTable) {
808
375
      free((char *)Private->HashTable);
809
375
    }
810
375
    free((char *)Private);
811
812
375
    if (File && fclose(File) != 0) {
813
0
      if (ErrorCode != NULL) {
814
0
        *ErrorCode = E_GIF_ERR_CLOSE_FAILED;
815
0
      }
816
0
      free(GifFile);
817
0
      return GIF_ERROR;
818
0
    }
819
820
375
    free(GifFile);
821
375
    if (ErrorCode != NULL) {
822
375
      *ErrorCode = E_GIF_SUCCEEDED;
823
375
    }
824
375
  }
825
375
  return GIF_OK;
826
375
}
827
828
/******************************************************************************
829
 Put 2 bytes (a word) into the given file in little-endian order:
830
******************************************************************************/
831
2.25k
static int EGifPutWord(int Word, GifFileType *GifFile) {
832
2.25k
  unsigned char c[2];
833
834
2.25k
  c[0] = LOBYTE(Word);
835
2.25k
  c[1] = HIBYTE(Word);
836
2.25k
  if (InternalWrite(GifFile, c, 2) == 2) {
837
2.25k
    return GIF_OK;
838
2.25k
  } else {
839
0
    return GIF_ERROR;
840
0
  }
841
2.25k
}
842
843
/******************************************************************************
844
 Setup the LZ compression for this image:
845
******************************************************************************/
846
375
static int EGifSetupCompress(GifFileType *GifFile) {
847
375
  int BitsPerPixel;
848
375
  GifByteType Buf;
849
375
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
850
851
  /* Test and see what color map to use, and from it # bits per pixel: */
852
375
  if (GifFile->Image.ColorMap) {
853
0
    BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel;
854
375
  } else if (GifFile->SColorMap) {
855
375
    BitsPerPixel = GifFile->SColorMap->BitsPerPixel;
856
375
  } else {
857
0
    GifFile->Error = E_GIF_ERR_NO_COLOR_MAP;
858
0
    return GIF_ERROR;
859
0
  }
860
861
375
  Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel);
862
375
  InternalWrite(GifFile, &Buf, 1); /* Write the Code size to file. */
863
864
375
  Private->Buf[0] = 0; /* Nothing was output yet. */
865
375
  Private->BitsPerPixel = BitsPerPixel;
866
375
  Private->ClearCode = (1 << BitsPerPixel);
867
375
  Private->EOFCode = Private->ClearCode + 1;
868
375
  Private->RunningCode = Private->EOFCode + 1;
869
375
  Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */
870
375
  Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */
871
375
  Private->CrntCode = FIRST_CODE; /* Signal that this is first one! */
872
375
  Private->CrntShiftState = 0;    /* No information in CrntShiftDWord. */
873
375
  Private->CrntShiftDWord = 0;
874
875
  /* Clear hash table and send Clear to make sure the decoder do the same.
876
   */
877
375
  _ClearHashTable(Private->HashTable);
878
879
375
  if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) {
880
0
    GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
881
0
    return GIF_ERROR;
882
0
  }
883
375
  return GIF_OK;
884
375
}
885
886
/******************************************************************************
887
 The LZ compression routine:
888
 This version compresses the given buffer Line of length LineLen.
889
 This routine can be called a few times (one per scan line, for example), in
890
 order to complete the whole image.
891
******************************************************************************/
892
static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line,
893
76.7k
                            const int LineLen) {
894
76.7k
  int i = 0, CrntCode;
895
76.7k
  GifHashTableType *HashTable;
896
76.7k
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
897
898
76.7k
  HashTable = Private->HashTable;
899
900
76.7k
  if (Private->CrntCode == FIRST_CODE) { /* Its first time! */
901
375
    CrntCode = Line[i++];
902
76.3k
  } else {
903
76.3k
    CrntCode =
904
76.3k
        Private->CrntCode; /* Get last code in compression. */
905
76.3k
  }
906
7.74M
  while (i < LineLen) { /* Decode LineLen items. */
907
7.67M
    GifPixelType Pixel =
908
7.67M
        Line[i++]; /* Get next pixel from stream. */
909
    /* Form a new unique key to search hash table for the code
910
     * combines CrntCode as Prefix string with Pixel as postfix
911
     * char.
912
     */
913
7.67M
    int NewCode;
914
7.67M
    unsigned long NewKey = (((uint32_t)CrntCode) << 8) + Pixel;
915
7.67M
    if ((NewCode = _ExistsHashTable(HashTable, NewKey)) >= 0) {
916
      /* This Key is already there, or the string is old one,
917
       * so simple take new code as our CrntCode:
918
       */
919
3.38M
      CrntCode = NewCode;
920
4.29M
    } else {
921
      /* Put it in hash table, output the prefix code, and
922
       * make our CrntCode equal to Pixel.
923
       */
924
4.29M
      if (EGifCompressOutput(GifFile, CrntCode) ==
925
4.29M
          GIF_ERROR) {
926
0
        GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
927
0
        return GIF_ERROR;
928
0
      }
929
4.29M
      CrntCode = Pixel;
930
931
      /* If however the HashTable if full, we send a clear
932
       * first and Clear the hash table.
933
       */
934
4.29M
      if (Private->RunningCode >= LZ_MAX_CODE) {
935
        /* Time to do some clearance: */
936
1.03k
        if (EGifCompressOutput(GifFile,
937
1.03k
                               Private->ClearCode) ==
938
1.03k
            GIF_ERROR) {
939
0
          GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
940
0
          return GIF_ERROR;
941
0
        }
942
1.03k
        Private->RunningCode = Private->EOFCode + 1;
943
1.03k
        Private->RunningBits =
944
1.03k
            Private->BitsPerPixel + 1;
945
1.03k
        Private->MaxCode1 = 1 << Private->RunningBits;
946
1.03k
        _ClearHashTable(HashTable);
947
4.28M
      } else {
948
        /* Put this unique key with its relative Code in
949
         * hash table: */
950
4.28M
        _InsertHashTable(HashTable, NewKey,
951
4.28M
                         Private->RunningCode++);
952
4.28M
      }
953
4.29M
    }
954
7.67M
  }
955
956
  /* Preserve the current state of the compression algorithm: */
957
76.7k
  Private->CrntCode = CrntCode;
958
959
76.7k
  if (Private->PixelCount == 0) {
960
    /* We are done - output last Code and flush output buffers: */
961
375
    if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) {
962
0
      GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
963
0
      return GIF_ERROR;
964
0
    }
965
375
    if (EGifCompressOutput(GifFile, Private->EOFCode) ==
966
375
        GIF_ERROR) {
967
0
      GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
968
0
      return GIF_ERROR;
969
0
    }
970
375
    if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) {
971
0
      GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
972
0
      return GIF_ERROR;
973
0
    }
974
375
  }
975
976
76.7k
  return GIF_OK;
977
76.7k
}
978
979
/******************************************************************************
980
 The LZ compression output routine:
981
 This routine is responsible for the compression of the bit stream into
982
 8 bits (bytes) packets.
983
 Returns GIF_OK if written successfully.
984
******************************************************************************/
985
4.29M
static int EGifCompressOutput(GifFileType *GifFile, const int Code) {
986
4.29M
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
987
4.29M
  int retval = GIF_OK;
988
989
4.29M
  if (Code == FLUSH_OUTPUT) {
990
684
    while (Private->CrntShiftState > 0) {
991
      /* Get Rid of what is left in DWord, and flush it. */
992
309
      if (EGifBufferedOutput(GifFile, Private->Buf,
993
309
                             Private->CrntShiftDWord &
994
309
                                 0xff) == GIF_ERROR) {
995
0
        retval = GIF_ERROR;
996
0
      }
997
309
      Private->CrntShiftDWord >>= 8;
998
309
      Private->CrntShiftState -= 8;
999
309
    }
1000
375
    Private->CrntShiftState = 0; /* For next time. */
1001
375
    if (EGifBufferedOutput(GifFile, Private->Buf, FLUSH_OUTPUT) ==
1002
375
        GIF_ERROR) {
1003
0
      retval = GIF_ERROR;
1004
0
    }
1005
4.29M
  } else {
1006
4.29M
    Private->CrntShiftDWord |= ((long)Code)
1007
4.29M
                               << Private->CrntShiftState;
1008
4.29M
    Private->CrntShiftState += Private->RunningBits;
1009
10.3M
    while (Private->CrntShiftState >= 8) {
1010
      /* Dump out full bytes: */
1011
6.01M
      if (EGifBufferedOutput(GifFile, Private->Buf,
1012
6.01M
                             Private->CrntShiftDWord &
1013
6.01M
                                 0xff) == GIF_ERROR) {
1014
0
        retval = GIF_ERROR;
1015
0
      }
1016
6.01M
      Private->CrntShiftDWord >>= 8;
1017
6.01M
      Private->CrntShiftState -= 8;
1018
6.01M
    }
1019
4.29M
  }
1020
1021
  /* If code cannt fit into RunningBits bits, must raise its size. Note */
1022
  /* however that codes above 4095 are used for special signaling.      */
1023
4.29M
  if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) {
1024
3.52k
    Private->MaxCode1 = 1 << ++Private->RunningBits;
1025
3.52k
  }
1026
1027
4.29M
  return retval;
1028
4.29M
}
1029
1030
/******************************************************************************
1031
 This routines buffers the given characters until 255 characters are ready
1032
 to be output. If Code is equal to -1 the buffer is flushed (EOF).
1033
 The buffer is Dumped with first byte as its size, as GIF format requires.
1034
 Returns GIF_OK if written successfully.
1035
******************************************************************************/
1036
6.01M
static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c) {
1037
6.01M
  if (c == FLUSH_OUTPUT) {
1038
    /* Flush everything out. */
1039
375
    if (Buf[0] != 0 && InternalWrite(GifFile, Buf, Buf[0] + 1) !=
1040
375
                           (unsigned)(Buf[0] + 1)) {
1041
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1042
0
      return GIF_ERROR;
1043
0
    }
1044
    /* Mark end of compressed data, by an empty block (see GIF doc):
1045
     */
1046
375
    Buf[0] = 0;
1047
375
    if (InternalWrite(GifFile, Buf, 1) != 1) {
1048
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1049
0
      return GIF_ERROR;
1050
0
    }
1051
6.01M
  } else {
1052
6.01M
    if (Buf[0] == 255) {
1053
      /* Dump out this buffer - it is full: */
1054
23.4k
      if (InternalWrite(GifFile, Buf, Buf[0] + 1) !=
1055
23.4k
          (unsigned)(Buf[0] + 1)) {
1056
0
        GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1057
0
        return GIF_ERROR;
1058
0
      }
1059
23.4k
      Buf[0] = 0;
1060
23.4k
    }
1061
6.01M
    Buf[++Buf[0]] = c;
1062
6.01M
  }
1063
1064
6.01M
  return GIF_OK;
1065
6.01M
}
1066
1067
/******************************************************************************
1068
 This routine writes to disk an in-core representation of a GIF previously
1069
 created by DGifSlurp().
1070
******************************************************************************/
1071
1072
static int EGifWriteExtensions(GifFileType *GifFileOut,
1073
                               ExtensionBlock *ExtensionBlocks,
1074
0
                               int ExtensionBlockCount) {
1075
0
  if (ExtensionBlocks) {
1076
0
    int j;
1077
1078
0
    for (j = 0; j < ExtensionBlockCount; j++) {
1079
0
      ExtensionBlock *ep = &ExtensionBlocks[j];
1080
0
      if (ep->Function != CONTINUE_EXT_FUNC_CODE) {
1081
0
        if (EGifPutExtensionLeader(GifFileOut,
1082
0
                                   ep->Function) ==
1083
0
            GIF_ERROR) {
1084
0
          return (GIF_ERROR);
1085
0
        }
1086
0
      }
1087
0
      if (EGifPutExtensionBlock(GifFileOut, ep->ByteCount,
1088
0
                                ep->Bytes) == GIF_ERROR) {
1089
0
        return (GIF_ERROR);
1090
0
      }
1091
0
      if (j == ExtensionBlockCount - 1 ||
1092
0
          (ep + 1)->Function != CONTINUE_EXT_FUNC_CODE) {
1093
0
        if (EGifPutExtensionTrailer(GifFileOut) ==
1094
0
            GIF_ERROR) {
1095
0
          return (GIF_ERROR);
1096
0
        }
1097
0
      }
1098
0
    }
1099
0
  }
1100
1101
0
  return (GIF_OK);
1102
0
}
1103
1104
0
int EGifSpew(GifFileType *GifFileOut, int *ErrorCode) {
1105
0
  int i, j;
1106
0
  int status = GIF_OK;
1107
0
  int err = E_GIF_SUCCEEDED;
1108
1109
0
  if (EGifPutScreenDesc(GifFileOut, GifFileOut->SWidth,
1110
0
                        GifFileOut->SHeight, GifFileOut->SColorResolution,
1111
0
                        GifFileOut->SBackGroundColor,
1112
0
                        GifFileOut->SColorMap) == GIF_ERROR) {
1113
0
    status = GIF_ERROR;
1114
0
    err = GifFileOut->Error;
1115
0
    goto cleanup;
1116
0
  }
1117
1118
0
  for (i = 0; i < GifFileOut->ImageCount; i++) {
1119
0
    SavedImage *sp = &GifFileOut->SavedImages[i];
1120
0
    int SavedHeight = sp->ImageDesc.Height;
1121
0
    int SavedWidth = sp->ImageDesc.Width;
1122
1123
    /* this allows us to delete images by nuking their rasters */
1124
0
    if (sp->RasterBits == NULL) {
1125
0
      continue;
1126
0
    }
1127
1128
0
    if (EGifWriteExtensions(GifFileOut, sp->ExtensionBlocks,
1129
0
                            sp->ExtensionBlockCount) == GIF_ERROR) {
1130
0
      status = GIF_ERROR;
1131
0
      err = GifFileOut->Error;
1132
0
      goto cleanup;
1133
0
    }
1134
1135
0
    if (EGifPutImageDesc(GifFileOut, sp->ImageDesc.Left,
1136
0
                         sp->ImageDesc.Top, SavedWidth, SavedHeight,
1137
0
                         sp->ImageDesc.Interlace,
1138
0
                         sp->ImageDesc.ColorMap) == GIF_ERROR) {
1139
0
      status = GIF_ERROR;
1140
0
      err = GifFileOut->Error;
1141
0
      goto cleanup;
1142
0
    }
1143
1144
0
    if (sp->ImageDesc.Interlace) {
1145
      /*
1146
       * The way an interlaced image should be written -
1147
       * offsets and jumps...
1148
       */
1149
0
      static const int InterlacedOffset[] = {0, 4, 2, 1};
1150
0
      static const int InterlacedJumps[] = {8, 8, 4, 2};
1151
0
      int k;
1152
      /* Need to perform 4 passes on the images: */
1153
0
      for (k = 0; k < 4; k++) {
1154
0
        for (j = InterlacedOffset[k]; j < SavedHeight;
1155
0
             j += InterlacedJumps[k]) {
1156
0
          if (EGifPutLine(
1157
0
                  GifFileOut,
1158
0
                  sp->RasterBits + (size_t)j * SavedWidth,
1159
0
                  SavedWidth) == GIF_ERROR) {
1160
0
            status = GIF_ERROR;
1161
0
            err = GifFileOut->Error;
1162
0
            goto cleanup;
1163
0
          }
1164
0
        }
1165
0
      }
1166
0
    } else {
1167
0
      for (j = 0; j < SavedHeight; j++) {
1168
0
        if (EGifPutLine(GifFileOut,
1169
0
                        sp->RasterBits + (size_t)j * SavedWidth,
1170
0
                        SavedWidth) == GIF_ERROR) {
1171
0
          status = GIF_ERROR;
1172
0
          err = GifFileOut->Error;
1173
0
          goto cleanup;
1174
0
        }
1175
0
      }
1176
0
    }
1177
0
  }
1178
1179
0
  if (EGifWriteExtensions(GifFileOut, GifFileOut->ExtensionBlocks,
1180
0
                          GifFileOut->ExtensionBlockCount) == GIF_ERROR) {
1181
0
    status = GIF_ERROR;
1182
0
    err = GifFileOut->Error;
1183
0
    goto cleanup;
1184
0
  }
1185
1186
0
cleanup:
1187
0
  GifFreeSavedImages(GifFileOut);
1188
0
  GifFreeExtensions(&GifFileOut->ExtensionBlockCount,
1189
0
                    &GifFileOut->ExtensionBlocks);
1190
1191
0
  {
1192
0
    int close_error = E_GIF_SUCCEEDED;
1193
0
    if (EGifCloseFile(GifFileOut, &close_error) == GIF_ERROR) {
1194
0
      status = GIF_ERROR;
1195
0
      if (err == E_GIF_SUCCEEDED) {
1196
0
        err = close_error;
1197
0
      }
1198
0
    }
1199
0
  }
1200
1201
0
  if (ErrorCode != NULL) {
1202
0
    *ErrorCode = (status == GIF_OK) ? E_GIF_SUCCEEDED : err;
1203
0
  }
1204
1205
0
  return status;
1206
0
}
1207
1208
/* end */