blob: 3b5186807ddabed2d0b058cad5852c0c4b6de9ec [file] [log] [blame]
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -08001/*****************************************************************************
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -08002
Chris Craik9aef3ea2013-06-24 19:34:25 -07003 GIF construction tools
4
5****************************************************************************/
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -08006
7#include <stdlib.h>
8#include <stdio.h>
9#include <string.h>
Chris Craik9aef3ea2013-06-24 19:34:25 -070010
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080011#include "gif_lib.h"
12
13#define MAX(x, y) (((x) > (y)) ? (x) : (y))
14
15/******************************************************************************
Chris Craik9aef3ea2013-06-24 19:34:25 -070016 Miscellaneous utility functions
17******************************************************************************/
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080018
19/* return smallest bitfield size n will fit in */
20int
Chris Craik9aef3ea2013-06-24 19:34:25 -070021GifBitSize(int n)
22{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080023 register int i;
24
25 for (i = 1; i <= 8; i++)
26 if ((1 << i) >= n)
27 break;
28 return (i);
29}
30
31/******************************************************************************
Chris Craik9aef3ea2013-06-24 19:34:25 -070032 Color map object functions
33******************************************************************************/
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080034
35/*
36 * Allocate a color map of given size; initialize with contents of
37 * ColorMap if that pointer is non-NULL.
38 */
39ColorMapObject *
Chris Craik9aef3ea2013-06-24 19:34:25 -070040GifMakeMapObject(int ColorCount, const GifColorType *ColorMap)
41{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080042 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? */
Chris Craik9aef3ea2013-06-24 19:34:25 -070046 if (ColorCount != (1 << GifBitSize(ColorCount))) {
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080047 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) {
Chris Craik9aef3ea2013-06-24 19:34:25 -070057 free(Object);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080058 return ((ColorMapObject *) NULL);
59 }
60
61 Object->ColorCount = ColorCount;
Chris Craik9aef3ea2013-06-24 19:34:25 -070062 Object->BitsPerPixel = GifBitSize(ColorCount);
Leon Scroggins IIIdf596d32017-03-13 14:03:21 -040063 Object->SortFlag = false;
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080064
Chris Craik9aef3ea2013-06-24 19:34:25 -070065 if (ColorMap != NULL) {
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080066 memcpy((char *)Object->Colors,
67 (char *)ColorMap, ColorCount * sizeof(GifColorType));
68 }
69
70 return (Object);
71}
72
Chris Craik9aef3ea2013-06-24 19:34:25 -070073/*******************************************************************************
74Free a color map object
75*******************************************************************************/
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080076void
Chris Craik9aef3ea2013-06-24 19:34:25 -070077GifFreeMapObject(ColorMapObject *Object)
78{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080079 if (Object != NULL) {
Chris Craik9aef3ea2013-06-24 19:34:25 -070080 (void)free(Object->Colors);
81 (void)free(Object);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080082 }
83}
84
85#ifdef DEBUG
86void
Chris Craik9aef3ea2013-06-24 19:34:25 -070087DumpColorMap(ColorMapObject *Object,
88 FILE * fp)
89{
90 if (Object != NULL) {
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080091 int i, j, Len = Object->ColorCount;
92
93 for (i = 0; i < Len; i += 4) {
94 for (j = 0; j < 4 && j < Len; j++) {
Chris Craik9aef3ea2013-06-24 19:34:25 -070095 (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);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080099 }
Chris Craik9aef3ea2013-06-24 19:34:25 -0700100 (void)fprintf(fp, "\n");
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800101 }
102 }
103}
104#endif /* DEBUG */
105
Chris Craik9aef3ea2013-06-24 19:34:25 -0700106/*******************************************************************************
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*******************************************************************************/
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800113ColorMapObject *
Chris Craik9aef3ea2013-06-24 19:34:25 -0700114GifUnionColorMap(const ColorMapObject *ColorIn1,
115 const ColorMapObject *ColorIn2,
116 GifPixelType ColorTransIn2[])
117{
118 int i, j, CrntSlot, RoundUpTo, NewGifBitSize;
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800119 ColorMapObject *ColorUnion;
120
Chris Craik9aef3ea2013-06-24 19:34:25 -0700121 /*
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.
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800125 */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700126
127 /* Allocate table which will hold the result for sure. */
128 ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount,
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800129 ColorIn2->ColorCount) * 2, NULL);
130
131 if (ColorUnion == NULL)
132 return (NULL);
133
Chris Craik9aef3ea2013-06-24 19:34:25 -0700134 /*
135 * Copy ColorIn1 to ColorUnion.
136 */
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800137 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
Chris Craik9aef3ea2013-06-24 19:34:25 -0700153 /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800154 for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
155 /* Let's see if this color already exists: */
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800156 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) {
Chris Craik9aef3ea2013-06-24 19:34:25 -0700171 GifFreeMapObject(ColorUnion);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800172 return ((ColorMapObject *) NULL);
173 }
174
Chris Craik9aef3ea2013-06-24 19:34:25 -0700175 NewGifBitSize = GifBitSize(CrntSlot);
176 RoundUpTo = (1 << NewGifBitSize);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800177
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? */
Leon Scroggins IIIdf596d32017-03-13 14:03:21 -0400190 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 }
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800199 }
200
201 ColorUnion->ColorCount = RoundUpTo;
Chris Craik9aef3ea2013-06-24 19:34:25 -0700202 ColorUnion->BitsPerPixel = NewGifBitSize;
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800203
204 return (ColorUnion);
205}
206
Chris Craik9aef3ea2013-06-24 19:34:25 -0700207/*******************************************************************************
208 Apply a given color translation to the raster bits of an image
209*******************************************************************************/
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800210void
Chris Craik9aef3ea2013-06-24 19:34:25 -0700211GifApplyTranslation(SavedImage *Image, GifPixelType Translation[])
212{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800213 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/******************************************************************************
Chris Craik9aef3ea2013-06-24 19:34:25 -0700221 Extension record functions
222******************************************************************************/
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800223int
Chris Craik9aef3ea2013-06-24 19:34:25 -0700224GifAddExtensionBlock(int *ExtensionBlockCount,
225 ExtensionBlock **ExtensionBlocks,
226 int Function,
227 unsigned int Len,
228 unsigned char ExtData[])
229{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800230 ExtensionBlock *ep;
231
Chris Craik9aef3ea2013-06-24 19:34:25 -0700232 if (*ExtensionBlocks == NULL)
233 *ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock));
Leon Scroggins IIIdf596d32017-03-13 14:03:21 -0400234 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 }
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800242
Chris Craik9aef3ea2013-06-24 19:34:25 -0700243 if (*ExtensionBlocks == NULL)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800244 return (GIF_ERROR);
245
Chris Craik9aef3ea2013-06-24 19:34:25 -0700246 ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800247
Chris Craik9aef3ea2013-06-24 19:34:25 -0700248 ep->Function = Function;
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800249 ep->ByteCount=Len;
Chris Craik9aef3ea2013-06-24 19:34:25 -0700250 ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800251 if (ep->Bytes == NULL)
252 return (GIF_ERROR);
253
Chris Craik9aef3ea2013-06-24 19:34:25 -0700254 if (ExtData != NULL) {
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800255 memcpy(ep->Bytes, ExtData, Len);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800256 }
257
258 return (GIF_OK);
259}
260
261void
Chris Craik9aef3ea2013-06-24 19:34:25 -0700262GifFreeExtensions(int *ExtensionBlockCount,
263 ExtensionBlock **ExtensionBlocks)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800264{
265 ExtensionBlock *ep;
266
Chris Craik9aef3ea2013-06-24 19:34:25 -0700267 if (*ExtensionBlocks == NULL)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800268 return;
Chris Craik9aef3ea2013-06-24 19:34:25 -0700269
270 for (ep = *ExtensionBlocks;
271 ep < (*ExtensionBlocks + *ExtensionBlockCount);
272 ep++)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800273 (void)free((char *)ep->Bytes);
Chris Craik9aef3ea2013-06-24 19:34:25 -0700274 (void)free((char *)*ExtensionBlocks);
275 *ExtensionBlocks = NULL;
276 *ExtensionBlockCount = 0;
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800277}
278
279/******************************************************************************
Chris Craik9aef3ea2013-06-24 19:34:25 -0700280 Image block allocation functions
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800281******************************************************************************/
282
283/* Private Function:
284 * Frees the last image in the GifFile->SavedImages array
285 */
286void
Chris Craik9aef3ea2013-06-24 19:34:25 -0700287FreeLastSavedImage(GifFileType *GifFile)
288{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800289 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 */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700299 if (sp->ImageDesc.ColorMap != NULL) {
300 GifFreeMapObject(sp->ImageDesc.ColorMap);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800301 sp->ImageDesc.ColorMap = NULL;
302 }
303
304 /* Deallocate the image data */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700305 if (sp->RasterBits != NULL)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800306 free((char *)sp->RasterBits);
307
308 /* Deallocate any extensions */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700309 GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800310
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
Chris Craik9aef3ea2013-06-24 19:34:25 -0700313 * time. If this is used in GifFreeSavedImages then it would be inefficient
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800314 * (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 */
322SavedImage *
Chris Craik9aef3ea2013-06-24 19:34:25 -0700323GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom)
324{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800325 if (GifFile->SavedImages == NULL)
326 GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
327 else
Leon Scroggins IIIdf596d32017-03-13 14:03:21 -0400328 GifFile->SavedImages = (SavedImage *)reallocarray(GifFile->SavedImages,
329 (GifFile->ImageCount + 1), sizeof(SavedImage));
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800330
331 if (GifFile->SavedImages == NULL)
332 return ((SavedImage *)NULL);
333 else {
Leon Scroggins IIIdf596d32017-03-13 14:03:21 -0400334 SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++];
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800335 memset((char *)sp, '\0', sizeof(SavedImage));
336
Chris Craik9aef3ea2013-06-24 19:34:25 -0700337 if (CopyFrom != NULL) {
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800338 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 */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700347 if (sp->ImageDesc.ColorMap != NULL) {
348 sp->ImageDesc.ColorMap = GifMakeMapObject(
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800349 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 */
Leon Scroggins IIIdf596d32017-03-13 14:03:21 -0400358 sp->RasterBits = (unsigned char *)reallocarray(NULL,
359 (CopyFrom->ImageDesc.Height *
360 CopyFrom->ImageDesc.Width),
361 sizeof(GifPixelType));
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800362 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 */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700371 if (sp->ExtensionBlocks != NULL) {
Leon Scroggins IIIdf596d32017-03-13 14:03:21 -0400372 sp->ExtensionBlocks = (ExtensionBlock *)reallocarray(NULL,
373 CopyFrom->ExtensionBlockCount,
374 sizeof(ExtensionBlock));
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800375 if (sp->ExtensionBlocks == NULL) {
376 FreeLastSavedImage(GifFile);
377 return (SavedImage *)(NULL);
378 }
379 memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks,
380 sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800381 }
382 }
383
384 return (sp);
385 }
386}
387
388void
Chris Craik9aef3ea2013-06-24 19:34:25 -0700389GifFreeSavedImages(GifFileType *GifFile)
390{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800391 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++) {
Chris Craik9aef3ea2013-06-24 19:34:25 -0700398 if (sp->ImageDesc.ColorMap != NULL) {
399 GifFreeMapObject(sp->ImageDesc.ColorMap);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800400 sp->ImageDesc.ColorMap = NULL;
401 }
402
Chris Craik9aef3ea2013-06-24 19:34:25 -0700403 if (sp->RasterBits != NULL)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800404 free((char *)sp->RasterBits);
Chris Craik9aef3ea2013-06-24 19:34:25 -0700405
406 GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800407 }
408 free((char *)GifFile->SavedImages);
Chris Craik9aef3ea2013-06-24 19:34:25 -0700409 GifFile->SavedImages = NULL;
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800410}
Chris Craik9aef3ea2013-06-24 19:34:25 -0700411
412/* end */