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