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 Object = NULL;
91 }
92 }
93
94 #ifdef DEBUG
95 void
DumpColorMap(ColorMapObject * Object,FILE * fp)96 DumpColorMap(ColorMapObject * Object,
97 FILE * fp) {
98
99 if (Object) {
100 int i, j, Len = Object->ColorCount;
101
102 for (i = 0; i < Len; i += 4) {
103 for (j = 0; j < 4 && j < Len; j++) {
104 fprintf(fp, "%3d: %02x %02x %02x ", i + j,
105 Object->Colors[i + j].Red,
106 Object->Colors[i + j].Green,
107 Object->Colors[i + j].Blue);
108 }
109 fprintf(fp, "\n");
110 }
111 }
112 }
113 #endif /* DEBUG */
114
115 /*
116 * Compute the union of two given color maps and return it. If result can't
117 * fit into 256 colors, NULL is returned, the allocated union otherwise.
118 * ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are
119 * copied iff they didn't exist before. ColorTransIn2 maps the old
120 * ColorIn2 into ColorUnion color map table.
121 */
122 ColorMapObject *
UnionColorMap(const ColorMapObject * ColorIn1,const ColorMapObject * ColorIn2,GifPixelType ColorTransIn2[])123 UnionColorMap(const ColorMapObject * ColorIn1,
124 const ColorMapObject * ColorIn2,
125 GifPixelType ColorTransIn2[]) {
126
127 int i, j, CrntSlot, RoundUpTo, NewBitSize;
128 ColorMapObject *ColorUnion;
129
130 /*
131 * Allocate table which will hold the result for sure.
132 */
133 ColorUnion = MakeMapObject(MAX(ColorIn1->ColorCount,
134 ColorIn2->ColorCount) * 2, NULL);
135
136 if (ColorUnion == NULL)
137 return (NULL);
138
139 /* Copy ColorIn1 to ColorUnionSize; */
140 /*** FIXME: What if there are duplicate entries into the colormap to begin
141 * with? */
142 for (i = 0; i < ColorIn1->ColorCount; i++)
143 ColorUnion->Colors[i] = ColorIn1->Colors[i];
144 CrntSlot = ColorIn1->ColorCount;
145
146 /*
147 * Potentially obnoxious hack:
148 *
149 * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
150 * of table 1. This is very useful if your display is limited to
151 * 16 colors.
152 */
153 while (ColorIn1->Colors[CrntSlot - 1].Red == 0
154 && ColorIn1->Colors[CrntSlot - 1].Green == 0
155 && ColorIn1->Colors[CrntSlot - 1].Blue == 0)
156 CrntSlot--;
157
158 /* Copy ColorIn2 to ColorUnionSize (use old colors if they exist): */
159 for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
160 /* Let's see if this color already exists: */
161 /*** FIXME: Will it ever occur that ColorIn2 will contain duplicate
162 * entries? So we should search from 0 to CrntSlot rather than
163 * ColorIn1->ColorCount?
164 */
165 for (j = 0; j < ColorIn1->ColorCount; j++)
166 if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i],
167 sizeof(GifColorType)) == 0)
168 break;
169
170 if (j < ColorIn1->ColorCount)
171 ColorTransIn2[i] = j; /* color exists in Color1 */
172 else {
173 /* Color is new - copy it to a new slot: */
174 ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
175 ColorTransIn2[i] = CrntSlot++;
176 }
177 }
178
179 if (CrntSlot > 256) {
180 FreeMapObject(ColorUnion);
181 return ((ColorMapObject *) NULL);
182 }
183
184 NewBitSize = BitSize(CrntSlot);
185 RoundUpTo = (1 << NewBitSize);
186
187 if (RoundUpTo != ColorUnion->ColorCount) {
188 register GifColorType *Map = ColorUnion->Colors;
189
190 /*
191 * Zero out slots up to next power of 2.
192 * We know these slots exist because of the way ColorUnion's
193 * start dimension was computed.
194 */
195 for (j = CrntSlot; j < RoundUpTo; j++)
196 Map[j].Red = Map[j].Green = Map[j].Blue = 0;
197
198 /* perhaps we can shrink the map? */
199 if (RoundUpTo < ColorUnion->ColorCount)
200 ColorUnion->Colors = (GifColorType *)realloc(Map,
201 sizeof(GifColorType) * RoundUpTo);
202 }
203
204 ColorUnion->ColorCount = RoundUpTo;
205 ColorUnion->BitsPerPixel = NewBitSize;
206
207 return (ColorUnion);
208 }
209
210 /*
211 * Apply a given color translation to the raster bits of an image
212 */
213 void
ApplyTranslation(SavedImage * Image,GifPixelType Translation[])214 ApplyTranslation(SavedImage * Image,
215 GifPixelType Translation[]) {
216
217 register int i;
218 register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width;
219
220 for (i = 0; i < RasterSize; i++)
221 Image->RasterBits[i] = Translation[Image->RasterBits[i]];
222 }
223
224 /******************************************************************************
225 * Extension record functions
226 *****************************************************************************/
227
228 void
MakeExtension(SavedImage * New,int Function)229 MakeExtension(SavedImage * New,
230 int Function) {
231
232 New->Function = Function;
233 /*** FIXME:
234 * Someday we might have to deal with multiple extensions.
235 * ??? Was this a note from Gershon or from me? Does the multiple
236 * extension blocks solve this or do we need multiple Functions? Or is
237 * this an obsolete function? (People should use AddExtensionBlock
238 * instead?)
239 * Looks like AddExtensionBlock needs to take the int Function argument
240 * then it can take the place of this function. Right now people have to
241 * use both. Fix AddExtensionBlock and add this to the deprecation list.
242 */
243 }
244
245 int
AddExtensionBlock(SavedImage * New,int Len,unsigned char ExtData[])246 AddExtensionBlock(SavedImage * New,
247 int Len,
248 unsigned char ExtData[]) {
249
250 ExtensionBlock *ep;
251
252 if (New->ExtensionBlocks == NULL)
253 New->ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock));
254 else
255 New->ExtensionBlocks = (ExtensionBlock *)realloc(New->ExtensionBlocks,
256 sizeof(ExtensionBlock) *
257 (New->ExtensionBlockCount + 1));
258
259 if (New->ExtensionBlocks == NULL)
260 return (GIF_ERROR);
261
262 ep = &New->ExtensionBlocks[New->ExtensionBlockCount++];
263
264 ep->ByteCount=Len;
265 ep->Bytes = (char *)malloc(ep->ByteCount);
266 if (ep->Bytes == NULL)
267 return (GIF_ERROR);
268
269 if (ExtData) {
270 memcpy(ep->Bytes, ExtData, Len);
271 ep->Function = New->Function;
272 }
273
274 return (GIF_OK);
275 }
276
277 void
FreeExtension(SavedImage * Image)278 FreeExtension(SavedImage * Image)
279 {
280 ExtensionBlock *ep;
281
282 if ((Image == NULL) || (Image->ExtensionBlocks == NULL)) {
283 return;
284 }
285 for (ep = Image->ExtensionBlocks;
286 ep < (Image->ExtensionBlocks + Image->ExtensionBlockCount); ep++)
287 (void)free((char *)ep->Bytes);
288 free((char *)Image->ExtensionBlocks);
289 Image->ExtensionBlocks = NULL;
290 }
291
292 /******************************************************************************
293 * Image block allocation functions
294 ******************************************************************************/
295
296 /* Private Function:
297 * Frees the last image in the GifFile->SavedImages array
298 */
299 void
FreeLastSavedImage(GifFileType * GifFile)300 FreeLastSavedImage(GifFileType *GifFile) {
301
302 SavedImage *sp;
303
304 if ((GifFile == NULL) || (GifFile->SavedImages == NULL))
305 return;
306
307 /* Remove one SavedImage from the GifFile */
308 GifFile->ImageCount--;
309 sp = &GifFile->SavedImages[GifFile->ImageCount];
310
311 /* Deallocate its Colormap */
312 if (sp->ImageDesc.ColorMap)
313 FreeMapObject(sp->ImageDesc.ColorMap);
314
315 /* Deallocate the image data */
316 if (sp->RasterBits)
317 free((char *)sp->RasterBits);
318
319 /* Deallocate any extensions */
320 if (sp->ExtensionBlocks)
321 FreeExtension(sp);
322
323 /*** FIXME: We could realloc the GifFile->SavedImages structure but is
324 * there a point to it? Saves some memory but we'd have to do it every
325 * time. If this is used in FreeSavedImages then it would be inefficient
326 * (The whole array is going to be deallocated.) If we just use it when
327 * we want to free the last Image it's convenient to do it here.
328 */
329 }
330
331 /*
332 * Append an image block to the SavedImages array
333 */
334 SavedImage *
MakeSavedImage(GifFileType * GifFile,const SavedImage * CopyFrom)335 MakeSavedImage(GifFileType * GifFile,
336 const SavedImage * CopyFrom) {
337
338 SavedImage *sp;
339
340 if (GifFile->SavedImages == NULL)
341 GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
342 else
343 GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages,
344 sizeof(SavedImage) * (GifFile->ImageCount + 1));
345
346 if (GifFile->SavedImages == NULL)
347 return ((SavedImage *)NULL);
348 else {
349 sp = &GifFile->SavedImages[GifFile->ImageCount++];
350 memset((char *)sp, '\0', sizeof(SavedImage));
351
352 if (CopyFrom) {
353 memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
354
355 /*
356 * Make our own allocated copies of the heap fields in the
357 * copied record. This guards against potential aliasing
358 * problems.
359 */
360
361 /* first, the local color map */
362 if (sp->ImageDesc.ColorMap) {
363 sp->ImageDesc.ColorMap = MakeMapObject(
364 CopyFrom->ImageDesc.ColorMap->ColorCount,
365 CopyFrom->ImageDesc.ColorMap->Colors);
366 if (sp->ImageDesc.ColorMap == NULL) {
367 FreeLastSavedImage(GifFile);
368 return (SavedImage *)(NULL);
369 }
370 }
371
372 /* next, the raster */
373 sp->RasterBits = (unsigned char *)malloc(sizeof(GifPixelType) *
374 CopyFrom->ImageDesc.Height *
375 CopyFrom->ImageDesc.Width);
376 if (sp->RasterBits == NULL) {
377 FreeLastSavedImage(GifFile);
378 return (SavedImage *)(NULL);
379 }
380 memcpy(sp->RasterBits, CopyFrom->RasterBits,
381 sizeof(GifPixelType) * CopyFrom->ImageDesc.Height *
382 CopyFrom->ImageDesc.Width);
383
384 /* finally, the extension blocks */
385 if (sp->ExtensionBlocks) {
386 sp->ExtensionBlocks = (ExtensionBlock *)malloc(
387 sizeof(ExtensionBlock) *
388 CopyFrom->ExtensionBlockCount);
389 if (sp->ExtensionBlocks == NULL) {
390 FreeLastSavedImage(GifFile);
391 return (SavedImage *)(NULL);
392 }
393 memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks,
394 sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount);
395
396 /*
397 * For the moment, the actual blocks can take their
398 * chances with free(). We'll fix this later.
399 *** FIXME: [Better check this out... Toshio]
400 */
401 }
402 }
403
404 return (sp);
405 }
406 }
407
408 void
FreeSavedImages(GifFileType * GifFile)409 FreeSavedImages(GifFileType * GifFile) {
410
411 SavedImage *sp;
412
413 if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
414 return;
415 }
416 for (sp = GifFile->SavedImages;
417 sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
418 if (sp->ImageDesc.ColorMap)
419 FreeMapObject(sp->ImageDesc.ColorMap);
420
421 if (sp->RasterBits)
422 free((char *)sp->RasterBits);
423
424 if (sp->ExtensionBlocks)
425 FreeExtension(sp);
426 }
427 free((char *)GifFile->SavedImages);
428 GifFile->SavedImages=NULL;
429 }
430