/src/giflib-code/gifalloc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | |
3 | | GIF construction tools |
4 | | |
5 | | ****************************************************************************/ |
6 | | // SPDX-License-Identifier: MIT |
7 | | // SPDX-FileCopyrightText: Copyright (C) Eric S. Raymond <esr@thyrsus.com> |
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 | 0 | #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 | 2.03k | int GifBitSize(int n) { |
24 | 2.03k | register int i; |
25 | | |
26 | 2.80k | for (i = 1; i <= 8; i++) { |
27 | 2.80k | if ((1 << i) >= n) { |
28 | 2.03k | break; |
29 | 2.03k | } |
30 | 2.80k | } |
31 | 2.03k | return (i); |
32 | 2.03k | } |
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 | 1.01k | ColorMapObject *GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) { |
43 | 1.01k | 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 | 1.01k | if (ColorCount != (1 << GifBitSize(ColorCount))) { |
49 | 0 | return ((ColorMapObject *)NULL); |
50 | 0 | } |
51 | | |
52 | 1.01k | Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); |
53 | 1.01k | if (Object == (ColorMapObject *)NULL) { |
54 | 0 | return ((ColorMapObject *)NULL); |
55 | 0 | } |
56 | | |
57 | 1.01k | Object->Colors = |
58 | 1.01k | (GifColorType *)calloc(ColorCount, sizeof(GifColorType)); |
59 | 1.01k | if (Object->Colors == (GifColorType *)NULL) { |
60 | 0 | free(Object); |
61 | 0 | return ((ColorMapObject *)NULL); |
62 | 0 | } |
63 | | |
64 | 1.01k | Object->ColorCount = ColorCount; |
65 | 1.01k | Object->BitsPerPixel = GifBitSize(ColorCount); |
66 | 1.01k | Object->SortFlag = false; |
67 | | |
68 | 1.01k | if (ColorMap != NULL) { |
69 | 469 | memcpy((char *)Object->Colors, (char *)ColorMap, |
70 | 469 | ColorCount * sizeof(GifColorType)); |
71 | 469 | } |
72 | | |
73 | 1.01k | return (Object); |
74 | 1.01k | } |
75 | | |
76 | | /******************************************************************************* |
77 | | Free a color map object |
78 | | *******************************************************************************/ |
79 | 1.00k | void GifFreeMapObject(ColorMapObject *Object) { |
80 | 1.00k | if (Object != NULL) { |
81 | 1.00k | (void)free(Object->Colors); |
82 | 1.00k | (void)free(Object); |
83 | 1.00k | } |
84 | 1.00k | } |
85 | | |
86 | | #ifdef DEBUG |
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 | | *******************************************************************************/ |
111 | | ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1, |
112 | | const ColorMapObject *ColorIn2, |
113 | 0 | GifPixelType ColorTransIn2[]) { |
114 | 0 | int i, j, CrntSlot, RoundUpTo, NewGifBitSize; |
115 | 0 | 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 | 0 | ColorUnion = GifMakeMapObject( |
125 | 0 | MAX(ColorIn1->ColorCount, ColorIn2->ColorCount) * 2, NULL); |
126 | |
|
127 | 0 | if (ColorUnion == NULL) { |
128 | 0 | return (NULL); |
129 | 0 | } |
130 | | |
131 | | /* |
132 | | * Copy ColorIn1 to ColorUnion. |
133 | | */ |
134 | 0 | for (i = 0; i < ColorIn1->ColorCount; i++) { |
135 | 0 | ColorUnion->Colors[i] = ColorIn1->Colors[i]; |
136 | 0 | } |
137 | 0 | 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 | 0 | while (ColorIn1->Colors[CrntSlot - 1].Red == 0 && |
147 | 0 | ColorIn1->Colors[CrntSlot - 1].Green == 0 && |
148 | 0 | ColorIn1->Colors[CrntSlot - 1].Blue == 0) { |
149 | 0 | CrntSlot--; |
150 | 0 | } |
151 | | |
152 | | /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */ |
153 | 0 | for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { |
154 | | /* Let's see if this color already exists: */ |
155 | 0 | for (j = 0; j < ColorIn1->ColorCount; j++) { |
156 | 0 | if (memcmp(&ColorIn1->Colors[j], &ColorIn2->Colors[i], |
157 | 0 | sizeof(GifColorType)) == 0) { |
158 | 0 | break; |
159 | 0 | } |
160 | 0 | } |
161 | |
|
162 | 0 | if (j < ColorIn1->ColorCount) { |
163 | 0 | ColorTransIn2[i] = j; /* color exists in Color1 */ |
164 | 0 | } else { |
165 | | /* Color is new - copy it to a new slot: */ |
166 | 0 | ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i]; |
167 | 0 | ColorTransIn2[i] = CrntSlot++; |
168 | 0 | } |
169 | 0 | } |
170 | |
|
171 | 0 | if (CrntSlot > 256) { |
172 | 0 | GifFreeMapObject(ColorUnion); |
173 | 0 | return ((ColorMapObject *)NULL); |
174 | 0 | } |
175 | | |
176 | 0 | NewGifBitSize = GifBitSize(CrntSlot); |
177 | 0 | RoundUpTo = (1 << NewGifBitSize); |
178 | |
|
179 | 0 | if (RoundUpTo != ColorUnion->ColorCount) { |
180 | 0 | 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 | 0 | for (j = CrntSlot; j < RoundUpTo; j++) { |
188 | 0 | Map[j].Red = Map[j].Green = Map[j].Blue = 0; |
189 | 0 | } |
190 | | |
191 | | /* perhaps we can shrink the map? */ |
192 | 0 | if (RoundUpTo < ColorUnion->ColorCount) { |
193 | 0 | GifColorType *new_map = (GifColorType *)reallocarray( |
194 | 0 | Map, RoundUpTo, sizeof(GifColorType)); |
195 | 0 | if (new_map == NULL) { |
196 | 0 | GifFreeMapObject(ColorUnion); |
197 | 0 | return ((ColorMapObject *)NULL); |
198 | 0 | } |
199 | 0 | ColorUnion->Colors = new_map; |
200 | 0 | } |
201 | 0 | } |
202 | | |
203 | 0 | ColorUnion->ColorCount = RoundUpTo; |
204 | 0 | ColorUnion->BitsPerPixel = NewGifBitSize; |
205 | |
|
206 | 0 | return (ColorUnion); |
207 | 0 | } |
208 | | |
209 | | /******************************************************************************* |
210 | | Apply a given color translation to the raster bits of an image |
211 | | *******************************************************************************/ |
212 | 0 | void GifApplyTranslation(SavedImage *Image, const GifPixelType Translation[]) { |
213 | 0 | register int i; |
214 | 0 | register int RasterSize = |
215 | 0 | Image->ImageDesc.Height * Image->ImageDesc.Width; |
216 | |
|
217 | 0 | for (i = 0; i < RasterSize; i++) { |
218 | 0 | Image->RasterBits[i] = Translation[Image->RasterBits[i]]; |
219 | 0 | } |
220 | 0 | } |
221 | | |
222 | | /****************************************************************************** |
223 | | Extension record functions |
224 | | ******************************************************************************/ |
225 | | int GifAddExtensionBlock(int *ExtensionBlockCount, |
226 | | ExtensionBlock **ExtensionBlocks, int Function, |
227 | 33.2k | unsigned int Len, unsigned char ExtData[]) { |
228 | 33.2k | ExtensionBlock *ep; |
229 | | |
230 | 33.2k | if (*ExtensionBlocks == NULL) { |
231 | 1.30k | *ExtensionBlocks = |
232 | 1.30k | (ExtensionBlock *)malloc(sizeof(ExtensionBlock)); |
233 | 31.9k | } else { |
234 | 31.9k | ExtensionBlock *ep_new = (ExtensionBlock *)reallocarray( |
235 | 31.9k | *ExtensionBlocks, (*ExtensionBlockCount + 1), |
236 | 31.9k | sizeof(ExtensionBlock)); |
237 | 31.9k | if (ep_new == NULL) { |
238 | 0 | return (GIF_ERROR); |
239 | 0 | } |
240 | 31.9k | *ExtensionBlocks = ep_new; |
241 | 31.9k | } |
242 | | |
243 | 33.2k | if (*ExtensionBlocks == NULL) { |
244 | 0 | return (GIF_ERROR); |
245 | 0 | } |
246 | | |
247 | 33.2k | ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++]; |
248 | | |
249 | 33.2k | ep->Function = Function; |
250 | 33.2k | ep->ByteCount = Len; |
251 | 33.2k | ep->Bytes = (GifByteType *)malloc(ep->ByteCount); |
252 | 33.2k | if (ep->Bytes == NULL) { |
253 | 0 | return (GIF_ERROR); |
254 | 0 | } |
255 | | |
256 | 33.2k | if (ExtData != NULL) { |
257 | 33.2k | memcpy(ep->Bytes, ExtData, Len); |
258 | 33.2k | } |
259 | | |
260 | 33.2k | return (GIF_OK); |
261 | 33.2k | } |
262 | | |
263 | | void GifFreeExtensions(int *ExtensionBlockCount, |
264 | 37.4k | ExtensionBlock **ExtensionBlocks) { |
265 | 37.4k | ExtensionBlock *ep; |
266 | | |
267 | 37.4k | if (*ExtensionBlocks == NULL) { |
268 | 36.1k | return; |
269 | 36.1k | } |
270 | | |
271 | 1.30k | for (ep = *ExtensionBlocks; |
272 | 34.5k | ep < (*ExtensionBlocks + *ExtensionBlockCount); ep++) { |
273 | 33.2k | (void)free((char *)ep->Bytes); |
274 | 33.2k | } |
275 | 1.30k | (void)free((char *)*ExtensionBlocks); |
276 | 1.30k | *ExtensionBlocks = NULL; |
277 | 1.30k | *ExtensionBlockCount = 0; |
278 | 1.30k | } |
279 | | |
280 | | /****************************************************************************** |
281 | | Image block allocation functions |
282 | | ******************************************************************************/ |
283 | | |
284 | | /* Private Function: |
285 | | * Frees the last image in the GifFile->SavedImages array |
286 | | */ |
287 | 0 | void FreeLastSavedImage(GifFileType *GifFile) { |
288 | 0 | SavedImage *sp; |
289 | |
|
290 | 0 | if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { |
291 | 0 | return; |
292 | 0 | } |
293 | | |
294 | | /* Remove one SavedImage from the GifFile */ |
295 | 0 | GifFile->ImageCount--; |
296 | 0 | sp = &GifFile->SavedImages[GifFile->ImageCount]; |
297 | | |
298 | | /* Deallocate its Colormap */ |
299 | 0 | if (sp->ImageDesc.ColorMap != NULL) { |
300 | 0 | GifFreeMapObject(sp->ImageDesc.ColorMap); |
301 | 0 | sp->ImageDesc.ColorMap = NULL; |
302 | 0 | } |
303 | | |
304 | | /* Deallocate the image data */ |
305 | 0 | if (sp->RasterBits != NULL) { |
306 | 0 | free((char *)sp->RasterBits); |
307 | 0 | } |
308 | | |
309 | | /* Deallocate any extensions */ |
310 | 0 | 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 | 0 | } |
320 | | |
321 | | /* |
322 | | * Append an image block to the SavedImages array |
323 | | */ |
324 | | SavedImage *GifMakeSavedImage(GifFileType *GifFile, |
325 | 0 | const SavedImage *CopyFrom) { |
326 | | // cppcheck-suppress ctunullpointer |
327 | 0 | if (GifFile->SavedImages == NULL) { |
328 | 0 | GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage)); |
329 | 0 | } else { |
330 | 0 | SavedImage *newSavedImages = (SavedImage *)reallocarray( |
331 | 0 | GifFile->SavedImages, (GifFile->ImageCount + 1), |
332 | 0 | sizeof(SavedImage)); |
333 | 0 | if (newSavedImages == NULL) { |
334 | 0 | return ((SavedImage *)NULL); |
335 | 0 | } |
336 | 0 | GifFile->SavedImages = newSavedImages; |
337 | 0 | } |
338 | 0 | if (GifFile->SavedImages == NULL) { |
339 | 0 | return ((SavedImage *)NULL); |
340 | 0 | } else { |
341 | 0 | SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++]; |
342 | |
|
343 | 0 | if (CopyFrom != NULL) { |
344 | 0 | 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 | 0 | if (CopyFrom->ImageDesc.ColorMap != NULL) { |
354 | 0 | sp->ImageDesc.ColorMap = GifMakeMapObject( |
355 | 0 | CopyFrom->ImageDesc.ColorMap->ColorCount, |
356 | 0 | CopyFrom->ImageDesc.ColorMap->Colors); |
357 | 0 | if (sp->ImageDesc.ColorMap == NULL) { |
358 | 0 | FreeLastSavedImage(GifFile); |
359 | 0 | return (SavedImage *)(NULL); |
360 | 0 | } |
361 | 0 | } |
362 | | |
363 | | /* next, the raster */ |
364 | 0 | sp->RasterBits = (unsigned char *)reallocarray( |
365 | 0 | NULL, |
366 | 0 | (CopyFrom->ImageDesc.Height * |
367 | 0 | CopyFrom->ImageDesc.Width), |
368 | 0 | sizeof(GifPixelType)); |
369 | 0 | if (sp->RasterBits == NULL) { |
370 | 0 | FreeLastSavedImage(GifFile); |
371 | 0 | return (SavedImage *)(NULL); |
372 | 0 | } |
373 | 0 | memcpy(sp->RasterBits, CopyFrom->RasterBits, |
374 | 0 | sizeof(GifPixelType) * |
375 | 0 | CopyFrom->ImageDesc.Height * |
376 | 0 | CopyFrom->ImageDesc.Width); |
377 | | |
378 | | /* finally, the extension blocks */ |
379 | 0 | if (CopyFrom->ExtensionBlocks != NULL) { |
380 | 0 | sp->ExtensionBlocks = |
381 | 0 | (ExtensionBlock *)reallocarray( |
382 | 0 | NULL, CopyFrom->ExtensionBlockCount, |
383 | 0 | sizeof(ExtensionBlock)); |
384 | 0 | if (sp->ExtensionBlocks == NULL) { |
385 | 0 | FreeLastSavedImage(GifFile); |
386 | 0 | return (SavedImage *)(NULL); |
387 | 0 | } |
388 | 0 | memcpy(sp->ExtensionBlocks, |
389 | 0 | CopyFrom->ExtensionBlocks, |
390 | 0 | sizeof(ExtensionBlock) * |
391 | 0 | CopyFrom->ExtensionBlockCount); |
392 | 0 | } |
393 | 0 | } else { |
394 | 0 | memset((char *)sp, '\0', sizeof(SavedImage)); |
395 | 0 | } |
396 | | |
397 | 0 | return (sp); |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | 569 | void GifFreeSavedImages(GifFileType *GifFile) { |
402 | 569 | SavedImage *sp; |
403 | | |
404 | 569 | if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { |
405 | 0 | return; |
406 | 0 | } |
407 | 569 | for (sp = GifFile->SavedImages; |
408 | 37.1k | sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { |
409 | 36.5k | if (sp->ImageDesc.ColorMap != NULL) { |
410 | 454 | GifFreeMapObject(sp->ImageDesc.ColorMap); |
411 | 454 | sp->ImageDesc.ColorMap = NULL; |
412 | 454 | } |
413 | | |
414 | 36.5k | if (sp->RasterBits != NULL) { |
415 | 36.5k | free((char *)sp->RasterBits); |
416 | 36.5k | } |
417 | | |
418 | 36.5k | GifFreeExtensions(&sp->ExtensionBlockCount, |
419 | 36.5k | &sp->ExtensionBlocks); |
420 | 36.5k | } |
421 | 569 | free((char *)GifFile->SavedImages); |
422 | 569 | GifFile->SavedImages = NULL; |
423 | 569 | } |
424 | | |
425 | | /* end */ |