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 |
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 * |
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 |
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 |
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 * |
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 |
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 |
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 |
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 |
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 * |
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 |
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 | |