gifalloc.c revision 1bc4596b116b3c829824c8b929ce48f864ca4a3c
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 */
29int
30BitSize(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 */
48ColorMapObject *
49MakeMapObject(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 */
84void
85FreeMapObject(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
100void
101DumpColorMap(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 */
127ColorMapObject *
128UnionColorMap(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 */
218void
219ApplyTranslation(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
233void
234MakeExtension(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
250int
251AddExtensionBlock(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
282void
283FreeExtension(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 */
304void
305FreeLastSavedImage(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 */
341SavedImage *
342MakeSavedImage(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
420void
421FreeSavedImages(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