• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*****************************************************************************
2 
3  GIF construction tools
4 
5 SPDX-License-Identifier: MIT
6 
7 ****************************************************************************/
8 
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12 
13 #include "gif_lib.h"
14 #include "gif_lib_private.h"
15 
16 #define MAX(x, y)    (((x) > (y)) ? (x) : (y))
17 
18 /******************************************************************************
19  Miscellaneous utility functions
20 ******************************************************************************/
21 
22 /* return smallest bitfield size n will fit in */
23 int
GifBitSize(int n)24 GifBitSize(int n)
25 {
26     register int i;
27 
28     for (i = 1; i <= 8; i++)
29         if ((1 << i) >= n)
30             break;
31     return (i);
32 }
33 
34 /******************************************************************************
35   Color map object functions
36 ******************************************************************************/
37 
38 /*
39  * Allocate a color map of given size; initialize with contents of
40  * ColorMap if that pointer is non-NULL.
41  */
42 ColorMapObject *
GifMakeMapObject(int ColorCount,const GifColorType * ColorMap)43 GifMakeMapObject(int ColorCount, const GifColorType *ColorMap)
44 {
45     ColorMapObject *Object;
46 
47     /*** FIXME: Our ColorCount has to be a power of two.  Is it necessary to
48      * make the user know that or should we automatically round up instead? */
49     if (ColorCount != (1 << GifBitSize(ColorCount))) {
50         return ((ColorMapObject *) NULL);
51     }
52 
53     Object = (ColorMapObject *)malloc(sizeof(ColorMapObject));
54     if (Object == (ColorMapObject *) NULL) {
55         return ((ColorMapObject *) NULL);
56     }
57 
58     Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
59     if (Object->Colors == (GifColorType *) NULL) {
60 	free(Object);
61         return ((ColorMapObject *) NULL);
62     }
63 
64     Object->ColorCount = ColorCount;
65     Object->BitsPerPixel = GifBitSize(ColorCount);
66     Object->SortFlag = false;
67 
68     if (ColorMap != NULL) {
69         memcpy((char *)Object->Colors,
70                (char *)ColorMap, ColorCount * sizeof(GifColorType));
71     }
72 
73     return (Object);
74 }
75 
76 /*******************************************************************************
77 Free a color map object
78 *******************************************************************************/
79 void
GifFreeMapObject(ColorMapObject * Object)80 GifFreeMapObject(ColorMapObject *Object)
81 {
82     if (Object != NULL) {
83         (void)free(Object->Colors);
84         (void)free(Object);
85     }
86 }
87 
88 #ifdef DEBUG
89 void
DumpColorMap(ColorMapObject * Object,FILE * fp)90 DumpColorMap(ColorMapObject *Object,
91              FILE * fp)
92 {
93     if (Object != NULL) {
94         int i, j, Len = Object->ColorCount;
95 
96         for (i = 0; i < Len; i += 4) {
97             for (j = 0; j < 4 && j < Len; j++) {
98                 (void)fprintf(fp, "%3d: %02x %02x %02x   ", i + j,
99 			      Object->Colors[i + j].Red,
100 			      Object->Colors[i + j].Green,
101 			      Object->Colors[i + j].Blue);
102             }
103             (void)fprintf(fp, "\n");
104         }
105     }
106 }
107 #endif /* DEBUG */
108 
109 /*******************************************************************************
110  Compute the union of two given color maps and return it.  If result can't
111  fit into 256 colors, NULL is returned, the allocated union otherwise.
112  ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are
113  copied iff they didn't exist before.  ColorTransIn2 maps the old
114  ColorIn2 into the ColorUnion color map table./
115 *******************************************************************************/
116 ColorMapObject *
GifUnionColorMap(const ColorMapObject * ColorIn1,const ColorMapObject * ColorIn2,GifPixelType ColorTransIn2[])117 GifUnionColorMap(const ColorMapObject *ColorIn1,
118               const ColorMapObject *ColorIn2,
119               GifPixelType ColorTransIn2[])
120 {
121     int i, j, CrntSlot, RoundUpTo, NewGifBitSize;
122     ColorMapObject *ColorUnion;
123 
124     /*
125      * We don't worry about duplicates within either color map; if
126      * the caller wants to resolve those, he can perform unions
127      * with an empty color map.
128      */
129 
130     /* Allocate table which will hold the result for sure. */
131     ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount,
132                                ColorIn2->ColorCount) * 2, NULL);
133 
134     if (ColorUnion == NULL)
135         return (NULL);
136 
137     /*
138      * Copy ColorIn1 to ColorUnion.
139      */
140     for (i = 0; i < ColorIn1->ColorCount; i++)
141         ColorUnion->Colors[i] = ColorIn1->Colors[i];
142     CrntSlot = ColorIn1->ColorCount;
143 
144     /*
145      * Potentially obnoxious hack:
146      *
147      * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
148      * of table 1.  This is very useful if your display is limited to
149      * 16 colors.
150      */
151     while (ColorIn1->Colors[CrntSlot - 1].Red == 0
152            && ColorIn1->Colors[CrntSlot - 1].Green == 0
153            && ColorIn1->Colors[CrntSlot - 1].Blue == 0)
154         CrntSlot--;
155 
156     /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */
157     for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
158         /* Let's see if this color already exists: */
159         for (j = 0; j < ColorIn1->ColorCount; j++)
160             if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i],
161                         sizeof(GifColorType)) == 0)
162                 break;
163 
164         if (j < ColorIn1->ColorCount)
165             ColorTransIn2[i] = j;    /* color exists in Color1 */
166         else {
167             /* Color is new - copy it to a new slot: */
168             ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
169             ColorTransIn2[i] = CrntSlot++;
170         }
171     }
172 
173     if (CrntSlot > 256) {
174         GifFreeMapObject(ColorUnion);
175         return ((ColorMapObject *) NULL);
176     }
177 
178     NewGifBitSize = GifBitSize(CrntSlot);
179     RoundUpTo = (1 << NewGifBitSize);
180 
181     if (RoundUpTo != ColorUnion->ColorCount) {
182         register GifColorType *Map = ColorUnion->Colors;
183 
184         /*
185          * Zero out slots up to next power of 2.
186          * We know these slots exist because of the way ColorUnion's
187          * start dimension was computed.
188          */
189         for (j = CrntSlot; j < RoundUpTo; j++)
190             Map[j].Red = Map[j].Green = Map[j].Blue = 0;
191 
192         /* perhaps we can shrink the map? */
193         if (RoundUpTo < ColorUnion->ColorCount) {
194             GifColorType *new_map = (GifColorType *)reallocarray(Map,
195                                  RoundUpTo, sizeof(GifColorType));
196             if( new_map == NULL ) {
197                 GifFreeMapObject(ColorUnion);
198                 return ((ColorMapObject *) NULL);
199             }
200             ColorUnion->Colors = new_map;
201         }
202     }
203 
204     ColorUnion->ColorCount = RoundUpTo;
205     ColorUnion->BitsPerPixel = NewGifBitSize;
206 
207     return (ColorUnion);
208 }
209 
210 /*******************************************************************************
211  Apply a given color translation to the raster bits of an image
212 *******************************************************************************/
213 void
GifApplyTranslation(SavedImage * Image,GifPixelType Translation[])214 GifApplyTranslation(SavedImage *Image, GifPixelType Translation[])
215 {
216     register int i;
217     register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width;
218 
219     for (i = 0; i < RasterSize; i++)
220         Image->RasterBits[i] = Translation[Image->RasterBits[i]];
221 }
222 
223 /******************************************************************************
224  Extension record functions
225 ******************************************************************************/
226 int
GifAddExtensionBlock(int * ExtensionBlockCount,ExtensionBlock ** ExtensionBlocks,int Function,unsigned int Len,unsigned char ExtData[])227 GifAddExtensionBlock(int *ExtensionBlockCount,
228 		     ExtensionBlock **ExtensionBlocks,
229 		     int Function,
230 		     unsigned int Len,
231 		     unsigned char ExtData[])
232 {
233     ExtensionBlock *ep;
234 
235     if (*ExtensionBlocks == NULL)
236         *ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock));
237     else {
238         ExtensionBlock* ep_new = (ExtensionBlock *)reallocarray
239 				 (*ExtensionBlocks, (*ExtensionBlockCount + 1),
240                                       sizeof(ExtensionBlock));
241         if( ep_new == NULL )
242             return (GIF_ERROR);
243         *ExtensionBlocks = ep_new;
244     }
245 
246     if (*ExtensionBlocks == NULL)
247         return (GIF_ERROR);
248 
249     ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
250 
251     ep->Function = Function;
252     ep->ByteCount=Len;
253     ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
254     if (ep->Bytes == NULL)
255         return (GIF_ERROR);
256 
257     if (ExtData != NULL) {
258         memcpy(ep->Bytes, ExtData, Len);
259     }
260 
261     return (GIF_OK);
262 }
263 
264 void
GifFreeExtensions(int * ExtensionBlockCount,ExtensionBlock ** ExtensionBlocks)265 GifFreeExtensions(int *ExtensionBlockCount,
266 		  ExtensionBlock **ExtensionBlocks)
267 {
268     ExtensionBlock *ep;
269 
270     if (*ExtensionBlocks == NULL)
271         return;
272 
273     for (ep = *ExtensionBlocks;
274 	 ep < (*ExtensionBlocks + *ExtensionBlockCount);
275 	 ep++)
276         (void)free((char *)ep->Bytes);
277     (void)free((char *)*ExtensionBlocks);
278     *ExtensionBlocks = NULL;
279     *ExtensionBlockCount = 0;
280 }
281 
282 /******************************************************************************
283  Image block allocation functions
284 ******************************************************************************/
285 
286 /* Private Function:
287  * Frees the last image in the GifFile->SavedImages array
288  */
289 void
FreeLastSavedImage(GifFileType * GifFile)290 FreeLastSavedImage(GifFileType *GifFile)
291 {
292     SavedImage *sp;
293 
294     if ((GifFile == NULL) || (GifFile->SavedImages == NULL))
295         return;
296 
297     /* Remove one SavedImage from the GifFile */
298     GifFile->ImageCount--;
299     sp = &GifFile->SavedImages[GifFile->ImageCount];
300 
301     /* Deallocate its Colormap */
302     if (sp->ImageDesc.ColorMap != NULL) {
303         GifFreeMapObject(sp->ImageDesc.ColorMap);
304         sp->ImageDesc.ColorMap = NULL;
305     }
306 
307     /* Deallocate the image data */
308     if (sp->RasterBits != NULL)
309         free((char *)sp->RasterBits);
310 
311     /* Deallocate any extensions */
312     GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
313 
314     /*** FIXME: We could realloc the GifFile->SavedImages structure but is
315      * there a point to it? Saves some memory but we'd have to do it every
316      * time.  If this is used in GifFreeSavedImages then it would be inefficient
317      * (The whole array is going to be deallocated.)  If we just use it when
318      * we want to free the last Image it's convenient to do it here.
319      */
320 }
321 
322 /*
323  * Append an image block to the SavedImages array
324  */
325 SavedImage *
GifMakeSavedImage(GifFileType * GifFile,const SavedImage * CopyFrom)326 GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom)
327 {
328     if (GifFile->SavedImages == NULL)
329         GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
330     else {
331         SavedImage* newSavedImages = (SavedImage *)reallocarray(GifFile->SavedImages,
332                                (GifFile->ImageCount + 1), sizeof(SavedImage));
333         if( newSavedImages == NULL)
334             return ((SavedImage *)NULL);
335         GifFile->SavedImages = newSavedImages;
336     }
337     if (GifFile->SavedImages == NULL)
338         return ((SavedImage *)NULL);
339     else {
340         SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++];
341 
342         if (CopyFrom != NULL) {
343             memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
344 
345             /*
346              * Make our own allocated copies of the heap fields in the
347              * copied record.  This guards against potential aliasing
348              * problems.
349              */
350 
351             /* first, the local color map */
352             if (CopyFrom->ImageDesc.ColorMap != NULL) {
353                 sp->ImageDesc.ColorMap = GifMakeMapObject(
354                                          CopyFrom->ImageDesc.ColorMap->ColorCount,
355                                          CopyFrom->ImageDesc.ColorMap->Colors);
356                 if (sp->ImageDesc.ColorMap == NULL) {
357                     FreeLastSavedImage(GifFile);
358                     return (SavedImage *)(NULL);
359                 }
360             }
361 
362             /* next, the raster */
363             sp->RasterBits = (unsigned char *)reallocarray(NULL,
364                                                   (CopyFrom->ImageDesc.Height *
365                                                   CopyFrom->ImageDesc.Width),
366 						  sizeof(GifPixelType));
367             if (sp->RasterBits == NULL) {
368                 FreeLastSavedImage(GifFile);
369                 return (SavedImage *)(NULL);
370             }
371             memcpy(sp->RasterBits, CopyFrom->RasterBits,
372                    sizeof(GifPixelType) * CopyFrom->ImageDesc.Height *
373                    CopyFrom->ImageDesc.Width);
374 
375             /* finally, the extension blocks */
376             if (CopyFrom->ExtensionBlocks != NULL) {
377                 sp->ExtensionBlocks = (ExtensionBlock *)reallocarray(NULL,
378                                       CopyFrom->ExtensionBlockCount,
379 				      sizeof(ExtensionBlock));
380                 if (sp->ExtensionBlocks == NULL) {
381                     FreeLastSavedImage(GifFile);
382                     return (SavedImage *)(NULL);
383                 }
384                 memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks,
385                        sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount);
386             }
387         }
388         else {
389             memset((char *)sp, '\0', sizeof(SavedImage));
390         }
391 
392         return (sp);
393     }
394 }
395 
396 void
GifFreeSavedImages(GifFileType * GifFile)397 GifFreeSavedImages(GifFileType *GifFile)
398 {
399     SavedImage *sp;
400 
401     if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
402         return;
403     }
404     for (sp = GifFile->SavedImages;
405          sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
406         if (sp->ImageDesc.ColorMap != NULL) {
407             GifFreeMapObject(sp->ImageDesc.ColorMap);
408             sp->ImageDesc.ColorMap = NULL;
409         }
410 
411         if (sp->RasterBits != NULL)
412             free((char *)sp->RasterBits);
413 
414 	GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
415     }
416     free((char *)GifFile->SavedImages);
417     GifFile->SavedImages = NULL;
418 }
419 
420 /* end */
421