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