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