Coverage Report

Created: 2026-01-15 06:10

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