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