| /****************************************************************************** |
| |
| egif_lib.c - GIF encoding |
| |
| The functions here and in dgif_lib.c are partitioned carefully so that |
| if you only require one of read and write capability, only one of these |
| two modules will be linked. Preserve this property! |
| |
| SPDX-License-Identifier: MIT |
| |
| *****************************************************************************/ |
| |
| #include <fcntl.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #ifdef _WIN32 |
| #include <io.h> |
| #else |
| #include <sys/types.h> |
| #include <unistd.h> |
| #endif /* _WIN32 */ |
| #include <sys/stat.h> |
| |
| #include "gif_lib.h" |
| #include "gif_lib_private.h" |
| |
| /* Masks given codes to BitsPerPixel, to make sure all codes are in range: */ |
| /*@+charint@*/ |
| static const GifPixelType CodeMask[] = {0x00, 0x01, 0x03, 0x07, 0x0f, |
| 0x1f, 0x3f, 0x7f, 0xff}; |
| /*@-charint@*/ |
| |
| static int EGifPutWord(int Word, GifFileType *GifFile); |
| static int EGifSetupCompress(GifFileType *GifFile); |
| static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line, |
| const int LineLen); |
| static int EGifCompressOutput(GifFileType *GifFile, int Code); |
| static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c); |
| |
| /* extract bytes from an unsigned word */ |
| #define LOBYTE(x) ((x)&0xff) |
| #define HIBYTE(x) (((x) >> 8) & 0xff) |
| |
| #ifndef S_IREAD |
| #define S_IREAD S_IRUSR |
| #endif |
| |
| #ifndef S_IWRITE |
| #define S_IWRITE S_IWUSR |
| #endif |
| /****************************************************************************** |
| Open a new GIF file for write, specified by name. If TestExistance then |
| if the file exists this routines fails (returns NULL). |
| Returns a dynamically allocated GifFileType pointer which serves as the GIF |
| info record. The Error member is cleared if successful. |
| ******************************************************************************/ |
| GifFileType *EGifOpenFileName(const char *FileName, const bool TestExistence, |
| int *Error) { |
| |
| int FileHandle; |
| GifFileType *GifFile; |
| |
| if (TestExistence) { |
| FileHandle = open(FileName, O_WRONLY | O_CREAT | O_EXCL, |
| S_IREAD | S_IWRITE); |
| } else { |
| FileHandle = open(FileName, O_WRONLY | O_CREAT | O_TRUNC, |
| S_IREAD | S_IWRITE); |
| } |
| |
| if (FileHandle == -1) { |
| if (Error != NULL) { |
| *Error = E_GIF_ERR_OPEN_FAILED; |
| } |
| return NULL; |
| } |
| GifFile = EGifOpenFileHandle(FileHandle, Error); |
| if (GifFile == (GifFileType *)NULL) { |
| (void)close(FileHandle); |
| } |
| return GifFile; |
| } |
| |
| /****************************************************************************** |
| Update a new GIF file, given its file handle, which must be opened for |
| write in binary mode. |
| Returns dynamically allocated a GifFileType pointer which serves as the GIF |
| info record. |
| Only fails on a memory allocation error. |
| ******************************************************************************/ |
| GifFileType *EGifOpenFileHandle(const int FileHandle, int *Error) { |
| GifFileType *GifFile; |
| GifFilePrivateType *Private; |
| FILE *f; |
| |
| GifFile = (GifFileType *)malloc(sizeof(GifFileType)); |
| if (GifFile == NULL) { |
| return NULL; |
| } |
| |
| memset(GifFile, '\0', sizeof(GifFileType)); |
| |
| Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); |
| if (Private == NULL) { |
| free(GifFile); |
| if (Error != NULL) { |
| *Error = E_GIF_ERR_NOT_ENOUGH_MEM; |
| } |
| return NULL; |
| } |
| /*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType)); |
| if ((Private->HashTable = _InitHashTable()) == NULL) { |
| free(GifFile); |
| free(Private); |
| if (Error != NULL) { |
| *Error = E_GIF_ERR_NOT_ENOUGH_MEM; |
| } |
| return NULL; |
| } |
| |
| #ifdef _WIN32 |
| _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ |
| #endif /* _WIN32 */ |
| |
| f = fdopen(FileHandle, "wb"); /* Make it into a stream: */ |
| |
| GifFile->Private = (void *)Private; |
| Private->FileHandle = FileHandle; |
| Private->File = f; |
| Private->FileState = FILE_STATE_WRITE; |
| Private->gif89 = false; |
| |
| Private->Write = (OutputFunc)0; /* No user write routine (MRB) */ |
| GifFile->UserData = (void *)NULL; /* No user write handle (MRB) */ |
| |
| GifFile->Error = 0; |
| |
| return GifFile; |
| } |
| |
| /****************************************************************************** |
| Output constructor that takes user supplied output function. |
| Basically just a copy of EGifOpenFileHandle. (MRB) |
| ******************************************************************************/ |
| GifFileType *EGifOpen(void *userData, OutputFunc writeFunc, int *Error) { |
| GifFileType *GifFile; |
| GifFilePrivateType *Private; |
| |
| GifFile = (GifFileType *)malloc(sizeof(GifFileType)); |
| if (GifFile == NULL) { |
| if (Error != NULL) { |
| *Error = E_GIF_ERR_NOT_ENOUGH_MEM; |
| } |
| return NULL; |
| } |
| |
| memset(GifFile, '\0', sizeof(GifFileType)); |
| |
| Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); |
| if (Private == NULL) { |
| free(GifFile); |
| if (Error != NULL) { |
| *Error = E_GIF_ERR_NOT_ENOUGH_MEM; |
| } |
| return NULL; |
| } |
| |
| memset(Private, '\0', sizeof(GifFilePrivateType)); |
| |
| Private->HashTable = _InitHashTable(); |
| if (Private->HashTable == NULL) { |
| free(GifFile); |
| free(Private); |
| if (Error != NULL) { |
| *Error = E_GIF_ERR_NOT_ENOUGH_MEM; |
| } |
| return NULL; |
| } |
| |
| GifFile->Private = (void *)Private; |
| Private->FileHandle = 0; |
| Private->File = (FILE *)0; |
| Private->FileState = FILE_STATE_WRITE; |
| |
| Private->Write = writeFunc; /* User write routine (MRB) */ |
| GifFile->UserData = userData; /* User write handle (MRB) */ |
| |
| Private->gif89 = false; /* initially, write GIF87 */ |
| |
| GifFile->Error = 0; |
| |
| return GifFile; |
| } |
| |
| /****************************************************************************** |
| Routine to compute the GIF version that will be written on output. |
| ******************************************************************************/ |
| const char *EGifGetGifVersion(GifFileType *GifFile) { |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| int i, j; |
| |
| /* |
| * Bulletproofing - always write GIF89 if we need to. |
| * Note, we don't clear the gif89 flag here because |
| * users of the sequential API might have called EGifSetGifVersion() |
| * in order to set that flag. |
| */ |
| for (i = 0; i < GifFile->ImageCount; i++) { |
| for (j = 0; j < GifFile->SavedImages[i].ExtensionBlockCount; |
| j++) { |
| int function = |
| GifFile->SavedImages[i].ExtensionBlocks[j].Function; |
| |
| if (function == COMMENT_EXT_FUNC_CODE || |
| function == GRAPHICS_EXT_FUNC_CODE || |
| function == PLAINTEXT_EXT_FUNC_CODE || |
| function == APPLICATION_EXT_FUNC_CODE) { |
| Private->gif89 = true; |
| } |
| } |
| } |
| for (i = 0; i < GifFile->ExtensionBlockCount; i++) { |
| int function = GifFile->ExtensionBlocks[i].Function; |
| |
| if (function == COMMENT_EXT_FUNC_CODE || |
| function == GRAPHICS_EXT_FUNC_CODE || |
| function == PLAINTEXT_EXT_FUNC_CODE || |
| function == APPLICATION_EXT_FUNC_CODE) { |
| Private->gif89 = true; |
| } |
| } |
| |
| if (Private->gif89) { |
| return GIF89_STAMP; |
| } else { |
| return GIF87_STAMP; |
| } |
| } |
| |
| /****************************************************************************** |
| Set the GIF version. In the extremely unlikely event that there is ever |
| another version, replace the bool argument with an enum in which the |
| GIF87 value is 0 (numerically the same as bool false) and the GIF89 value |
| is 1 (numerically the same as bool true). That way we'll even preserve |
| object-file compatibility! |
| ******************************************************************************/ |
| void EGifSetGifVersion(GifFileType *GifFile, const bool gif89) { |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| Private->gif89 = gif89; |
| } |
| |
| /****************************************************************************** |
| All writes to the GIF should go through this. |
| ******************************************************************************/ |
| static int InternalWrite(GifFileType *GifFileOut, const unsigned char *buf, |
| size_t len) { |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFileOut->Private; |
| if (Private->Write) { |
| return Private->Write(GifFileOut, buf, len); |
| } else { |
| return fwrite(buf, 1, len, Private->File); |
| } |
| } |
| |
| /****************************************************************************** |
| This routine should be called before any other EGif calls, immediately |
| following the GIF file opening. |
| ******************************************************************************/ |
| int EGifPutScreenDesc(GifFileType *GifFile, const int Width, const int Height, |
| const int ColorRes, const int BackGround, |
| const ColorMapObject *ColorMap) { |
| GifByteType Buf[3]; |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| const char *write_version; |
| GifFile->SColorMap = NULL; |
| |
| if (Private->FileState & FILE_STATE_SCREEN) { |
| /* If already has screen descriptor - something is wrong! */ |
| GifFile->Error = E_GIF_ERR_HAS_SCRN_DSCR; |
| return GIF_ERROR; |
| } |
| if (!IS_WRITEABLE(Private)) { |
| /* This file was NOT open for writing: */ |
| GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; |
| return GIF_ERROR; |
| } |
| |
| write_version = EGifGetGifVersion(GifFile); |
| |
| /* First write the version prefix into the file. */ |
| if (InternalWrite(GifFile, (unsigned char *)write_version, |
| strlen(write_version)) != strlen(write_version)) { |
| GifFile->Error = E_GIF_ERR_WRITE_FAILED; |
| return GIF_ERROR; |
| } |
| |
| GifFile->SWidth = Width; |
| GifFile->SHeight = Height; |
| GifFile->SColorResolution = ColorRes; |
| GifFile->SBackGroundColor = BackGround; |
| if (ColorMap) { |
| GifFile->SColorMap = |
| GifMakeMapObject(ColorMap->ColorCount, ColorMap->Colors); |
| if (GifFile->SColorMap == NULL) { |
| GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM; |
| return GIF_ERROR; |
| } |
| } else { |
| GifFile->SColorMap = NULL; |
| } |
| |
| /* |
| * Put the logical screen descriptor into the file: |
| */ |
| /* Logical Screen Descriptor: Dimensions */ |
| (void)EGifPutWord(Width, GifFile); |
| (void)EGifPutWord(Height, GifFile); |
| |
| /* Logical Screen Descriptor: Packed Fields */ |
| /* Note: We have actual size of the color table default to the largest |
| * possible size (7+1 == 8 bits) because the decoder can use it to |
| * decide how to display the files. |
| */ |
| Buf[0] = |
| (ColorMap ? 0x80 : 0x00) | /* Yes/no global colormap */ |
| ((ColorRes - 1) << 4) | /* Bits allocated to each primary color */ |
| (ColorMap ? ColorMap->BitsPerPixel - 1 |
| : 0x07); /* Actual size of the |
| color table. */ |
| if (ColorMap != NULL && ColorMap->SortFlag) { |
| Buf[0] |= 0x08; |
| } |
| Buf[1] = |
| BackGround; /* Index into the ColorTable for background color */ |
| Buf[2] = GifFile->AspectByte; /* Pixel Aspect Ratio */ |
| InternalWrite(GifFile, Buf, 3); |
| |
| /* If we have Global color map - dump it also: */ |
| if (ColorMap != NULL) { |
| int i; |
| for (i = 0; i < ColorMap->ColorCount; i++) { |
| /* Put the ColorMap out also: */ |
| Buf[0] = ColorMap->Colors[i].Red; |
| Buf[1] = ColorMap->Colors[i].Green; |
| Buf[2] = ColorMap->Colors[i].Blue; |
| if (InternalWrite(GifFile, Buf, 3) != 3) { |
| GifFile->Error = E_GIF_ERR_WRITE_FAILED; |
| return GIF_ERROR; |
| } |
| } |
| } |
| |
| /* Mark this file as has screen descriptor, and no pixel written yet: */ |
| Private->FileState |= FILE_STATE_SCREEN; |
| |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| This routine should be called before any attempt to dump an image - any |
| call to any of the pixel dump routines. |
| ******************************************************************************/ |
| int EGifPutImageDesc(GifFileType *GifFile, const int Left, const int Top, |
| const int Width, const int Height, const bool Interlace, |
| const ColorMapObject *ColorMap) { |
| GifByteType Buf[3]; |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| if (Private->FileState & FILE_STATE_IMAGE && |
| Private->PixelCount > 0xffff0000UL) { |
| /* If already has active image descriptor - something is wrong! |
| */ |
| GifFile->Error = E_GIF_ERR_HAS_IMAG_DSCR; |
| return GIF_ERROR; |
| } |
| if (!IS_WRITEABLE(Private)) { |
| /* This file was NOT open for writing: */ |
| GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; |
| return GIF_ERROR; |
| } |
| GifFile->Image.Left = Left; |
| GifFile->Image.Top = Top; |
| GifFile->Image.Width = Width; |
| GifFile->Image.Height = Height; |
| GifFile->Image.Interlace = Interlace; |
| if (ColorMap != GifFile->Image.ColorMap) { |
| if (ColorMap) { |
| if (GifFile->Image.ColorMap != NULL) { |
| GifFreeMapObject(GifFile->Image.ColorMap); |
| GifFile->Image.ColorMap = NULL; |
| } |
| GifFile->Image.ColorMap = GifMakeMapObject( |
| ColorMap->ColorCount, ColorMap->Colors); |
| if (GifFile->Image.ColorMap == NULL) { |
| GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM; |
| return GIF_ERROR; |
| } |
| } else { |
| GifFile->Image.ColorMap = NULL; |
| } |
| } |
| |
| /* Put the image descriptor into the file: */ |
| Buf[0] = DESCRIPTOR_INTRODUCER; /* Image separator character. */ |
| InternalWrite(GifFile, Buf, 1); |
| (void)EGifPutWord(Left, GifFile); |
| (void)EGifPutWord(Top, GifFile); |
| (void)EGifPutWord(Width, GifFile); |
| (void)EGifPutWord(Height, GifFile); |
| Buf[0] = (ColorMap ? 0x80 : 0x00) | (Interlace ? 0x40 : 0x00) | |
| (ColorMap ? ColorMap->BitsPerPixel - 1 : 0); |
| InternalWrite(GifFile, Buf, 1); |
| |
| /* If we have Global color map - dump it also: */ |
| if (ColorMap != NULL) { |
| int i; |
| for (i = 0; i < ColorMap->ColorCount; i++) { |
| /* Put the ColorMap out also: */ |
| Buf[0] = ColorMap->Colors[i].Red; |
| Buf[1] = ColorMap->Colors[i].Green; |
| Buf[2] = ColorMap->Colors[i].Blue; |
| if (InternalWrite(GifFile, Buf, 3) != 3) { |
| GifFile->Error = E_GIF_ERR_WRITE_FAILED; |
| return GIF_ERROR; |
| } |
| } |
| } |
| if (GifFile->SColorMap == NULL && GifFile->Image.ColorMap == NULL) { |
| GifFile->Error = E_GIF_ERR_NO_COLOR_MAP; |
| return GIF_ERROR; |
| } |
| |
| /* Mark this file as has screen descriptor: */ |
| Private->FileState |= FILE_STATE_IMAGE; |
| Private->PixelCount = (long)Width * (long)Height; |
| |
| /* Reset compress algorithm parameters. */ |
| (void)EGifSetupCompress(GifFile); |
| |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| Put one full scanned line (Line) of length LineLen into GIF file. |
| ******************************************************************************/ |
| int EGifPutLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) { |
| int i; |
| GifPixelType Mask; |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| if (!IS_WRITEABLE(Private)) { |
| /* This file was NOT open for writing: */ |
| GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; |
| return GIF_ERROR; |
| } |
| |
| if (!LineLen) { |
| LineLen = GifFile->Image.Width; |
| } |
| if (Private->PixelCount < (unsigned)LineLen) { |
| GifFile->Error = E_GIF_ERR_DATA_TOO_BIG; |
| return GIF_ERROR; |
| } |
| Private->PixelCount -= LineLen; |
| |
| /* Make sure the codes are not out of bit range, as we might generate |
| * wrong code (because of overflow when we combine them) in this case: |
| */ |
| Mask = CodeMask[Private->BitsPerPixel]; |
| for (i = 0; i < LineLen; i++) { |
| Line[i] &= Mask; |
| } |
| |
| return EGifCompressLine(GifFile, Line, LineLen); |
| } |
| |
| /****************************************************************************** |
| Put one pixel (Pixel) into GIF file. |
| ******************************************************************************/ |
| int EGifPutPixel(GifFileType *GifFile, GifPixelType Pixel) { |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| if (!IS_WRITEABLE(Private)) { |
| /* This file was NOT open for writing: */ |
| GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; |
| return GIF_ERROR; |
| } |
| |
| if (Private->PixelCount == 0) { |
| GifFile->Error = E_GIF_ERR_DATA_TOO_BIG; |
| return GIF_ERROR; |
| } |
| --Private->PixelCount; |
| |
| /* Make sure the code is not out of bit range, as we might generate |
| * wrong code (because of overflow when we combine them) in this case: |
| */ |
| Pixel &= CodeMask[Private->BitsPerPixel]; |
| |
| return EGifCompressLine(GifFile, &Pixel, 1); |
| } |
| |
| /****************************************************************************** |
| Put a comment into GIF file using the GIF89 comment extension block. |
| ******************************************************************************/ |
| int EGifPutComment(GifFileType *GifFile, const char *Comment) { |
| unsigned int length; |
| char *buf; |
| |
| length = strlen(Comment); |
| if (length <= 255) { |
| return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, length, |
| Comment); |
| } else { |
| buf = (char *)Comment; |
| if (EGifPutExtensionLeader(GifFile, COMMENT_EXT_FUNC_CODE) == |
| GIF_ERROR) { |
| return GIF_ERROR; |
| } |
| |
| /* Break the comment into 255 byte sub blocks */ |
| while (length > 255) { |
| if (EGifPutExtensionBlock(GifFile, 255, buf) == |
| GIF_ERROR) { |
| return GIF_ERROR; |
| } |
| buf = buf + 255; |
| length -= 255; |
| } |
| /* Output any partial block and the clear code. */ |
| if (length > 0) { |
| if (EGifPutExtensionBlock(GifFile, length, buf) == |
| GIF_ERROR) { |
| return GIF_ERROR; |
| } |
| } |
| if (EGifPutExtensionTrailer(GifFile) == GIF_ERROR) { |
| return GIF_ERROR; |
| } |
| } |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| Begin an extension block (see GIF manual). More |
| extensions can be dumped using EGifPutExtensionBlock until |
| EGifPutExtensionTrailer is invoked. |
| ******************************************************************************/ |
| int EGifPutExtensionLeader(GifFileType *GifFile, const int ExtCode) { |
| GifByteType Buf[3]; |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| if (!IS_WRITEABLE(Private)) { |
| /* This file was NOT open for writing: */ |
| GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; |
| return GIF_ERROR; |
| } |
| |
| Buf[0] = EXTENSION_INTRODUCER; |
| Buf[1] = ExtCode; |
| InternalWrite(GifFile, Buf, 2); |
| |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| Put extension block data (see GIF manual) into a GIF file. |
| ******************************************************************************/ |
| int EGifPutExtensionBlock(GifFileType *GifFile, const int ExtLen, |
| const void *Extension) { |
| GifByteType Buf; |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| if (!IS_WRITEABLE(Private)) { |
| /* This file was NOT open for writing: */ |
| GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; |
| return GIF_ERROR; |
| } |
| |
| Buf = ExtLen; |
| InternalWrite(GifFile, &Buf, 1); |
| InternalWrite(GifFile, Extension, ExtLen); |
| |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| Put a terminating block (see GIF manual) into a GIF file. |
| ******************************************************************************/ |
| int EGifPutExtensionTrailer(GifFileType *GifFile) { |
| |
| GifByteType Buf; |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| if (!IS_WRITEABLE(Private)) { |
| /* This file was NOT open for writing: */ |
| GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; |
| return GIF_ERROR; |
| } |
| |
| /* Write the block terminator */ |
| Buf = 0; |
| InternalWrite(GifFile, &Buf, 1); |
| |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| Put an extension block (see GIF manual) into a GIF file. |
| Warning: This function is only useful for Extension blocks that have at |
| most one subblock. Extensions with more than one subblock need to use the |
| EGifPutExtension{Leader,Block,Trailer} functions instead. |
| ******************************************************************************/ |
| int EGifPutExtension(GifFileType *GifFile, const int ExtCode, const int ExtLen, |
| const void *Extension) { |
| |
| GifByteType Buf[3]; |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| if (!IS_WRITEABLE(Private)) { |
| /* This file was NOT open for writing: */ |
| GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; |
| return GIF_ERROR; |
| } |
| |
| if (ExtCode == 0) { |
| InternalWrite(GifFile, (GifByteType *)&ExtLen, 1); |
| } else { |
| Buf[0] = EXTENSION_INTRODUCER; |
| Buf[1] = ExtCode; /* Extension Label */ |
| Buf[2] = ExtLen; /* Extension length */ |
| InternalWrite(GifFile, Buf, 3); |
| } |
| InternalWrite(GifFile, Extension, ExtLen); |
| Buf[0] = 0; |
| InternalWrite(GifFile, Buf, 1); |
| |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| Render a Graphics Control Block as raw extension data |
| ******************************************************************************/ |
| |
| size_t EGifGCBToExtension(const GraphicsControlBlock *GCB, |
| GifByteType *GifExtension) { |
| GifExtension[0] = 0; |
| GifExtension[0] |= |
| (GCB->TransparentColor == NO_TRANSPARENT_COLOR) ? 0x00 : 0x01; |
| GifExtension[0] |= GCB->UserInputFlag ? 0x02 : 0x00; |
| GifExtension[0] |= ((GCB->DisposalMode & 0x07) << 2); |
| GifExtension[1] = LOBYTE(GCB->DelayTime); |
| GifExtension[2] = HIBYTE(GCB->DelayTime); |
| GifExtension[3] = (char)GCB->TransparentColor; |
| return 4; |
| } |
| |
| /****************************************************************************** |
| Replace the Graphics Control Block for a saved image, if it exists. |
| ******************************************************************************/ |
| |
| int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, |
| GifFileType *GifFile, int ImageIndex) { |
| int i; |
| size_t Len; |
| GifByteType buf[sizeof(GraphicsControlBlock)]; /* a bit dodgy... */ |
| |
| if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) { |
| return GIF_ERROR; |
| } |
| |
| for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; |
| i++) { |
| ExtensionBlock *ep = |
| &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; |
| if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { |
| EGifGCBToExtension(GCB, ep->Bytes); |
| return GIF_OK; |
| } |
| } |
| |
| Len = EGifGCBToExtension(GCB, (GifByteType *)buf); |
| if (GifAddExtensionBlock( |
| &GifFile->SavedImages[ImageIndex].ExtensionBlockCount, |
| &GifFile->SavedImages[ImageIndex].ExtensionBlocks, |
| GRAPHICS_EXT_FUNC_CODE, Len, |
| (unsigned char *)buf) == GIF_ERROR) { |
| return (GIF_ERROR); |
| } |
| |
| return (GIF_OK); |
| } |
| |
| /****************************************************************************** |
| Put the image code in compressed form. This routine can be called if the |
| information needed to be piped out as is. Obviously this is much faster |
| than decoding and encoding again. This routine should be followed by calls |
| to EGifPutCodeNext, until NULL block is given. |
| The block should NOT be freed by the user (not dynamically allocated). |
| ******************************************************************************/ |
| int EGifPutCode(GifFileType *GifFile, int CodeSize, |
| const GifByteType *CodeBlock) { |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| if (!IS_WRITEABLE(Private)) { |
| /* This file was NOT open for writing: */ |
| GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; |
| return GIF_ERROR; |
| } |
| |
| /* No need to dump code size as Compression set up does any for us: */ |
| /* |
| * Buf = CodeSize; |
| * if (InternalWrite(GifFile, &Buf, 1) != 1) { |
| * GifFile->Error = E_GIF_ERR_WRITE_FAILED; |
| * return GIF_ERROR; |
| * } |
| */ |
| |
| return EGifPutCodeNext(GifFile, CodeBlock); |
| } |
| |
| /****************************************************************************** |
| Continue to put the image code in compressed form. This routine should be |
| called with blocks of code as read via DGifGetCode/DGifGetCodeNext. If |
| given buffer pointer is NULL, empty block is written to mark end of code. |
| ******************************************************************************/ |
| int EGifPutCodeNext(GifFileType *GifFile, const GifByteType *CodeBlock) { |
| GifByteType Buf; |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| if (CodeBlock != NULL) { |
| if (InternalWrite(GifFile, CodeBlock, CodeBlock[0] + 1) != |
| (unsigned)(CodeBlock[0] + 1)) { |
| GifFile->Error = E_GIF_ERR_WRITE_FAILED; |
| return GIF_ERROR; |
| } |
| } else { |
| Buf = 0; |
| if (InternalWrite(GifFile, &Buf, 1) != 1) { |
| GifFile->Error = E_GIF_ERR_WRITE_FAILED; |
| return GIF_ERROR; |
| } |
| Private->PixelCount = |
| 0; /* And local info. indicate image read. */ |
| } |
| |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| This routine should be called last, to close the GIF file. |
| ******************************************************************************/ |
| int EGifCloseFile(GifFileType *GifFile, int *ErrorCode) { |
| GifByteType Buf; |
| GifFilePrivateType *Private; |
| FILE *File; |
| |
| if (GifFile == NULL) { |
| return GIF_ERROR; |
| } |
| |
| Private = (GifFilePrivateType *)GifFile->Private; |
| if (Private == NULL) { |
| return GIF_ERROR; |
| } else if (!IS_WRITEABLE(Private)) { |
| /* This file was NOT open for writing: */ |
| if (ErrorCode != NULL) { |
| *ErrorCode = E_GIF_ERR_NOT_WRITEABLE; |
| } |
| free(GifFile); |
| return GIF_ERROR; |
| } else { |
| File = Private->File; |
| |
| Buf = TERMINATOR_INTRODUCER; |
| InternalWrite(GifFile, &Buf, 1); |
| |
| if (GifFile->Image.ColorMap) { |
| GifFreeMapObject(GifFile->Image.ColorMap); |
| GifFile->Image.ColorMap = NULL; |
| } |
| if (GifFile->SColorMap) { |
| GifFreeMapObject(GifFile->SColorMap); |
| GifFile->SColorMap = NULL; |
| } |
| if (Private->HashTable) { |
| free((char *)Private->HashTable); |
| } |
| free((char *)Private); |
| |
| if (File && fclose(File) != 0) { |
| if (ErrorCode != NULL) { |
| *ErrorCode = E_GIF_ERR_CLOSE_FAILED; |
| } |
| free(GifFile); |
| return GIF_ERROR; |
| } |
| |
| free(GifFile); |
| if (ErrorCode != NULL) { |
| *ErrorCode = E_GIF_SUCCEEDED; |
| } |
| } |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| Put 2 bytes (a word) into the given file in little-endian order: |
| ******************************************************************************/ |
| static int EGifPutWord(int Word, GifFileType *GifFile) { |
| unsigned char c[2]; |
| |
| c[0] = LOBYTE(Word); |
| c[1] = HIBYTE(Word); |
| if (InternalWrite(GifFile, c, 2) == 2) { |
| return GIF_OK; |
| } else { |
| return GIF_ERROR; |
| } |
| } |
| |
| /****************************************************************************** |
| Setup the LZ compression for this image: |
| ******************************************************************************/ |
| static int EGifSetupCompress(GifFileType *GifFile) { |
| int BitsPerPixel; |
| GifByteType Buf; |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| /* Test and see what color map to use, and from it # bits per pixel: */ |
| if (GifFile->Image.ColorMap) { |
| BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel; |
| } else if (GifFile->SColorMap) { |
| BitsPerPixel = GifFile->SColorMap->BitsPerPixel; |
| } else { |
| GifFile->Error = E_GIF_ERR_NO_COLOR_MAP; |
| return GIF_ERROR; |
| } |
| |
| Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel); |
| InternalWrite(GifFile, &Buf, 1); /* Write the Code size to file. */ |
| |
| Private->Buf[0] = 0; /* Nothing was output yet. */ |
| Private->BitsPerPixel = BitsPerPixel; |
| Private->ClearCode = (1 << BitsPerPixel); |
| Private->EOFCode = Private->ClearCode + 1; |
| Private->RunningCode = Private->EOFCode + 1; |
| Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ |
| Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ |
| Private->CrntCode = FIRST_CODE; /* Signal that this is first one! */ |
| Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ |
| Private->CrntShiftDWord = 0; |
| |
| /* Clear hash table and send Clear to make sure the decoder do the same. |
| */ |
| _ClearHashTable(Private->HashTable); |
| |
| if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) { |
| GifFile->Error = E_GIF_ERR_DISK_IS_FULL; |
| return GIF_ERROR; |
| } |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| The LZ compression routine: |
| This version compresses the given buffer Line of length LineLen. |
| This routine can be called a few times (one per scan line, for example), in |
| order to complete the whole image. |
| ******************************************************************************/ |
| static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line, |
| const int LineLen) { |
| int i = 0, CrntCode; |
| GifHashTableType *HashTable; |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| |
| HashTable = Private->HashTable; |
| |
| if (Private->CrntCode == FIRST_CODE) { /* Its first time! */ |
| CrntCode = Line[i++]; |
| } else { |
| CrntCode = |
| Private->CrntCode; /* Get last code in compression. */ |
| } |
| while (i < LineLen) { /* Decode LineLen items. */ |
| GifPixelType Pixel = |
| Line[i++]; /* Get next pixel from stream. */ |
| /* Form a new unique key to search hash table for the code |
| * combines CrntCode as Prefix string with Pixel as postfix |
| * char. |
| */ |
| int NewCode; |
| unsigned long NewKey = (((uint32_t)CrntCode) << 8) + Pixel; |
| if ((NewCode = _ExistsHashTable(HashTable, NewKey)) >= 0) { |
| /* This Key is already there, or the string is old one, |
| * so simple take new code as our CrntCode: |
| */ |
| CrntCode = NewCode; |
| } else { |
| /* Put it in hash table, output the prefix code, and |
| * make our CrntCode equal to Pixel. |
| */ |
| if (EGifCompressOutput(GifFile, CrntCode) == |
| GIF_ERROR) { |
| GifFile->Error = E_GIF_ERR_DISK_IS_FULL; |
| return GIF_ERROR; |
| } |
| CrntCode = Pixel; |
| |
| /* If however the HashTable if full, we send a clear |
| * first and Clear the hash table. |
| */ |
| if (Private->RunningCode >= LZ_MAX_CODE) { |
| /* Time to do some clearance: */ |
| if (EGifCompressOutput(GifFile, |
| Private->ClearCode) == |
| GIF_ERROR) { |
| GifFile->Error = E_GIF_ERR_DISK_IS_FULL; |
| return GIF_ERROR; |
| } |
| Private->RunningCode = Private->EOFCode + 1; |
| Private->RunningBits = |
| Private->BitsPerPixel + 1; |
| Private->MaxCode1 = 1 << Private->RunningBits; |
| _ClearHashTable(HashTable); |
| } else { |
| /* Put this unique key with its relative Code in |
| * hash table: */ |
| _InsertHashTable(HashTable, NewKey, |
| Private->RunningCode++); |
| } |
| } |
| } |
| |
| /* Preserve the current state of the compression algorithm: */ |
| Private->CrntCode = CrntCode; |
| |
| if (Private->PixelCount == 0) { |
| /* We are done - output last Code and flush output buffers: */ |
| if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) { |
| GifFile->Error = E_GIF_ERR_DISK_IS_FULL; |
| return GIF_ERROR; |
| } |
| if (EGifCompressOutput(GifFile, Private->EOFCode) == |
| GIF_ERROR) { |
| GifFile->Error = E_GIF_ERR_DISK_IS_FULL; |
| return GIF_ERROR; |
| } |
| if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) { |
| GifFile->Error = E_GIF_ERR_DISK_IS_FULL; |
| return GIF_ERROR; |
| } |
| } |
| |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| The LZ compression output routine: |
| This routine is responsible for the compression of the bit stream into |
| 8 bits (bytes) packets. |
| Returns GIF_OK if written successfully. |
| ******************************************************************************/ |
| static int EGifCompressOutput(GifFileType *GifFile, const int Code) { |
| GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; |
| int retval = GIF_OK; |
| |
| if (Code == FLUSH_OUTPUT) { |
| while (Private->CrntShiftState > 0) { |
| /* Get Rid of what is left in DWord, and flush it. */ |
| if (EGifBufferedOutput(GifFile, Private->Buf, |
| Private->CrntShiftDWord & |
| 0xff) == GIF_ERROR) { |
| retval = GIF_ERROR; |
| } |
| Private->CrntShiftDWord >>= 8; |
| Private->CrntShiftState -= 8; |
| } |
| Private->CrntShiftState = 0; /* For next time. */ |
| if (EGifBufferedOutput(GifFile, Private->Buf, FLUSH_OUTPUT) == |
| GIF_ERROR) { |
| retval = GIF_ERROR; |
| } |
| } else { |
| Private->CrntShiftDWord |= ((long)Code) |
| << Private->CrntShiftState; |
| Private->CrntShiftState += Private->RunningBits; |
| while (Private->CrntShiftState >= 8) { |
| /* Dump out full bytes: */ |
| if (EGifBufferedOutput(GifFile, Private->Buf, |
| Private->CrntShiftDWord & |
| 0xff) == GIF_ERROR) { |
| retval = GIF_ERROR; |
| } |
| Private->CrntShiftDWord >>= 8; |
| Private->CrntShiftState -= 8; |
| } |
| } |
| |
| /* If code cannt fit into RunningBits bits, must raise its size. Note */ |
| /* however that codes above 4095 are used for special signaling. */ |
| if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) { |
| Private->MaxCode1 = 1 << ++Private->RunningBits; |
| } |
| |
| return retval; |
| } |
| |
| /****************************************************************************** |
| This routines buffers the given characters until 255 characters are ready |
| to be output. If Code is equal to -1 the buffer is flushed (EOF). |
| The buffer is Dumped with first byte as its size, as GIF format requires. |
| Returns GIF_OK if written successfully. |
| ******************************************************************************/ |
| static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c) { |
| if (c == FLUSH_OUTPUT) { |
| /* Flush everything out. */ |
| if (Buf[0] != 0 && InternalWrite(GifFile, Buf, Buf[0] + 1) != |
| (unsigned)(Buf[0] + 1)) { |
| GifFile->Error = E_GIF_ERR_WRITE_FAILED; |
| return GIF_ERROR; |
| } |
| /* Mark end of compressed data, by an empty block (see GIF doc): |
| */ |
| Buf[0] = 0; |
| if (InternalWrite(GifFile, Buf, 1) != 1) { |
| GifFile->Error = E_GIF_ERR_WRITE_FAILED; |
| return GIF_ERROR; |
| } |
| } else { |
| if (Buf[0] == 255) { |
| /* Dump out this buffer - it is full: */ |
| if (InternalWrite(GifFile, Buf, Buf[0] + 1) != |
| (unsigned)(Buf[0] + 1)) { |
| GifFile->Error = E_GIF_ERR_WRITE_FAILED; |
| return GIF_ERROR; |
| } |
| Buf[0] = 0; |
| } |
| Buf[++Buf[0]] = c; |
| } |
| |
| return GIF_OK; |
| } |
| |
| /****************************************************************************** |
| This routine writes to disk an in-core representation of a GIF previously |
| created by DGifSlurp(). |
| ******************************************************************************/ |
| |
| static int EGifWriteExtensions(GifFileType *GifFileOut, |
| ExtensionBlock *ExtensionBlocks, |
| int ExtensionBlockCount) { |
| if (ExtensionBlocks) { |
| int j; |
| |
| for (j = 0; j < ExtensionBlockCount; j++) { |
| ExtensionBlock *ep = &ExtensionBlocks[j]; |
| if (ep->Function != CONTINUE_EXT_FUNC_CODE) { |
| if (EGifPutExtensionLeader(GifFileOut, |
| ep->Function) == |
| GIF_ERROR) { |
| return (GIF_ERROR); |
| } |
| } |
| if (EGifPutExtensionBlock(GifFileOut, ep->ByteCount, |
| ep->Bytes) == GIF_ERROR) { |
| return (GIF_ERROR); |
| } |
| if (j == ExtensionBlockCount - 1 || |
| (ep + 1)->Function != CONTINUE_EXT_FUNC_CODE) { |
| if (EGifPutExtensionTrailer(GifFileOut) == |
| GIF_ERROR) { |
| return (GIF_ERROR); |
| } |
| } |
| } |
| } |
| |
| return (GIF_OK); |
| } |
| |
| int EGifSpew(GifFileType *GifFileOut) { |
| int i, j; |
| |
| if (EGifPutScreenDesc(GifFileOut, GifFileOut->SWidth, |
| GifFileOut->SHeight, GifFileOut->SColorResolution, |
| GifFileOut->SBackGroundColor, |
| GifFileOut->SColorMap) == GIF_ERROR) { |
| return (GIF_ERROR); |
| } |
| |
| for (i = 0; i < GifFileOut->ImageCount; i++) { |
| SavedImage *sp = &GifFileOut->SavedImages[i]; |
| int SavedHeight = sp->ImageDesc.Height; |
| int SavedWidth = sp->ImageDesc.Width; |
| |
| /* this allows us to delete images by nuking their rasters */ |
| if (sp->RasterBits == NULL) { |
| continue; |
| } |
| |
| if (EGifWriteExtensions(GifFileOut, sp->ExtensionBlocks, |
| sp->ExtensionBlockCount) == GIF_ERROR) { |
| return (GIF_ERROR); |
| } |
| |
| if (EGifPutImageDesc(GifFileOut, sp->ImageDesc.Left, |
| sp->ImageDesc.Top, SavedWidth, SavedHeight, |
| sp->ImageDesc.Interlace, |
| sp->ImageDesc.ColorMap) == GIF_ERROR) { |
| return (GIF_ERROR); |
| } |
| |
| if (sp->ImageDesc.Interlace) { |
| /* |
| * The way an interlaced image should be written - |
| * offsets and jumps... |
| */ |
| static const int InterlacedOffset[] = {0, 4, 2, 1}; |
| static const int InterlacedJumps[] = {8, 8, 4, 2}; |
| int k; |
| /* Need to perform 4 passes on the images: */ |
| for (k = 0; k < 4; k++) { |
| for (j = InterlacedOffset[k]; j < SavedHeight; |
| j += InterlacedJumps[k]) { |
| if (EGifPutLine( |
| GifFileOut, |
| sp->RasterBits + j * SavedWidth, |
| SavedWidth) == GIF_ERROR) { |
| return (GIF_ERROR); |
| } |
| } |
| } |
| } else { |
| for (j = 0; j < SavedHeight; j++) { |
| if (EGifPutLine(GifFileOut, |
| sp->RasterBits + j * SavedWidth, |
| SavedWidth) == GIF_ERROR) { |
| return (GIF_ERROR); |
| } |
| } |
| } |
| } |
| |
| if (EGifWriteExtensions(GifFileOut, GifFileOut->ExtensionBlocks, |
| GifFileOut->ExtensionBlockCount) == GIF_ERROR) { |
| return (GIF_ERROR); |
| } |
| |
| if (EGifCloseFile(GifFileOut, NULL) == GIF_ERROR) { |
| return (GIF_ERROR); |
| } |
| |
| return (GIF_OK); |
| } |
| |
| /* end */ |