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