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