blob: 4cb8cde1c8d7435e4328bf7b6c5ebe98c1e6234c [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);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080063
Chris Craik9aef3ea2013-06-24 19:34:25 -070064 if (ColorMap != NULL) {
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080065 memcpy((char *)Object->Colors,
66 (char *)ColorMap, ColorCount * sizeof(GifColorType));
67 }
68
69 return (Object);
70}
71
Chris Craik9aef3ea2013-06-24 19:34:25 -070072/*******************************************************************************
73Free a color map object
74*******************************************************************************/
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080075void
Chris Craik9aef3ea2013-06-24 19:34:25 -070076GifFreeMapObject(ColorMapObject *Object)
77{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080078 if (Object != NULL) {
Chris Craik9aef3ea2013-06-24 19:34:25 -070079 (void)free(Object->Colors);
80 (void)free(Object);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080081 }
82}
83
84#ifdef DEBUG
85void
Chris Craik9aef3ea2013-06-24 19:34:25 -070086DumpColorMap(ColorMapObject *Object,
87 FILE * fp)
88{
89 if (Object != NULL) {
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080090 int i, j, Len = Object->ColorCount;
91
92 for (i = 0; i < Len; i += 4) {
93 for (j = 0; j < 4 && j < Len; j++) {
Chris Craik9aef3ea2013-06-24 19:34:25 -070094 (void)fprintf(fp, "%3d: %02x %02x %02x ", i + j,
95 Object->Colors[i + j].Red,
96 Object->Colors[i + j].Green,
97 Object->Colors[i + j].Blue);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -080098 }
Chris Craik9aef3ea2013-06-24 19:34:25 -070099 (void)fprintf(fp, "\n");
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800100 }
101 }
102}
103#endif /* DEBUG */
104
Chris Craik9aef3ea2013-06-24 19:34:25 -0700105/*******************************************************************************
106 Compute the union of two given color maps and return it. If result can't
107 fit into 256 colors, NULL is returned, the allocated union otherwise.
108 ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are
109 copied iff they didn't exist before. ColorTransIn2 maps the old
110 ColorIn2 into the ColorUnion color map table./
111*******************************************************************************/
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800112ColorMapObject *
Chris Craik9aef3ea2013-06-24 19:34:25 -0700113GifUnionColorMap(const ColorMapObject *ColorIn1,
114 const ColorMapObject *ColorIn2,
115 GifPixelType ColorTransIn2[])
116{
117 int i, j, CrntSlot, RoundUpTo, NewGifBitSize;
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800118 ColorMapObject *ColorUnion;
119
Chris Craik9aef3ea2013-06-24 19:34:25 -0700120 /*
121 * We don't worry about duplicates within either color map; if
122 * the caller wants to resolve those, he can perform unions
123 * with an empty color map.
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800124 */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700125
126 /* Allocate table which will hold the result for sure. */
127 ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount,
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800128 ColorIn2->ColorCount) * 2, NULL);
129
130 if (ColorUnion == NULL)
131 return (NULL);
132
Chris Craik9aef3ea2013-06-24 19:34:25 -0700133 /*
134 * Copy ColorIn1 to ColorUnion.
135 */
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800136 for (i = 0; i < ColorIn1->ColorCount; i++)
137 ColorUnion->Colors[i] = ColorIn1->Colors[i];
138 CrntSlot = ColorIn1->ColorCount;
139
140 /*
141 * Potentially obnoxious hack:
142 *
143 * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
144 * of table 1. This is very useful if your display is limited to
145 * 16 colors.
146 */
147 while (ColorIn1->Colors[CrntSlot - 1].Red == 0
148 && ColorIn1->Colors[CrntSlot - 1].Green == 0
149 && ColorIn1->Colors[CrntSlot - 1].Blue == 0)
150 CrntSlot--;
151
Chris Craik9aef3ea2013-06-24 19:34:25 -0700152 /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800153 for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
154 /* Let's see if this color already exists: */
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800155 for (j = 0; j < ColorIn1->ColorCount; j++)
156 if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i],
157 sizeof(GifColorType)) == 0)
158 break;
159
160 if (j < ColorIn1->ColorCount)
161 ColorTransIn2[i] = j; /* color exists in Color1 */
162 else {
163 /* Color is new - copy it to a new slot: */
164 ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
165 ColorTransIn2[i] = CrntSlot++;
166 }
167 }
168
169 if (CrntSlot > 256) {
Chris Craik9aef3ea2013-06-24 19:34:25 -0700170 GifFreeMapObject(ColorUnion);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800171 return ((ColorMapObject *) NULL);
172 }
173
Chris Craik9aef3ea2013-06-24 19:34:25 -0700174 NewGifBitSize = GifBitSize(CrntSlot);
175 RoundUpTo = (1 << NewGifBitSize);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800176
177 if (RoundUpTo != ColorUnion->ColorCount) {
178 register GifColorType *Map = ColorUnion->Colors;
179
180 /*
181 * Zero out slots up to next power of 2.
182 * We know these slots exist because of the way ColorUnion's
183 * start dimension was computed.
184 */
185 for (j = CrntSlot; j < RoundUpTo; j++)
186 Map[j].Red = Map[j].Green = Map[j].Blue = 0;
187
188 /* perhaps we can shrink the map? */
189 if (RoundUpTo < ColorUnion->ColorCount)
190 ColorUnion->Colors = (GifColorType *)realloc(Map,
191 sizeof(GifColorType) * RoundUpTo);
192 }
193
194 ColorUnion->ColorCount = RoundUpTo;
Chris Craik9aef3ea2013-06-24 19:34:25 -0700195 ColorUnion->BitsPerPixel = NewGifBitSize;
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800196
197 return (ColorUnion);
198}
199
Chris Craik9aef3ea2013-06-24 19:34:25 -0700200/*******************************************************************************
201 Apply a given color translation to the raster bits of an image
202*******************************************************************************/
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800203void
Chris Craik9aef3ea2013-06-24 19:34:25 -0700204GifApplyTranslation(SavedImage *Image, GifPixelType Translation[])
205{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800206 register int i;
207 register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width;
208
209 for (i = 0; i < RasterSize; i++)
210 Image->RasterBits[i] = Translation[Image->RasterBits[i]];
211}
212
213/******************************************************************************
Chris Craik9aef3ea2013-06-24 19:34:25 -0700214 Extension record functions
215******************************************************************************/
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800216int
Chris Craik9aef3ea2013-06-24 19:34:25 -0700217GifAddExtensionBlock(int *ExtensionBlockCount,
218 ExtensionBlock **ExtensionBlocks,
219 int Function,
220 unsigned int Len,
221 unsigned char ExtData[])
222{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800223 ExtensionBlock *ep;
224
Chris Craik9aef3ea2013-06-24 19:34:25 -0700225 if (*ExtensionBlocks == NULL)
226 *ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock));
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800227 else
Chris Craik9aef3ea2013-06-24 19:34:25 -0700228 *ExtensionBlocks = (ExtensionBlock *)realloc(*ExtensionBlocks,
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800229 sizeof(ExtensionBlock) *
Chris Craik9aef3ea2013-06-24 19:34:25 -0700230 (*ExtensionBlockCount + 1));
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800231
Chris Craik9aef3ea2013-06-24 19:34:25 -0700232 if (*ExtensionBlocks == NULL)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800233 return (GIF_ERROR);
234
Chris Craik9aef3ea2013-06-24 19:34:25 -0700235 ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800236
Chris Craik9aef3ea2013-06-24 19:34:25 -0700237 ep->Function = Function;
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800238 ep->ByteCount=Len;
Chris Craik9aef3ea2013-06-24 19:34:25 -0700239 ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800240 if (ep->Bytes == NULL)
241 return (GIF_ERROR);
242
Chris Craik9aef3ea2013-06-24 19:34:25 -0700243 if (ExtData != NULL) {
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800244 memcpy(ep->Bytes, ExtData, Len);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800245 }
246
247 return (GIF_OK);
248}
249
250void
Chris Craik9aef3ea2013-06-24 19:34:25 -0700251GifFreeExtensions(int *ExtensionBlockCount,
252 ExtensionBlock **ExtensionBlocks)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800253{
254 ExtensionBlock *ep;
255
Chris Craik9aef3ea2013-06-24 19:34:25 -0700256 if (*ExtensionBlocks == NULL)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800257 return;
Chris Craik9aef3ea2013-06-24 19:34:25 -0700258
259 for (ep = *ExtensionBlocks;
260 ep < (*ExtensionBlocks + *ExtensionBlockCount);
261 ep++)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800262 (void)free((char *)ep->Bytes);
Chris Craik9aef3ea2013-06-24 19:34:25 -0700263 (void)free((char *)*ExtensionBlocks);
264 *ExtensionBlocks = NULL;
265 *ExtensionBlockCount = 0;
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800266}
267
268/******************************************************************************
Chris Craik9aef3ea2013-06-24 19:34:25 -0700269 Image block allocation functions
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800270******************************************************************************/
271
272/* Private Function:
273 * Frees the last image in the GifFile->SavedImages array
274 */
275void
Chris Craik9aef3ea2013-06-24 19:34:25 -0700276FreeLastSavedImage(GifFileType *GifFile)
277{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800278 SavedImage *sp;
279
280 if ((GifFile == NULL) || (GifFile->SavedImages == NULL))
281 return;
282
283 /* Remove one SavedImage from the GifFile */
284 GifFile->ImageCount--;
285 sp = &GifFile->SavedImages[GifFile->ImageCount];
286
287 /* Deallocate its Colormap */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700288 if (sp->ImageDesc.ColorMap != NULL) {
289 GifFreeMapObject(sp->ImageDesc.ColorMap);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800290 sp->ImageDesc.ColorMap = NULL;
291 }
292
293 /* Deallocate the image data */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700294 if (sp->RasterBits != NULL)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800295 free((char *)sp->RasterBits);
296
297 /* Deallocate any extensions */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700298 GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800299
300 /*** FIXME: We could realloc the GifFile->SavedImages structure but is
301 * there a point to it? Saves some memory but we'd have to do it every
Chris Craik9aef3ea2013-06-24 19:34:25 -0700302 * time. If this is used in GifFreeSavedImages then it would be inefficient
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800303 * (The whole array is going to be deallocated.) If we just use it when
304 * we want to free the last Image it's convenient to do it here.
305 */
306}
307
308/*
309 * Append an image block to the SavedImages array
310 */
311SavedImage *
Chris Craik9aef3ea2013-06-24 19:34:25 -0700312GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom)
313{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800314 SavedImage *sp;
315
316 if (GifFile->SavedImages == NULL)
317 GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
318 else
319 GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages,
320 sizeof(SavedImage) * (GifFile->ImageCount + 1));
321
322 if (GifFile->SavedImages == NULL)
323 return ((SavedImage *)NULL);
324 else {
325 sp = &GifFile->SavedImages[GifFile->ImageCount++];
326 memset((char *)sp, '\0', sizeof(SavedImage));
327
Chris Craik9aef3ea2013-06-24 19:34:25 -0700328 if (CopyFrom != NULL) {
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800329 memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
330
331 /*
332 * Make our own allocated copies of the heap fields in the
333 * copied record. This guards against potential aliasing
334 * problems.
335 */
336
337 /* first, the local color map */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700338 if (sp->ImageDesc.ColorMap != NULL) {
339 sp->ImageDesc.ColorMap = GifMakeMapObject(
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800340 CopyFrom->ImageDesc.ColorMap->ColorCount,
341 CopyFrom->ImageDesc.ColorMap->Colors);
342 if (sp->ImageDesc.ColorMap == NULL) {
343 FreeLastSavedImage(GifFile);
344 return (SavedImage *)(NULL);
345 }
346 }
347
348 /* next, the raster */
349 sp->RasterBits = (unsigned char *)malloc(sizeof(GifPixelType) *
350 CopyFrom->ImageDesc.Height *
351 CopyFrom->ImageDesc.Width);
352 if (sp->RasterBits == NULL) {
353 FreeLastSavedImage(GifFile);
354 return (SavedImage *)(NULL);
355 }
356 memcpy(sp->RasterBits, CopyFrom->RasterBits,
357 sizeof(GifPixelType) * CopyFrom->ImageDesc.Height *
358 CopyFrom->ImageDesc.Width);
359
360 /* finally, the extension blocks */
Chris Craik9aef3ea2013-06-24 19:34:25 -0700361 if (sp->ExtensionBlocks != NULL) {
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800362 sp->ExtensionBlocks = (ExtensionBlock *)malloc(
363 sizeof(ExtensionBlock) *
364 CopyFrom->ExtensionBlockCount);
365 if (sp->ExtensionBlocks == NULL) {
366 FreeLastSavedImage(GifFile);
367 return (SavedImage *)(NULL);
368 }
369 memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks,
370 sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800371 }
372 }
373
374 return (sp);
375 }
376}
377
378void
Chris Craik9aef3ea2013-06-24 19:34:25 -0700379GifFreeSavedImages(GifFileType *GifFile)
380{
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800381 SavedImage *sp;
382
383 if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
384 return;
385 }
386 for (sp = GifFile->SavedImages;
387 sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
Chris Craik9aef3ea2013-06-24 19:34:25 -0700388 if (sp->ImageDesc.ColorMap != NULL) {
389 GifFreeMapObject(sp->ImageDesc.ColorMap);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800390 sp->ImageDesc.ColorMap = NULL;
391 }
392
Chris Craik9aef3ea2013-06-24 19:34:25 -0700393 if (sp->RasterBits != NULL)
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800394 free((char *)sp->RasterBits);
Chris Craik9aef3ea2013-06-24 19:34:25 -0700395
396 GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800397 }
398 free((char *)GifFile->SavedImages);
Chris Craik9aef3ea2013-06-24 19:34:25 -0700399 GifFile->SavedImages = NULL;
The Android Open Source Projectc2eacae2009-03-03 19:29:32 -0800400}
Chris Craik9aef3ea2013-06-24 19:34:25 -0700401
402/* end */