You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
412 lines
13 KiB
412 lines
13 KiB
/***************************************************************************** |
|
|
|
GIF construction tools |
|
|
|
****************************************************************************/ |
|
|
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
|
|
#include "gif_lib.h" |
|
|
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y)) |
|
|
|
/****************************************************************************** |
|
Miscellaneous utility functions |
|
******************************************************************************/ |
|
|
|
/* return smallest bitfield size n will fit in */ |
|
int |
|
GifBitSize(int n) |
|
{ |
|
register int i; |
|
|
|
for (i = 1; i <= 8; i++) |
|
if ((1 << i) >= n) |
|
break; |
|
return (i); |
|
} |
|
|
|
/****************************************************************************** |
|
Color map object functions |
|
******************************************************************************/ |
|
|
|
/* |
|
* Allocate a color map of given size; initialize with contents of |
|
* ColorMap if that pointer is non-NULL. |
|
*/ |
|
ColorMapObject * |
|
GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) |
|
{ |
|
ColorMapObject *Object; |
|
|
|
/*** FIXME: Our ColorCount has to be a power of two. Is it necessary to |
|
* make the user know that or should we automatically round up instead? */ |
|
if (ColorCount != (1 << GifBitSize(ColorCount))) { |
|
return ((ColorMapObject *) NULL); |
|
} |
|
|
|
Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); |
|
if (Object == (ColorMapObject *) NULL) { |
|
return ((ColorMapObject *) NULL); |
|
} |
|
|
|
Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType)); |
|
if (Object->Colors == (GifColorType *) NULL) { |
|
free(Object); |
|
return ((ColorMapObject *) NULL); |
|
} |
|
|
|
Object->ColorCount = ColorCount; |
|
Object->BitsPerPixel = GifBitSize(ColorCount); |
|
Object->SortFlag = false; |
|
|
|
if (ColorMap != NULL) { |
|
memcpy((char *)Object->Colors, |
|
(char *)ColorMap, ColorCount * sizeof(GifColorType)); |
|
} |
|
|
|
return (Object); |
|
} |
|
|
|
/******************************************************************************* |
|
Free a color map object |
|
*******************************************************************************/ |
|
void |
|
GifFreeMapObject(ColorMapObject *Object) |
|
{ |
|
if (Object != NULL) { |
|
(void)free(Object->Colors); |
|
(void)free(Object); |
|
} |
|
} |
|
|
|
#ifdef DEBUG |
|
void |
|
DumpColorMap(ColorMapObject *Object, |
|
FILE * fp) |
|
{ |
|
if (Object != NULL) { |
|
int i, j, Len = Object->ColorCount; |
|
|
|
for (i = 0; i < Len; i += 4) { |
|
for (j = 0; j < 4 && j < Len; j++) { |
|
(void)fprintf(fp, "%3d: %02x %02x %02x ", i + j, |
|
Object->Colors[i + j].Red, |
|
Object->Colors[i + j].Green, |
|
Object->Colors[i + j].Blue); |
|
} |
|
(void)fprintf(fp, "\n"); |
|
} |
|
} |
|
} |
|
#endif /* DEBUG */ |
|
|
|
/******************************************************************************* |
|
Compute the union of two given color maps and return it. If result can't |
|
fit into 256 colors, NULL is returned, the allocated union otherwise. |
|
ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are |
|
copied iff they didn't exist before. ColorTransIn2 maps the old |
|
ColorIn2 into the ColorUnion color map table./ |
|
*******************************************************************************/ |
|
ColorMapObject * |
|
GifUnionColorMap(const ColorMapObject *ColorIn1, |
|
const ColorMapObject *ColorIn2, |
|
GifPixelType ColorTransIn2[]) |
|
{ |
|
int i, j, CrntSlot, RoundUpTo, NewGifBitSize; |
|
ColorMapObject *ColorUnion; |
|
|
|
/* |
|
* We don't worry about duplicates within either color map; if |
|
* the caller wants to resolve those, he can perform unions |
|
* with an empty color map. |
|
*/ |
|
|
|
/* Allocate table which will hold the result for sure. */ |
|
ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount, |
|
ColorIn2->ColorCount) * 2, NULL); |
|
|
|
if (ColorUnion == NULL) |
|
return (NULL); |
|
|
|
/* |
|
* Copy ColorIn1 to ColorUnion. |
|
*/ |
|
for (i = 0; i < ColorIn1->ColorCount; i++) |
|
ColorUnion->Colors[i] = ColorIn1->Colors[i]; |
|
CrntSlot = ColorIn1->ColorCount; |
|
|
|
/* |
|
* Potentially obnoxious hack: |
|
* |
|
* Back CrntSlot down past all contiguous {0, 0, 0} slots at the end |
|
* of table 1. This is very useful if your display is limited to |
|
* 16 colors. |
|
*/ |
|
while (ColorIn1->Colors[CrntSlot - 1].Red == 0 |
|
&& ColorIn1->Colors[CrntSlot - 1].Green == 0 |
|
&& ColorIn1->Colors[CrntSlot - 1].Blue == 0) |
|
CrntSlot--; |
|
|
|
/* Copy ColorIn2 to ColorUnion (use old colors if they exist): */ |
|
for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { |
|
/* Let's see if this color already exists: */ |
|
for (j = 0; j < ColorIn1->ColorCount; j++) |
|
if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i], |
|
sizeof(GifColorType)) == 0) |
|
break; |
|
|
|
if (j < ColorIn1->ColorCount) |
|
ColorTransIn2[i] = j; /* color exists in Color1 */ |
|
else { |
|
/* Color is new - copy it to a new slot: */ |
|
ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i]; |
|
ColorTransIn2[i] = CrntSlot++; |
|
} |
|
} |
|
|
|
if (CrntSlot > 256) { |
|
GifFreeMapObject(ColorUnion); |
|
return ((ColorMapObject *) NULL); |
|
} |
|
|
|
NewGifBitSize = GifBitSize(CrntSlot); |
|
RoundUpTo = (1 << NewGifBitSize); |
|
|
|
if (RoundUpTo != ColorUnion->ColorCount) { |
|
register GifColorType *Map = ColorUnion->Colors; |
|
|
|
/* |
|
* Zero out slots up to next power of 2. |
|
* We know these slots exist because of the way ColorUnion's |
|
* start dimension was computed. |
|
*/ |
|
for (j = CrntSlot; j < RoundUpTo; j++) |
|
Map[j].Red = Map[j].Green = Map[j].Blue = 0; |
|
|
|
/* perhaps we can shrink the map? */ |
|
if (RoundUpTo < ColorUnion->ColorCount) { |
|
GifColorType *new_map = (GifColorType *)reallocarray(Map, |
|
RoundUpTo, sizeof(GifColorType)); |
|
if( new_map == NULL ) { |
|
GifFreeMapObject(ColorUnion); |
|
return ((ColorMapObject *) NULL); |
|
} |
|
ColorUnion->Colors = new_map; |
|
} |
|
} |
|
|
|
ColorUnion->ColorCount = RoundUpTo; |
|
ColorUnion->BitsPerPixel = NewGifBitSize; |
|
|
|
return (ColorUnion); |
|
} |
|
|
|
/******************************************************************************* |
|
Apply a given color translation to the raster bits of an image |
|
*******************************************************************************/ |
|
void |
|
GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]) |
|
{ |
|
register int i; |
|
register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width; |
|
|
|
for (i = 0; i < RasterSize; i++) |
|
Image->RasterBits[i] = Translation[Image->RasterBits[i]]; |
|
} |
|
|
|
/****************************************************************************** |
|
Extension record functions |
|
******************************************************************************/ |
|
int |
|
GifAddExtensionBlock(int *ExtensionBlockCount, |
|
ExtensionBlock **ExtensionBlocks, |
|
int Function, |
|
unsigned int Len, |
|
unsigned char ExtData[]) |
|
{ |
|
ExtensionBlock *ep; |
|
|
|
if (*ExtensionBlocks == NULL) |
|
*ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock)); |
|
else { |
|
ExtensionBlock* ep_new = (ExtensionBlock *)reallocarray |
|
(*ExtensionBlocks, (*ExtensionBlockCount + 1), |
|
sizeof(ExtensionBlock)); |
|
if( ep_new == NULL ) |
|
return (GIF_ERROR); |
|
*ExtensionBlocks = ep_new; |
|
} |
|
|
|
if (*ExtensionBlocks == NULL) |
|
return (GIF_ERROR); |
|
|
|
ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++]; |
|
|
|
ep->Function = Function; |
|
ep->ByteCount=Len; |
|
ep->Bytes = (GifByteType *)malloc(ep->ByteCount); |
|
if (ep->Bytes == NULL) |
|
return (GIF_ERROR); |
|
|
|
if (ExtData != NULL) { |
|
memcpy(ep->Bytes, ExtData, Len); |
|
} |
|
|
|
return (GIF_OK); |
|
} |
|
|
|
void |
|
GifFreeExtensions(int *ExtensionBlockCount, |
|
ExtensionBlock **ExtensionBlocks) |
|
{ |
|
ExtensionBlock *ep; |
|
|
|
if (*ExtensionBlocks == NULL) |
|
return; |
|
|
|
for (ep = *ExtensionBlocks; |
|
ep < (*ExtensionBlocks + *ExtensionBlockCount); |
|
ep++) |
|
(void)free((char *)ep->Bytes); |
|
(void)free((char *)*ExtensionBlocks); |
|
*ExtensionBlocks = NULL; |
|
*ExtensionBlockCount = 0; |
|
} |
|
|
|
/****************************************************************************** |
|
Image block allocation functions |
|
******************************************************************************/ |
|
|
|
/* Private Function: |
|
* Frees the last image in the GifFile->SavedImages array |
|
*/ |
|
void |
|
FreeLastSavedImage(GifFileType *GifFile) |
|
{ |
|
SavedImage *sp; |
|
|
|
if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) |
|
return; |
|
|
|
/* Remove one SavedImage from the GifFile */ |
|
GifFile->ImageCount--; |
|
sp = &GifFile->SavedImages[GifFile->ImageCount]; |
|
|
|
/* Deallocate its Colormap */ |
|
if (sp->ImageDesc.ColorMap != NULL) { |
|
GifFreeMapObject(sp->ImageDesc.ColorMap); |
|
sp->ImageDesc.ColorMap = NULL; |
|
} |
|
|
|
/* Deallocate the image data */ |
|
if (sp->RasterBits != NULL) |
|
free((char *)sp->RasterBits); |
|
|
|
/* Deallocate any extensions */ |
|
GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); |
|
|
|
/*** FIXME: We could realloc the GifFile->SavedImages structure but is |
|
* there a point to it? Saves some memory but we'd have to do it every |
|
* time. If this is used in GifFreeSavedImages then it would be inefficient |
|
* (The whole array is going to be deallocated.) If we just use it when |
|
* we want to free the last Image it's convenient to do it here. |
|
*/ |
|
} |
|
|
|
/* |
|
* Append an image block to the SavedImages array |
|
*/ |
|
SavedImage * |
|
GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom) |
|
{ |
|
if (GifFile->SavedImages == NULL) |
|
GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage)); |
|
else |
|
GifFile->SavedImages = (SavedImage *)reallocarray(GifFile->SavedImages, |
|
(GifFile->ImageCount + 1), sizeof(SavedImage)); |
|
|
|
if (GifFile->SavedImages == NULL) |
|
return ((SavedImage *)NULL); |
|
else { |
|
SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++]; |
|
memset((char *)sp, '\0', sizeof(SavedImage)); |
|
|
|
if (CopyFrom != NULL) { |
|
memcpy((char *)sp, CopyFrom, sizeof(SavedImage)); |
|
|
|
/* |
|
* Make our own allocated copies of the heap fields in the |
|
* copied record. This guards against potential aliasing |
|
* problems. |
|
*/ |
|
|
|
/* first, the local color map */ |
|
if (sp->ImageDesc.ColorMap != NULL) { |
|
sp->ImageDesc.ColorMap = GifMakeMapObject( |
|
CopyFrom->ImageDesc.ColorMap->ColorCount, |
|
CopyFrom->ImageDesc.ColorMap->Colors); |
|
if (sp->ImageDesc.ColorMap == NULL) { |
|
FreeLastSavedImage(GifFile); |
|
return (SavedImage *)(NULL); |
|
} |
|
} |
|
|
|
/* next, the raster */ |
|
sp->RasterBits = (unsigned char *)reallocarray(NULL, |
|
(CopyFrom->ImageDesc.Height * |
|
CopyFrom->ImageDesc.Width), |
|
sizeof(GifPixelType)); |
|
if (sp->RasterBits == NULL) { |
|
FreeLastSavedImage(GifFile); |
|
return (SavedImage *)(NULL); |
|
} |
|
memcpy(sp->RasterBits, CopyFrom->RasterBits, |
|
sizeof(GifPixelType) * CopyFrom->ImageDesc.Height * |
|
CopyFrom->ImageDesc.Width); |
|
|
|
/* finally, the extension blocks */ |
|
if (sp->ExtensionBlocks != NULL) { |
|
sp->ExtensionBlocks = (ExtensionBlock *)reallocarray(NULL, |
|
CopyFrom->ExtensionBlockCount, |
|
sizeof(ExtensionBlock)); |
|
if (sp->ExtensionBlocks == NULL) { |
|
FreeLastSavedImage(GifFile); |
|
return (SavedImage *)(NULL); |
|
} |
|
memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks, |
|
sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount); |
|
} |
|
} |
|
|
|
return (sp); |
|
} |
|
} |
|
|
|
void |
|
GifFreeSavedImages(GifFileType *GifFile) |
|
{ |
|
SavedImage *sp; |
|
|
|
if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { |
|
return; |
|
} |
|
for (sp = GifFile->SavedImages; |
|
sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { |
|
if (sp->ImageDesc.ColorMap != NULL) { |
|
GifFreeMapObject(sp->ImageDesc.ColorMap); |
|
sp->ImageDesc.ColorMap = NULL; |
|
} |
|
|
|
if (sp->RasterBits != NULL) |
|
free((char *)sp->RasterBits); |
|
|
|
GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); |
|
} |
|
free((char *)GifFile->SavedImages); |
|
GifFile->SavedImages = NULL; |
|
} |
|
|
|
/* end */
|
|
|