| 1 | /*****************************************************************************
|
|---|
| 2 | * "Gif-Lib" - Yet another gif library. *
|
|---|
| 3 | * *
|
|---|
| 4 | * Written by: Gershon Elber Ver 0.1, Jun. 1989 *
|
|---|
| 5 | * Extensively hacked by: Eric S. Raymond Ver 1.?, Sep 1992 *
|
|---|
| 6 | ******************************************************************************
|
|---|
| 7 | * GIF construction tools *
|
|---|
| 8 | ******************************************************************************
|
|---|
| 9 | * History: *
|
|---|
| 10 | * 15 Sep 92 - Version 1.0 by Eric Raymond. *
|
|---|
| 11 | *****************************************************************************/
|
|---|
| 12 | #include <stdlib.h>
|
|---|
| 13 | #include <stdio.h>
|
|---|
| 14 | #include <string.h>
|
|---|
| 15 | #include <malloc.h>
|
|---|
| 16 | #include "gif_lib.h"
|
|---|
| 17 |
|
|---|
| 18 | #define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
|---|
| 19 |
|
|---|
| 20 | /******************************************************************************
|
|---|
| 21 | * Miscellaneous utility functions *
|
|---|
| 22 | ******************************************************************************/
|
|---|
| 23 |
|
|---|
| 24 | int BitSize(int n)
|
|---|
| 25 | /* return smallest bitfield size n will fit in */
|
|---|
| 26 | {
|
|---|
| 27 | register i;
|
|---|
| 28 |
|
|---|
| 29 | for (i = 1; i <= 8; i++)
|
|---|
| 30 | if ((1 << i) >= n)
|
|---|
| 31 | break;
|
|---|
| 32 | return(i);
|
|---|
| 33 | }
|
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 | /******************************************************************************
|
|---|
| 37 | * Color map object functions *
|
|---|
| 38 | ******************************************************************************/
|
|---|
| 39 |
|
|---|
| 40 | ColorMapObject *MakeMapObject(int ColorCount, GifColorType *ColorMap)
|
|---|
| 41 | /*
|
|---|
| 42 | * Allocate a color map of given size; initialize with contents of
|
|---|
| 43 | * ColorMap if that pointer is non-NULL.
|
|---|
| 44 | */
|
|---|
| 45 | {
|
|---|
| 46 | ColorMapObject *Object;
|
|---|
| 47 |
|
|---|
| 48 | if (ColorCount != (1 << BitSize(ColorCount)))
|
|---|
| 49 | return((ColorMapObject *)NULL);
|
|---|
| 50 |
|
|---|
| 51 | Object = (ColorMapObject *)malloc(sizeof(ColorMapObject));
|
|---|
| 52 | if (Object == (ColorMapObject *)NULL)
|
|---|
| 53 | return((ColorMapObject *)NULL);
|
|---|
| 54 |
|
|---|
| 55 | Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
|
|---|
| 56 | if (Object->Colors == (GifColorType *)NULL)
|
|---|
| 57 | return((ColorMapObject *)NULL);
|
|---|
| 58 |
|
|---|
| 59 | Object->ColorCount = ColorCount;
|
|---|
| 60 | Object->BitsPerPixel = BitSize(ColorCount);
|
|---|
| 61 |
|
|---|
| 62 | if (ColorMap)
|
|---|
| 63 | memcpy((char *)Object->Colors,
|
|---|
| 64 | (char *)ColorMap, ColorCount * sizeof(GifColorType));
|
|---|
| 65 |
|
|---|
| 66 | return(Object);
|
|---|
| 67 | }
|
|---|
| 68 |
|
|---|
| 69 | void FreeMapObject(ColorMapObject *Object)
|
|---|
| 70 | /*
|
|---|
| 71 | * Free a color map object
|
|---|
| 72 | */
|
|---|
| 73 | {
|
|---|
| 74 | free(Object->Colors);
|
|---|
| 75 | free(Object);
|
|---|
| 76 | }
|
|---|
| 77 |
|
|---|
| 78 | #ifdef DEBUG
|
|---|
| 79 | void DumpColorMap(ColorMapObject *Object, FILE *fp)
|
|---|
| 80 | {
|
|---|
| 81 | if (Object)
|
|---|
| 82 | {
|
|---|
| 83 | int i, j, Len = Object->ColorCount;
|
|---|
| 84 |
|
|---|
| 85 | for (i = 0; i < Len; i+=4) {
|
|---|
| 86 | for (j = 0; j < 4 && j < Len; j++) {
|
|---|
| 87 | fprintf(fp,
|
|---|
| 88 | "%3d: %02x %02x %02x ", i + j,
|
|---|
| 89 | Object->Colors[i + j].Red,
|
|---|
| 90 | Object->Colors[i + j].Green,
|
|---|
| 91 | Object->Colors[i + j].Blue);
|
|---|
| 92 | }
|
|---|
| 93 | fprintf(fp, "\n");
|
|---|
| 94 | }
|
|---|
| 95 | }
|
|---|
| 96 | }
|
|---|
| 97 | #endif /* DEBUG */
|
|---|
| 98 |
|
|---|
| 99 | ColorMapObject *UnionColorMap(
|
|---|
| 100 | ColorMapObject *ColorIn1,
|
|---|
| 101 | ColorMapObject *ColorIn2,
|
|---|
| 102 | GifPixelType ColorTransIn2[])
|
|---|
| 103 | /*
|
|---|
| 104 | * Compute the union of two given color maps and return it. If result can't
|
|---|
| 105 | * fit into 256 colors, NULL is returned, the allocated union otherwise.
|
|---|
| 106 | * ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are
|
|---|
| 107 | * copied iff they didn't exist before. ColorTransIn2 maps the old
|
|---|
| 108 | * ColorIn2 into ColorUnion color map table.
|
|---|
| 109 | */
|
|---|
| 110 | {
|
|---|
| 111 | int i, j, CrntSlot, RoundUpTo, NewBitSize;
|
|---|
| 112 | ColorMapObject *ColorUnion;
|
|---|
| 113 |
|
|---|
| 114 | /*
|
|---|
| 115 | * Allocate table which will hold the result for sure.
|
|---|
| 116 | */
|
|---|
| 117 | ColorUnion
|
|---|
| 118 | = MakeMapObject(MAX(ColorIn1->ColorCount,ColorIn2->ColorCount)*2,NULL);
|
|---|
| 119 |
|
|---|
| 120 | if (ColorUnion == NULL)
|
|---|
| 121 | return(NULL);
|
|---|
| 122 |
|
|---|
| 123 | /* Copy ColorIn1 to ColorUnionSize; */
|
|---|
| 124 | for (i = 0; i < ColorIn1->ColorCount; i++)
|
|---|
| 125 | ColorUnion->Colors[i] = ColorIn1->Colors[i];
|
|---|
| 126 | CrntSlot = ColorIn1->ColorCount;
|
|---|
| 127 |
|
|---|
| 128 | /*
|
|---|
| 129 | * Potentially obnoxious hack:
|
|---|
| 130 | *
|
|---|
| 131 | * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
|
|---|
| 132 | * of table 1. This is very useful if your display is limited to
|
|---|
| 133 | * 16 colors.
|
|---|
| 134 | */
|
|---|
| 135 | while (ColorIn1->Colors[CrntSlot-1].Red == 0
|
|---|
| 136 | && ColorIn1->Colors[CrntSlot-1].Green == 0
|
|---|
| 137 | && ColorIn1->Colors[CrntSlot-1].Red == 0)
|
|---|
| 138 | CrntSlot--;
|
|---|
| 139 |
|
|---|
| 140 | /* Copy ColorIn2 to ColorUnionSize (use old colors if they exist): */
|
|---|
| 141 | for (i = 0; i < ColorIn2->ColorCount && CrntSlot<=256; i++)
|
|---|
| 142 | {
|
|---|
| 143 | /* Let's see if this color already exists: */
|
|---|
| 144 | for (j = 0; j < ColorIn1->ColorCount; j++)
|
|---|
| 145 | if (memcmp(&ColorIn1->Colors[j], &ColorIn2->Colors[i], sizeof(GifColorType)) == 0)
|
|---|
| 146 | break;
|
|---|
| 147 |
|
|---|
| 148 | if (j < ColorIn1->ColorCount)
|
|---|
| 149 | ColorTransIn2[i] = j; /* color exists in Color1 */
|
|---|
| 150 | else
|
|---|
| 151 | {
|
|---|
| 152 | /* Color is new - copy it to a new slot: */
|
|---|
| 153 | ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
|
|---|
| 154 | ColorTransIn2[i] = CrntSlot++;
|
|---|
| 155 | }
|
|---|
| 156 | }
|
|---|
| 157 |
|
|---|
| 158 | if (CrntSlot > 256)
|
|---|
| 159 | {
|
|---|
| 160 | FreeMapObject(ColorUnion);
|
|---|
| 161 | return((ColorMapObject *)NULL);
|
|---|
| 162 | }
|
|---|
| 163 |
|
|---|
| 164 | NewBitSize = BitSize(CrntSlot);
|
|---|
| 165 | RoundUpTo = (1 << NewBitSize);
|
|---|
| 166 |
|
|---|
| 167 | if (RoundUpTo != ColorUnion->ColorCount)
|
|---|
| 168 | {
|
|---|
| 169 | register GifColorType *Map = ColorUnion->Colors;
|
|---|
| 170 |
|
|---|
| 171 | /*
|
|---|
| 172 | * Zero out slots up to next power of 2.
|
|---|
| 173 | * We know these slots exist because of the way ColorUnion's
|
|---|
| 174 | * start dimension was computed.
|
|---|
| 175 | */
|
|---|
| 176 | for (j = CrntSlot; j < RoundUpTo; j++)
|
|---|
| 177 | Map[j].Red = Map[j].Green = Map[j].Blue = 0;
|
|---|
| 178 |
|
|---|
| 179 | /* perhaps we can shrink the map? */
|
|---|
| 180 | if (RoundUpTo < ColorUnion->ColorCount)
|
|---|
| 181 | ColorUnion->Colors
|
|---|
| 182 | = (GifColorType *)realloc(Map, sizeof(GifColorType)*RoundUpTo);
|
|---|
| 183 | }
|
|---|
| 184 |
|
|---|
| 185 | ColorUnion->ColorCount = RoundUpTo;
|
|---|
| 186 | ColorUnion->BitsPerPixel = NewBitSize;
|
|---|
| 187 |
|
|---|
| 188 | return(ColorUnion);
|
|---|
| 189 | }
|
|---|
| 190 |
|
|---|
| 191 | void ApplyTranslation(SavedImage *Image, GifPixelType Translation[])
|
|---|
| 192 | /*
|
|---|
| 193 | * Apply a given color translation to the raster bits of an image
|
|---|
| 194 | */
|
|---|
| 195 | {
|
|---|
| 196 | register int i;
|
|---|
| 197 | register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width;
|
|---|
| 198 |
|
|---|
| 199 | for (i = 0; i < RasterSize; i++)
|
|---|
| 200 | Image->RasterBits[i] = Translation[Image->RasterBits[i]];
|
|---|
| 201 | }
|
|---|
| 202 |
|
|---|
| 203 | /******************************************************************************
|
|---|
| 204 | * Extension record functions *
|
|---|
| 205 | ******************************************************************************/
|
|---|
| 206 |
|
|---|
| 207 | void MakeExtension(SavedImage *New, int Function)
|
|---|
| 208 | {
|
|---|
| 209 | New->Function = Function;
|
|---|
| 210 | /*
|
|---|
| 211 | * Someday we might have to deal with multiple extensions.
|
|---|
| 212 | */
|
|---|
| 213 | }
|
|---|
| 214 |
|
|---|
| 215 | int AddExtensionBlock(SavedImage *New, int Len, char ExtData[])
|
|---|
| 216 | {
|
|---|
| 217 | ExtensionBlock *ep;
|
|---|
| 218 |
|
|---|
| 219 | if (New->ExtensionBlocks == NULL)
|
|---|
| 220 | New->ExtensionBlocks = (ExtensionBlock *)malloc(sizeof(ExtensionBlock));
|
|---|
| 221 | else
|
|---|
| 222 | New->ExtensionBlocks =
|
|---|
| 223 | (ExtensionBlock *)realloc(New->ExtensionBlocks,
|
|---|
| 224 | sizeof(ExtensionBlock) * (New->ExtensionBlockCount + 1));
|
|---|
| 225 |
|
|---|
| 226 | if (New->ExtensionBlocks == NULL)
|
|---|
| 227 | return(GIF_ERROR);
|
|---|
| 228 |
|
|---|
| 229 | ep = &New->ExtensionBlocks[New->ExtensionBlockCount++];
|
|---|
| 230 |
|
|---|
| 231 | if ((ep->Bytes = (char *)malloc(ep->ByteCount = Len)) == NULL)
|
|---|
| 232 | return(GIF_ERROR);
|
|---|
| 233 |
|
|---|
| 234 | if (ExtData) {
|
|---|
| 235 | memcpy(ep->Bytes, ExtData, Len);
|
|---|
| 236 | ep->Function = New->Function;
|
|---|
| 237 | }
|
|---|
| 238 |
|
|---|
| 239 | return(GIF_OK);
|
|---|
| 240 | }
|
|---|
| 241 |
|
|---|
| 242 | void FreeExtension(SavedImage *Image)
|
|---|
| 243 | {
|
|---|
| 244 | ExtensionBlock *ep;
|
|---|
| 245 |
|
|---|
| 246 | for (ep = Image->ExtensionBlocks;
|
|---|
| 247 | ep < Image->ExtensionBlocks + Image->ExtensionBlockCount;
|
|---|
| 248 | ep++)
|
|---|
| 249 | (void) free((char *)ep->Bytes);
|
|---|
| 250 | free((char *)Image->ExtensionBlocks);
|
|---|
| 251 | Image->ExtensionBlocks = NULL;
|
|---|
| 252 | }
|
|---|
| 253 |
|
|---|
| 254 | /******************************************************************************
|
|---|
| 255 | * Image block allocation functions *
|
|---|
| 256 | ******************************************************************************/
|
|---|
| 257 | SavedImage *MakeSavedImage(GifFileType *GifFile, SavedImage *CopyFrom)
|
|---|
| 258 | /*
|
|---|
| 259 | * Append an image block to the SavedImages array
|
|---|
| 260 | */
|
|---|
| 261 | {
|
|---|
| 262 | SavedImage *sp;
|
|---|
| 263 |
|
|---|
| 264 | if (GifFile->SavedImages == NULL)
|
|---|
| 265 | GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
|
|---|
| 266 | else
|
|---|
| 267 | GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages,
|
|---|
| 268 | sizeof(SavedImage) * (GifFile->ImageCount+1));
|
|---|
| 269 |
|
|---|
| 270 | if (GifFile->SavedImages == NULL)
|
|---|
| 271 | return((SavedImage *)NULL);
|
|---|
| 272 | else
|
|---|
| 273 | {
|
|---|
| 274 | sp = &GifFile->SavedImages[GifFile->ImageCount++];
|
|---|
| 275 | memset((char *)sp, '\0', sizeof(SavedImage));
|
|---|
| 276 |
|
|---|
| 277 | if (CopyFrom)
|
|---|
| 278 | {
|
|---|
| 279 | memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
|
|---|
| 280 |
|
|---|
| 281 | /*
|
|---|
| 282 | * Make our own allocated copies of the heap fields in the
|
|---|
| 283 | * copied record. This guards against potential aliasing
|
|---|
| 284 | * problems.
|
|---|
| 285 | */
|
|---|
| 286 |
|
|---|
| 287 | /* first, the local color map */
|
|---|
| 288 | if (sp->ImageDesc.ColorMap)
|
|---|
| 289 | sp->ImageDesc.ColorMap =
|
|---|
| 290 | MakeMapObject(CopyFrom->ImageDesc.ColorMap->ColorCount,
|
|---|
| 291 | CopyFrom->ImageDesc.ColorMap->Colors);
|
|---|
| 292 |
|
|---|
| 293 | /* next, the raster */
|
|---|
| 294 | sp->RasterBits = (char *)malloc(sizeof(GifPixelType)
|
|---|
| 295 | * CopyFrom->ImageDesc.Height
|
|---|
| 296 | * CopyFrom->ImageDesc.Width);
|
|---|
| 297 | memcpy(sp->RasterBits,
|
|---|
| 298 | CopyFrom->RasterBits,
|
|---|
| 299 | sizeof(GifPixelType)
|
|---|
| 300 | * CopyFrom->ImageDesc.Height
|
|---|
| 301 | * CopyFrom->ImageDesc.Width);
|
|---|
| 302 |
|
|---|
| 303 | /* finally, the extension blocks */
|
|---|
| 304 | if (sp->ExtensionBlocks)
|
|---|
| 305 | {
|
|---|
| 306 | sp->ExtensionBlocks
|
|---|
| 307 | = (ExtensionBlock*)malloc(sizeof(ExtensionBlock)
|
|---|
| 308 | * CopyFrom->ExtensionBlockCount);
|
|---|
| 309 | memcpy(sp->ExtensionBlocks,
|
|---|
| 310 | CopyFrom->ExtensionBlocks,
|
|---|
| 311 | sizeof(ExtensionBlock)
|
|---|
| 312 | * CopyFrom->ExtensionBlockCount);
|
|---|
| 313 |
|
|---|
| 314 | /*
|
|---|
| 315 | * For the moment, the actual blocks can take their
|
|---|
| 316 | * chances with free(). We'll fix this later.
|
|---|
| 317 | */
|
|---|
| 318 | }
|
|---|
| 319 | }
|
|---|
| 320 |
|
|---|
| 321 | return(sp);
|
|---|
| 322 | }
|
|---|
| 323 | }
|
|---|
| 324 |
|
|---|
| 325 | void FreeSavedImages(GifFileType *GifFile)
|
|---|
| 326 | {
|
|---|
| 327 | SavedImage *sp;
|
|---|
| 328 |
|
|---|
| 329 | for (sp = GifFile->SavedImages;
|
|---|
| 330 | sp < GifFile->SavedImages + GifFile->ImageCount;
|
|---|
| 331 | sp++)
|
|---|
| 332 | {
|
|---|
| 333 | if (sp->ImageDesc.ColorMap)
|
|---|
| 334 | FreeMapObject(sp->ImageDesc.ColorMap);
|
|---|
| 335 |
|
|---|
| 336 | if (sp->RasterBits)
|
|---|
| 337 | free((char *)sp->RasterBits);
|
|---|
| 338 |
|
|---|
| 339 | if (sp->ExtensionBlocks)
|
|---|
| 340 | FreeExtension(sp);
|
|---|
| 341 | }
|
|---|
| 342 | free((char *) GifFile->SavedImages);
|
|---|
| 343 | }
|
|---|
| 344 |
|
|---|
| 345 |
|
|---|
| 346 |
|
|---|