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 */
20int
21GifBitSize(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 */
39ColorMapObject *
40GifMakeMapObject(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/*******************************************************************************
73Free a color map object
74*******************************************************************************/
75void
76GifFreeMapObject(ColorMapObject *Object)
77{
78    if (Object != NULL) {
79        (void)free(Object->Colors);
80        (void)free(Object);
81    }
82}
83
84#ifdef DEBUG
85void
86DumpColorMap(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*******************************************************************************/
112ColorMapObject *
113GifUnionColorMap(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*******************************************************************************/
203void
204GifApplyTranslation(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******************************************************************************/
216int
217GifAddExtensionBlock(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
250void
251GifFreeExtensions(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 */
275void
276FreeLastSavedImage(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 */
311SavedImage *
312GifMakeSavedImage(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
378void
379GifFreeSavedImages(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