Coverage Report

Created: 2026-03-29 06:25

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 = (long)Width * (long)Height;
452
453
  /* Reset compress algorithm parameters. */
454
375
  (void)EGifSetupCompress(GifFile);
455
456
375
  return GIF_OK;
457
375
}
458
459
/******************************************************************************
460
 Put one full scanned line (Line) of length LineLen into GIF file.
461
******************************************************************************/
462
76.7k
int EGifPutLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) {
463
76.7k
  int i;
464
76.7k
  GifPixelType Mask;
465
76.7k
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
466
467
76.7k
  if (!IS_WRITEABLE(Private)) {
468
    /* This file was NOT open for writing: */
469
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
470
0
    return GIF_ERROR;
471
0
  }
472
473
76.7k
  if (!LineLen) {
474
0
    LineLen = GifFile->Image.Width;
475
0
  }
476
76.7k
  if (Private->PixelCount < (unsigned)LineLen) {
477
0
    GifFile->Error = E_GIF_ERR_DATA_TOO_BIG;
478
0
    return GIF_ERROR;
479
0
  }
480
76.7k
  Private->PixelCount -= LineLen;
481
482
  /* Make sure the codes are not out of bit range, as we might generate
483
   * wrong code (because of overflow when we combine them) in this case:
484
   */
485
76.7k
  Mask = CodeMask[Private->BitsPerPixel];
486
7.75M
  for (i = 0; i < LineLen; i++) {
487
7.67M
    Line[i] &= Mask;
488
7.67M
  }
489
490
76.7k
  return EGifCompressLine(GifFile, Line, LineLen);
491
76.7k
}
492
493
/******************************************************************************
494
 Put one pixel (Pixel) into GIF file.
495
******************************************************************************/
496
0
int EGifPutPixel(GifFileType *GifFile, GifPixelType Pixel) {
497
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
498
499
0
  if (!IS_WRITEABLE(Private)) {
500
    /* This file was NOT open for writing: */
501
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
502
0
    return GIF_ERROR;
503
0
  }
504
505
0
  if (Private->PixelCount == 0) {
506
0
    GifFile->Error = E_GIF_ERR_DATA_TOO_BIG;
507
0
    return GIF_ERROR;
508
0
  }
509
0
  --Private->PixelCount;
510
511
  /* Make sure the code is not out of bit range, as we might generate
512
   * wrong code (because of overflow when we combine them) in this case:
513
   */
514
0
  Pixel &= CodeMask[Private->BitsPerPixel];
515
516
0
  return EGifCompressLine(GifFile, &Pixel, 1);
517
0
}
518
519
/******************************************************************************
520
 Put a comment into GIF file using the GIF89 comment extension block.
521
******************************************************************************/
522
0
int EGifPutComment(GifFileType *GifFile, const char *Comment) {
523
0
  unsigned int length;
524
0
  char *buf;
525
526
0
  length = strlen(Comment);
527
0
  if (length <= 255) {
528
0
    return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, length,
529
0
                            Comment);
530
0
  } else {
531
0
    buf = (char *)Comment;
532
0
    if (EGifPutExtensionLeader(GifFile, COMMENT_EXT_FUNC_CODE) ==
533
0
        GIF_ERROR) {
534
0
      return GIF_ERROR;
535
0
    }
536
537
    /* Break the comment into 255 byte sub blocks */
538
0
    while (length > 255) {
539
0
      if (EGifPutExtensionBlock(GifFile, 255, buf) ==
540
0
          GIF_ERROR) {
541
0
        return GIF_ERROR;
542
0
      }
543
0
      buf = buf + 255;
544
0
      length -= 255;
545
0
    }
546
    /* Output any partial block and the clear code. */
547
0
    if (length > 0) {
548
0
      if (EGifPutExtensionBlock(GifFile, length, buf) ==
549
0
          GIF_ERROR) {
550
0
        return GIF_ERROR;
551
0
      }
552
0
    }
553
0
    if (EGifPutExtensionTrailer(GifFile) == GIF_ERROR) {
554
0
      return GIF_ERROR;
555
0
    }
556
0
  }
557
0
  return GIF_OK;
558
0
}
559
560
/******************************************************************************
561
 Begin an extension block (see GIF manual).  More
562
 extensions can be dumped using EGifPutExtensionBlock until
563
 EGifPutExtensionTrailer is invoked.
564
******************************************************************************/
565
0
int EGifPutExtensionLeader(GifFileType *GifFile, const int ExtCode) {
566
0
  GifByteType Buf[3];
567
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
568
569
0
  if (!IS_WRITEABLE(Private)) {
570
    /* This file was NOT open for writing: */
571
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
572
0
    return GIF_ERROR;
573
0
  }
574
575
0
  Buf[0] = EXTENSION_INTRODUCER;
576
0
  Buf[1] = ExtCode;
577
0
  InternalWrite(GifFile, Buf, 2);
578
579
0
  return GIF_OK;
580
0
}
581
582
/******************************************************************************
583
 Put extension block data (see GIF manual) into a GIF file.
584
******************************************************************************/
585
int EGifPutExtensionBlock(GifFileType *GifFile, const int ExtLen,
586
0
                          const void *Extension) {
587
0
  GifByteType Buf;
588
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
589
590
0
  if (!IS_WRITEABLE(Private)) {
591
    /* This file was NOT open for writing: */
592
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
593
0
    return GIF_ERROR;
594
0
  }
595
596
0
  Buf = ExtLen;
597
0
  InternalWrite(GifFile, &Buf, 1);
598
0
  InternalWrite(GifFile, Extension, ExtLen);
599
600
0
  return GIF_OK;
601
0
}
602
603
/******************************************************************************
604
 Put a terminating block (see GIF manual) into a GIF file.
605
******************************************************************************/
606
0
int EGifPutExtensionTrailer(GifFileType *GifFile) {
607
608
0
  GifByteType Buf;
609
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
610
611
0
  if (!IS_WRITEABLE(Private)) {
612
    /* This file was NOT open for writing: */
613
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
614
0
    return GIF_ERROR;
615
0
  }
616
617
  /* Write the block terminator */
618
0
  Buf = 0;
619
0
  InternalWrite(GifFile, &Buf, 1);
620
621
0
  return GIF_OK;
622
0
}
623
624
/******************************************************************************
625
 Put an extension block (see GIF manual) into a GIF file.
626
 Warning: This function is only useful for Extension blocks that have at
627
 most one subblock.  Extensions with more than one subblock need to use the
628
 EGifPutExtension{Leader,Block,Trailer} functions instead.
629
******************************************************************************/
630
int EGifPutExtension(GifFileType *GifFile, const int ExtCode, const int ExtLen,
631
0
                     const void *Extension) {
632
633
0
  GifByteType Buf[3];
634
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
635
636
0
  if (!IS_WRITEABLE(Private)) {
637
    /* This file was NOT open for writing: */
638
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
639
0
    return GIF_ERROR;
640
0
  }
641
642
0
  if (ExtCode == 0) {
643
0
    InternalWrite(GifFile, (GifByteType *)&ExtLen, 1);
644
0
  } else {
645
0
    Buf[0] = EXTENSION_INTRODUCER;
646
0
    Buf[1] = ExtCode; /* Extension Label */
647
0
    Buf[2] = ExtLen;  /* Extension length */
648
0
    InternalWrite(GifFile, Buf, 3);
649
0
  }
650
0
  InternalWrite(GifFile, Extension, ExtLen);
651
0
  Buf[0] = 0;
652
0
  InternalWrite(GifFile, Buf, 1);
653
654
0
  return GIF_OK;
655
0
}
656
657
/******************************************************************************
658
 Render a Graphics Control Block as raw extension data
659
******************************************************************************/
660
661
size_t EGifGCBToExtension(const GraphicsControlBlock *GCB,
662
0
                          GifByteType *GifExtension) {
663
0
  GifExtension[0] = 0;
664
0
  GifExtension[0] |=
665
0
      (GCB->TransparentColor == NO_TRANSPARENT_COLOR) ? 0x00 : 0x01;
666
0
  GifExtension[0] |= GCB->UserInputFlag ? 0x02 : 0x00;
667
0
  GifExtension[0] |= ((GCB->DisposalMode & 0x07) << 2);
668
0
  GifExtension[1] = LOBYTE(GCB->DelayTime);
669
0
  GifExtension[2] = HIBYTE(GCB->DelayTime);
670
0
  GifExtension[3] = (char)GCB->TransparentColor;
671
0
  return 4;
672
0
}
673
674
/******************************************************************************
675
 Replace the Graphics Control Block for a saved image, if it exists.
676
******************************************************************************/
677
678
int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB,
679
0
                            GifFileType *GifFile, int ImageIndex) {
680
0
  int i;
681
0
  size_t Len;
682
0
  GifByteType buf[sizeof(GraphicsControlBlock)]; /* a bit dodgy... */
683
684
0
  if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) {
685
0
    return GIF_ERROR;
686
0
  }
687
688
0
  for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount;
689
0
       i++) {
690
0
    ExtensionBlock *ep =
691
0
        &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
692
0
    if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
693
0
      EGifGCBToExtension(GCB, ep->Bytes);
694
0
      return GIF_OK;
695
0
    }
696
0
  }
697
698
0
  Len = EGifGCBToExtension(GCB, (GifByteType *)buf);
699
0
  if (GifAddExtensionBlock(
700
0
          &GifFile->SavedImages[ImageIndex].ExtensionBlockCount,
701
0
          &GifFile->SavedImages[ImageIndex].ExtensionBlocks,
702
0
          GRAPHICS_EXT_FUNC_CODE, Len,
703
0
          (unsigned char *)buf) == GIF_ERROR) {
704
0
    return (GIF_ERROR);
705
0
  }
706
707
0
  return (GIF_OK);
708
0
}
709
710
/******************************************************************************
711
 Put the image code in compressed form. This routine can be called if the
712
 information needed to be piped out as is. Obviously this is much faster
713
 than decoding and encoding again. This routine should be followed by calls
714
 to EGifPutCodeNext, until NULL block is given.
715
 The block should NOT be freed by the user (not dynamically allocated).
716
******************************************************************************/
717
int EGifPutCode(GifFileType *GifFile, int CodeSize,
718
0
                const GifByteType *CodeBlock) {
719
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
720
721
0
  if (!IS_WRITEABLE(Private)) {
722
    /* This file was NOT open for writing: */
723
0
    GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
724
0
    return GIF_ERROR;
725
0
  }
726
727
  /* No need to dump code size as Compression set up does any for us: */
728
  /*
729
   * Buf = CodeSize;
730
   * if (InternalWrite(GifFile, &Buf, 1) != 1) {
731
   *      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
732
   *      return GIF_ERROR;
733
   * }
734
   */
735
736
0
  return EGifPutCodeNext(GifFile, CodeBlock);
737
0
}
738
739
/******************************************************************************
740
 Continue to put the image code in compressed form. This routine should be
741
 called with blocks of code as read via DGifGetCode/DGifGetCodeNext. If
742
 given buffer pointer is NULL, empty block is written to mark end of code.
743
******************************************************************************/
744
0
int EGifPutCodeNext(GifFileType *GifFile, const GifByteType *CodeBlock) {
745
0
  GifByteType Buf;
746
0
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
747
748
0
  if (CodeBlock != NULL) {
749
0
    if (InternalWrite(GifFile, CodeBlock, CodeBlock[0] + 1) !=
750
0
        (unsigned)(CodeBlock[0] + 1)) {
751
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
752
0
      return GIF_ERROR;
753
0
    }
754
0
  } else {
755
0
    Buf = 0;
756
0
    if (InternalWrite(GifFile, &Buf, 1) != 1) {
757
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
758
0
      return GIF_ERROR;
759
0
    }
760
0
    Private->PixelCount =
761
0
        0; /* And local info. indicate image read. */
762
0
  }
763
764
0
  return GIF_OK;
765
0
}
766
767
/******************************************************************************
768
 This routine should be called last, to close the GIF file.
769
******************************************************************************/
770
375
int EGifCloseFile(GifFileType *GifFile, int *ErrorCode) {
771
375
  GifByteType Buf;
772
375
  GifFilePrivateType *Private;
773
375
  FILE *File;
774
775
375
  if (GifFile == NULL) {
776
0
    return GIF_ERROR;
777
0
  }
778
779
375
  Private = (GifFilePrivateType *)GifFile->Private;
780
375
  if (Private == NULL) {
781
0
    return GIF_ERROR;
782
375
  } else if (!IS_WRITEABLE(Private)) {
783
    /* This file was NOT open for writing: */
784
0
    if (ErrorCode != NULL) {
785
0
      *ErrorCode = E_GIF_ERR_NOT_WRITEABLE;
786
0
    }
787
0
    free(GifFile);
788
0
    return GIF_ERROR;
789
375
  } else {
790
375
    File = Private->File;
791
792
375
    Buf = TERMINATOR_INTRODUCER;
793
375
    InternalWrite(GifFile, &Buf, 1);
794
795
375
    if (GifFile->Image.ColorMap) {
796
0
      GifFreeMapObject(GifFile->Image.ColorMap);
797
0
      GifFile->Image.ColorMap = NULL;
798
0
    }
799
375
    if (GifFile->SColorMap) {
800
375
      GifFreeMapObject(GifFile->SColorMap);
801
375
      GifFile->SColorMap = NULL;
802
375
    }
803
375
    if (Private->HashTable) {
804
375
      free((char *)Private->HashTable);
805
375
    }
806
375
    free((char *)Private);
807
808
375
    if (File && fclose(File) != 0) {
809
0
      if (ErrorCode != NULL) {
810
0
        *ErrorCode = E_GIF_ERR_CLOSE_FAILED;
811
0
      }
812
0
      free(GifFile);
813
0
      return GIF_ERROR;
814
0
    }
815
816
375
    free(GifFile);
817
375
    if (ErrorCode != NULL) {
818
375
      *ErrorCode = E_GIF_SUCCEEDED;
819
375
    }
820
375
  }
821
375
  return GIF_OK;
822
375
}
823
824
/******************************************************************************
825
 Put 2 bytes (a word) into the given file in little-endian order:
826
******************************************************************************/
827
2.25k
static int EGifPutWord(int Word, GifFileType *GifFile) {
828
2.25k
  unsigned char c[2];
829
830
2.25k
  c[0] = LOBYTE(Word);
831
2.25k
  c[1] = HIBYTE(Word);
832
2.25k
  if (InternalWrite(GifFile, c, 2) == 2) {
833
2.25k
    return GIF_OK;
834
2.25k
  } else {
835
0
    return GIF_ERROR;
836
0
  }
837
2.25k
}
838
839
/******************************************************************************
840
 Setup the LZ compression for this image:
841
******************************************************************************/
842
375
static int EGifSetupCompress(GifFileType *GifFile) {
843
375
  int BitsPerPixel;
844
375
  GifByteType Buf;
845
375
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
846
847
  /* Test and see what color map to use, and from it # bits per pixel: */
848
375
  if (GifFile->Image.ColorMap) {
849
0
    BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel;
850
375
  } else if (GifFile->SColorMap) {
851
375
    BitsPerPixel = GifFile->SColorMap->BitsPerPixel;
852
375
  } else {
853
0
    GifFile->Error = E_GIF_ERR_NO_COLOR_MAP;
854
0
    return GIF_ERROR;
855
0
  }
856
857
375
  Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel);
858
375
  InternalWrite(GifFile, &Buf, 1); /* Write the Code size to file. */
859
860
375
  Private->Buf[0] = 0; /* Nothing was output yet. */
861
375
  Private->BitsPerPixel = BitsPerPixel;
862
375
  Private->ClearCode = (1 << BitsPerPixel);
863
375
  Private->EOFCode = Private->ClearCode + 1;
864
375
  Private->RunningCode = Private->EOFCode + 1;
865
375
  Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */
866
375
  Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */
867
375
  Private->CrntCode = FIRST_CODE; /* Signal that this is first one! */
868
375
  Private->CrntShiftState = 0;    /* No information in CrntShiftDWord. */
869
375
  Private->CrntShiftDWord = 0;
870
871
  /* Clear hash table and send Clear to make sure the decoder do the same.
872
   */
873
375
  _ClearHashTable(Private->HashTable);
874
875
375
  if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) {
876
0
    GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
877
0
    return GIF_ERROR;
878
0
  }
879
375
  return GIF_OK;
880
375
}
881
882
/******************************************************************************
883
 The LZ compression routine:
884
 This version compresses the given buffer Line of length LineLen.
885
 This routine can be called a few times (one per scan line, for example), in
886
 order to complete the whole image.
887
******************************************************************************/
888
static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line,
889
76.7k
                            const int LineLen) {
890
76.7k
  int i = 0, CrntCode;
891
76.7k
  GifHashTableType *HashTable;
892
76.7k
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
893
894
76.7k
  HashTable = Private->HashTable;
895
896
76.7k
  if (Private->CrntCode == FIRST_CODE) { /* Its first time! */
897
375
    CrntCode = Line[i++];
898
76.3k
  } else {
899
76.3k
    CrntCode =
900
76.3k
        Private->CrntCode; /* Get last code in compression. */
901
76.3k
  }
902
7.74M
  while (i < LineLen) { /* Decode LineLen items. */
903
7.67M
    GifPixelType Pixel =
904
7.67M
        Line[i++]; /* Get next pixel from stream. */
905
    /* Form a new unique key to search hash table for the code
906
     * combines CrntCode as Prefix string with Pixel as postfix
907
     * char.
908
     */
909
7.67M
    int NewCode;
910
7.67M
    unsigned long NewKey = (((uint32_t)CrntCode) << 8) + Pixel;
911
7.67M
    if ((NewCode = _ExistsHashTable(HashTable, NewKey)) >= 0) {
912
      /* This Key is already there, or the string is old one,
913
       * so simple take new code as our CrntCode:
914
       */
915
3.38M
      CrntCode = NewCode;
916
4.29M
    } else {
917
      /* Put it in hash table, output the prefix code, and
918
       * make our CrntCode equal to Pixel.
919
       */
920
4.29M
      if (EGifCompressOutput(GifFile, CrntCode) ==
921
4.29M
          GIF_ERROR) {
922
0
        GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
923
0
        return GIF_ERROR;
924
0
      }
925
4.29M
      CrntCode = Pixel;
926
927
      /* If however the HashTable if full, we send a clear
928
       * first and Clear the hash table.
929
       */
930
4.29M
      if (Private->RunningCode >= LZ_MAX_CODE) {
931
        /* Time to do some clearance: */
932
1.03k
        if (EGifCompressOutput(GifFile,
933
1.03k
                               Private->ClearCode) ==
934
1.03k
            GIF_ERROR) {
935
0
          GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
936
0
          return GIF_ERROR;
937
0
        }
938
1.03k
        Private->RunningCode = Private->EOFCode + 1;
939
1.03k
        Private->RunningBits =
940
1.03k
            Private->BitsPerPixel + 1;
941
1.03k
        Private->MaxCode1 = 1 << Private->RunningBits;
942
1.03k
        _ClearHashTable(HashTable);
943
4.28M
      } else {
944
        /* Put this unique key with its relative Code in
945
         * hash table: */
946
4.28M
        _InsertHashTable(HashTable, NewKey,
947
4.28M
                         Private->RunningCode++);
948
4.28M
      }
949
4.29M
    }
950
7.67M
  }
951
952
  /* Preserve the current state of the compression algorithm: */
953
76.7k
  Private->CrntCode = CrntCode;
954
955
76.7k
  if (Private->PixelCount == 0) {
956
    /* We are done - output last Code and flush output buffers: */
957
375
    if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) {
958
0
      GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
959
0
      return GIF_ERROR;
960
0
    }
961
375
    if (EGifCompressOutput(GifFile, Private->EOFCode) ==
962
375
        GIF_ERROR) {
963
0
      GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
964
0
      return GIF_ERROR;
965
0
    }
966
375
    if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) {
967
0
      GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
968
0
      return GIF_ERROR;
969
0
    }
970
375
  }
971
972
76.7k
  return GIF_OK;
973
76.7k
}
974
975
/******************************************************************************
976
 The LZ compression output routine:
977
 This routine is responsible for the compression of the bit stream into
978
 8 bits (bytes) packets.
979
 Returns GIF_OK if written successfully.
980
******************************************************************************/
981
4.29M
static int EGifCompressOutput(GifFileType *GifFile, const int Code) {
982
4.29M
  GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
983
4.29M
  int retval = GIF_OK;
984
985
4.29M
  if (Code == FLUSH_OUTPUT) {
986
684
    while (Private->CrntShiftState > 0) {
987
      /* Get Rid of what is left in DWord, and flush it. */
988
309
      if (EGifBufferedOutput(GifFile, Private->Buf,
989
309
                             Private->CrntShiftDWord &
990
309
                                 0xff) == GIF_ERROR) {
991
0
        retval = GIF_ERROR;
992
0
      }
993
309
      Private->CrntShiftDWord >>= 8;
994
309
      Private->CrntShiftState -= 8;
995
309
    }
996
375
    Private->CrntShiftState = 0; /* For next time. */
997
375
    if (EGifBufferedOutput(GifFile, Private->Buf, FLUSH_OUTPUT) ==
998
375
        GIF_ERROR) {
999
0
      retval = GIF_ERROR;
1000
0
    }
1001
4.29M
  } else {
1002
4.29M
    Private->CrntShiftDWord |= ((long)Code)
1003
4.29M
                               << Private->CrntShiftState;
1004
4.29M
    Private->CrntShiftState += Private->RunningBits;
1005
10.3M
    while (Private->CrntShiftState >= 8) {
1006
      /* Dump out full bytes: */
1007
6.01M
      if (EGifBufferedOutput(GifFile, Private->Buf,
1008
6.01M
                             Private->CrntShiftDWord &
1009
6.01M
                                 0xff) == GIF_ERROR) {
1010
0
        retval = GIF_ERROR;
1011
0
      }
1012
6.01M
      Private->CrntShiftDWord >>= 8;
1013
6.01M
      Private->CrntShiftState -= 8;
1014
6.01M
    }
1015
4.29M
  }
1016
1017
  /* If code cannt fit into RunningBits bits, must raise its size. Note */
1018
  /* however that codes above 4095 are used for special signaling.      */
1019
4.29M
  if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) {
1020
3.52k
    Private->MaxCode1 = 1 << ++Private->RunningBits;
1021
3.52k
  }
1022
1023
4.29M
  return retval;
1024
4.29M
}
1025
1026
/******************************************************************************
1027
 This routines buffers the given characters until 255 characters are ready
1028
 to be output. If Code is equal to -1 the buffer is flushed (EOF).
1029
 The buffer is Dumped with first byte as its size, as GIF format requires.
1030
 Returns GIF_OK if written successfully.
1031
******************************************************************************/
1032
6.01M
static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c) {
1033
6.01M
  if (c == FLUSH_OUTPUT) {
1034
    /* Flush everything out. */
1035
375
    if (Buf[0] != 0 && InternalWrite(GifFile, Buf, Buf[0] + 1) !=
1036
375
                           (unsigned)(Buf[0] + 1)) {
1037
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1038
0
      return GIF_ERROR;
1039
0
    }
1040
    /* Mark end of compressed data, by an empty block (see GIF doc):
1041
     */
1042
375
    Buf[0] = 0;
1043
375
    if (InternalWrite(GifFile, Buf, 1) != 1) {
1044
0
      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1045
0
      return GIF_ERROR;
1046
0
    }
1047
6.01M
  } else {
1048
6.01M
    if (Buf[0] == 255) {
1049
      /* Dump out this buffer - it is full: */
1050
23.4k
      if (InternalWrite(GifFile, Buf, Buf[0] + 1) !=
1051
23.4k
          (unsigned)(Buf[0] + 1)) {
1052
0
        GifFile->Error = E_GIF_ERR_WRITE_FAILED;
1053
0
        return GIF_ERROR;
1054
0
      }
1055
23.4k
      Buf[0] = 0;
1056
23.4k
    }
1057
6.01M
    Buf[++Buf[0]] = c;
1058
6.01M
  }
1059
1060
6.01M
  return GIF_OK;
1061
6.01M
}
1062
1063
/******************************************************************************
1064
 This routine writes to disk an in-core representation of a GIF previously
1065
 created by DGifSlurp().
1066
******************************************************************************/
1067
1068
static int EGifWriteExtensions(GifFileType *GifFileOut,
1069
                               ExtensionBlock *ExtensionBlocks,
1070
0
                               int ExtensionBlockCount) {
1071
0
  if (ExtensionBlocks) {
1072
0
    int j;
1073
1074
0
    for (j = 0; j < ExtensionBlockCount; j++) {
1075
0
      ExtensionBlock *ep = &ExtensionBlocks[j];
1076
0
      if (ep->Function != CONTINUE_EXT_FUNC_CODE) {
1077
0
        if (EGifPutExtensionLeader(GifFileOut,
1078
0
                                   ep->Function) ==
1079
0
            GIF_ERROR) {
1080
0
          return (GIF_ERROR);
1081
0
        }
1082
0
      }
1083
0
      if (EGifPutExtensionBlock(GifFileOut, ep->ByteCount,
1084
0
                                ep->Bytes) == GIF_ERROR) {
1085
0
        return (GIF_ERROR);
1086
0
      }
1087
0
      if (j == ExtensionBlockCount - 1 ||
1088
0
          (ep + 1)->Function != CONTINUE_EXT_FUNC_CODE) {
1089
0
        if (EGifPutExtensionTrailer(GifFileOut) ==
1090
0
            GIF_ERROR) {
1091
0
          return (GIF_ERROR);
1092
0
        }
1093
0
      }
1094
0
    }
1095
0
  }
1096
1097
0
  return (GIF_OK);
1098
0
}
1099
1100
0
int EGifSpew(GifFileType *GifFileOut, int *ErrorCode) {
1101
0
  int i, j;
1102
0
  int status = GIF_OK;
1103
0
  int err = E_GIF_SUCCEEDED;
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
    status = GIF_ERROR;
1110
0
    err = GifFileOut->Error;
1111
0
    goto cleanup;
1112
0
  }
1113
1114
0
  for (i = 0; i < GifFileOut->ImageCount; i++) {
1115
0
    SavedImage *sp = &GifFileOut->SavedImages[i];
1116
0
    int SavedHeight = sp->ImageDesc.Height;
1117
0
    int SavedWidth = sp->ImageDesc.Width;
1118
1119
    /* this allows us to delete images by nuking their rasters */
1120
0
    if (sp->RasterBits == NULL) {
1121
0
      continue;
1122
0
    }
1123
1124
0
    if (EGifWriteExtensions(GifFileOut, sp->ExtensionBlocks,
1125
0
                            sp->ExtensionBlockCount) == GIF_ERROR) {
1126
0
      status = GIF_ERROR;
1127
0
      err = GifFileOut->Error;
1128
0
      goto cleanup;
1129
0
    }
1130
1131
0
    if (EGifPutImageDesc(GifFileOut, sp->ImageDesc.Left,
1132
0
                         sp->ImageDesc.Top, SavedWidth, SavedHeight,
1133
0
                         sp->ImageDesc.Interlace,
1134
0
                         sp->ImageDesc.ColorMap) == GIF_ERROR) {
1135
0
      status = GIF_ERROR;
1136
0
      err = GifFileOut->Error;
1137
0
      goto cleanup;
1138
0
    }
1139
1140
0
    if (sp->ImageDesc.Interlace) {
1141
      /*
1142
       * The way an interlaced image should be written -
1143
       * offsets and jumps...
1144
       */
1145
0
      static const int InterlacedOffset[] = {0, 4, 2, 1};
1146
0
      static const int InterlacedJumps[] = {8, 8, 4, 2};
1147
0
      int k;
1148
      /* Need to perform 4 passes on the images: */
1149
0
      for (k = 0; k < 4; k++) {
1150
0
        for (j = InterlacedOffset[k]; j < SavedHeight;
1151
0
             j += InterlacedJumps[k]) {
1152
0
          if (EGifPutLine(
1153
0
                  GifFileOut,
1154
0
                  sp->RasterBits + j * SavedWidth,
1155
0
                  SavedWidth) == GIF_ERROR) {
1156
0
            status = GIF_ERROR;
1157
0
            err = GifFileOut->Error;
1158
0
            goto cleanup;
1159
0
          }
1160
0
        }
1161
0
      }
1162
0
    } else {
1163
0
      for (j = 0; j < SavedHeight; j++) {
1164
0
        if (EGifPutLine(GifFileOut,
1165
0
                        sp->RasterBits + j * SavedWidth,
1166
0
                        SavedWidth) == GIF_ERROR) {
1167
0
          status = GIF_ERROR;
1168
0
          err = GifFileOut->Error;
1169
0
          goto cleanup;
1170
0
        }
1171
0
      }
1172
0
    }
1173
0
  }
1174
1175
0
  if (EGifWriteExtensions(GifFileOut, GifFileOut->ExtensionBlocks,
1176
0
                          GifFileOut->ExtensionBlockCount) == GIF_ERROR) {
1177
0
    status = GIF_ERROR;
1178
0
    err = GifFileOut->Error;
1179
0
    goto cleanup;
1180
0
  }
1181
1182
0
cleanup:
1183
0
  GifFreeSavedImages(GifFileOut);
1184
0
  GifFreeExtensions(&GifFileOut->ExtensionBlockCount,
1185
0
                    &GifFileOut->ExtensionBlocks);
1186
1187
0
  {
1188
0
    int close_error = E_GIF_SUCCEEDED;
1189
0
    if (EGifCloseFile(GifFileOut, &close_error) == GIF_ERROR) {
1190
0
      status = GIF_ERROR;
1191
0
      if (err == E_GIF_SUCCEEDED) {
1192
0
        err = close_error;
1193
0
      }
1194
0
    }
1195
0
  }
1196
1197
0
  if (ErrorCode != NULL) {
1198
0
    *ErrorCode = (status == GIF_OK) ? E_GIF_SUCCEEDED : err;
1199
0
  }
1200
1201
0
  return status;
1202
0
}
1203
1204
/* end */