1 /*****************************************************************************
   2 
   3  GIF construction tools
   4 
   5 ****************************************************************************/
   6 
   7 #include <stdlib.h>
   8 #include <stdio.h>
   9 #include <string.h>
  10 
  11 #include "gif_lib.h"
  12 
  13 #define MAX(x, y)    (((x) > (y)) ? (x) : (y))
  14 
  15 /******************************************************************************
  16  Miscellaneous utility functions                          
  17 ******************************************************************************/
  18 
  19 /* return smallest bitfield size n will fit in */
  20 int
  21 GifBitSize(int n)
  22 {
  23     register int i;
  24 
  25     for (i = 1; i <= 8; i++)
  26         if ((1 << i) >= n)
  27             break;
  28     return (i);
  29 }
  30 
  31 /******************************************************************************
  32   Color map object functions                              
  33 ******************************************************************************/
  34 
  35 /*
  36  * Allocate a color map of given size; initialize with contents of
  37  * ColorMap if that pointer is non-NULL.
  38  */
  39 ColorMapObject *
  40 GifMakeMapObject(int ColorCount, const GifColorType *ColorMap)
  41 {
  42     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? */
  46     if (ColorCount != (1 << GifBitSize(ColorCount))) {
  47         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) {
  57         free(Object);
  58         return ((ColorMapObject *) NULL);
  59     }
  60 
  61     Object->ColorCount = ColorCount;
  62     Object->BitsPerPixel = GifBitSize(ColorCount);
  63     Object->SortFlag = false;
  64 
  65     if (ColorMap != NULL) {
  66         memcpy((char *)Object->Colors,
  67                (char *)ColorMap, ColorCount * sizeof(GifColorType));
  68     }
  69 
  70     return (Object);
  71 }
  72 
  73 /*******************************************************************************
  74 Free a color map object
  75 *******************************************************************************/
  76 void
  77 GifFreeMapObject(ColorMapObject *Object)
  78 {
  79     if (Object != NULL) {
  80         (void)free(Object->Colors);
  81         (void)free(Object);
  82     }
  83 }
  84 
  85 #ifdef DEBUG
  86 void
  87 DumpColorMap(ColorMapObject *Object,
  88              FILE * fp)
  89 {
  90     if (Object != NULL) {
  91         int i, j, Len = Object->ColorCount;
  92 
  93         for (i = 0; i < Len; i += 4) {
  94             for (j = 0; j < 4 && j < Len; j++) {
  95                 (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);
  99             }
 100             (void)fprintf(fp, "\n");
 101         }
 102     }
 103 }
 104 #endif /* DEBUG */
 105 
 106 /*******************************************************************************
 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 *******************************************************************************/
 113 ColorMapObject *
 114 GifUnionColorMap(const ColorMapObject *ColorIn1,
 115               const ColorMapObject *ColorIn2,
 116               GifPixelType ColorTransIn2[])
 117 {
 118     int i, j, CrntSlot, RoundUpTo, NewGifBitSize;
 119     ColorMapObject *ColorUnion;
 120 
 121     /*
 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.
 125      */
 126 
 127     /* Allocate table which will hold the result for sure. */
 128     ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount,
 129                                ColorIn2->ColorCount) * 2, NULL);
 130 
 131     if (ColorUnion == NULL)
 132         return (NULL);
 133 
 134     /* 
 135      * Copy ColorIn1 to ColorUnion.
 136      */
 137     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 
 153     /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */
 154     for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
 155         /* Let's see if this color already exists: */
 156         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) {
 171         GifFreeMapObject(ColorUnion);
 172         return ((ColorMapObject *) NULL);
 173     }
 174 
 175     NewGifBitSize = GifBitSize(CrntSlot);
 176     RoundUpTo = (1 << NewGifBitSize);
 177 
 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? */
 190         if (RoundUpTo < ColorUnion->ColorCount) {
 191             GifColorType *new_map = (GifColorType *)realloc(Map,
 192                                  sizeof(GifColorType) * RoundUpTo);
 193             if( new_map == NULL ) {
 194                 GifFreeMapObject(ColorUnion);
 195                 return ((ColorMapObject *) NULL);
 196             }
 197             ColorUnion->Colors = new_map;
 198         }
 199     }
 200 
 201     ColorUnion->ColorCount = RoundUpTo;
 202     ColorUnion->BitsPerPixel = NewGifBitSize;
 203 
 204     return (ColorUnion);
 205 }
 206 
 207 /*******************************************************************************
 208  Apply a given color translation to the raster bits of an image
 209 *******************************************************************************/
 210 void
 211 GifApplyTranslation(SavedImage *Image, GifPixelType Translation[])
 212 {
 213     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 /******************************************************************************
 221  Extension record functions                              
 222 ******************************************************************************/
 223 int
 224 GifAddExtensionBlock(int *ExtensionBlockCount,
 225                      ExtensionBlock **ExtensionBlocks,
 226                      int Function,
 227                      unsigned int Len,
 228                      unsigned char ExtData[])
 229 {
 230     ExtensionBlock *ep;
 231 
 232     if (*ExtensionBlocks == NULL)
 233         *ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock));
 234     else {
 235         ExtensionBlock* ep_new = (ExtensionBlock *)realloc(*ExtensionBlocks,
 236                                       sizeof(ExtensionBlock) *
 237                                       (*ExtensionBlockCount + 1));
 238         if( ep_new == NULL )
 239             return (GIF_ERROR);
 240         *ExtensionBlocks = ep_new;
 241     }
 242 
 243     if (*ExtensionBlocks == NULL)
 244         return (GIF_ERROR);
 245 
 246     ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
 247 
 248     ep->Function = Function;
 249     ep->ByteCount=Len;
 250     ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
 251     if (ep->Bytes == NULL)
 252         return (GIF_ERROR);
 253 
 254     if (ExtData != NULL) {
 255         memcpy(ep->Bytes, ExtData, Len);
 256     }
 257 
 258     return (GIF_OK);
 259 }
 260 
 261 void
 262 GifFreeExtensions(int *ExtensionBlockCount,
 263                   ExtensionBlock **ExtensionBlocks)
 264 {
 265     ExtensionBlock *ep;
 266 
 267     if (*ExtensionBlocks == NULL)
 268         return;
 269 
 270     for (ep = *ExtensionBlocks;
 271          ep < (*ExtensionBlocks + *ExtensionBlockCount); 
 272          ep++)
 273         (void)free((char *)ep->Bytes);
 274     (void)free((char *)*ExtensionBlocks);
 275     *ExtensionBlocks = NULL;
 276     *ExtensionBlockCount = 0;
 277 }
 278 
 279 /******************************************************************************
 280  Image block allocation functions                          
 281 ******************************************************************************/
 282 
 283 /* Private Function:
 284  * Frees the last image in the GifFile->SavedImages array
 285  */
 286 void
 287 FreeLastSavedImage(GifFileType *GifFile)
 288 {
 289     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 */
 299     if (sp->ImageDesc.ColorMap != NULL) {
 300         GifFreeMapObject(sp->ImageDesc.ColorMap);
 301         sp->ImageDesc.ColorMap = NULL;
 302     }
 303 
 304     /* Deallocate the image data */
 305     if (sp->RasterBits != NULL)
 306         free((char *)sp->RasterBits);
 307 
 308     /* Deallocate any extensions */
 309     GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
 310 
 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
 313      * time.  If this is used in GifFreeSavedImages then it would be inefficient
 314      * (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  */
 322 SavedImage *
 323 GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom)
 324 {
 325     if (GifFile->SavedImages == NULL)
 326         GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
 327     else
 328         GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages,
 329                                sizeof(SavedImage) * (GifFile->ImageCount + 1));
 330 
 331     if (GifFile->SavedImages == NULL)
 332         return ((SavedImage *)NULL);
 333     else {
 334         SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++];
 335         memset((char *)sp, '\0', sizeof(SavedImage));
 336 
 337         if (CopyFrom != NULL) {
 338             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 */
 347             if (sp->ImageDesc.ColorMap != NULL) {
 348                 sp->ImageDesc.ColorMap = GifMakeMapObject(
 349                                          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 */
 358             sp->RasterBits = (unsigned char *)malloc(sizeof(GifPixelType) *
 359                                                    CopyFrom->ImageDesc.Height *
 360                                                    CopyFrom->ImageDesc.Width);
 361             if (sp->RasterBits == NULL) {
 362                 FreeLastSavedImage(GifFile);
 363                 return (SavedImage *)(NULL);
 364             }
 365             memcpy(sp->RasterBits, CopyFrom->RasterBits,
 366                    sizeof(GifPixelType) * CopyFrom->ImageDesc.Height *
 367                    CopyFrom->ImageDesc.Width);
 368 
 369             /* finally, the extension blocks */
 370             if (sp->ExtensionBlocks != NULL) {
 371                 sp->ExtensionBlocks = (ExtensionBlock *)malloc(
 372                                       sizeof(ExtensionBlock) *
 373                                       CopyFrom->ExtensionBlockCount);
 374                 if (sp->ExtensionBlocks == NULL) {
 375                     FreeLastSavedImage(GifFile);
 376                     return (SavedImage *)(NULL);
 377                 }
 378                 memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks,
 379                        sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount);
 380             }
 381         }
 382 
 383         return (sp);
 384     }
 385 }
 386 
 387 void
 388 GifFreeSavedImages(GifFileType *GifFile)
 389 {
 390     SavedImage *sp;
 391 
 392     if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
 393         return;
 394     }
 395     for (sp = GifFile->SavedImages;
 396          sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
 397         if (sp->ImageDesc.ColorMap != NULL) {
 398             GifFreeMapObject(sp->ImageDesc.ColorMap);
 399             sp->ImageDesc.ColorMap = NULL;
 400         }
 401 
 402         if (sp->RasterBits != NULL)
 403             free((char *)sp->RasterBits);
 404         
 405         GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
 406     }
 407     free((char *)GifFile->SavedImages);
 408     GifFile->SavedImages = NULL;
 409 }
 410 
 411 /* end */
--- EOF ---