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 */
20 int
GifBitSize(int n)21 GifBitSize(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 */
39 ColorMapObject *
GifMakeMapObject(int ColorCount,const GifColorType * ColorMap)40 GifMakeMapObject(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 /*******************************************************************************
73 Free a color map object
74 *******************************************************************************/
75 void
GifFreeMapObject(ColorMapObject * Object)76 GifFreeMapObject(ColorMapObject *Object)
77 {
78 if (Object != NULL) {
79 (void)free(Object->Colors);
80 (void)free(Object);
81 }
82 }
83
84 #ifdef DEBUG
85 void
DumpColorMap(ColorMapObject * Object,FILE * fp)86 DumpColorMap(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 *******************************************************************************/
112 ColorMapObject *
GifUnionColorMap(const ColorMapObject * ColorIn1,const ColorMapObject * ColorIn2,GifPixelType ColorTransIn2[])113 GifUnionColorMap(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 *******************************************************************************/
203 void
GifApplyTranslation(SavedImage * Image,GifPixelType Translation[])204 GifApplyTranslation(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 ******************************************************************************/
216 int
GifAddExtensionBlock(int * ExtensionBlockCount,ExtensionBlock ** ExtensionBlocks,int Function,unsigned int Len,unsigned char ExtData[])217 GifAddExtensionBlock(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
250 void
GifFreeExtensions(int * ExtensionBlockCount,ExtensionBlock ** ExtensionBlocks)251 GifFreeExtensions(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 */
275 void
FreeLastSavedImage(GifFileType * GifFile)276 FreeLastSavedImage(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 */
311 SavedImage *
GifMakeSavedImage(GifFileType * GifFile,const SavedImage * CopyFrom)312 GifMakeSavedImage(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
378 void
GifFreeSavedImages(GifFileType * GifFile)379 GifFreeSavedImages(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