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