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