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