• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         /*** FIXME:
91          * When we are willing to break API we need to make this function
92          * FreeMapObject(ColorMapObject **Object)
93          * and do this assignment to NULL here:
94          * *Object = NULL;
95          */
96     }
97 }
98 
99 #ifdef DEBUG
100 void
DumpColorMap(ColorMapObject * Object,FILE * fp)101 DumpColorMap(ColorMapObject * Object,
102              FILE * fp) {
103 
104     if (Object) {
105         int i, j, Len = Object->ColorCount;
106 
107         for (i = 0; i < Len; i += 4) {
108             for (j = 0; j < 4 && j < Len; j++) {
109                 fprintf(fp, "%3d: %02x %02x %02x   ", i + j,
110                         Object->Colors[i + j].Red,
111                         Object->Colors[i + j].Green,
112                         Object->Colors[i + j].Blue);
113             }
114             fprintf(fp, "\n");
115         }
116     }
117 }
118 #endif /* DEBUG */
119 
120 /*
121  * Compute the union of two given color maps and return it.  If result can't
122  * fit into 256 colors, NULL is returned, the allocated union otherwise.
123  * ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are
124  * copied iff they didn't exist before.  ColorTransIn2 maps the old
125  * ColorIn2 into ColorUnion color map table.
126  */
127 ColorMapObject *
UnionColorMap(const ColorMapObject * ColorIn1,const ColorMapObject * ColorIn2,GifPixelType ColorTransIn2[])128 UnionColorMap(const ColorMapObject * ColorIn1,
129               const ColorMapObject * ColorIn2,
130               GifPixelType ColorTransIn2[]) {
131 
132     int i, j, CrntSlot, RoundUpTo, NewBitSize;
133     ColorMapObject *ColorUnion;
134 
135     /*
136      * Allocate table which will hold the result for sure.
137      */
138     ColorUnion = MakeMapObject(MAX(ColorIn1->ColorCount,
139                                ColorIn2->ColorCount) * 2, NULL);
140 
141     if (ColorUnion == NULL)
142         return (NULL);
143 
144     /* Copy ColorIn1 to ColorUnionSize; */
145     /*** FIXME: What if there are duplicate entries into the colormap to begin
146      * with? */
147     for (i = 0; i < ColorIn1->ColorCount; i++)
148         ColorUnion->Colors[i] = ColorIn1->Colors[i];
149     CrntSlot = ColorIn1->ColorCount;
150 
151     /*
152      * Potentially obnoxious hack:
153      *
154      * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
155      * of table 1.  This is very useful if your display is limited to
156      * 16 colors.
157      */
158     while (ColorIn1->Colors[CrntSlot - 1].Red == 0
159            && ColorIn1->Colors[CrntSlot - 1].Green == 0
160            && ColorIn1->Colors[CrntSlot - 1].Blue == 0)
161         CrntSlot--;
162 
163     /* Copy ColorIn2 to ColorUnionSize (use old colors if they exist): */
164     for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
165         /* Let's see if this color already exists: */
166         /*** FIXME: Will it ever occur that ColorIn2 will contain duplicate
167          * entries?  So we should search from 0 to CrntSlot rather than
168          * ColorIn1->ColorCount?
169          */
170         for (j = 0; j < ColorIn1->ColorCount; j++)
171             if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i],
172                         sizeof(GifColorType)) == 0)
173                 break;
174 
175         if (j < ColorIn1->ColorCount)
176             ColorTransIn2[i] = j;    /* color exists in Color1 */
177         else {
178             /* Color is new - copy it to a new slot: */
179             ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
180             ColorTransIn2[i] = CrntSlot++;
181         }
182     }
183 
184     if (CrntSlot > 256) {
185         FreeMapObject(ColorUnion);
186         return ((ColorMapObject *) NULL);
187     }
188 
189     NewBitSize = BitSize(CrntSlot);
190     RoundUpTo = (1 << NewBitSize);
191 
192     if (RoundUpTo != ColorUnion->ColorCount) {
193         register GifColorType *Map = ColorUnion->Colors;
194 
195         /*
196          * Zero out slots up to next power of 2.
197          * We know these slots exist because of the way ColorUnion's
198          * start dimension was computed.
199          */
200         for (j = CrntSlot; j < RoundUpTo; j++)
201             Map[j].Red = Map[j].Green = Map[j].Blue = 0;
202 
203         /* perhaps we can shrink the map? */
204         if (RoundUpTo < ColorUnion->ColorCount)
205             ColorUnion->Colors = (GifColorType *)realloc(Map,
206                                  sizeof(GifColorType) * RoundUpTo);
207     }
208 
209     ColorUnion->ColorCount = RoundUpTo;
210     ColorUnion->BitsPerPixel = NewBitSize;
211 
212     return (ColorUnion);
213 }
214 
215 /*
216  * Apply a given color translation to the raster bits of an image
217  */
218 void
ApplyTranslation(SavedImage * Image,GifPixelType Translation[])219 ApplyTranslation(SavedImage * Image,
220                  GifPixelType Translation[]) {
221 
222     register int i;
223     register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width;
224 
225     for (i = 0; i < RasterSize; i++)
226         Image->RasterBits[i] = Translation[Image->RasterBits[i]];
227 }
228 
229 /******************************************************************************
230  * Extension record functions
231  *****************************************************************************/
232 
233 void
MakeExtension(SavedImage * New,int Function)234 MakeExtension(SavedImage * New,
235               int Function) {
236 
237     New->Function = Function;
238     /*** FIXME:
239      * Someday we might have to deal with multiple extensions.
240      * ??? Was this a note from Gershon or from me?  Does the multiple
241      * extension blocks solve this or do we need multiple Functions?  Or is
242      * this an obsolete function?  (People should use AddExtensionBlock
243      * instead?)
244      * Looks like AddExtensionBlock needs to take the int Function argument
245      * then it can take the place of this function.  Right now people have to
246      * use both.  Fix AddExtensionBlock and add this to the deprecation list.
247      */
248 }
249 
250 int
AddExtensionBlock(SavedImage * New,int Len,unsigned char ExtData[])251 AddExtensionBlock(SavedImage * New,
252                   int Len,
253                   unsigned char ExtData[]) {
254 
255     ExtensionBlock *ep;
256 
257     if (New->ExtensionBlocks == NULL)
258         New->ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock));
259     else
260         New->ExtensionBlocks = (ExtensionBlock *)realloc(New->ExtensionBlocks,
261                                       sizeof(ExtensionBlock) *
262                                       (New->ExtensionBlockCount + 1));
263 
264     if (New->ExtensionBlocks == NULL)
265         return (GIF_ERROR);
266 
267     ep = &New->ExtensionBlocks[New->ExtensionBlockCount++];
268 
269     ep->ByteCount=Len;
270     ep->Bytes = (char *)malloc(ep->ByteCount);
271     if (ep->Bytes == NULL)
272         return (GIF_ERROR);
273 
274     if (ExtData) {
275         memcpy(ep->Bytes, ExtData, Len);
276         ep->Function = New->Function;
277     }
278 
279     return (GIF_OK);
280 }
281 
282 void
FreeExtension(SavedImage * Image)283 FreeExtension(SavedImage * Image)
284 {
285     ExtensionBlock *ep;
286 
287     if ((Image == NULL) || (Image->ExtensionBlocks == NULL)) {
288         return;
289     }
290     for (ep = Image->ExtensionBlocks;
291          ep < (Image->ExtensionBlocks + Image->ExtensionBlockCount); ep++)
292         (void)free((char *)ep->Bytes);
293     free((char *)Image->ExtensionBlocks);
294     Image->ExtensionBlocks = NULL;
295 }
296 
297 /******************************************************************************
298  * Image block allocation functions
299 ******************************************************************************/
300 
301 /* Private Function:
302  * Frees the last image in the GifFile->SavedImages array
303  */
304 void
FreeLastSavedImage(GifFileType * GifFile)305 FreeLastSavedImage(GifFileType *GifFile) {
306 
307     SavedImage *sp;
308 
309     if ((GifFile == NULL) || (GifFile->SavedImages == NULL))
310         return;
311 
312     /* Remove one SavedImage from the GifFile */
313     GifFile->ImageCount--;
314     sp = &GifFile->SavedImages[GifFile->ImageCount];
315 
316     /* Deallocate its Colormap */
317     if (sp->ImageDesc.ColorMap) {
318         FreeMapObject(sp->ImageDesc.ColorMap);
319         sp->ImageDesc.ColorMap = NULL;
320     }
321 
322     /* Deallocate the image data */
323     if (sp->RasterBits)
324         free((char *)sp->RasterBits);
325 
326     /* Deallocate any extensions */
327     if (sp->ExtensionBlocks)
328         FreeExtension(sp);
329 
330     /*** FIXME: We could realloc the GifFile->SavedImages structure but is
331      * there a point to it? Saves some memory but we'd have to do it every
332      * time.  If this is used in FreeSavedImages then it would be inefficient
333      * (The whole array is going to be deallocated.)  If we just use it when
334      * we want to free the last Image it's convenient to do it here.
335      */
336 }
337 
338 /*
339  * Append an image block to the SavedImages array
340  */
341 SavedImage *
MakeSavedImage(GifFileType * GifFile,const SavedImage * CopyFrom)342 MakeSavedImage(GifFileType * GifFile,
343                const SavedImage * CopyFrom) {
344 
345     SavedImage *sp;
346 
347     if (GifFile->SavedImages == NULL)
348         GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
349     else
350         GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages,
351                                sizeof(SavedImage) * (GifFile->ImageCount + 1));
352 
353     if (GifFile->SavedImages == NULL)
354         return ((SavedImage *)NULL);
355     else {
356         sp = &GifFile->SavedImages[GifFile->ImageCount++];
357         memset((char *)sp, '\0', sizeof(SavedImage));
358 
359         if (CopyFrom) {
360             memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
361 
362             /*
363              * Make our own allocated copies of the heap fields in the
364              * copied record.  This guards against potential aliasing
365              * problems.
366              */
367 
368             /* first, the local color map */
369             if (sp->ImageDesc.ColorMap) {
370                 sp->ImageDesc.ColorMap = MakeMapObject(
371                                          CopyFrom->ImageDesc.ColorMap->ColorCount,
372                                          CopyFrom->ImageDesc.ColorMap->Colors);
373                 if (sp->ImageDesc.ColorMap == NULL) {
374                     FreeLastSavedImage(GifFile);
375                     return (SavedImage *)(NULL);
376                 }
377             }
378 
379             /* next, the raster */
380             sp->RasterBits = (unsigned char *)malloc(sizeof(GifPixelType) *
381                                                    CopyFrom->ImageDesc.Height *
382                                                    CopyFrom->ImageDesc.Width);
383             if (sp->RasterBits == NULL) {
384                 FreeLastSavedImage(GifFile);
385                 return (SavedImage *)(NULL);
386             }
387             memcpy(sp->RasterBits, CopyFrom->RasterBits,
388                    sizeof(GifPixelType) * CopyFrom->ImageDesc.Height *
389                    CopyFrom->ImageDesc.Width);
390 
391             /* finally, the extension blocks */
392             if (sp->ExtensionBlocks) {
393                 sp->ExtensionBlocks = (ExtensionBlock *)malloc(
394                                       sizeof(ExtensionBlock) *
395                                       CopyFrom->ExtensionBlockCount);
396                 if (sp->ExtensionBlocks == NULL) {
397                     FreeLastSavedImage(GifFile);
398                     return (SavedImage *)(NULL);
399                 }
400                 memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks,
401                        sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount);
402 
403                 /*
404                  * For the moment, the actual blocks can take their
405                  * chances with free().  We'll fix this later.
406                  *** FIXME: [Better check this out... Toshio]
407                  * 2004 May 27: Looks like this was an ESR note.
408                  * It means the blocks are shallow copied from InFile to
409                  * OutFile.  However, I don't see that in this code....
410                  * Did ESR fix it but never remove this note (And other notes
411                  * in gifspnge?)
412                  */
413             }
414         }
415 
416         return (sp);
417     }
418 }
419 
420 void
FreeSavedImages(GifFileType * GifFile)421 FreeSavedImages(GifFileType * GifFile) {
422 
423     SavedImage *sp;
424 
425     if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
426         return;
427     }
428     for (sp = GifFile->SavedImages;
429          sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
430         if (sp->ImageDesc.ColorMap) {
431             FreeMapObject(sp->ImageDesc.ColorMap);
432             sp->ImageDesc.ColorMap = NULL;
433         }
434 
435         if (sp->RasterBits)
436             free((char *)sp->RasterBits);
437 
438         if (sp->ExtensionBlocks)
439             FreeExtension(sp);
440     }
441     free((char *)GifFile->SavedImages);
442     GifFile->SavedImages=NULL;
443 }
444