From 7f15550da1f7f55458e13b039d18363cb5917ecc Mon Sep 17 00:00:00 2001 From: luoliangyi <87842688@qq.com> Date: Tue, 12 Jul 2022 11:19:36 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BC=96=E8=AF=91uos-amd64=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=9A=84giflib=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/linux/HGImgFmt/HGImgFmt.cbp | 26 + third_party/giflib/giflib/dgif_lib.c | 1241 +++++++++++++++++ third_party/giflib/giflib/egif_lib.c | 1165 ++++++++++++++++ third_party/giflib/giflib/getarg.c | 639 +++++++++ third_party/giflib/giflib/getarg.h | 52 + third_party/giflib/giflib/gif2rgb.c | 538 +++++++ third_party/giflib/giflib/gif_err.c | 99 ++ third_party/giflib/giflib/gif_font.c | 261 ++++ third_party/giflib/giflib/gif_hash.c | 133 ++ third_party/giflib/giflib/gif_hash.h | 41 + third_party/giflib/giflib/gif_lib.h | 303 ++++ third_party/giflib/giflib/gif_lib_private.h | 70 + third_party/giflib/giflib/gifalloc.c | 420 ++++++ third_party/giflib/giflib/gifbg.c | 349 +++++ third_party/giflib/giflib/gifbuild.c | 940 +++++++++++++ third_party/giflib/giflib/gifclrmp.c | 361 +++++ third_party/giflib/giflib/gifcolor.c | 170 +++ third_party/giflib/giflib/gifecho.c | 209 +++ third_party/giflib/giflib/giffilter.c | 154 ++ third_party/giflib/giflib/giffix.c | 218 +++ third_party/giflib/giflib/gifhisto.c | 259 ++++ third_party/giflib/giflib/gifinto.c | 191 +++ third_party/giflib/giflib/giflib.cbp | 79 ++ third_party/giflib/giflib/gifsponge.c | 87 ++ third_party/giflib/giflib/giftext.c | 466 +++++++ third_party/giflib/giflib/giftool.c | 579 ++++++++ third_party/giflib/giflib/gifwedge.c | 143 ++ .../giflib/giflib/openbsd-reallocarray.c | 74 + third_party/giflib/giflib/qprintf.c | 48 + third_party/giflib/giflib/quantize.c | 332 +++++ .../giflib/uos/amd64/include/gif_lib.h | 303 ++++ third_party/giflib/uos/amd64/lib/libgiflib.a | Bin 0 -> 67272 bytes 32 files changed, 9950 insertions(+) create mode 100644 third_party/giflib/giflib/dgif_lib.c create mode 100644 third_party/giflib/giflib/egif_lib.c create mode 100644 third_party/giflib/giflib/getarg.c create mode 100644 third_party/giflib/giflib/getarg.h create mode 100644 third_party/giflib/giflib/gif2rgb.c create mode 100644 third_party/giflib/giflib/gif_err.c create mode 100644 third_party/giflib/giflib/gif_font.c create mode 100644 third_party/giflib/giflib/gif_hash.c create mode 100644 third_party/giflib/giflib/gif_hash.h create mode 100644 third_party/giflib/giflib/gif_lib.h create mode 100644 third_party/giflib/giflib/gif_lib_private.h create mode 100644 third_party/giflib/giflib/gifalloc.c create mode 100644 third_party/giflib/giflib/gifbg.c create mode 100644 third_party/giflib/giflib/gifbuild.c create mode 100644 third_party/giflib/giflib/gifclrmp.c create mode 100644 third_party/giflib/giflib/gifcolor.c create mode 100644 third_party/giflib/giflib/gifecho.c create mode 100644 third_party/giflib/giflib/giffilter.c create mode 100644 third_party/giflib/giflib/giffix.c create mode 100644 third_party/giflib/giflib/gifhisto.c create mode 100644 third_party/giflib/giflib/gifinto.c create mode 100644 third_party/giflib/giflib/giflib.cbp create mode 100644 third_party/giflib/giflib/gifsponge.c create mode 100644 third_party/giflib/giflib/giftext.c create mode 100644 third_party/giflib/giflib/giftool.c create mode 100644 third_party/giflib/giflib/gifwedge.c create mode 100644 third_party/giflib/giflib/openbsd-reallocarray.c create mode 100644 third_party/giflib/giflib/qprintf.c create mode 100644 third_party/giflib/giflib/quantize.c create mode 100644 third_party/giflib/uos/amd64/include/gif_lib.h create mode 100644 third_party/giflib/uos/amd64/lib/libgiflib.a diff --git a/build/linux/HGImgFmt/HGImgFmt.cbp b/build/linux/HGImgFmt/HGImgFmt.cbp index 5f947621..99134fc1 100644 --- a/build/linux/HGImgFmt/HGImgFmt.cbp +++ b/build/linux/HGImgFmt/HGImgFmt.cbp @@ -20,6 +20,7 @@ + @@ -30,6 +31,7 @@ + @@ -50,6 +52,7 @@ + @@ -61,6 +64,7 @@ + @@ -81,6 +85,7 @@ + @@ -91,6 +96,7 @@ + @@ -111,6 +117,7 @@ + @@ -122,6 +129,7 @@ + @@ -142,6 +150,7 @@ + @@ -152,6 +161,7 @@ + @@ -172,6 +182,7 @@ + @@ -183,6 +194,7 @@ + @@ -203,6 +215,7 @@ + @@ -213,6 +226,7 @@ + @@ -233,6 +247,7 @@ + @@ -244,6 +259,7 @@ + @@ -264,6 +280,7 @@ + @@ -274,6 +291,7 @@ + @@ -294,6 +312,7 @@ + @@ -305,6 +324,7 @@ + @@ -325,6 +345,7 @@ + @@ -335,6 +356,7 @@ + @@ -355,6 +377,7 @@ + @@ -366,6 +389,7 @@ + @@ -386,6 +410,8 @@ + + diff --git a/third_party/giflib/giflib/dgif_lib.c b/third_party/giflib/giflib/dgif_lib.c new file mode 100644 index 00000000..82fc0975 --- /dev/null +++ b/third_party/giflib/giflib/dgif_lib.c @@ -0,0 +1,1241 @@ +/****************************************************************************** + +dgif_lib.c - GIF decoding + +The functions here and in egif_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 +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif /* _WIN32 */ + +#include "gif_lib.h" +#include "gif_lib_private.h" + +/* compose unsigned little endian value */ +#define UNSIGNED_LITTLE_ENDIAN(lo, hi) ((lo) | ((hi) << 8)) + +/* avoid extra function call in case we use fread (TVT) */ +static int InternalRead(GifFileType *gif, GifByteType *buf, int len) { + //fprintf(stderr, "### Read: %d\n", len); + return + (((GifFilePrivateType*)gif->Private)->Read ? + ((GifFilePrivateType*)gif->Private)->Read(gif,buf,len) : + fread(buf,1,len,((GifFilePrivateType*)gif->Private)->File)); +} + +static int DGifGetWord(GifFileType *GifFile, GifWord *Word); +static int DGifSetupDecompress(GifFileType *GifFile); +static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, + int LineLen); +static int DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode); +static int DGifDecompressInput(GifFileType *GifFile, int *Code); +static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, + GifByteType *NextByte); + +/****************************************************************************** + Open a new GIF file for read, given by its name. + Returns dynamically allocated GifFileType pointer which serves as the GIF + info record. +******************************************************************************/ +GifFileType * +DGifOpenFileName(const char *FileName, int *Error) +{ + int FileHandle; + GifFileType *GifFile; + + if ((FileHandle = open(FileName, O_RDONLY)) == -1) { + if (Error != NULL) + *Error = D_GIF_ERR_OPEN_FAILED; + return NULL; + } + + GifFile = DGifOpenFileHandle(FileHandle, Error); + return GifFile; +} + +/****************************************************************************** + Update a new GIF file, given its file handle. + Returns dynamically allocated GifFileType pointer which serves as the GIF + info record. +******************************************************************************/ +GifFileType * +DGifOpenFileHandle(int FileHandle, int *Error) +{ + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + FILE *f; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + (void)close(FileHandle); + return NULL; + } + + /*@i1@*/memset(GifFile, '\0', sizeof(GifFileType)); + + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; + + Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType)); + if (Private == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + (void)close(FileHandle); + free((char *)GifFile); + return NULL; + } + + /*@i1@*/memset(Private, '\0', sizeof(GifFilePrivateType)); + +#ifdef _WIN32 + _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* _WIN32 */ + + f = fdopen(FileHandle, "rb"); /* Make it into a stream: */ + + /*@-mustfreeonly@*/ + GifFile->Private = (void *)Private; + Private->FileHandle = FileHandle; + Private->File = f; + Private->FileState = FILE_STATE_READ; + Private->Read = NULL; /* don't use alternate input method (TVT) */ + GifFile->UserData = NULL; /* TVT */ + /*@=mustfreeonly@*/ + + /* Let's see if this is a GIF file: */ + /* coverity[check_return] */ + if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + if (Error != NULL) + *Error = D_GIF_ERR_READ_FAILED; + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = 0; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_GIF_FILE; + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + GifFile->Error = 0; + + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS] == '9'); + + return GifFile; +} + +/****************************************************************************** + GifFileType constructor with user supplied input function (TVT) +******************************************************************************/ +GifFileType * +DGifOpen(void *userData, InputFunc readFunc, int *Error) +{ + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; + + Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType)); + if (!Private) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + free((char *)GifFile); + return NULL; + } + /*@i1@*/memset(Private, '\0', sizeof(GifFilePrivateType)); + + GifFile->Private = (void *)Private; + Private->FileHandle = 0; + Private->File = NULL; + Private->FileState = FILE_STATE_READ; + + Private->Read = readFunc; /* TVT */ + GifFile->UserData = userData; /* TVT */ + + /* Lets see if this is a GIF file: */ + /* coverity[check_return] */ + if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + if (Error != NULL) + *Error = D_GIF_ERR_READ_FAILED; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = '\0'; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_GIF_FILE; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + free((char *)Private); + free((char *)GifFile); + if (Error != NULL) + *Error = D_GIF_ERR_NO_SCRN_DSCR; + return NULL; + } + + GifFile->Error = 0; + + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS] == '9'); + + return GifFile; +} + +/****************************************************************************** + This routine should be called before any other DGif calls. Note that + this routine is called automatically from DGif file open routines. +******************************************************************************/ +int +DGifGetScreenDesc(GifFileType *GifFile) +{ + int BitsPerPixel; + bool SortFlag; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + /* Put the screen descriptor into the file: */ + if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR) + return GIF_ERROR; + + if (InternalRead(GifFile, Buf, 3) != 3) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + return GIF_ERROR; + } + GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1; + SortFlag = (Buf[0] & 0x08) != 0; + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->SBackGroundColor = Buf[1]; + GifFile->AspectByte = Buf[2]; + if (Buf[0] & 0x80) { /* Do we have global color map? */ + int i; + + GifFile->SColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->SColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the global color map: */ + GifFile->SColorMap->SortFlag = SortFlag; + for (i = 0; i < GifFile->SColorMap->ColorCount; i++) { + /* coverity[check_return] */ + if (InternalRead(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + GifFile->SColorMap->Colors[i].Red = Buf[0]; + GifFile->SColorMap->Colors[i].Green = Buf[1]; + GifFile->SColorMap->Colors[i].Blue = Buf[2]; + } + } else { + GifFile->SColorMap = NULL; + } + + /* + * No check here for whether the background color is in range for the + * screen color map. Possibly there should be. + */ + + return GIF_OK; +} + +const char * +DGifGetGifVersion(GifFileType *GifFile) +{ + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (Private->gif89) + return GIF89_STAMP; + else + return GIF87_STAMP; +} + +/****************************************************************************** + This routine should be called before any attempt to read an image. +******************************************************************************/ +int +DGifGetRecordType(GifFileType *GifFile, GifRecordType* Type) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + /* coverity[check_return] */ + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + //fprintf(stderr, "### DGifGetRecordType: %02x\n", Buf); + switch (Buf) { + case DESCRIPTOR_INTRODUCER: + *Type = IMAGE_DESC_RECORD_TYPE; + break; + case EXTENSION_INTRODUCER: + *Type = EXTENSION_RECORD_TYPE; + break; + case TERMINATOR_INTRODUCER: + *Type = TERMINATE_RECORD_TYPE; + break; + default: + *Type = UNDEFINED_RECORD_TYPE; + GifFile->Error = D_GIF_ERR_WRONG_RECORD; + return GIF_ERROR; + } + + return GIF_OK; +} + +int +DGifGetImageHeader(GifFileType *GifFile) +{ + unsigned int BitsPerPixel; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR) + return GIF_ERROR; + if (InternalRead(GifFile, Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->Image.Interlace = (Buf[0] & 0x40) ? true : false; + + /* Setup the colormap */ + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + /* Does this image have local color map? */ + if (Buf[0] & 0x80) { + unsigned int i; + + GifFile->Image.ColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->Image.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the image local color map: */ + for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) { + /* coverity[check_return] */ + if (InternalRead(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + GifFile->Image.ColorMap->Colors[i].Red = Buf[0]; + GifFile->Image.ColorMap->Colors[i].Green = Buf[1]; + GifFile->Image.ColorMap->Colors[i].Blue = Buf[2]; + } + } + + Private->PixelCount = (long)GifFile->Image.Width * + (long)GifFile->Image.Height; + + /* Reset decompress algorithm parameters. */ + return DGifSetupDecompress(GifFile); +} + +/****************************************************************************** + This routine should be called before any attempt to read an image. + Note it is assumed the Image desc. header has been read. +******************************************************************************/ +int +DGifGetImageDesc(GifFileType *GifFile) +{ + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + SavedImage *sp; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifGetImageHeader(GifFile) == GIF_ERROR) { + return GIF_ERROR; + } + + if (GifFile->SavedImages) { + SavedImage* new_saved_images = + (SavedImage *)reallocarray(GifFile->SavedImages, + (GifFile->ImageCount + 1), sizeof(SavedImage)); + if (new_saved_images == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + GifFile->SavedImages = new_saved_images; + } else { + if ((GifFile->SavedImages = + (SavedImage *) malloc(sizeof(SavedImage))) == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + + sp = &GifFile->SavedImages[GifFile->ImageCount]; + memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc)); + if (GifFile->Image.ColorMap != NULL) { + sp->ImageDesc.ColorMap = GifMakeMapObject( + GifFile->Image.ColorMap->ColorCount, + GifFile->Image.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + sp->RasterBits = (unsigned char *)NULL; + sp->ExtensionBlockCount = 0; + sp->ExtensionBlocks = (ExtensionBlock *) NULL; + + GifFile->ImageCount++; + + return GIF_OK; +} + +/****************************************************************************** + Get one full scanned line (Line) of length LineLen from GIF file. +******************************************************************************/ +int +DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) +{ + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (!LineLen) + LineLen = GifFile->Image.Width; + + if ((Private->PixelCount -= LineLen) > 0xffff0000UL) { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean up + * everything before we return: need to flush out all the + * rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + Put one pixel (Pixel) into GIF file. +******************************************************************************/ +int +DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel) +{ + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + if (--Private->PixelCount > 0xffff0000UL) + { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean up + * everything before we return: need to flush out all the + * rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + Get an extension block (see GIF manual) from GIF file. This routine only + returns the first data block, and DGifGetExtensionNext should be called + after this one until NULL extension is returned. + The Extension should NOT be freed by the user (not dynamically allocated). + Note it is assumed the Extension description header has been read. +******************************************************************************/ +int +DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + //fprintf(stderr, "### -> DGifGetExtension:\n"); + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + /* coverity[check_return] */ + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *ExtCode = Buf; + //fprintf(stderr, "### <- DGifGetExtension: %02x, about to call next\n", Buf); + + return DGifGetExtensionNext(GifFile, Extension); +} + +/****************************************************************************** + Get a following extension block (see GIF manual) from GIF file. This + routine should be called until NULL Extension is returned. + The Extension should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetExtensionNext(GifFileType *GifFile, GifByteType ** Extension) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + //fprintf(stderr, "### -> DGifGetExtensionNext\n"); + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + //fprintf(stderr, "### DGifGetExtensionNext sees %d\n", Buf); + + if (Buf > 0) { + *Extension = Private->Buf; /* Use private unused buffer. */ + (*Extension)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data,check_return] */ + if (InternalRead(GifFile, &((*Extension)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else + *Extension = NULL; + //fprintf(stderr, "### <- DGifGetExtensionNext: %p\n", Extension); + + return GIF_OK; +} + +/****************************************************************************** + Extract a Graphics Control Block from raw extension data +******************************************************************************/ + +int DGifExtensionToGCB(const size_t GifExtensionLength, + const GifByteType *GifExtension, + GraphicsControlBlock *GCB) +{ + if (GifExtensionLength != 4) { + return GIF_ERROR; + } + + GCB->DisposalMode = (GifExtension[0] >> 2) & 0x07; + GCB->UserInputFlag = (GifExtension[0] & 0x02) != 0; + GCB->DelayTime = UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]); + if (GifExtension[0] & 0x01) + GCB->TransparentColor = (int)GifExtension[3]; + else + GCB->TransparentColor = NO_TRANSPARENT_COLOR; + + return GIF_OK; +} + +/****************************************************************************** + Extract the Graphics Control Block for a saved image, if it exists. +******************************************************************************/ + +int DGifSavedExtensionToGCB(GifFileType *GifFile, + int ImageIndex, GraphicsControlBlock *GCB) +{ + int i; + + if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) + return GIF_ERROR; + + GCB->DisposalMode = DISPOSAL_UNSPECIFIED; + GCB->UserInputFlag = false; + GCB->DelayTime = 0; + GCB->TransparentColor = NO_TRANSPARENT_COLOR; + + for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) { + ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; + if (ep->Function == GRAPHICS_EXT_FUNC_CODE) + return DGifExtensionToGCB(ep->ByteCount, ep->Bytes, GCB); + } + + return GIF_ERROR; +} + +/****************************************************************************** + This routine should be called last, to close the GIF file. +******************************************************************************/ +int +DGifCloseFile(GifFileType *GifFile, int *ErrorCode) +{ + GifFilePrivateType *Private; + + if (GifFile == NULL || GifFile->Private == NULL) + return GIF_ERROR; + + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + + if (GifFile->SColorMap) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + } + + if (GifFile->SavedImages) { + GifFreeSavedImages(GifFile); + GifFile->SavedImages = NULL; + } + + GifFreeExtensions(&GifFile->ExtensionBlockCount, &GifFile->ExtensionBlocks); + + Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + if (ErrorCode != NULL) + *ErrorCode = D_GIF_ERR_NOT_READABLE; + free((char *)GifFile->Private); + free(GifFile); + return GIF_ERROR; + } + + if (Private->File && (fclose(Private->File) != 0)) { + if (ErrorCode != NULL) + *ErrorCode = D_GIF_ERR_CLOSE_FAILED; + free((char *)GifFile->Private); + free(GifFile); + return GIF_ERROR; + } + + free((char *)GifFile->Private); + free(GifFile); + if (ErrorCode != NULL) + *ErrorCode = D_GIF_SUCCEEDED; + return GIF_OK; +} + +/****************************************************************************** + Get 2 bytes (word) from the given file: +******************************************************************************/ +static int +DGifGetWord(GifFileType *GifFile, GifWord *Word) +{ + unsigned char c[2]; + + /* coverity[check_return] */ + if (InternalRead(GifFile, c, 2) != 2) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + *Word = (GifWord)UNSIGNED_LITTLE_ENDIAN(c[0], c[1]); + return GIF_OK; +} + +/****************************************************************************** + Get 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 DGifGetCodeNext, until NULL block is returned. + The block should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock) +{ + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + *CodeSize = Private->BitsPerPixel; + + return DGifGetCodeNext(GifFile, CodeBlock); +} + +/****************************************************************************** + Continue to get the image code in compressed form. This routine should be + called until NULL block is returned. + The block should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + /* coverity[tainted_data_argument] */ + /* coverity[check_return] */ + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + /* coverity[lower_bounds] */ + if (Buf > 0) { + *CodeBlock = Private->Buf; /* Use private unused buffer. */ + (*CodeBlock)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data] */ + if (InternalRead(GifFile, &((*CodeBlock)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else { + *CodeBlock = NULL; + Private->Buf[0] = 0; /* Make sure the buffer is empty! */ + Private->PixelCount = 0; /* And local info. indicate image read. */ + } + + return GIF_OK; +} + +/****************************************************************************** + Setup the LZ decompression for this image: +******************************************************************************/ +static int +DGifSetupDecompress(GifFileType *GifFile) +{ + int i, BitsPerPixel; + GifByteType CodeSize; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + /* coverity[check_return] */ + if (InternalRead(GifFile, &CodeSize, 1) < 1) { /* Read Code size from file. */ + return GIF_ERROR; /* Failed to read Code size. */ + } + BitsPerPixel = CodeSize; + + /* this can only happen on a severely malformed GIF */ + if (BitsPerPixel > 8) { + GifFile->Error = D_GIF_ERR_READ_FAILED; /* somewhat bogus error code */ + return GIF_ERROR; /* Failed to read Code size. */ + } + + Private->Buf[0] = 0; /* Input Buffer empty. */ + 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->StackPtr = 0; /* No pixels on the pixel stack. */ + Private->LastCode = NO_SUCH_CODE; + Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ + Private->CrntShiftDWord = 0; + + Prefix = Private->Prefix; + for (i = 0; i <= LZ_MAX_CODE; i++) + Prefix[i] = NO_SUCH_CODE; + + return GIF_OK; +} + +/****************************************************************************** + The LZ decompression routine: + This version decompress the given GIF file into Line of length LineLen. + This routine can be called few times (one per scan line, for example), in + order the complete the whole image. +******************************************************************************/ +static int +DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) +{ + int i = 0; + int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr; + GifByteType *Stack, *Suffix; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + StackPtr = Private->StackPtr; + Prefix = Private->Prefix; + Suffix = Private->Suffix; + Stack = Private->Stack; + EOFCode = Private->EOFCode; + ClearCode = Private->ClearCode; + LastCode = Private->LastCode; + + if (StackPtr > LZ_MAX_CODE) { + return GIF_ERROR; + } + + if (StackPtr != 0) { + /* Let pop the stack off before continueing to read the GIF file: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + + while (i < LineLen) { /* Decode LineLen items. */ + if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR) + return GIF_ERROR; + + if (CrntCode == EOFCode) { + /* Note however that usually we will not be here as we will stop + * decoding as soon as we got all the pixel, or EOF code will + * not be read at all, and DGifGetLine/Pixel clean everything. */ + GifFile->Error = D_GIF_ERR_EOF_TOO_SOON; + return GIF_ERROR; + } else if (CrntCode == ClearCode) { + /* We need to start over again: */ + for (j = 0; j <= LZ_MAX_CODE; j++) + Prefix[j] = NO_SUCH_CODE; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + LastCode = Private->LastCode = NO_SUCH_CODE; + } else { + /* Its regular code - if in pixel range simply add it to output + * stream, otherwise trace to codes linked list until the prefix + * is in pixel range: */ + if (CrntCode < ClearCode) { + /* This is simple - its pixel scalar, so add it to output: */ + Line[i++] = CrntCode; + } else { + /* Its a code to needed to be traced: trace the linked list + * until the prefix is a pixel, while pushing the suffix + * pixels on our stack. If we done, pop the stack in reverse + * (thats what stack is good for!) order to output. */ + if (Prefix[CrntCode] == NO_SUCH_CODE) { + CrntPrefix = LastCode; + + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + if (CrntCode == Private->RunningCode - 2) { + Suffix[Private->RunningCode - 2] = + Stack[StackPtr++] = DGifGetPrefixChar(Prefix, + LastCode, + ClearCode); + } else { + Suffix[Private->RunningCode - 2] = + Stack[StackPtr++] = DGifGetPrefixChar(Prefix, + CrntCode, + ClearCode); + } + } else + CrntPrefix = CrntCode; + + /* Now (if image is O.K.) we should not get a NO_SUCH_CODE + * during the trace. As we might loop forever, in case of + * defective image, we use StackPtr as loop counter and stop + * before overflowing Stack[]. */ + while (StackPtr < LZ_MAX_CODE && + CrntPrefix > ClearCode && CrntPrefix <= LZ_MAX_CODE) { + Stack[StackPtr++] = Suffix[CrntPrefix]; + CrntPrefix = Prefix[CrntPrefix]; + } + if (StackPtr >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* Push the last character on stack: */ + Stack[StackPtr++] = CrntPrefix; + + /* Now lets pop all the stack into output: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + if (LastCode != NO_SUCH_CODE && Private->RunningCode - 2 < (LZ_MAX_CODE+1) && Prefix[Private->RunningCode - 2] == NO_SUCH_CODE) { + Prefix[Private->RunningCode - 2] = LastCode; + + if (CrntCode == Private->RunningCode - 2) { + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, LastCode, ClearCode); + } else { + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, CrntCode, ClearCode); + } + } + LastCode = CrntCode; + } + } + + Private->LastCode = LastCode; + Private->StackPtr = StackPtr; + + return GIF_OK; +} + +/****************************************************************************** + Routine to trace the Prefixes linked list until we get a prefix which is + not code, but a pixel value (less than ClearCode). Returns that pixel value. + If image is defective, we might loop here forever, so we limit the loops to + the maximum possible if image O.k. - LZ_MAX_CODE times. +******************************************************************************/ +static int +DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode) +{ + int i = 0; + + while (Code > ClearCode && i++ <= LZ_MAX_CODE) { + if (Code > LZ_MAX_CODE) { + return NO_SUCH_CODE; + } + Code = Prefix[Code]; + } + return Code; +} + +/****************************************************************************** + Interface for accessing the LZ codes directly. Set Code to the real code + (12bits), or to -1 if EOF code is returned. +******************************************************************************/ +int +DGifGetLZCodes(GifFileType *GifFile, int *Code) +{ + GifByteType *CodeBlock; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifDecompressInput(GifFile, Code) == GIF_ERROR) + return GIF_ERROR; + + if (*Code == Private->EOFCode) { + /* Skip rest of codes (hopefully only NULL terminating block): */ + do { + if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) + return GIF_ERROR; + } while (CodeBlock != NULL) ; + + *Code = -1; + } else if (*Code == Private->ClearCode) { + /* We need to start over again: */ + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + } + + return GIF_OK; +} + +/****************************************************************************** + The LZ decompression input routine: + This routine is responsable for the decompression of the bit stream from + 8 bits (bytes) packets, into the real codes. + Returns GIF_OK if read successfully. +******************************************************************************/ +static int +DGifDecompressInput(GifFileType *GifFile, int *Code) +{ + static const unsigned short CodeMasks[] = { + 0x0000, 0x0001, 0x0003, 0x0007, + 0x000f, 0x001f, 0x003f, 0x007f, + 0x00ff, 0x01ff, 0x03ff, 0x07ff, + 0x0fff + }; + + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + GifByteType NextByte; + + /* The image can't contain more than LZ_BITS per code. */ + if (Private->RunningBits > LZ_BITS) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + + while (Private->CrntShiftState < Private->RunningBits) { + /* Needs to get more bytes from input stream for next code: */ + if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == GIF_ERROR) { + return GIF_ERROR; + } + Private->CrntShiftDWord |= + ((unsigned long)NextByte) << Private->CrntShiftState; + Private->CrntShiftState += 8; + } + *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits]; + + Private->CrntShiftDWord >>= Private->RunningBits; + Private->CrntShiftState -= Private->RunningBits; + + /* If code cannot fit into RunningBits bits, must raise its size. Note + * however that codes above 4095 are used for special signaling. + * If we're using LZ_BITS bits already and we're at the max code, just + * keep using the table as it is, don't increment Private->RunningCode. + */ + if (Private->RunningCode < LZ_MAX_CODE + 2 && + ++Private->RunningCode > Private->MaxCode1 && + Private->RunningBits < LZ_BITS) { + Private->MaxCode1 <<= 1; + Private->RunningBits++; + } + return GIF_OK; +} + +/****************************************************************************** + This routines read one GIF data block at a time and buffers it internally + so that the decompression routine could access it. + The routine returns the next byte from its internal buffer (or read next + block in if buffer empty) and returns GIF_OK if succesful. +******************************************************************************/ +static int +DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte) +{ + if (Buf[0] == 0) { + /* Needs to read the next buffer - this one is empty: */ + /* coverity[check_return] */ + if (InternalRead(GifFile, Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore we + * shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + if (InternalRead(GifFile, &Buf[1], Buf[0]) != Buf[0]) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *NextByte = Buf[1]; + Buf[1] = 2; /* We use now the second place as last char read! */ + Buf[0]--; + } else { + *NextByte = Buf[Buf[1]++]; + Buf[0]--; + } + + return GIF_OK; +} + +/****************************************************************************** + This routine reads an entire GIF into core, hanging all its state info off + the GifFileType pointer. Call DGifOpenFileName() or DGifOpenFileHandle() + first to initialize I/O. Its inverse is EGifSpew(). +*******************************************************************************/ +int +DGifSlurp(GifFileType *GifFile) +{ + size_t ImageSize; + GifRecordType RecordType; + SavedImage *sp; + GifByteType *ExtData; + int ExtFunction; + + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; + + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) + return (GIF_ERROR); + + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) + return (GIF_ERROR); + + sp = &GifFile->SavedImages[GifFile->ImageCount - 1]; + /* Allocate memory for the image */ + if (sp->ImageDesc.Width <= 0 || sp->ImageDesc.Height <= 0 || + sp->ImageDesc.Width > (INT_MAX / sp->ImageDesc.Height)) { + return GIF_ERROR; + } + ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height; + + if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) { + return GIF_ERROR; + } + sp->RasterBits = (unsigned char *)reallocarray(NULL, ImageSize, + sizeof(GifPixelType)); + + if (sp->RasterBits == NULL) { + return GIF_ERROR; + } + + if (sp->ImageDesc.Interlace) { + int i, j; + /* + * The way an interlaced image should be read - + * offsets and jumps... + */ + int InterlacedOffset[] = { 0, 4, 2, 1 }; + int InterlacedJumps[] = { 8, 8, 4, 2 }; + /* Need to perform 4 passes on the image */ + for (i = 0; i < 4; i++) + for (j = InterlacedOffset[i]; + j < sp->ImageDesc.Height; + j += InterlacedJumps[i]) { + if (DGifGetLine(GifFile, + sp->RasterBits+j*sp->ImageDesc.Width, + sp->ImageDesc.Width) == GIF_ERROR) + return GIF_ERROR; + } + } + else { + if (DGifGetLine(GifFile,sp->RasterBits,ImageSize)==GIF_ERROR) + return (GIF_ERROR); + } + + if (GifFile->ExtensionBlocks) { + sp->ExtensionBlocks = GifFile->ExtensionBlocks; + sp->ExtensionBlockCount = GifFile->ExtensionBlockCount; + + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; + } + break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(GifFile,&ExtFunction,&ExtData) == GIF_ERROR) + return (GIF_ERROR); + /* Create an extension block with our data */ + if (ExtData != NULL) { + if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, + ExtFunction, ExtData[0], &ExtData[1]) + == GIF_ERROR) + return (GIF_ERROR); + } + for (;;) { + if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR) + return (GIF_ERROR); + if (ExtData == NULL) + break; + /* Continue the extension block */ + if (ExtData != NULL) + if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, + CONTINUE_EXT_FUNC_CODE, + ExtData[0], &ExtData[1]) == GIF_ERROR) + return (GIF_ERROR); + } + break; + + case TERMINATE_RECORD_TYPE: + break; + + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); + + /* Sanity check for corrupted file */ + if (GifFile->ImageCount == 0) { + GifFile->Error = D_GIF_ERR_NO_IMAG_DSCR; + return(GIF_ERROR); + } + + return (GIF_OK); +} + +/* end */ diff --git a/third_party/giflib/giflib/egif_lib.c b/third_party/giflib/giflib/egif_lib.c new file mode 100644 index 00000000..6219af02 --- /dev/null +++ b/third_party/giflib/giflib/egif_lib.c @@ -0,0 +1,1165 @@ +/****************************************************************************** + +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 +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif /* _WIN32 */ +#include + +#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, GifPixelType * Line, + 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 { + //cppcheck-suppress nullPointerRedundantCheck + 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) { + 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, + 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... + */ + int InterlacedOffset[] = { 0, 4, 2, 1 }; + 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 */ diff --git a/third_party/giflib/giflib/getarg.c b/third_party/giflib/giflib/getarg.c new file mode 100644 index 00000000..d569f6cb --- /dev/null +++ b/third_party/giflib/giflib/getarg.c @@ -0,0 +1,639 @@ +/*************************************************************************** + +getarg.c - routines to grab the parameters from the command line: + +Names of all the routines except the main one start with GA (Get +Arguments) to prevent conflicts. + +The following routines are available in this module: + +1. int GAGetArgs(argc, argv, CtrlStr, Variables...) +where argc, argv are received on entry. +CtrlStr is the contrl string (see below) +Variables are all the variables to be set according to CtrlStr. +Note that all the variables MUST be transfered by address. +Return 0 on correct parsing, otherwise error number (see GetArg.h). + +2. GAPrintHowTo(CtrlStr) +Print the control string to stderr, in the correct format. +This feature is very useful in case of an error during GetArgs parsing. +Chars equal to SPACE_CHAR are not printed (regular spaces are NOT +allowed, and so using SPACE_CHAR you can create space in PrintHowTo). + +3. GAPrintErrMsg(Error) +Describe the error to stderr, according to Error (usually returned by +GAGetArgs). + +CtrlStr format: + +The control string passed to GetArgs controls the way argv (argc) are +parsed. Each entry in this string must not have any spaces in it. The +First Entry is the name of the program, which is usually ignored except +when GAPrintHowTo is called. All the other entries (except the last one +which we will come back to later) must have the following format: + +1. One letter which sets the option letter. +2. '!' or '%' to determines if this option is really optional ('%') or + required ('!')... +3. '-' must always be given. +4. Alphanumeric string, usually ignored, but used by GAPrintHowTo to + print the meaning of this option. +5. Sequences starts with '!' or '%'. Again if '!' then this sequence + must exist (only if its option flag is given of course), and if '%' + it is optional. Each sequence will continue with one or two + characters which defines the kind of the input: +a: d, x, o, u - integer is expected (decimal, hex, octal base or unsigned). +b: D, X, O, U - long integer is expected (same as above). +c: f - float number is expected. +d: F - double number is expected. +e: s - string is expected. +f: *? - any number of '?' kind (d, x, o, u, D, X, O, U, f, F, s) + will match this one. If '?' is numeric, it scans until + non-numeric input is given. If '?' is 's' then it scans + up to the next option or end of argv. + +If the last parameter given in the CtrlStr, is not an option (i.e. the +second char is not in ['!', '%'] and the third one is not '-'), all what +remained from argv is linked to it. + +The variables passed to GAGetArgs (starting from 4th parameter) MUST +match the order of the CtrlStr: + +For each option, one integer address must be passed. This integer must +be initialized with 0. If that option is given in the command line, it will +be set. + +In addition, the sequences that might follow an option require the +following parameters to pass: + +1. d, x, o, u - pointer to integer (int *). +2. D, X, O, U - pointer to long (long *). +3. f - pointer to float (float *). +4. F - pointer to double (double *). +5. s - pointer to char (char *). NO allocation is needed! +6. *? - TWO variables are passed for each wild request. the first + one is (address of) integer, and it will return number of + parameters actually matched this sequence, and the second + one is a pointer to pointer to ? (? **), and will return an + address to a block of pointers to ? kind, terminated with + NULL pointer. NO pre-allocation is required. The caller is + responsible for freeing this memory, including the pointed to + memory. + +Note that these two variables are pretty like the argv/argc pair... + +Examples: + +"Example1 i%-OneInteger!d s%-Strings!*s j%- k!-Float!f Files" + +Will match: Example1 -i 77 -s String1 String2 String3 -k 88.2 File1 File2 +or: Example1 -s String1 -k 88.3 -i 999 -j +but not: Example1 -i 77 78 (option i expects one integer, k must be). + +Note the option k must exist, and that the order of the options is not +important. In the first examples File1 & File2 will match the Files +in the command line. + +A call to GAPrintHowTo with this CtrlStr will print to stderr: +Example1 [-i OneIngeter] [-s Strings...] [-j] -k Float Files... + +Notes: + +1. This module assumes that all the pointers to all kind of data types +have the same length and format, i.e. sizeof(int *) == sizeof(char *). + +SPDX-License-Identifier: MIT + +**************************************************************************/ + +#include +#include +#include +#include +#include + +#include "getarg.h" + +#define MAX_PARAM 100 /* maximum number of parameters allowed. */ +#define CTRL_STR_MAX_LEN 1024 + +#define SPACE_CHAR '|' /* The character not to print using HowTo. */ + +#define ARG_OK false + +#define ISSPACE(x) ((x) <= ' ') /* Not conventional - but works fine! */ + +/* The two characters '%' and '!' are used in the control string: */ +#define ISCTRLCHAR(x) (((x) == '%') || ((x) == '!')) + +static char *GAErrorToken; /* On error, ErrorToken is set to point to it. */ +static int GATestAllSatis(char *CtrlStrCopy, char *CtrlStr, char **argv_end, + char ***argv, void *Parameters[MAX_PARAM], + int *ParamCount); +static bool GAUpdateParameters(void *Parameters[], int *ParamCount, + char *Option, char *CtrlStrCopy, char *CtrlStr, + char **argv_end, char ***argv); +static int GAGetParmeters(void *Parameters[], int *ParamCount, + char *CtrlStrCopy, char *Option, char **argv_end, + char ***argv); +static int GAGetMultiParmeters(void *Parameters[], int *ParamCount, + char *CtrlStrCopy, char **argv_end, char ***argv); +static void GASetParamCount(char *CtrlStr, int Max, int *ParamCount); +static bool GAOptionExists(char **argv_end, char **argv); + +/*************************************************************************** + Allocate or die +***************************************************************************/ +static void * +xmalloc(unsigned size) { + + void *p; + + if ((p = malloc(size)) != NULL) + return p; + + fprintf(stderr, "Not enough memory, exit.\n"); + exit(2); + + return NULL; /* Makes warning silent. */ +} +/*************************************************************************** + Routine to access the command line argument and interpret them: + Return ARG_OK (0) is case of successful parsing, error code else... +***************************************************************************/ +bool +GAGetArgs(int argc, + char **argv, + char *CtrlStr, ...) { + + int i, ParamCount = 0; + void *Parameters[MAX_PARAM]; /* Save here parameter addresses. */ + char CtrlStrCopy[CTRL_STR_MAX_LEN]; + char **argv_end = argv + argc; + va_list ap; + + strncpy(CtrlStrCopy, CtrlStr, sizeof(CtrlStrCopy)-1); + GASetParamCount(CtrlStr, strlen(CtrlStr), &ParamCount); + va_start(ap, CtrlStr); + for (i = 1; i <= ParamCount; i++) + Parameters[i - 1] = va_arg(ap, void *); + va_end(ap); + + argv++; /* Skip the program name (first in argv/c list). */ + while (argv < argv_end) { + bool Error = false; + if (!GAOptionExists(argv_end, argv)) + break; /* The loop. */ + char *Option = *argv++; + if ((Error = GAUpdateParameters(Parameters, &ParamCount, Option, + CtrlStrCopy, CtrlStr, argv_end, + &argv)) != false) + return Error; + } + /* Check for results and update trail of command line: */ + return GATestAllSatis(CtrlStrCopy, CtrlStr, argv_end, &argv, Parameters, + &ParamCount) != ARG_OK; +} + +/*************************************************************************** + Routine to search for unsatisfied flags - simply scan the list for !- + sequence. Before this scan, this routine updates the rest of the command + line into the last two parameters if it is requested by the CtrlStr + (last item in CtrlStr is NOT an option). + Return ARG_OK if all satisfied, CMD_ERR_AllSatis error else. +***************************************************************************/ +static int +GATestAllSatis(char *CtrlStrCopy, + char *CtrlStr, + char **argv_end, + char ***argv, + void *Parameters[MAX_PARAM], + int *ParamCount) { + + int i; + static char *LocalToken = NULL; + + /* If LocalToken is not initialized - do it now. Note that this string + * should be writable as well so we can not assign it directly. + */ + if (LocalToken == NULL) { + LocalToken = (char *)malloc(3); + strcpy(LocalToken, "-?"); + } + + /* Check if last item is an option. If not then copy rest of command + * line into it as 1. NumOfprm, 2. pointer to block of pointers. + */ + for (i = strlen(CtrlStr) - 1; i > 0 && !ISSPACE(CtrlStr[i]); i--) ; + if (!ISCTRLCHAR(CtrlStr[i + 2])) { + GASetParamCount(CtrlStr, i, ParamCount); /* Point in correct param. */ + *(int *)Parameters[(*ParamCount)++] = argv_end - *argv; + *(char ***)Parameters[(*ParamCount)++] = *argv; + } + + i = 0; + while (++i < (int)strlen(CtrlStrCopy)) + if ((CtrlStrCopy[i] == '-') && (CtrlStrCopy[i - 1] == '!')) { + GAErrorToken = LocalToken; + LocalToken[1] = CtrlStrCopy[i - 2]; /* Set the correct flag. */ + return CMD_ERR_AllSatis; + } + + return ARG_OK; +} + +/*************************************************************************** + Routine to update the parameters according to the given Option: + **************************************************************************/ +static bool +GAUpdateParameters(void *Parameters[], + int *ParamCount, + char *Option, + char *CtrlStrCopy, + char *CtrlStr, + char **argv_end, + char ***argv) { + + int i; + bool BooleanTrue = Option[2] != '-'; + + if (Option[0] != '-') { + GAErrorToken = Option; + return CMD_ERR_NotAnOpt; + } + i = 0; /* Scan the CtrlStrCopy for that option: */ + while (i + 2 < (int)strlen(CtrlStrCopy)) { + if ((CtrlStrCopy[i] == Option[1]) && (ISCTRLCHAR(CtrlStrCopy[i + 1])) + && (CtrlStrCopy[i + 2] == '-')) { + /* We found that option! */ + break; + } + i++; + } + if (i + 2 >= (int)strlen(CtrlStrCopy)) { + GAErrorToken = Option; + return CMD_ERR_NoSuchOpt; + } + + /* If we are here, then we found that option in CtrlStr - Strip it off: */ + CtrlStrCopy[i] = CtrlStrCopy[i + 1] = CtrlStrCopy[i + 2] = (char)' '; + GASetParamCount(CtrlStr, i, ParamCount); /* Set it to point in + correct prm. */ + i += 3; + /* Set boolean flag for that option. */ + *(bool *)Parameters[(*ParamCount)++] = BooleanTrue; + if (ISSPACE(CtrlStrCopy[i])) + return ARG_OK; /* Only a boolean flag is needed. */ + + /* Skip the text between the boolean option and data follows: */ + while (!ISCTRLCHAR(CtrlStrCopy[i])) + i++; + /* Get the parameters and return the appropriete return code: */ + return GAGetParmeters(Parameters, ParamCount, &CtrlStrCopy[i], + Option, argv_end, argv); +} + +/*************************************************************************** + Routine to get parameters according to the CtrlStr given from argv/argc +***************************************************************************/ +static int +GAGetParmeters(void *Parameters[], + int *ParamCount, + char *CtrlStrCopy, + char *Option, + char **argv_end, + char ***argv) { + + int i = 0, ScanRes; + + while (!(ISSPACE(CtrlStrCopy[i]))) { + switch (CtrlStrCopy[i + 1]) { + case 'd': /* Get signed integers. */ + ScanRes = sscanf(*((*argv)++), "%d", + (int *)Parameters[(*ParamCount)++]); + break; + case 'u': /* Get unsigned integers. */ + ScanRes = sscanf(*((*argv)++), "%u", + (unsigned *)Parameters[(*ParamCount)++]); + break; + case 'x': /* Get hex integers. */ + ScanRes = sscanf(*((*argv)++), "%x", + (unsigned int *)Parameters[(*ParamCount)++]); + break; + case 'o': /* Get octal integers. */ + ScanRes = sscanf(*((*argv)++), "%o", + (unsigned int *)Parameters[(*ParamCount)++]); + break; + case 'D': /* Get signed long integers. */ + ScanRes = sscanf(*((*argv)++), "%ld", + (long *)Parameters[(*ParamCount)++]); + break; + case 'U': /* Get unsigned long integers. */ + ScanRes = sscanf(*((*argv)++), "%lu", + (unsigned long *)Parameters[(*ParamCount)++]); + break; + case 'X': /* Get hex long integers. */ + ScanRes = sscanf(*((*argv)++), "%lx", + (unsigned long *)Parameters[(*ParamCount)++]); + break; + case 'O': /* Get octal long integers. */ + ScanRes = sscanf(*((*argv)++), "%lo", + (unsigned long *)Parameters[(*ParamCount)++]); + break; + case 'f': /* Get float number. */ + ScanRes = sscanf(*((*argv)++), "%f", + (float *)Parameters[(*ParamCount)++]); + break; + case 'F': /* Get double float number. */ + ScanRes = sscanf(*((*argv)++), "%lf", + (double *)Parameters[(*ParamCount)++]); + break; + case 's': /* It as a string. */ + ScanRes = 1; /* Allways O.K. */ + *(char **)Parameters[(*ParamCount)++] = *((*argv)++); + break; + case '*': /* Get few parameters into one: */ + ScanRes = GAGetMultiParmeters(Parameters, ParamCount, + &CtrlStrCopy[i], argv_end, argv); + if ((ScanRes == 0) && (CtrlStrCopy[i] == '!')) { + GAErrorToken = Option; + return CMD_ERR_WildEmpty; + } + break; + default: + ScanRes = 0; /* Make optimizer warning silent. */ + } + /* If reading fails and this number is a must (!) then error: */ + if ((ScanRes == 0) && (CtrlStrCopy[i] == '!')) { + GAErrorToken = Option; + return CMD_ERR_NumRead; + } + if (CtrlStrCopy[i + 1] != '*') { + i += 2; /* Skip to next parameter (if any). */ + } else + i += 3; /* Skip the '*' also! */ + } + + return ARG_OK; +} + +/*************************************************************************** + Routine to get a few parameters into one pointer such that the returned + pointer actually points on a block of pointers to the parameters... + For example *F means a pointer to pointers on floats. + Returns number of parameters actually read. + This routine assumes that all pointers (on any kind of scalar) has the + same size (and the union below is totally ovelapped bteween dif. arrays) +***************************************************************************/ +static int +GAGetMultiParmeters(void *Parameters[], + int *ParamCount, + char *CtrlStrCopy, + char **argv_end, + char ***argv) { + + int i = 0, ScanRes, NumOfPrm = 0; + void **Pmain, **Ptemp; + union TmpArray { /* Save here the temporary data before copying it to */ + void *VoidArray[MAX_PARAM]; /* the returned pointer block. */ + int *IntArray[MAX_PARAM]; + long *LngArray[MAX_PARAM]; + float *FltArray[MAX_PARAM]; + double *DblArray[MAX_PARAM]; + char *ChrArray[MAX_PARAM]; + } TmpArray; + + do { + switch (CtrlStrCopy[2]) { /* CtrlStr == '!*?' or '%*?' where ? is. */ + case 'd': /* Format to read the parameters: */ + TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); + ScanRes = sscanf(*((*argv)++), "%d", + (int *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'u': + TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); + ScanRes = sscanf(*((*argv)++), "%u", + (unsigned int *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'o': + TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); + ScanRes = sscanf(*((*argv)++), "%o", + (unsigned int *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'x': + TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); + ScanRes = sscanf(*((*argv)++), "%x", + (unsigned int *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'D': + TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); + ScanRes = sscanf(*((*argv)++), "%ld", + (long *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'U': + TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); + ScanRes = sscanf(*((*argv)++), "%lu", + (unsigned long *)TmpArray. + IntArray[NumOfPrm++]); + break; + case 'O': + TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); + ScanRes = sscanf(*((*argv)++), "%lo", + (unsigned long *)TmpArray. + IntArray[NumOfPrm++]); + break; + case 'X': + TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); + ScanRes = sscanf(*((*argv)++), "%lx", + (unsigned long *)TmpArray. + IntArray[NumOfPrm++]); + break; + case 'f': + TmpArray.FltArray[NumOfPrm] = xmalloc(sizeof(float)); + ScanRes = sscanf(*((*argv)++), "%f", + // cppcheck-suppress invalidPointerCast + (float *)TmpArray.LngArray[NumOfPrm++]); + break; + case 'F': + TmpArray.DblArray[NumOfPrm] = xmalloc(sizeof(double)); + ScanRes = sscanf(*((*argv)++), "%lf", + // cppcheck-suppress invalidPointerCast + (double *)TmpArray.LngArray[NumOfPrm++]); + break; + case 's': + while ((*argv < argv_end) && ((**argv)[0] != '-')) { + TmpArray.ChrArray[NumOfPrm++] = *((*argv)++); + } + ScanRes = 0; /* Force quit from do - loop. */ + NumOfPrm++; /* Updated again immediately after loop! */ + (*argv)++; /* "" */ + break; + default: + ScanRes = 0; /* Make optimizer warning silent. */ + } + } + while (ScanRes == 1); /* Exactly one parameter was read. */ + (*argv)--; + NumOfPrm--; + + /* Now allocate the block with the exact size, and set it: */ + Ptemp = Pmain = xmalloc((unsigned)(NumOfPrm + 1) * sizeof(void *)); + /* And here we use the assumption that all pointers are the same: */ + for (i = 0; i < NumOfPrm; i++) + *Ptemp++ = TmpArray.VoidArray[i]; + *Ptemp = NULL; /* Close the block with NULL pointer. */ + + /* That it save the number of parameters read as first parameter to + * return and the pointer to the block as second, and return: */ + *(int *)Parameters[(*ParamCount)++] = NumOfPrm; + *(void ***)Parameters[(*ParamCount)++] = Pmain; + /* free(Pmain); -- can not free here as caller needs to access memory */ + return NumOfPrm; +} + +/*************************************************************************** + Routine to scan the CtrlStr, up to Max and count the number of parameters + to that point: + 1. Each option is counted as one parameter - boolean variable (int) + 2. Within an option, each %? or !? is counted once - pointer to something + 3. Within an option, %*? or !*? is counted twice - one for item count + and one for pointer to block pointers. + Note ALL variables are passed by address and so of fixed size (address). +***************************************************************************/ +static void +GASetParamCount(char *CtrlStr, + int Max, + int *ParamCount) { + int i; + + *ParamCount = 0; + for (i = 0; i < Max; i++) + if (ISCTRLCHAR(CtrlStr[i])) { + if (CtrlStr[i + 1] == '*') + *ParamCount += 2; + else + (*ParamCount)++; + } +} + +/*************************************************************************** + Routine to check if more option (i.e. first char == '-') exists in the + given list argc, argv: +***************************************************************************/ +static bool +GAOptionExists(char **argv_end, + char **argv) { + + while (argv < argv_end) + if ((*argv++)[0] == '-') + return true; + return false; +} + +/*************************************************************************** + Routine to print some error messages, for this module: +***************************************************************************/ +void +GAPrintErrMsg(int Error) { + + fprintf(stderr, "Error in command line parsing - "); + switch (Error) { + case 0:; + fprintf(stderr, "Undefined error"); + break; + case CMD_ERR_NotAnOpt: + fprintf(stderr, "None option Found"); + break; + case CMD_ERR_NoSuchOpt: + fprintf(stderr, "Undefined option Found"); + break; + case CMD_ERR_WildEmpty: + fprintf(stderr, "Empty input for '!*?' seq."); + break; + case CMD_ERR_NumRead: + fprintf(stderr, "Failed on reading number"); + break; + case CMD_ERR_AllSatis: + fprintf(stderr, "Fail to satisfy"); + break; + } + fprintf(stderr, " - '%s'.\n", GAErrorToken); +} + +/*************************************************************************** + Routine to print correct format of command line allowed: +***************************************************************************/ +void +GAPrintHowTo(char *CtrlStr) { + + int i = 0; + bool SpaceFlag; + + fprintf(stderr, "Usage: "); + /* Print program name - first word in ctrl. str. (optional): */ + while (!(ISSPACE(CtrlStr[i])) && (!ISCTRLCHAR(CtrlStr[i + 1]))) + fprintf(stderr, "%c", CtrlStr[i++]); + + while (i < (int)strlen(CtrlStr)) { + // cppcheck-suppress arrayIndexThenCheck + while ((ISSPACE(CtrlStr[i])) && (i < (int)strlen(CtrlStr))) + i++; + switch (CtrlStr[i + 1]) { + case '%': + fprintf(stderr, " [-%c", CtrlStr[i++]); + i += 2; /* Skip the '%-' or '!- after the char! */ + SpaceFlag = true; + while (!ISCTRLCHAR(CtrlStr[i]) && (i < (int)strlen(CtrlStr)) && + (!ISSPACE(CtrlStr[i]))) + if (SpaceFlag) { + if (CtrlStr[i++] == SPACE_CHAR) + fprintf(stderr, " "); + else + fprintf(stderr, " %c", CtrlStr[i - 1]); + SpaceFlag = false; + } else if (CtrlStr[i++] == SPACE_CHAR) + fprintf(stderr, " "); + else + fprintf(stderr, "%c", CtrlStr[i - 1]); + while (!ISSPACE(CtrlStr[i]) && (i < (int)strlen(CtrlStr))) { + if (CtrlStr[i] == '*') + fprintf(stderr, "..."); + i++; /* Skip the rest of it. */ + } + fprintf(stderr, "]"); + break; + case '!': + fprintf(stderr, " -%c", CtrlStr[i++]); + i += 2; /* Skip the '%-' or '!- after the char! */ + SpaceFlag = true; + while (!ISCTRLCHAR(CtrlStr[i]) && (i < (int)strlen(CtrlStr)) && + (!ISSPACE(CtrlStr[i]))) + if (SpaceFlag) { + if (CtrlStr[i++] == SPACE_CHAR) + fprintf(stderr, " "); + else + fprintf(stderr, " %c", CtrlStr[i - 1]); + SpaceFlag = false; + } else if (CtrlStr[i++] == SPACE_CHAR) + fprintf(stderr, " "); + else + fprintf(stderr, "%c", CtrlStr[i - 1]); + while (!ISSPACE(CtrlStr[i]) && (i < (int)strlen(CtrlStr))) { + if (CtrlStr[i] == '*') + fprintf(stderr, "..."); + i++; /* Skip the rest of it. */ + } + break; + default: /* Not checked, but must be last one! */ + fprintf(stderr, " "); + while (!ISSPACE(CtrlStr[i]) && (i < (int)strlen(CtrlStr)) && + !ISCTRLCHAR(CtrlStr[i])) + fprintf(stderr, "%c", CtrlStr[i++]); + fprintf(stderr, "\n"); + return; + } + } + fprintf(stderr, "\n"); +} + +/* end */ diff --git a/third_party/giflib/giflib/getarg.h b/third_party/giflib/giflib/getarg.h new file mode 100644 index 00000000..4a4559dc --- /dev/null +++ b/third_party/giflib/giflib/getarg.h @@ -0,0 +1,52 @@ +/*************************************************************************** + +getarg.h - Support routines for the giflib utilities + +SPDX-License-Identifier: MIT + +**************************************************************************/ + +#ifndef _GETARG_H +#define _GETARG_H + +#include "gif_lib.h" +#include + +#define VERSION_COOKIE " Version %d.%d, " + +/*************************************************************************** + Error numbers as returned by GAGetArg routine: +***************************************************************************/ +#define CMD_ERR_NotAnOpt 1 /* None Option found. */ +#define CMD_ERR_NoSuchOpt 2 /* Undefined Option Found. */ +#define CMD_ERR_WildEmpty 3 /* Empty input for !*? seq. */ +#define CMD_ERR_NumRead 4 /* Failed on reading number. */ +#define CMD_ERR_AllSatis 5 /* Fail to satisfy (must-'!') option. */ + +bool GAGetArgs(int argc, char **argv, char *CtrlStr, ...); +void GAPrintErrMsg(int Error); +void GAPrintHowTo(char *CtrlStr); + +/****************************************************************************** + From qprintf.c +******************************************************************************/ +extern bool GifNoisyPrint; +extern void GifQprintf(char *Format, ...); +extern void PrintGifError(int ErrorCode); + +/****************************************************************************** + Color table quantization +******************************************************************************/ +int GifQuantizeBuffer(unsigned int Width, unsigned int Height, + int *ColorMapSize, GifByteType * RedInput, + GifByteType * GreenInput, GifByteType * BlueInput, + GifByteType * OutputBuffer, + GifColorType * OutputColorMap); + +/* These used to live in the library header */ +#define GIF_MESSAGE(Msg) fprintf(stderr, "\n%s: %s\n", PROGRAM_NAME, Msg) +#define GIF_EXIT(Msg) { GIF_MESSAGE(Msg); exit(-3); } + +#endif /* _GETARG_H */ + +/* end */ diff --git a/third_party/giflib/giflib/gif2rgb.c b/third_party/giflib/giflib/gif2rgb.c new file mode 100644 index 00000000..8d7c0fff --- /dev/null +++ b/third_party/giflib/giflib/gif2rgb.c @@ -0,0 +1,538 @@ +/***************************************************************************** + +gif2rgb - convert GIF to 24-bit RGB pixel triples or vice-versa + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +/*************************************************************************** + +Toshio Kuratomi had written this in a comment about the rgb2gif code: + + Besides fixing bugs, what's really needed is for someone to work out how to + calculate a colormap for writing GIFs from rgb sources. Right now, an rgb + source that has only two colors (b/w) is being converted into an 8 bit GIF.... + Which is horrendously wasteful without compression. + +I (ESR) took this off the main to-do list in 2012 because I don't think +the GIFLIB project actually needs to be in the converters-and-tools business. +Plenty of hackers do that; our job is to supply stable library capability +with our utilities mainly interesting as test tools. + +***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif /* _WIN32 */ + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "gif2rgb" + +static char + *VersionStr = + PROGRAM_NAME + VERSION_COOKIE + " Gershon Elber, " + __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char + *CtrlStr = + PROGRAM_NAME + " v%- c%-#Colors!d s%-Width|Height!d!d 1%- o%-OutFileName!s h%- GifFile!*s"; + +static void LoadRGB(char *FileName, + int OneFileFlag, + GifByteType **RedBuffer, + GifByteType **GreenBuffer, + GifByteType **BlueBuffer, + int Width, int Height); +static void SaveGif(GifByteType *OutputBuffer, + int Width, int Height, + int ExpColorMapSize, ColorMapObject *OutputColorMap); + +/****************************************************************************** + Load RGB file into internal frame buffer. +******************************************************************************/ +static void LoadRGB(char *FileName, + int OneFileFlag, + GifByteType **RedBuffer, + GifByteType **GreenBuffer, + GifByteType **BlueBuffer, + int Width, int Height) +{ + int i; + unsigned long Size; + GifByteType *RedP, *GreenP, *BlueP; + FILE *rgbfp[3]; + + Size = ((long) Width) * Height * sizeof(GifByteType); + + if ((*RedBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL || + (*GreenBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL || + (*BlueBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + RedP = *RedBuffer; + GreenP = *GreenBuffer; + BlueP = *BlueBuffer; + + if (FileName != NULL) { + if (OneFileFlag) { + if ((rgbfp[0] = fopen(FileName, "rb")) == NULL) + GIF_EXIT("Can't open input file name."); + } + else { + static char *Postfixes[] = { ".R", ".G", ".B" }; + char OneFileName[80]; + + for (i = 0; i < 3; i++) { + strncpy(OneFileName, FileName, sizeof(OneFileName)-1); + strncat(OneFileName, Postfixes[i], + sizeof(OneFileName) - 1 - strlen(OneFileName)); + + if ((rgbfp[i] = fopen(OneFileName, "rb")) == NULL) + GIF_EXIT("Can't open input file name."); + } + } + } + else { + OneFileFlag = true; + +#ifdef _WIN32 + _setmode(0, O_BINARY); +#endif /* _WIN32 */ + + rgbfp[0] = stdin; + } + + GifQprintf("\n%s: RGB image: ", PROGRAM_NAME); + + if (OneFileFlag) { + GifByteType *Buffer, *BufferP; + + if ((Buffer = (GifByteType *) malloc(Width * 3)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + for (i = 0; i < Height; i++) { + int j; + GifQprintf("\b\b\b\b%-4d", i); + if (fread(Buffer, Width * 3, 1, rgbfp[0]) != 1) + GIF_EXIT("Input file(s) terminated prematurly."); + for (j = 0, BufferP = Buffer; j < Width; j++) { + *RedP++ = *BufferP++; + *GreenP++ = *BufferP++; + *BlueP++ = *BufferP++; + } + } + + free((char *) Buffer); + fclose(rgbfp[0]); + } + else { + for (i = 0; i < Height; i++) { + GifQprintf("\b\b\b\b%-4d", i); + if (fread(RedP, Width, 1, rgbfp[0]) != 1 || + fread(GreenP, Width, 1, rgbfp[1]) != 1 || + fread(BlueP, Width, 1, rgbfp[2]) != 1) + GIF_EXIT("Input file(s) terminated prematurly."); + RedP += Width; + GreenP += Width; + BlueP += Width; + } + + fclose(rgbfp[0]); + fclose(rgbfp[1]); + fclose(rgbfp[2]); + } +} + +/****************************************************************************** + Save the GIF resulting image. +******************************************************************************/ +static void SaveGif(GifByteType *OutputBuffer, + int Width, int Height, + int ExpColorMapSize, ColorMapObject *OutputColorMap) +{ + int i, Error; + GifFileType *GifFile; + GifByteType *Ptr = OutputBuffer; + + /* Open stdout for the output file: */ + if ((GifFile = EGifOpenFileHandle(1, &Error)) == NULL) { + PrintGifError(Error); + exit(EXIT_FAILURE); + } + + if (EGifPutScreenDesc(GifFile, + Width, Height, ExpColorMapSize, 0, + OutputColorMap) == GIF_ERROR || + EGifPutImageDesc(GifFile, + 0, 0, Width, Height, false, NULL) == GIF_ERROR) { + PrintGifError(Error); + exit(EXIT_FAILURE); + } + + GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top, + GifFile->Image.Width, GifFile->Image.Height); + + for (i = 0; i < Height; i++) { + if (EGifPutLine(GifFile, Ptr, Width) == GIF_ERROR) + exit(EXIT_FAILURE); + GifQprintf("\b\b\b\b%-4d", Height - i - 1); + + Ptr += Width; + } + + if (EGifCloseFile(GifFile, &Error) == GIF_ERROR) { + PrintGifError(Error); + exit(EXIT_FAILURE); + } +} + +/****************************************************************************** + Close output file (if open), and exit. +******************************************************************************/ +static void RGB2GIF(bool OneFileFlag, int NumFiles, char *FileName, + int ExpNumOfColors, int Width, int Height) +{ + int ColorMapSize; + + GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL, + *OutputBuffer = NULL; + ColorMapObject *OutputColorMap = NULL; + + ColorMapSize = 1 << ExpNumOfColors; + + if (NumFiles == 1) { + LoadRGB(FileName, OneFileFlag, + &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height); + } + else { + LoadRGB(NULL, OneFileFlag, + &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height); + } + + if ((OutputColorMap = GifMakeMapObject(ColorMapSize, NULL)) == NULL || + (OutputBuffer = (GifByteType *) malloc(Width * Height * + sizeof(GifByteType))) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + if (GifQuantizeBuffer(Width, Height, &ColorMapSize, + RedBuffer, GreenBuffer, BlueBuffer, + OutputBuffer, OutputColorMap->Colors) == GIF_ERROR) + exit(EXIT_FAILURE); + free((char *) RedBuffer); + free((char *) GreenBuffer); + free((char *) BlueBuffer); + + SaveGif(OutputBuffer, Width, Height, ExpNumOfColors, OutputColorMap); +} + +/****************************************************************************** + The real screen dumping routine. +******************************************************************************/ +static void DumpScreen2RGB(char *FileName, int OneFileFlag, + ColorMapObject *ColorMap, + GifRowType *ScreenBuffer, + int ScreenWidth, int ScreenHeight) +{ + int i, j; + GifRowType GifRow; + GifColorType *ColorMapEntry; + FILE *rgbfp[3]; + + if (FileName != NULL) { + if (OneFileFlag) { + if ((rgbfp[0] = fopen(FileName, "wb")) == NULL) + GIF_EXIT("Can't open input file name."); + } else { + static char *Postfixes[] = { ".R", ".G", ".B" }; + char OneFileName[80]; + + for (i = 0; i < 3; i++) { + strncpy(OneFileName, FileName, sizeof(OneFileName)-1); + strncat(OneFileName, Postfixes[i], + sizeof(OneFileName) - 1 - strlen(OneFileName)); + + if ((rgbfp[i] = fopen(OneFileName, "wb")) == NULL) { + GIF_EXIT("Can't open input file name."); + } + } + } + } else { + OneFileFlag = true; + +#ifdef _WIN32 + _setmode(1, O_BINARY); +#endif /* _WIN32 */ + + rgbfp[0] = stdout; + } + + if (ColorMap == NULL) { + fprintf(stderr, "Color map pointer is NULL.\n"); + exit(EXIT_FAILURE); + } + + if (OneFileFlag) { + unsigned char *Buffer, *BufferP; + + if ((Buffer = (unsigned char *) malloc(ScreenWidth * 3)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + for (i = 0; i < ScreenHeight; i++) { + GifRow = ScreenBuffer[i]; + GifQprintf("\b\b\b\b%-4d", ScreenHeight - i); + for (j = 0, BufferP = Buffer; j < ScreenWidth; j++) { + ColorMapEntry = &ColorMap->Colors[GifRow[j]]; + *BufferP++ = ColorMapEntry->Red; + *BufferP++ = ColorMapEntry->Green; + *BufferP++ = ColorMapEntry->Blue; + } + if (fwrite(Buffer, ScreenWidth * 3, 1, rgbfp[0]) != 1) + GIF_EXIT("Write to file(s) failed."); + } + + free((char *) Buffer); + fclose(rgbfp[0]); + } else { + unsigned char *Buffers[3]; + + if ((Buffers[0] = (unsigned char *) malloc(ScreenWidth)) == NULL || + (Buffers[1] = (unsigned char *) malloc(ScreenWidth)) == NULL || + (Buffers[2] = (unsigned char *) malloc(ScreenWidth)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + for (i = 0; i < ScreenHeight; i++) { + GifRow = ScreenBuffer[i]; + GifQprintf("\b\b\b\b%-4d", ScreenHeight - i); + for (j = 0; j < ScreenWidth; j++) { + ColorMapEntry = &ColorMap->Colors[GifRow[j]]; + Buffers[0][j] = ColorMapEntry->Red; + Buffers[1][j] = ColorMapEntry->Green; + Buffers[2][j] = ColorMapEntry->Blue; + } + if (fwrite(Buffers[0], ScreenWidth, 1, rgbfp[0]) != 1 || + fwrite(Buffers[1], ScreenWidth, 1, rgbfp[1]) != 1 || + fwrite(Buffers[2], ScreenWidth, 1, rgbfp[2]) != 1) + GIF_EXIT("Write to file(s) failed."); + } + + free((char *) Buffers[0]); + free((char *) Buffers[1]); + free((char *) Buffers[2]); + fclose(rgbfp[0]); + fclose(rgbfp[1]); + fclose(rgbfp[2]); + } +} + +static void GIF2RGB(int NumFiles, char *FileName, + bool OneFileFlag, + char *OutFileName) +{ + int i, j, Size, Row, Col, Width, Height, ExtCode, Count; + GifRecordType RecordType; + GifByteType *Extension; + GifRowType *ScreenBuffer; + GifFileType *GifFile; + int + InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */ + InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */ + int ImageNum = 0; + ColorMapObject *ColorMap; + int Error; + + if (NumFiles == 1) { + int Error; + if ((GifFile = DGifOpenFileName(FileName, &Error)) == NULL) { + PrintGifError(Error); + exit(EXIT_FAILURE); + } + } + else { + int Error; + /* Use stdin instead: */ + if ((GifFile = DGifOpenFileHandle(0, &Error)) == NULL) { + PrintGifError(Error); + exit(EXIT_FAILURE); + } + } + + if (GifFile->SHeight == 0 || GifFile->SWidth == 0) { + fprintf(stderr, "Image of width or height 0\n"); + exit(EXIT_FAILURE); + } + + /* + * Allocate the screen as vector of column of rows. Note this + * screen is device independent - it's the screen defined by the + * GIF file parameters. + */ + if ((ScreenBuffer = (GifRowType *) + malloc(GifFile->SHeight * sizeof(GifRowType))) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + Size = GifFile->SWidth * sizeof(GifPixelType);/* Size in bytes one row.*/ + if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) /* First row. */ + GIF_EXIT("Failed to allocate memory required, aborted."); + + for (i = 0; i < GifFile->SWidth; i++) /* Set its color to BackGround. */ + ScreenBuffer[0][i] = GifFile->SBackGroundColor; + for (i = 1; i < GifFile->SHeight; i++) { + /* Allocate the other rows, and set their color to background too: */ + if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + memcpy(ScreenBuffer[i], ScreenBuffer[0], Size); + } + + /* Scan the content of the GIF file and load the image(s) in: */ + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + Row = GifFile->Image.Top; /* Image Position relative to Screen. */ + Col = GifFile->Image.Left; + Width = GifFile->Image.Width; + Height = GifFile->Image.Height; + GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height); + if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth || + GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) { + fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n",ImageNum); + exit(EXIT_FAILURE); + } + if (GifFile->Image.Interlace) { + /* Need to perform 4 passes on the images: */ + for (Count = i = 0; i < 4; i++) + for (j = Row + InterlacedOffset[i]; j < Row + Height; + j += InterlacedJumps[i]) { + GifQprintf("\b\b\b\b%-4d", Count++); + if (DGifGetLine(GifFile, &ScreenBuffer[j][Col], + Width) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + } + } + else { + for (i = 0; i < Height; i++) { + GifQprintf("\b\b\b\b%-4d", i); + if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col], + Width) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + } + } + break; + case EXTENSION_RECORD_TYPE: + /* Skip any extension blocks in file: */ + if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + while (Extension != NULL) { + if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + } + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType. */ + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); + + /* Lets dump it - set the global variables required and do it: */ + ColorMap = (GifFile->Image.ColorMap + ? GifFile->Image.ColorMap + : GifFile->SColorMap); + if (ColorMap == NULL) { + fprintf(stderr, "Gif Image does not have a colormap\n"); + exit(EXIT_FAILURE); + } + + /* check that the background color isn't garbage (SF bug #87) */ + if (GifFile->SBackGroundColor < 0 || GifFile->SBackGroundColor >= ColorMap->ColorCount) { + fprintf(stderr, "Background color out of range for colormap\n"); + exit(EXIT_FAILURE); + } + + DumpScreen2RGB(OutFileName, OneFileFlag, + ColorMap, + ScreenBuffer, + GifFile->SWidth, GifFile->SHeight); + + (void)free(ScreenBuffer); + + if (DGifCloseFile(GifFile, &Error) == GIF_ERROR) { + PrintGifError(Error); + exit(EXIT_FAILURE); + } + +} + +/****************************************************************************** +* Interpret the command line and scan the given GIF file. +******************************************************************************/ +int main(int argc, char **argv) +{ + bool Error, OutFileFlag = false, ColorFlag = false, SizeFlag = false; + int NumFiles, Width = 0, Height = 0, ExpNumOfColors = 8; + char *OutFileName, + **FileName = NULL; + static bool + OneFileFlag = false, + HelpFlag = false; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, + &ColorFlag, &ExpNumOfColors, &SizeFlag, &Width, &Height, + &OneFileFlag, &OutFileFlag, &OutFileName, + &HelpFlag, &NumFiles, &FileName)) != false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) + GAPrintErrMsg(Error); + else if (NumFiles > 1) + GIF_MESSAGE("Error in command line parsing - one input file please."); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + if (!OutFileFlag) OutFileName = NULL; + + if (SizeFlag && Width > 0 && Height > 0) + RGB2GIF(OneFileFlag, NumFiles, *FileName, + ExpNumOfColors, Width, Height); + else + GIF2RGB(NumFiles, *FileName, OneFileFlag, OutFileName); + + return 0; +} + +/* end */ diff --git a/third_party/giflib/giflib/gif_err.c b/third_party/giflib/giflib/gif_err.c new file mode 100644 index 00000000..e69ad2df --- /dev/null +++ b/third_party/giflib/giflib/gif_err.c @@ -0,0 +1,99 @@ +/***************************************************************************** + +gif_err.c - handle error reporting for the GIF library. + +SPDX-License-Identifier: MIT + +****************************************************************************/ + +#include + +#include "gif_lib.h" +#include "gif_lib_private.h" + +/***************************************************************************** + Return a string description of the last GIF error +*****************************************************************************/ +const char * +GifErrorString(int ErrorCode) +{ + const char *Err; + + switch (ErrorCode) { + case E_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case E_GIF_ERR_WRITE_FAILED: + Err = "Failed to write to given file"; + break; + case E_GIF_ERR_HAS_SCRN_DSCR: + Err = "Screen descriptor has already been set"; + break; + case E_GIF_ERR_HAS_IMAG_DSCR: + Err = "Image descriptor is still active"; + break; + case E_GIF_ERR_NO_COLOR_MAP: + Err = "Neither global nor local color map"; + break; + case E_GIF_ERR_DATA_TOO_BIG: + Err = "Number of pixels bigger than width * height"; + break; + case E_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Failed to allocate required memory"; + break; + case E_GIF_ERR_DISK_IS_FULL: + Err = "Write failed (disk full?)"; + break; + case E_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case E_GIF_ERR_NOT_WRITEABLE: + Err = "Given file was not opened for write"; + break; + case D_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case D_GIF_ERR_READ_FAILED: + Err = "Failed to read from given file"; + break; + case D_GIF_ERR_NOT_GIF_FILE: + Err = "Data is not in GIF format"; + break; + case D_GIF_ERR_NO_SCRN_DSCR: + Err = "No screen descriptor detected"; + break; + case D_GIF_ERR_NO_IMAG_DSCR: + Err = "No Image Descriptor detected"; + break; + case D_GIF_ERR_NO_COLOR_MAP: + Err = "Neither global nor local color map"; + break; + case D_GIF_ERR_WRONG_RECORD: + Err = "Wrong record type detected"; + break; + case D_GIF_ERR_DATA_TOO_BIG: + Err = "Number of pixels bigger than width * height"; + break; + case D_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Failed to allocate required memory"; + break; + case D_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case D_GIF_ERR_NOT_READABLE: + Err = "Given file was not opened for read"; + break; + case D_GIF_ERR_IMAGE_DEFECT: + Err = "Image is defective, decoding aborted"; + break; + case D_GIF_ERR_EOF_TOO_SOON: + Err = "Image EOF detected before image complete"; + break; + default: + Err = NULL; + break; + } + return Err; +} + +/* end */ diff --git a/third_party/giflib/giflib/gif_font.c b/third_party/giflib/giflib/gif_font.c new file mode 100644 index 00000000..d90783ce --- /dev/null +++ b/third_party/giflib/giflib/gif_font.c @@ -0,0 +1,261 @@ +/***************************************************************************** + +gif_font.c - utility font handling and simple drawing for the GIF library + +SPDX-License-Identifier: MIT + +****************************************************************************/ + +#include +#include + +#include "gif_lib.h" + +/***************************************************************************** + Ascii 8 by 8 regular font - only first 128 characters are supported. +*****************************************************************************/ + +/* + * Each array entry holds the bits for 8 horizontal scan lines, topmost + * first. The most significant bit of each constant is the leftmost bit of + * the scan line. + */ +/*@+charint@*/ +const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* Ascii 0 */ + {0x3c, 0x42, 0xa5, 0x81, 0xbd, 0x42, 0x3c, 0x00}, /* Ascii 1 */ + {0x3c, 0x7e, 0xdb, 0xff, 0xc3, 0x7e, 0x3c, 0x00}, /* Ascii 2 */ + {0x00, 0xee, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 3 */ + {0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 4 */ + {0x00, 0x3c, 0x18, 0xff, 0xff, 0x08, 0x18, 0x00}, /* Ascii 5 */ + {0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x10, 0x38, 0x00}, /* Ascii 6 */ + {0x00, 0x00, 0x18, 0x3c, 0x18, 0x00, 0x00, 0x00}, /* Ascii 7 */ + {0xff, 0xff, 0xe7, 0xc3, 0xe7, 0xff, 0xff, 0xff}, /* Ascii 8 */ + {0x00, 0x3c, 0x42, 0x81, 0x81, 0x42, 0x3c, 0x00}, /* Ascii 9 */ + {0xff, 0xc3, 0xbd, 0x7e, 0x7e, 0xbd, 0xc3, 0xff}, /* Ascii 10 */ + {0x1f, 0x07, 0x0d, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* Ascii 11 */ + {0x00, 0x7e, 0xc3, 0xc3, 0x7e, 0x18, 0x7e, 0x18}, /* Ascii 12 */ + {0x04, 0x06, 0x07, 0x04, 0x04, 0xfc, 0xf8, 0x00}, /* Ascii 13 */ + {0x0c, 0x0a, 0x0d, 0x0b, 0xf9, 0xf9, 0x1f, 0x1f}, /* Ascii 14 */ + {0x00, 0x92, 0x7c, 0x44, 0xc6, 0x7c, 0x92, 0x00}, /* Ascii 15 */ + {0x00, 0x00, 0x60, 0x78, 0x7e, 0x78, 0x60, 0x00}, /* Ascii 16 */ + {0x00, 0x00, 0x06, 0x1e, 0x7e, 0x1e, 0x06, 0x00}, /* Ascii 17 */ + {0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18}, /* Ascii 18 */ + {0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00}, /* Ascii 19 */ + {0xff, 0xb6, 0x76, 0x36, 0x36, 0x36, 0x36, 0x00}, /* Ascii 20 */ + {0x7e, 0xc1, 0xdc, 0x22, 0x22, 0x1f, 0x83, 0x7e}, /* Ascii 21 */ + {0x00, 0x00, 0x00, 0x7e, 0x7e, 0x00, 0x00, 0x00}, /* Ascii 22 */ + {0x18, 0x7e, 0x18, 0x18, 0x7e, 0x18, 0x00, 0xff}, /* Ascii 23 */ + {0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, /* Ascii 24 */ + {0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x00}, /* Ascii 25 */ + {0x00, 0x04, 0x06, 0xff, 0x06, 0x04, 0x00, 0x00}, /* Ascii 26 */ + {0x00, 0x20, 0x60, 0xff, 0x60, 0x20, 0x00, 0x00}, /* Ascii 27 */ + {0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xff, 0x00}, /* Ascii 28 */ + {0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00}, /* Ascii 29 */ + {0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x00, 0x00}, /* Ascii 30 */ + {0x00, 0x00, 0x00, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 31 */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* */ + {0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00}, /* ! */ + {0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* " */ + {0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00}, /* # */ + {0x10, 0x7c, 0xd2, 0x7c, 0x86, 0x7c, 0x10, 0x00}, /* $ */ + {0xf0, 0x96, 0xfc, 0x18, 0x3e, 0x72, 0xde, 0x00}, /* % */ + {0x30, 0x48, 0x30, 0x78, 0xce, 0xcc, 0x78, 0x00}, /* & */ + {0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ' */ + {0x10, 0x60, 0xc0, 0xc0, 0xc0, 0x60, 0x10, 0x00}, /* ( */ + {0x10, 0x0c, 0x06, 0x06, 0x06, 0x0c, 0x10, 0x00}, /* ) */ + {0x00, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x00, 0x00}, /* * */ + {0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00}, /* + */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x70}, /* , */ + {0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00}, /* - */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00}, /* . */ + {0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00}, /* / */ + {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* 0 */ + {0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* 1 */ + {0x7c, 0xc6, 0x06, 0x0c, 0x30, 0x60, 0xfe, 0x00}, /* 2 */ + {0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00}, /* 3 */ + {0x0e, 0x1e, 0x36, 0x66, 0xfe, 0x06, 0x06, 0x00}, /* 4 */ + {0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xfc, 0x00}, /* 5 */ + {0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00}, /* 6 */ + {0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x00}, /* 7 */ + {0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* 8 */ + {0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00}, /* 9 */ + {0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00}, /* : */ + {0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x20, 0x00}, /* }, */ + {0x00, 0x1c, 0x30, 0x60, 0x30, 0x1c, 0x00, 0x00}, /* < */ + {0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00}, /* = */ + {0x00, 0x70, 0x18, 0x0c, 0x18, 0x70, 0x00, 0x00}, /* > */ + {0x7c, 0xc6, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00}, /* ? */ + {0x7c, 0x82, 0x9a, 0xaa, 0xaa, 0x9e, 0x7c, 0x00}, /* @ */ + {0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00}, /* A */ + {0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0x00}, /* B */ + {0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00}, /* C */ + {0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00}, /* D */ + {0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xfe, 0x00}, /* E */ + {0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* F */ + {0x7c, 0xc6, 0xc0, 0xce, 0xc6, 0xc6, 0x7e, 0x00}, /* G */ + {0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00}, /* H */ + {0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, /* I */ + {0x1e, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00}, /* J */ + {0xc6, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xc6, 0x00}, /* K */ + {0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00}, /* L */ + {0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0x00}, /* M */ + {0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00}, /* N */ + {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* O */ + {0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* P */ + {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x06}, /* Q */ + {0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0x00}, /* R */ + {0x78, 0xcc, 0x60, 0x30, 0x18, 0xcc, 0x78, 0x00}, /* S */ + {0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00}, /* T */ + {0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* U */ + {0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* V */ + {0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00}, /* W */ + {0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00}, /* X */ + {0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00}, /* Y */ + {0xfe, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00}, /* Z */ + {0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00}, /* [ */ + {0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x00}, /* \ */ + {0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00}, /* ] */ + {0x00, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00}, /* ^ */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}, /* _ */ + {0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ` */ + {0x00, 0x00, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00}, /* a */ + {0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0x00}, /* b */ + {0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0x7e, 0x00}, /* c */ + {0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x00}, /* d */ + {0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7e, 0x00}, /* e */ + {0x1e, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x30, 0x00}, /* f */ + {0x00, 0x00, 0x7e, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* g */ + {0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* */ + {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* i */ + {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0xf0}, /* j */ + {0xc0, 0xc0, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0x00}, /* k */ + {0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* l */ + {0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xc6, 0xc6, 0x00}, /* m */ + {0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* n */ + {0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* o */ + {0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0xc0}, /* p */ + {0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x06}, /* q */ + {0x00, 0x00, 0x6e, 0x70, 0x60, 0x60, 0x60, 0x00}, /* r */ + {0x00, 0x00, 0x7c, 0xc0, 0x7c, 0x06, 0xfc, 0x00}, /* s */ + {0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x1c, 0x00}, /* t */ + {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00}, /* u */ + {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* v */ + {0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00}, /* w */ + {0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00}, /* x */ + {0x00, 0x00, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* y */ + {0x00, 0x00, 0xfc, 0x18, 0x30, 0x60, 0xfc, 0x00}, /* z */ + {0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00}, /* { */ + {0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, /* | */ + {0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00}, /* } */ + {0x00, 0x00, 0x70, 0x9a, 0x0e, 0x00, 0x00, 0x00}, /* ~ */ + {0x00, 0x00, 0x18, 0x3c, 0x66, 0xff, 0x00, 0x00} /* Ascii 127 */ +}; +/*@=charint@*/ + +void +GifDrawText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, + const int color) +{ + int i, j; + const char *cp; + + for (i = 0; i < GIF_FONT_HEIGHT; i++) { + int base = Image->ImageDesc.Width * (y + i) + x; + + for (cp = legend; *cp; cp++) + for (j = 0; j < GIF_FONT_WIDTH; j++) { + if (GifAsciiTable8x8[(short)(*cp)][i] & (1 << (GIF_FONT_WIDTH - j))) + Image->RasterBits[base] = color; + base++; + } + } +} + +void +GifDrawBox(SavedImage *Image, + const int x, const int y, + const int w, const int d, + const int color) +{ + int j, base = Image->ImageDesc.Width * y + x; + + for (j = 0; j < w; j++) + Image->RasterBits[base + j] = + Image->RasterBits[base + (d * Image->ImageDesc.Width) + j] = color; + + for (j = 0; j < d; j++) + Image->RasterBits[base + j * Image->ImageDesc.Width] = + Image->RasterBits[base + j * Image->ImageDesc.Width + w] = color; +} + +void +GifDrawRectangle(SavedImage *Image, + const int x, const int y, + const int w, const int d, + const int color) +{ + unsigned char *bp = Image->RasterBits + Image->ImageDesc.Width * y + x; + int i; + + for (i = 0; i < d; i++) + memset(bp + (i * Image->ImageDesc.Width), color, (size_t)w); +} + +void +GifDrawBoxedText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, + const int border, + const int bg, const int fg) +{ + int j = 0, LineCount = 0, TextWidth = 0; + const char *cp; + char *dup; + + /* compute size of text to box */ + for (cp = legend; *cp; cp++) + if (*cp == '\r') { + if (j > TextWidth) + TextWidth = j; + j = 0; + LineCount++; + } else if (*cp != '\t') + ++j; + LineCount++; /* count last line */ + if (j > TextWidth) /* last line might be longer than any previous */ + TextWidth = j; + + /* draw the text */ + dup = malloc(strlen(legend)+1); + /* FIXME: should return bad status, but that would require API change */ + if (dup != NULL) { + int i = 0; + /* fill the box */ + GifDrawRectangle(Image, x + 1, y + 1, + border + TextWidth * GIF_FONT_WIDTH + border - 1, + border + LineCount * GIF_FONT_HEIGHT + border - 1, bg); + (void)strcpy(dup, (char *)legend); + char *lasts; + cp = strtok_r(dup, "\r\n", &lasts); + do { + int leadspace = 0; + + if (cp[0] == '\t') + leadspace = (TextWidth - strlen(++cp)) / 2; + + GifDrawText8x8(Image, x + border + (leadspace * GIF_FONT_WIDTH), + y + border + (GIF_FONT_HEIGHT * i++), cp, fg); + cp = strtok_r(NULL, "\r\n", &lasts); + } while (cp); + (void)free((void *)dup); + + /* outline the box */ + GifDrawBox(Image, x, y, border + TextWidth * GIF_FONT_WIDTH + border, + border + LineCount * GIF_FONT_HEIGHT + border, fg); + } +} + +/* end */ diff --git a/third_party/giflib/giflib/gif_hash.c b/third_party/giflib/giflib/gif_hash.c new file mode 100644 index 00000000..2db5c3d9 --- /dev/null +++ b/third_party/giflib/giflib/gif_hash.c @@ -0,0 +1,133 @@ +/***************************************************************************** + +gif_hash.c -- module to support the following operations: + +1. InitHashTable - initialize hash table. +2. ClearHashTable - clear the hash table to an empty state. +2. InsertHashTable - insert one item into data structure. +3. ExistsHashTable - test if item exists in data structure. + +This module is used to hash the GIF codes during encoding. + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "gif_lib.h" +#include "gif_hash.h" +#include "gif_lib_private.h" + +/* #define DEBUG_HIT_RATE Debug number of misses per hash Insert/Exists. */ + +#ifdef DEBUG_HIT_RATE +static long NumberOfTests = 0, + NumberOfMisses = 0; +#endif /* DEBUG_HIT_RATE */ + +static int KeyItem(uint32_t Item); + +/****************************************************************************** + Initialize HashTable - allocate the memory needed and clear it. * +******************************************************************************/ +GifHashTableType *_InitHashTable(void) +{ + GifHashTableType *HashTable; + + if ((HashTable = (GifHashTableType *) malloc(sizeof(GifHashTableType))) + == NULL) + return NULL; + + _ClearHashTable(HashTable); + + return HashTable; +} + +/****************************************************************************** + Routine to clear the HashTable to an empty state. * + This part is a little machine depended. Use the commented part otherwise. * +******************************************************************************/ +void _ClearHashTable(GifHashTableType *HashTable) +{ + memset(HashTable -> HTable, 0xFF, HT_SIZE * sizeof(uint32_t)); +} + +/****************************************************************************** + Routine to insert a new Item into the HashTable. The data is assumed to be * + new one. * +******************************************************************************/ +void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code) +{ + int HKey = KeyItem(Key); + uint32_t *HTable = HashTable -> HTable; + +#ifdef DEBUG_HIT_RATE + NumberOfTests++; + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + + while (HT_GET_KEY(HTable[HKey]) != 0xFFFFFL) { +#ifdef DEBUG_HIT_RATE + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + HKey = (HKey + 1) & HT_KEY_MASK; + } + HTable[HKey] = HT_PUT_KEY(Key) | HT_PUT_CODE(Code); +} + +/****************************************************************************** + Routine to test if given Key exists in HashTable and if so returns its code * + Returns the Code if key was found, -1 if not. * +******************************************************************************/ +int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key) +{ + int HKey = KeyItem(Key); + uint32_t *HTable = HashTable -> HTable, HTKey; + +#ifdef DEBUG_HIT_RATE + NumberOfTests++; + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + + while ((HTKey = HT_GET_KEY(HTable[HKey])) != 0xFFFFFL) { +#ifdef DEBUG_HIT_RATE + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + if (Key == HTKey) return HT_GET_CODE(HTable[HKey]); + HKey = (HKey + 1) & HT_KEY_MASK; + } + + return -1; +} + +/****************************************************************************** + Routine to generate an HKey for the hashtable out of the given unique key. * + The given Key is assumed to be 20 bits as follows: lower 8 bits are the * + new postfix character, while the upper 12 bits are the prefix code. * + Because the average hit ratio is only 2 (2 hash references per entry), * + evaluating more complex keys (such as twin prime keys) does not worth it! * +******************************************************************************/ +static int KeyItem(uint32_t Item) +{ + return ((Item >> 12) ^ Item) & HT_KEY_MASK; +} + +#ifdef DEBUG_HIT_RATE +/****************************************************************************** + Debugging routine to print the hit ratio - number of times the hash table * + was tested per operation. This routine was used to test the KeyItem routine * +******************************************************************************/ +void HashTablePrintHitRatio(void) +{ + printf("Hash Table Hit Ratio is %ld/%ld = %ld%%.\n", + NumberOfMisses, NumberOfTests, + NumberOfMisses * 100 / NumberOfTests); +} +#endif /* DEBUG_HIT_RATE */ + +/* end */ diff --git a/third_party/giflib/giflib/gif_hash.h b/third_party/giflib/giflib/gif_hash.h new file mode 100644 index 00000000..6a1b585e --- /dev/null +++ b/third_party/giflib/giflib/gif_hash.h @@ -0,0 +1,41 @@ +/****************************************************************************** + +gif_hash.h - magfic constants and declarations for GIF LZW + +SPDX-License-Identifier: MIT + +******************************************************************************/ + +#ifndef _GIF_HASH_H_ +#define _GIF_HASH_H_ + +#include +#include + +#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */ +#define HT_KEY_MASK 0x1FFF /* 13bits keys */ +#define HT_KEY_NUM_BITS 13 /* 13bits keys */ +#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */ +#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ + +/* The 32 bits of the long are divided into two parts for the key & code: */ +/* 1. The code is 12 bits as our compression algorithm is limited to 12bits */ +/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */ +/* The key is the upper 20 bits. The code is the lower 12. */ +#define HT_GET_KEY(l) (l >> 12) +#define HT_GET_CODE(l) (l & 0x0FFF) +#define HT_PUT_KEY(l) (l << 12) +#define HT_PUT_CODE(l) (l & 0x0FFF) + +typedef struct GifHashTableType { + uint32_t HTable[HT_SIZE]; +} GifHashTableType; + +GifHashTableType *_InitHashTable(void); +void _ClearHashTable(GifHashTableType *HashTable); +void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code); +int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key); + +#endif /* _GIF_HASH_H_ */ + +/* end */ diff --git a/third_party/giflib/giflib/gif_lib.h b/third_party/giflib/giflib/gif_lib.h new file mode 100644 index 00000000..ebdbd3cd --- /dev/null +++ b/third_party/giflib/giflib/gif_lib.h @@ -0,0 +1,303 @@ +/****************************************************************************** + +gif_lib.h - service library for decoding and encoding GIF images + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#ifndef _GIF_LIB_H_ +#define _GIF_LIB_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GIFLIB_MAJOR 5 +#define GIFLIB_MINOR 2 +#define GIFLIB_RELEASE 1 + +#define GIF_ERROR 0 +#define GIF_OK 1 + +#include +#include + +#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */ +#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1 +#define GIF_VERSION_POS 3 /* Version first character in stamp. */ +#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */ +#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */ + +typedef unsigned char GifPixelType; +typedef unsigned char *GifRowType; +typedef unsigned char GifByteType; +typedef unsigned int GifPrefixType; +typedef int GifWord; + +typedef struct GifColorType { + GifByteType Red, Green, Blue; +} GifColorType; + +typedef struct ColorMapObject { + int ColorCount; + int BitsPerPixel; + bool SortFlag; + GifColorType *Colors; /* on malloc(3) heap */ +} ColorMapObject; + +typedef struct GifImageDesc { + GifWord Left, Top, Width, Height; /* Current image dimensions. */ + bool Interlace; /* Sequential/Interlaced lines. */ + ColorMapObject *ColorMap; /* The local color map */ +} GifImageDesc; + +typedef struct ExtensionBlock { + int ByteCount; + GifByteType *Bytes; /* on malloc(3) heap */ + int Function; /* The block function code */ +#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */ +#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */ +#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */ +#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */ +#define APPLICATION_EXT_FUNC_CODE 0xff /* application block (GIF89) */ +} ExtensionBlock; + +typedef struct SavedImage { + GifImageDesc ImageDesc; + GifByteType *RasterBits; /* on malloc(3) heap */ + int ExtensionBlockCount; /* Count of extensions before image */ + ExtensionBlock *ExtensionBlocks; /* Extensions before image */ +} SavedImage; + +typedef struct GifFileType { + GifWord SWidth, SHeight; /* Size of virtual canvas */ + GifWord SColorResolution; /* How many colors can we generate? */ + GifWord SBackGroundColor; /* Background color for virtual canvas */ + GifByteType AspectByte; /* Used to compute pixel aspect ratio */ + ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */ + int ImageCount; /* Number of current image (both APIs) */ + GifImageDesc Image; /* Current image (low-level API) */ + SavedImage *SavedImages; /* Image sequence (high-level API) */ + int ExtensionBlockCount; /* Count extensions past last image */ + ExtensionBlock *ExtensionBlocks; /* Extensions past last image */ + int Error; /* Last error condition reported */ + void *UserData; /* hook to attach user data (TVT) */ + void *Private; /* Don't mess with this! */ +} GifFileType; + +#define GIF_ASPECT_RATIO(n) ((n)+15.0/64.0) + +typedef enum { + UNDEFINED_RECORD_TYPE, + SCREEN_DESC_RECORD_TYPE, + IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ + EXTENSION_RECORD_TYPE, /* Begin with '!' */ + TERMINATE_RECORD_TYPE /* Begin with ';' */ +} GifRecordType; + +/* func type to read gif data from arbitrary sources (TVT) */ +typedef int (*InputFunc) (GifFileType *, GifByteType *, int); + +/* func type to write gif data to arbitrary targets. + * Returns count of bytes written. (MRB) + */ +typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int); + +/****************************************************************************** + GIF89 structures +******************************************************************************/ + +typedef struct GraphicsControlBlock { + int DisposalMode; +#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */ +#define DISPOSE_DO_NOT 1 /* Leave image in place */ +#define DISPOSE_BACKGROUND 2 /* Set area too background color */ +#define DISPOSE_PREVIOUS 3 /* Restore to previous content */ + bool UserInputFlag; /* User confirmation required before disposal */ + int DelayTime; /* pre-display delay in 0.01sec units */ + int TransparentColor; /* Palette index for transparency, -1 if none */ +#define NO_TRANSPARENT_COLOR -1 +} GraphicsControlBlock; + +/****************************************************************************** + GIF encoding routines +******************************************************************************/ + +/* Main entry points */ +GifFileType *EGifOpenFileName(const char *GifFileName, + const bool GifTestExistence, int *Error); +GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error); +GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error); +int EGifSpew(GifFileType * GifFile); +const char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */ +int EGifCloseFile(GifFileType *GifFile, int *ErrorCode); + +#define E_GIF_SUCCEEDED 0 +#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */ +#define E_GIF_ERR_WRITE_FAILED 2 +#define E_GIF_ERR_HAS_SCRN_DSCR 3 +#define E_GIF_ERR_HAS_IMAG_DSCR 4 +#define E_GIF_ERR_NO_COLOR_MAP 5 +#define E_GIF_ERR_DATA_TOO_BIG 6 +#define E_GIF_ERR_NOT_ENOUGH_MEM 7 +#define E_GIF_ERR_DISK_IS_FULL 8 +#define E_GIF_ERR_CLOSE_FAILED 9 +#define E_GIF_ERR_NOT_WRITEABLE 10 + +/* These are legacy. You probably do not want to call them directly */ +int EGifPutScreenDesc(GifFileType *GifFile, + const int GifWidth, const int GifHeight, + const int GifColorRes, + const int GifBackGround, + const ColorMapObject *GifColorMap); +int EGifPutImageDesc(GifFileType *GifFile, + const int GifLeft, const int GifTop, + const int GifWidth, const int GifHeight, + const bool GifInterlace, + const ColorMapObject *GifColorMap); +void EGifSetGifVersion(GifFileType *GifFile, const bool gif89); +int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine, + int GifLineLen); +int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel); +int EGifPutComment(GifFileType *GifFile, const char *GifComment); +int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode); +int EGifPutExtensionBlock(GifFileType *GifFile, + const int GifExtLen, const void *GifExtension); +int EGifPutExtensionTrailer(GifFileType *GifFile); +int EGifPutExtension(GifFileType *GifFile, const int GifExtCode, + const int GifExtLen, + const void *GifExtension); +int EGifPutCode(GifFileType *GifFile, int GifCodeSize, + const GifByteType *GifCodeBlock); +int EGifPutCodeNext(GifFileType *GifFile, + const GifByteType *GifCodeBlock); + +/****************************************************************************** + GIF decoding routines +******************************************************************************/ + +/* Main entry points */ +GifFileType *DGifOpenFileName(const char *GifFileName, int *Error); +GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error); +int DGifSlurp(GifFileType * GifFile); +GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */ + int DGifCloseFile(GifFileType * GifFile, int *ErrorCode); + +#define D_GIF_SUCCEEDED 0 +#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */ +#define D_GIF_ERR_READ_FAILED 102 +#define D_GIF_ERR_NOT_GIF_FILE 103 +#define D_GIF_ERR_NO_SCRN_DSCR 104 +#define D_GIF_ERR_NO_IMAG_DSCR 105 +#define D_GIF_ERR_NO_COLOR_MAP 106 +#define D_GIF_ERR_WRONG_RECORD 107 +#define D_GIF_ERR_DATA_TOO_BIG 108 +#define D_GIF_ERR_NOT_ENOUGH_MEM 109 +#define D_GIF_ERR_CLOSE_FAILED 110 +#define D_GIF_ERR_NOT_READABLE 111 +#define D_GIF_ERR_IMAGE_DEFECT 112 +#define D_GIF_ERR_EOF_TOO_SOON 113 + +/* These are legacy. You probably do not want to call them directly */ +int DGifGetScreenDesc(GifFileType *GifFile); +int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType); +int DGifGetImageHeader(GifFileType *GifFile); +int DGifGetImageDesc(GifFileType *GifFile); +int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen); +int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel); +int DGifGetExtension(GifFileType *GifFile, int *GifExtCode, + GifByteType **GifExtension); +int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension); +int DGifGetCode(GifFileType *GifFile, int *GifCodeSize, + GifByteType **GifCodeBlock); +int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock); +int DGifGetLZCodes(GifFileType *GifFile, int *GifCode); +const char *DGifGetGifVersion(GifFileType *GifFile); + + +/****************************************************************************** + Error handling and reporting. +******************************************************************************/ +extern const char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */ + +/***************************************************************************** + Everything below this point is new after version 1.2, supporting `slurp + mode' for doing I/O in two big belts with all the image-bashing in core. +******************************************************************************/ + +/****************************************************************************** + Color map handling from gif_alloc.c +******************************************************************************/ + +extern ColorMapObject *GifMakeMapObject(int ColorCount, + const GifColorType *ColorMap); +extern void GifFreeMapObject(ColorMapObject *Object); +extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1, + const ColorMapObject *ColorIn2, + GifPixelType ColorTransIn2[]); +extern int GifBitSize(int n); + +/****************************************************************************** + Support for the in-core structures allocation (slurp mode). +******************************************************************************/ + +extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]); +extern int GifAddExtensionBlock(int *ExtensionBlock_Count, + ExtensionBlock **ExtensionBlocks, + int Function, + unsigned int Len, unsigned char ExtData[]); +extern void GifFreeExtensions(int *ExtensionBlock_Count, + ExtensionBlock **ExtensionBlocks); +extern SavedImage *GifMakeSavedImage(GifFileType *GifFile, + const SavedImage *CopyFrom); +extern void GifFreeSavedImages(GifFileType *GifFile); + +/****************************************************************************** + 5.x functions for GIF89 graphics control blocks +******************************************************************************/ + +int DGifExtensionToGCB(const size_t GifExtensionLength, + const GifByteType *GifExtension, + GraphicsControlBlock *GCB); +size_t EGifGCBToExtension(const GraphicsControlBlock *GCB, + GifByteType *GifExtension); + +int DGifSavedExtensionToGCB(GifFileType *GifFile, + int ImageIndex, + GraphicsControlBlock *GCB); +int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, + GifFileType *GifFile, + int ImageIndex); + +/****************************************************************************** + The library's internal utility font +******************************************************************************/ + +#define GIF_FONT_WIDTH 8 +#define GIF_FONT_HEIGHT 8 +extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH]; + +extern void GifDrawText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, const int color); + +extern void GifDrawBox(SavedImage *Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void GifDrawRectangle(SavedImage *Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void GifDrawBoxedText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, + const int border, const int bg, const int fg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _GIF_LIB_H */ + +/* end */ diff --git a/third_party/giflib/giflib/gif_lib_private.h b/third_party/giflib/giflib/gif_lib_private.h new file mode 100644 index 00000000..14e5e30d --- /dev/null +++ b/third_party/giflib/giflib/gif_lib_private.h @@ -0,0 +1,70 @@ +/**************************************************************************** + +gif_lib_private.h - internal giflib routines and structures + +SPDX-License-Identifier: MIT + +****************************************************************************/ + +#ifndef _GIF_LIB_PRIVATE_H +#define _GIF_LIB_PRIVATE_H + +#include "gif_lib.h" +#include "gif_hash.h" + +#ifndef SIZE_MAX + #define SIZE_MAX UINTPTR_MAX +#endif + +#define EXTENSION_INTRODUCER 0x21 +#define DESCRIPTOR_INTRODUCER 0x2c +#define TERMINATOR_INTRODUCER 0x3b + +#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ +#define LZ_BITS 12 + +#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */ +#define FIRST_CODE 4097 /* Impossible code, to signal first. */ +#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */ + +#define FILE_STATE_WRITE 0x01 +#define FILE_STATE_SCREEN 0x02 +#define FILE_STATE_IMAGE 0x04 +#define FILE_STATE_READ 0x08 + +#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ) +#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE) + +typedef struct GifFilePrivateType { + GifWord FileState, FileHandle, /* Where all this data goes to! */ + BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */ + ClearCode, /* The CLEAR LZ code. */ + EOFCode, /* The EOF LZ code. */ + RunningCode, /* The next code algorithm can generate. */ + RunningBits, /* The number of bits required to represent RunningCode. */ + MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */ + LastCode, /* The code before the current code. */ + CrntCode, /* Current algorithm code. */ + StackPtr, /* For character stack (see below). */ + CrntShiftState; /* Number of bits in CrntShiftDWord. */ + unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */ + unsigned long PixelCount; /* Number of pixels in image. */ + FILE *File; /* File as stream. */ + InputFunc Read; /* function to read gif input (TVT) */ + OutputFunc Write; /* function to write gif output (MRB) */ + GifByteType Buf[256]; /* Compressed input is buffered here. */ + GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */ + GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */ + GifPrefixType Prefix[LZ_MAX_CODE + 1]; + GifHashTableType *HashTable; + bool gif89; +} GifFilePrivateType; + +#ifndef HAVE_REALLOCARRAY +extern void *openbsd_reallocarray(void *optr, size_t nmemb, size_t size); +#define reallocarray openbsd_reallocarray +#endif + +#endif /* _GIF_LIB_PRIVATE_H */ + +/* end */ diff --git a/third_party/giflib/giflib/gifalloc.c b/third_party/giflib/giflib/gifalloc.c new file mode 100644 index 00000000..9cac7e49 --- /dev/null +++ b/third_party/giflib/giflib/gifalloc.c @@ -0,0 +1,420 @@ +/***************************************************************************** + + GIF construction tools + +SPDX-License-Identifier: MIT + +****************************************************************************/ + +#include +#include +#include + +#include "gif_lib.h" +#include "gif_lib_private.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 { + SavedImage* newSavedImages = (SavedImage *)reallocarray(GifFile->SavedImages, + (GifFile->ImageCount + 1), sizeof(SavedImage)); + if( newSavedImages == NULL) + return ((SavedImage *)NULL); + GifFile->SavedImages = newSavedImages; + } + if (GifFile->SavedImages == NULL) + return ((SavedImage *)NULL); + else { + SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++]; + + 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 (CopyFrom->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 (CopyFrom->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); + } + } + else { + memset((char *)sp, '\0', sizeof(SavedImage)); + } + + 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 */ diff --git a/third_party/giflib/giflib/gifbg.c b/third_party/giflib/giflib/gifbg.c new file mode 100644 index 00000000..98fe6bd0 --- /dev/null +++ b/third_party/giflib/giflib/gifbg.c @@ -0,0 +1,349 @@ +/***************************************************************************** + +gifbg - generate a test-pattern GIF + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "gifbg" + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 350 + +#define DEFAULT_COLOR_RED 0 +#define DEFAULT_COLOR_GREEN 0 +#define DEFAULT_COLOR_BLUE 255 + +#define DEFAULT_MIN_INTENSITY 10 /* In percent. */ +#define DEFAULT_MAX_INTENSITY 100 + +#define DEFAULT_NUM_LEVELS 16 /* Number of colors to gen in image. */ + +#define DIR_NONE 0 /* Direction the levels can be changed: */ +#define DIR_TOP 1 +#define DIR_TOP_RIGHT 2 +#define DIR_RIGHT 3 +#define DIR_BOT_RIGHT 4 +#define DIR_BOT 5 +#define DIR_BOT_LEFT 6 +#define DIR_LEFT 7 +#define DIR_TOP_LEFT 8 + +#define DEFAULT_DIR "T" /* TOP (North) direction. */ + +static char + *VersionStr = + PROGRAM_NAME + VERSION_COOKIE + " Gershon Elber, " + __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char + *CtrlStr = + PROGRAM_NAME + " v%- d%-Dir!s l%-#Lvls!d c%-R|G|B!d!d!d m%-MinI!d M%-MaxI!d s%-W|H!d!d h%-"; + +static int + MaximumIntensity = DEFAULT_MAX_INTENSITY, /* In percent. */ + MinimumIntensity = DEFAULT_MIN_INTENSITY, + NumLevels = DEFAULT_NUM_LEVELS, + ImageWidth = DEFAULT_WIDTH, + ImageHeight = DEFAULT_HEIGHT, + Direction; +static unsigned int + RedColor = DEFAULT_COLOR_RED, + GreenColor = DEFAULT_COLOR_GREEN, + BlueColor = DEFAULT_COLOR_BLUE; + +static void QuitGifError(GifFileType *GifFile); + +/****************************************************************************** + Interpret the command line and scan the given GIF file. +******************************************************************************/ +int main(int argc, char **argv) +{ + int i, l, LevelWidth, LogNumLevels, ErrorCode, Count = 0; + bool Error, FlipDir, DoAllMaximum = false, + DirectionFlag = false, LevelsFlag = false, ColorFlag = false, + MinFlag = false, MaxFlag = false, SizeFlag = false, HelpFlag = false; + GifPixelType Color; + char *DirectionStr = DEFAULT_DIR; + GifRowType Line; + ColorMapObject *ColorMap; + GifFileType *GifFile; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, + &DirectionFlag, &DirectionStr, &LevelsFlag, &NumLevels, + &ColorFlag, &RedColor, &GreenColor, &BlueColor, + &MinFlag, &MinimumIntensity, &MaxFlag, &MaximumIntensity, + &SizeFlag, &ImageWidth, &ImageHeight, + &HelpFlag)) != false) { + GAPrintErrMsg(Error); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + /* Make sure intensities are in the right range: */ + if (MinimumIntensity < 0 || MinimumIntensity > 100 || + MaximumIntensity < 0 || MaximumIntensity > 100) + GIF_EXIT("Intensities (-m or -M options) are not in [0..100] range (percent)."); + + /* Convert DirectionStr to our local representation: */ + Direction = DIR_NONE; + FlipDir = false; + /* Make sure it's upper case. */ + for (i = 0; i < (int)strlen(DirectionStr); i++) + if (islower(DirectionStr[i])) + DirectionStr[i] = toupper(DirectionStr[i]); + + switch(DirectionStr[0]) { + case 'T': /* Top or North */ + case 'N': + if (strlen(DirectionStr) < 2) + Direction = DIR_TOP; + else + switch(DirectionStr[1]) { + case 'R': + case 'E': + Direction = DIR_TOP_RIGHT; + break; + case 'L': + case 'W': + Direction = DIR_TOP_LEFT; + FlipDir = true; + break; + } + break; + case 'R': /* Right or East */ + case 'E': + Direction = DIR_RIGHT; + break; + case 'B': /* Bottom or South */ + case 'S': + if (strlen(DirectionStr) < 2) { + Direction = DIR_BOT; + FlipDir = true; + } + else + switch(DirectionStr[1]) { + case 'R': + case 'E': + Direction = DIR_BOT_RIGHT; + break; + case 'L': + case 'W': + Direction = DIR_BOT_LEFT; + FlipDir = true; + break; + } + break; + case 'L': /* Left or West */ + case 'W': + Direction = DIR_LEFT; + FlipDir = true; + break; + } + if (Direction == DIR_NONE) + GIF_EXIT("Direction requested (-d option) is weird!"); + + /* We are going to handle only TOP, TOP_RIGHT, RIGHT, BOT_RIGHT so flip */ + /* the complement cases (TOP <-> BOT for example) by flipping the */ + /* Color i with color (NumLevels - i - 1). */ + if (FlipDir) { + switch (Direction) { + case DIR_BOT: + Direction = DIR_TOP; + break; + case DIR_BOT_LEFT: + Direction = DIR_TOP_RIGHT; + break; + case DIR_LEFT: + Direction = DIR_RIGHT; + break; + case DIR_TOP_LEFT: + Direction = DIR_BOT_RIGHT; + break; + } + } + + /* If binary mask is requested (special case): */ + if (MinimumIntensity == 100 && MaximumIntensity == 100 && NumLevels == 2) { + MinimumIntensity = 0; + DoAllMaximum = true; + Direction = DIR_RIGHT; + } + + /* Make sure colors are in the right range: */ + if (RedColor > 255 || GreenColor > 255 || BlueColor > 255) + GIF_EXIT("Colors are not in the ragne [0..255]."); + + /* Make sure number of levels is power of 2 (up to 8 bits per pixel). */ + for (i = 1; i < 8; i++) if (NumLevels == (1 << i)) break; + if (i == 8) GIF_EXIT("#Lvls (-l option) is not power of 2."); + LogNumLevels = i; + + /* Open stdout for the output file: */ + if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* Dump out screen description with given size and generated color map: */ + if ((ColorMap = GifMakeMapObject(NumLevels, NULL)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + for (i = 1; i <= NumLevels; i++) { + /* Ratio will be in the range of 0..100 for required intensity: */ + unsigned int Ratio = (MaximumIntensity * (i * (256 / NumLevels)) + + MinimumIntensity * ((NumLevels - i) * (256 / NumLevels))) / + 256; + ColorMap->Colors[i-1].Red = (RedColor * Ratio) / 100; + ColorMap->Colors[i-1].Green = (GreenColor * Ratio) / 100; + ColorMap->Colors[i-1].Blue = (BlueColor * Ratio) / 100; + } + if (EGifPutScreenDesc(GifFile, + ImageWidth, ImageHeight, LogNumLevels, 0, ColorMap) + == GIF_ERROR) + QuitGifError(GifFile); + + /* Dump out the image descriptor: */ + if (EGifPutImageDesc(GifFile, + 0, 0, ImageWidth, ImageHeight, false, NULL) == GIF_ERROR) + QuitGifError(GifFile); + + GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top, + GifFile->Image.Width, GifFile->Image.Height); + + /* Allocate one scan line twice as big as image is, as we are going to */ + /* shift along it, while we dump the scan lines: */ + if ((Line = (GifRowType) malloc(sizeof(GifPixelType) * ImageWidth * 2)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + if (Direction == DIR_TOP) { + int LevelHeight; + /* We must evaluate the line each time level is changing: */ + LevelHeight = ImageHeight / NumLevels; + for (Color = NumLevels, i = l = 0; i < ImageHeight; i++) { + if (i == l) { + int j; + /* Time to update the line to next color level: */ + if (Color != 0) Color--; + for (j = 0; j < ImageWidth; j++) + Line[j] = (FlipDir ? NumLevels - Color - 1 : Color); + l += LevelHeight; + } + if (EGifPutLine(GifFile, Line, ImageWidth) == GIF_ERROR) + QuitGifError(GifFile); + GifQprintf("\b\b\b\b%-4d", Count++); + } + } + else if (Direction == DIR_RIGHT) { + /* We pre-prepare the scan lines as going from color zero to maximum */ + /* color and dump the same scan line Height times: */ + /* Note this case should handle the Boolean Mask special case. */ + LevelWidth = ImageWidth / NumLevels; + if (DoAllMaximum) { + /* Special case - do all in maximum color: */ + for (i = 0; i < ImageWidth; i++) Line[i] = 1; + } + else { + for (Color = i = 0, l = LevelWidth; i < ImageWidth; i++, l--) { + if (l == 0) { + l = LevelWidth; + if (Color < NumLevels - 1) Color++; + } + Line[i] = (FlipDir ? NumLevels - Color - 1 : Color); + } + } + + for (i = 0; i < ImageHeight; i++) { + /* coverity[uninit_use_in_call] */ + if (EGifPutLine(GifFile, Line, ImageWidth) == GIF_ERROR) + QuitGifError(GifFile); + GifQprintf("\b\b\b\b%-4d", Count++); + } + } + else { + int Accumulator, StartX, StepX; + /* We are in one of the TOP_RIGHT, BOT_RIGHT cases: we will */ + /* initialize the Line with its double ImageWidth length from the */ + /* minimum intensity to the maximum intensity and shift along it */ + /* while we go along the image height. */ + LevelWidth = ImageWidth * 2 / NumLevels; + for (Color = i = 0, l = LevelWidth; i < ImageWidth * 2; i++, l--) { + if (l == 0) { + l = LevelWidth; + if (Color < NumLevels - 1) Color++; + } + Line[i] = (FlipDir ? NumLevels - Color - 1 : Color); + } + /* We need to implement a DDA to know how much to shift Line while */ + /* we go down along image height. we set the parameters for it now: */ + Accumulator = 0; + switch(Direction) { + case DIR_TOP_RIGHT: + StartX = ImageWidth; + StepX = -1; + break; + case DIR_BOT_RIGHT: + default: + StartX = 0; + StepX = 1; + break; + } + + /* Time to dump information out: */ + for (i = 0; i < ImageHeight; i++) { + if (EGifPutLine(GifFile, &Line[StartX], ImageWidth) == GIF_ERROR) + QuitGifError(GifFile); + GifQprintf("\b\b\b\b%-4d", Count++); + if ((Accumulator += ImageWidth) > ImageHeight) { + while (Accumulator > ImageHeight) { + Accumulator -= ImageHeight; + StartX += StepX; + } + if (Direction < 0) Direction = 0; + if (Direction > ImageWidth) Direction = ImageWidth; + } + } + } + + if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) + { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + return 0; +} + +/****************************************************************************** + Close output file (if open), and exit. +******************************************************************************/ +static void QuitGifError(GifFileType *GifFile) +{ + if (GifFile != NULL) { + PrintGifError(GifFile->Error); + EGifCloseFile(GifFile, NULL); + } + exit(EXIT_FAILURE); +} + +/* end */ diff --git a/third_party/giflib/giflib/gifbuild.c b/third_party/giflib/giflib/gifbuild.c new file mode 100644 index 00000000..d2c9c665 --- /dev/null +++ b/third_party/giflib/giflib/gifbuild.c @@ -0,0 +1,940 @@ +/***************************************************************************** + +gifbuild - dump GIF data in a textual format, or undump it to a GIF + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "gifbuild" + +static char + *VersionStr = + PROGRAM_NAME + VERSION_COOKIE + " Eric Raymond, " + __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1992 Eric Raymond.\n"; +static char + *CtrlStr = + PROGRAM_NAME + " v%- d%- t%-Characters!s h%- GifFile(s)!*s"; + +static char KeyLetters[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:<=>?@[\\]^_`{|}~"; +#define PRINTABLES (sizeof(KeyLetters) - 1) + +static void Icon2Gif(char *FileName, FILE *txtin, int fdout); +static void Gif2Icon(char *FileName, + int fdin, int fdout, + char NameTable[]); +static int EscapeString(char *cp, char *tp); + +/****************************************************************************** + Main sequence +******************************************************************************/ +int main(int argc, char **argv) +{ + int NumFiles; + bool Error, DisasmFlag = false, HelpFlag = false, TextLineFlag = false; + char **FileNames = NULL; + char *TextLines[1]; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, + &GifNoisyPrint, &DisasmFlag, &TextLineFlag, &TextLines[0], + &HelpFlag, &NumFiles, &FileNames)) != false) { + GAPrintErrMsg(Error); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + if (!DisasmFlag && NumFiles > 1) { + GIF_MESSAGE("Error in command line parsing - one text input please."); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (!DisasmFlag && TextLineFlag) { + GIF_MESSAGE("Error in command line parsing - -t invalid without -d."); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + + if (NumFiles == 0) + { + if (DisasmFlag) + Gif2Icon("Stdin", 0, 1, TextLineFlag ? TextLines[0] : KeyLetters); + else + Icon2Gif("Stdin", stdin, 1); + } + else + { + int i; + for (i = 0; i < NumFiles; i++) + { + FILE *fp; + + if ((fp = fopen(FileNames[i], "r")) == (FILE *)NULL) + { + (void) fprintf(stderr, "Can't open %s\n", FileNames[i]); + exit(EXIT_FAILURE); + } + + if (DisasmFlag) + { + printf("#\n# GIF information from %s\n", FileNames[i]); + Gif2Icon(FileNames[i], -1, 1, TextLineFlag ? TextLines[0] : KeyLetters); + } + else + { + Icon2Gif(FileNames[i], fp, 1); + } + + (void) fclose(fp); + } + } + + return 0; +} + +/****************************************************************************** + Parse image directives +******************************************************************************/ +#define PARSE_ERROR(str) (void) fprintf(stderr,"%s:%d: %s\n",FileName,LineNum,str); + +static void Icon2Gif(char *FileName, FILE *txtin, int fdout) +{ + unsigned int ColorMapSize = 0; + GifColorType GlobalColorMap[256], LocalColorMap[256], + *ColorMap = GlobalColorMap; + char GlobalColorKeys[PRINTABLES], LocalColorKeys[PRINTABLES], + *KeyTable = GlobalColorKeys; + bool SortFlag = false; + unsigned int ExtCode, intval; + int red, green, blue, n; + char buf[BUFSIZ * 2], InclusionFile[64]; + GifFileType *GifFileOut; + SavedImage *NewImage = NULL; + int LeadingExtensionBlockCount = 0; + ExtensionBlock *LeadingExtensionBlocks = NULL; + int ErrorCode, LineNum = 0; + + if ((GifFileOut = EGifOpenFileHandle(fdout, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* OK, interpret directives */ + /* coverity[tainted_data_transitive] */ + while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) + { + char *cp; + + ++LineNum; + + /* + * Skip lines consisting only of whitespace and comments + */ + for (cp = buf; isspace((int)(*cp)); cp++) + continue; + if (*cp == '#' || *cp == '\0') + continue; + + /* + * If there's a trailing comment, nuke it and all preceding whitespace. + * But preserve the EOL. + */ + if ((cp = strchr(buf, '#')) && (cp == strrchr(cp, '#'))) + { + while (isspace((int)(*--cp))) + continue; + *++cp = '\n'; + *++cp = '\0'; + } + + /* + * Explicit header declarations + */ + + if (sscanf(buf, "screen width %d\n", &GifFileOut->SWidth) == 1) + continue; + + else if (sscanf(buf, "screen height %d\n", &GifFileOut->SHeight) == 1) + continue; + + else if (sscanf(buf, "screen colors %d\n", &n) == 1) + { + int ResBits = GifBitSize(n); + + if (n > 256 || n < 0 || n != (1 << ResBits)) + { + PARSE_ERROR("Invalid color resolution value."); + exit(EXIT_FAILURE); + } + + GifFileOut->SColorResolution = ResBits; + continue; + } + + else if (sscanf(buf, + "screen background %d\n", + &GifFileOut->SBackGroundColor) == 1) + continue; + + else if (sscanf(buf, "pixel aspect byte %u\n", &intval) == 1) { + GifFileOut->AspectByte = (GifByteType)(intval & 0xff); + continue; + } + + /* + * Color table parsing + */ + + else if (strcmp(buf, "screen map\n") == 0) + { + if (GifFileOut->SColorMap != NULL) + { + PARSE_ERROR("You've already declared a global color map."); + exit(EXIT_FAILURE); + } + + ColorMapSize = 0; + ColorMap = GlobalColorMap; + SortFlag = false; + KeyTable = GlobalColorKeys; + memset(GlobalColorKeys, '\0', sizeof(GlobalColorKeys)); + } + + else if (strcmp(buf, "image map\n") == 0) + { + if (NewImage == NULL) + { + PARSE_ERROR("No previous image declaration."); + exit(EXIT_FAILURE); + } + + ColorMapSize = 0; + ColorMap = LocalColorMap; + KeyTable = LocalColorKeys; + memset(LocalColorKeys, '\0', sizeof(LocalColorKeys)); + } + + else if (sscanf(buf, " rgb %d %d %d is %c", + &red, &green, &blue, &KeyTable[ColorMapSize]) == 4) + { + ColorMap[ColorMapSize].Red = red; + ColorMap[ColorMapSize].Green = green; + ColorMap[ColorMapSize].Blue = blue; + ColorMapSize++; + } + + else if (sscanf(buf, " rgb %d %d %d", &red, &green, &blue) == 3) + { + ColorMap[ColorMapSize].Red = red; + ColorMap[ColorMapSize].Green = green; + ColorMap[ColorMapSize].Blue = blue; + ColorMapSize++; + } + + else if (strcmp(buf, " sort flag on\n") == 0) + SortFlag = true; + + else if (strcmp(buf, " sort flag off\n") == 0) + SortFlag = false; + + else if (strcmp(buf, "end\n") == 0) + { + ColorMapObject *NewMap; + + NewMap = GifMakeMapObject(1 << GifBitSize(ColorMapSize), ColorMap); + if (NewMap == (ColorMapObject *)NULL) + { + PARSE_ERROR("Out of memory while allocating new color map."); + exit(EXIT_FAILURE); + } + + NewMap->SortFlag = SortFlag; + + if (NewImage) + NewImage->ImageDesc.ColorMap = NewMap; + else + GifFileOut->SColorMap = NewMap; + } + + /* GIF inclusion */ + /* ugly magic number is because scanf has no */ + else if (sscanf(buf, "include %63s", InclusionFile) == 1) + { + int ErrorCode; + bool DoTranslation; + GifPixelType Translation[256]; + + GifFileType *Inclusion; + SavedImage *CopyFrom; + + if ((Inclusion = DGifOpenFileName(InclusionFile, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + if (DGifSlurp(Inclusion) == GIF_ERROR) + { + PARSE_ERROR("Inclusion read failed."); + if (Inclusion != NULL) { + PrintGifError(Inclusion->Error); + DGifCloseFile(Inclusion, NULL); + } + if (GifFileOut != NULL) { + EGifCloseFile(GifFileOut, NULL); + }; + exit(EXIT_FAILURE); + } + + //cppcheck-suppress nullPointerRedundantCheck + if ((DoTranslation = (GifFileOut->SColorMap!=(ColorMapObject*)NULL))) + { + ColorMapObject *UnionMap; + + //cppcheck-suppress nullPointerRedundantCheck + UnionMap = GifUnionColorMap(GifFileOut->SColorMap, Inclusion->SColorMap, Translation); + + if (UnionMap == NULL) + { + PARSE_ERROR("Inclusion failed --- global map conflict."); + //cppcheck-suppress nullPointerRedundantCheck + PrintGifError(GifFileOut->Error); + if (Inclusion != NULL) DGifCloseFile(Inclusion, NULL); + if (GifFileOut != NULL) EGifCloseFile(GifFileOut, NULL); + exit(EXIT_FAILURE); + } + + GifFreeMapObject(GifFileOut->SColorMap); + GifFileOut->SColorMap = UnionMap; + } + + //cppcheck-suppress nullPointerRedundantCheck + for (CopyFrom = Inclusion->SavedImages; + //cppcheck-suppress nullPointerRedundantCheck + CopyFrom < Inclusion->SavedImages + Inclusion->ImageCount; + CopyFrom++) + { + SavedImage *NewImage; + if ((NewImage = GifMakeSavedImage(GifFileOut, CopyFrom)) == NULL) + { + PARSE_ERROR("Inclusion failed --- out of memory."); + //cppcheck-suppress nullPointerRedundantCheck + PrintGifError(GifFileOut->Error); + if (Inclusion != NULL) DGifCloseFile(Inclusion, NULL); + if (GifFileOut != NULL) EGifCloseFile(GifFileOut, NULL); + exit(EXIT_FAILURE); + } + else if (DoTranslation) + GifApplyTranslation(NewImage, Translation); + + GifQprintf( + "%s: Image %d at (%d, %d) [%dx%d]: from %s\n", + PROGRAM_NAME, GifFileOut->ImageCount, + NewImage->ImageDesc.Left, NewImage->ImageDesc.Top, + NewImage->ImageDesc.Width, NewImage->ImageDesc.Height, + InclusionFile); + } + + (void) DGifCloseFile(Inclusion, NULL); + } + + /* + * Extension blocks. + */ + else if (strcmp(buf, "comment\n") == 0) + { + int bc = 0; + while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) + if (strcmp(buf, "end\n") == 0) + break; + else + { + int Len; + + buf[strlen(buf) - 1] = '\0'; + Len = EscapeString(buf, buf); + if (GifAddExtensionBlock(&LeadingExtensionBlockCount, + &LeadingExtensionBlocks, + bc++ == CONTINUE_EXT_FUNC_CODE ? COMMENT_EXT_FUNC_CODE : 0, + Len, + (unsigned char *)buf) == GIF_ERROR) { + PARSE_ERROR("out of memory while adding comment block."); + exit(EXIT_FAILURE); + } + } + } + else if (strcmp(buf, "plaintext\n") == 0) + { + int bc = 0; + while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) + if (strcmp(buf, "end\n") == 0) + break; + else + { + int Len; + + buf[strlen(buf) - 1] = '\0'; + Len = EscapeString(buf, buf); + if (GifAddExtensionBlock(&LeadingExtensionBlockCount, + &LeadingExtensionBlocks, + bc++ == CONTINUE_EXT_FUNC_CODE ? PLAINTEXT_EXT_FUNC_CODE : 0, + Len, + (unsigned char *)buf) == GIF_ERROR) { + PARSE_ERROR("out of memory while adding plaintext block."); + exit(EXIT_FAILURE); + } + } + } + else if (strcmp(buf, "graphics control\n") == 0) + { + GraphicsControlBlock gcb; + size_t Len; + + memset(&gcb, '\0', sizeof(gcb)); + gcb.TransparentColor = NO_TRANSPARENT_COLOR; + while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) + if (strcmp(buf, "end\n") == 0) + break; + else + { + char *tp = buf; + + while (isspace(*tp)) + tp++; + if (sscanf(tp, "disposal mode %d\n", &gcb.DisposalMode)) + continue; + if (strcmp(tp, "user input flag on\n") == 0) { + gcb.UserInputFlag = true; + continue; + } + if (strcmp(tp, "user input flag off\n") == 0) { + gcb.UserInputFlag = false; + continue; + } + if (sscanf(tp, "delay %d\n", &gcb.DelayTime)) + continue; + if (sscanf(tp, "transparent index %d\n", + &gcb.TransparentColor)) + continue; + (void) fputs(tp, stderr); + PARSE_ERROR("unrecognized directive in GCB block."); + exit(EXIT_FAILURE); + } + Len = EGifGCBToExtension(&gcb, (GifByteType *)buf); + if (GifAddExtensionBlock(&LeadingExtensionBlockCount, + &LeadingExtensionBlocks, + GRAPHICS_EXT_FUNC_CODE, + Len, + (unsigned char *)buf) == GIF_ERROR) { + PARSE_ERROR("out of memory while adding GCB."); + exit(EXIT_FAILURE); + } + + } + else if (sscanf(buf, "netscape loop %u", &intval)) + { + unsigned char params[3] = {1, 0, 0}; + /* Create a Netscape 2.0 loop block */ + if (GifAddExtensionBlock(&LeadingExtensionBlockCount, + &LeadingExtensionBlocks, + APPLICATION_EXT_FUNC_CODE, + 11, + (unsigned char *)"NETSCAPE2.0")==GIF_ERROR) { + PARSE_ERROR("out of memory while adding loop block."); + exit(EXIT_FAILURE); + } + params[1] = (intval & 0xff); + params[2] = (intval >> 8) & 0xff; + if (GifAddExtensionBlock(&LeadingExtensionBlockCount, + &LeadingExtensionBlocks, + 0, sizeof(params), params) == GIF_ERROR) { + PARSE_ERROR("out of memory while adding loop continuation."); + exit(EXIT_FAILURE); + } + + } + else if (sscanf(buf, "extension %x", &ExtCode)) + { + int bc = 0; + while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) + if (strcmp(buf, "end\n") == 0) + break; + else + { + int Len; + + buf[strlen(buf) - 1] = '\0'; + Len = EscapeString(buf, buf); + if (GifAddExtensionBlock(&LeadingExtensionBlockCount, + &LeadingExtensionBlocks, + bc++ == CONTINUE_EXT_FUNC_CODE ? ExtCode : 0, + Len, + (unsigned char *)buf) == GIF_ERROR) { + PARSE_ERROR("out of memory while adding extension block."); + exit(EXIT_FAILURE); + } + } + } + + /* + * Explicit image declarations + */ + + else if (strcmp(buf, "image\n") == 0) + { + if ((NewImage = GifMakeSavedImage(GifFileOut, NULL)) == (SavedImage *)NULL) + { + PARSE_ERROR("Out of memory while allocating image block."); + exit(EXIT_FAILURE); + } + + /* use global table unless user specifies a local one */ + ColorMap = GlobalColorMap; + KeyTable = GlobalColorKeys; + + /* connect leading extension blocks */ + NewImage->ExtensionBlockCount = LeadingExtensionBlockCount; + NewImage->ExtensionBlocks = LeadingExtensionBlocks; + LeadingExtensionBlockCount = 0; + LeadingExtensionBlocks = NULL; + } + + /* + * Nothing past this point is valid unless we've seen a previous + * image declaration. + */ + else if (NewImage == (SavedImage *)NULL) + { + (void) fputs(buf, stderr); + PARSE_ERROR("Syntax error in header block."); + exit(EXIT_FAILURE); + } + + /* + * Accept image attributes + */ + else if (sscanf(buf, "image top %d\n", &NewImage->ImageDesc.Top) == 1) + continue; + + else if (sscanf(buf, "image left %d\n", &NewImage->ImageDesc.Left)== 1) + continue; + + else if (strcmp(buf, "image interlaced\n") == 0) + { + NewImage->ImageDesc.Interlace = true; + continue; + } + + else if (sscanf(buf, + "image bits %d by %d", + &NewImage->ImageDesc.Width, + &NewImage->ImageDesc.Height) == 2) + { + int i, j; + static GifPixelType *Raster, *cp; + int c; + bool hex = (strstr(buf, "hex") != NULL); + + /* coverity[overflow_sink] */ + if ((Raster = (GifPixelType *) malloc(sizeof(GifPixelType) * NewImage->ImageDesc.Width * NewImage->ImageDesc.Height)) + == NULL) { + PARSE_ERROR("Failed to allocate raster block, aborted."); + exit(EXIT_FAILURE); + } + + GifQprintf("%s: Image %d at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, GifFileOut->ImageCount, + NewImage->ImageDesc.Left, NewImage->ImageDesc.Top, + NewImage->ImageDesc.Width, NewImage->ImageDesc.Height); + + cp = Raster; + for (i = 0; i < NewImage->ImageDesc.Height; i++) { + + char *dp; + + for (j = 0; j < NewImage->ImageDesc.Width; j++) + if ((c = fgetc(txtin)) == EOF) { + PARSE_ERROR("input file ended prematurely."); + exit(EXIT_FAILURE); + } + else if (c == '\n') + { + --j; + ++LineNum; + } + else if (isspace(c)) + --j; + else if (hex) + { + const static char *hexdigits = "0123456789ABCDEF"; + unsigned char hi, lo; + dp = strchr(hexdigits, toupper(c)); + if (dp == NULL) { + PARSE_ERROR("Invalid hex high byte."); + exit(EXIT_FAILURE); + } + hi = (dp - hexdigits); + if ((c = fgetc(txtin)) == EOF) { + PARSE_ERROR("input file ended prematurely."); + exit(EXIT_FAILURE); + } + dp = strchr(hexdigits, toupper(c)); + if (dp == NULL) { + PARSE_ERROR("Invalid hex low byte."); + exit(EXIT_FAILURE); + } + lo = (dp - hexdigits); + *cp++ = (hi << 4) | lo; + } + else if ((dp = strchr(KeyTable, c))) + *cp++ = (dp - KeyTable); + else { + PARSE_ERROR("Invalid ASCII pixel key."); + exit(EXIT_FAILURE); + } + + if (GifNoisyPrint) + fprintf(stderr, "\b\b\b\b%-4d", i); + } + + if (GifNoisyPrint) + putc('\n', stderr); + + NewImage->RasterBits = (unsigned char *) Raster; + } + else + { + (void) fputs(buf, stderr); + PARSE_ERROR("Syntax error in image description."); + exit(EXIT_FAILURE); + } + } + + /* connect trailing extension blocks */ + GifFileOut->ExtensionBlockCount = LeadingExtensionBlockCount; + GifFileOut->ExtensionBlocks = LeadingExtensionBlocks; + //LeadingExtensionBlockCount = 0; + LeadingExtensionBlocks = NULL; + + EGifSpew(GifFileOut); +} + +static void VisibleDumpBuffer(GifByteType *buf, int len) +/* Visibilize a given string */ +{ + GifByteType *cp; + + for (cp = buf; cp < buf + len; cp++) + { + if (isprint((int)(*cp)) || *cp == ' ') + putchar(*cp); + else if (*cp == '\n') + { + putchar('\\'); putchar('n'); + } + else if (*cp == '\r') + { + putchar('\\'); putchar('r'); + } + else if (*cp == '\b') + { + putchar('\\'); putchar('b'); + } + else if (*cp < ' ') + { + putchar('\\'); putchar('^'); putchar('@' + *cp); + } + else + printf("\\0x%02x", *cp); + } +} + +static void DumpExtensions(GifFileType *GifFileOut, + int ExtensionBlockCount, + ExtensionBlock *ExtensionBlocks) +{ + ExtensionBlock *ep; + + for (ep = ExtensionBlocks; + ep < ExtensionBlocks + ExtensionBlockCount; + ep++) { + bool last = (ep - ExtensionBlocks == (ExtensionBlockCount - 1)); + if (ep->Function == COMMENT_EXT_FUNC_CODE) { + printf("comment\n"); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + putchar('\n'); + while (!last && ep[1].Function == CONTINUE_EXT_FUNC_CODE) { + ++ep; + last = (ep - ExtensionBlocks == (ExtensionBlockCount - 1)); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + putchar('\n'); + } + printf("end\n\n"); + } + else if (ep->Function == PLAINTEXT_EXT_FUNC_CODE) { + printf("plaintext\n"); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + putchar('\n'); + while (!last && ep[1].Function == CONTINUE_EXT_FUNC_CODE) { + ++ep; + last = (ep - ExtensionBlocks == (ExtensionBlockCount - 1)); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + putchar('\n'); + } + printf("end\n\n"); + } + else if (ep->Function == GRAPHICS_EXT_FUNC_CODE) + { + GraphicsControlBlock gcb; + printf("graphics control\n"); + if (DGifExtensionToGCB(ep->ByteCount, ep->Bytes, &gcb) == GIF_ERROR) { + GIF_MESSAGE("invalid graphics control block"); + exit(EXIT_FAILURE); + } + printf("\tdisposal mode %d\n", gcb.DisposalMode); + printf("\tuser input flag %s\n", + gcb.UserInputFlag ? "on" : "off"); + printf("\tdelay %d\n", gcb.DelayTime); + printf("\ttransparent index %d\n", gcb.TransparentColor); + printf("end\n\n"); + } + else if (!last + && ep->Function == APPLICATION_EXT_FUNC_CODE + && ep->ByteCount >= 11 + && (ep+1)->ByteCount >= 3 + && memcmp(ep->Bytes, "NETSCAPE2.0", 11) == 0) { + unsigned char *params = (++ep)->Bytes; + unsigned int loopcount = params[1] | (params[2] << 8); + printf("netscape loop %u\n\n", loopcount); + } + else { + printf("extension 0x%02x\n", ep->Function); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + while (!last && ep[1].Function == CONTINUE_EXT_FUNC_CODE) { + ++ep; + last = (ep - ExtensionBlocks == (ExtensionBlockCount - 1)); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + putchar('\n'); + } + printf("end\n\n"); + } + } +} + +static void Gif2Icon(char *FileName, + int fdin, int fdout, + char NameTable[]) +{ + int ErrorCode, im, i, j, ColorCount = 0; + GifFileType *GifFile; + + if (fdin == -1) { + if ((GifFile = DGifOpenFileName(FileName, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + else { + /* Use stdin instead: */ + if ((GifFile = DGifOpenFileHandle(fdin, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + + if (DGifSlurp(GifFile) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + + printf("screen width %d\nscreen height %d\n", + GifFile->SWidth, GifFile->SHeight); + + printf("screen colors %d\nscreen background %d\npixel aspect byte %u\n\n", + 1 << GifFile->SColorResolution, + GifFile->SBackGroundColor, + (unsigned)GifFile->AspectByte); + + if (GifFile->SColorMap) + { + printf("screen map\n"); + + printf("\tsort flag %s\n", GifFile->SColorMap->SortFlag ? "on" : "off"); + + for (i = 0; i < GifFile->SColorMap->ColorCount; i++) + if (GifFile->SColorMap->ColorCount < PRINTABLES) + printf("\trgb %03d %03d %03d is %c\n", + GifFile->SColorMap ->Colors[i].Red, + GifFile->SColorMap ->Colors[i].Green, + GifFile->SColorMap ->Colors[i].Blue, + NameTable[i]); + else + printf("\trgb %03d %03d %03d\n", + GifFile->SColorMap ->Colors[i].Red, + GifFile->SColorMap ->Colors[i].Green, + GifFile->SColorMap ->Colors[i].Blue); + printf("end\n\n"); + } + + for (im = 0; im < GifFile->ImageCount; im++) { + SavedImage *image = &GifFile->SavedImages[im]; + + DumpExtensions(GifFile, + image->ExtensionBlockCount, image->ExtensionBlocks); + + printf("image # %d\nimage left %d\nimage top %d\n", + im+1, image->ImageDesc.Left, image->ImageDesc.Top); + if (image->ImageDesc.Interlace) + printf("image interlaced\n"); + + if (image->ImageDesc.ColorMap) + { + printf("image map\n"); + + printf("\tsort flag %s\n", + image->ImageDesc.ColorMap->SortFlag ? "on" : "off"); + + if (image->ImageDesc.ColorMap->ColorCount < PRINTABLES) + for (i = 0; i < image->ImageDesc.ColorMap->ColorCount; i++) + printf("\trgb %03d %03d %03d is %c\n", + image->ImageDesc.ColorMap ->Colors[i].Red, + image->ImageDesc.ColorMap ->Colors[i].Green, + image->ImageDesc.ColorMap ->Colors[i].Blue, + NameTable[i]); + else + for (i = 0; i < image->ImageDesc.ColorMap->ColorCount; i++) + printf("\trgb %03d %03d %03d\n", + image->ImageDesc.ColorMap ->Colors[i].Red, + image->ImageDesc.ColorMap ->Colors[i].Green, + image->ImageDesc.ColorMap ->Colors[i].Blue); + printf("end\n\n"); + } + + /* one of these conditions has to be true */ + if (image->ImageDesc.ColorMap) + ColorCount = image->ImageDesc.ColorMap->ColorCount; + else if (GifFile->SColorMap) + ColorCount = GifFile->SColorMap->ColorCount; + + if (ColorCount < PRINTABLES) + printf("image bits %d by %d\n", + image->ImageDesc.Width, image->ImageDesc.Height); + else + printf("image bits %d by %d hex\n", + image->ImageDesc.Width, image->ImageDesc.Height); + for (i = 0; i < image->ImageDesc.Height; i++) { + for (j = 0; j < image->ImageDesc.Width; j++) { + GifByteType ch = image->RasterBits[i*image->ImageDesc.Width + j]; + if (ColorCount < PRINTABLES && ch < PRINTABLES) + putchar(NameTable[ch]); + else + printf("%02x", ch); + } + putchar('\n'); + } + putchar('\n'); + } + + DumpExtensions(GifFile, + GifFile->ExtensionBlockCount, GifFile->ExtensionBlocks); + + /* Tell EMACS this is a picture... */ + printf("# The following sets edit modes for GNU EMACS\n"); + printf("# Local "); /* ...break this up, so that EMACS doesn't */ + printf("Variables:\n"); /* get confused when visiting *this* file! */ + printf("# mode:picture\n"); + printf("# truncate-lines:t\n"); + printf("# End:\n"); + + if (fdin == -1) + (void) printf("# End of %s dump\n", FileName); + + + /* + * Sanity checks. + */ + + /* check that the background color isn't garbage (SF bug #87) */ + if (GifFile->SBackGroundColor < 0 + || (GifFile->SColorMap && GifFile->SBackGroundColor >= GifFile->SColorMap->ColorCount)) { + fprintf(stderr, "gifbuild: background color invalid for screen colormap.\n"); + } + + if (DGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } +} + +static int EscapeString(char *cp, char *tp) +/* process standard C-style escape sequences in a string */ +{ + char *StartAddr = tp; + + while (*cp) + { + int cval = 0; + + if (*cp == '\\' && strchr("0123456789xX", cp[1])) + { + int dcount = 0; + + if (*++cp == 'x' || *cp == 'X') { + char *dp, *hex = "00112233445566778899aAbBcCdDeEfF"; + for (++cp; (dp = strchr(hex, *cp)) && (dcount++ < 2); cp++) + cval = (cval * 16) + (dp - hex) / 2; + } else if (*cp == '0') + while (strchr("01234567",*cp) != (char*)NULL && (dcount++ < 3)) + cval = (cval * 8) + (*cp++ - '0'); + else + while ((strchr("0123456789",*cp)!=(char*)NULL)&&(dcount++ < 3)) + cval = (cval * 10) + (*cp++ - '0'); + } + else if (*cp == '\\') /* C-style character escapes */ + { + switch (*++cp) + { + case '\\': cval = '\\'; break; + case 'n': cval = '\n'; break; + case 't': cval = '\t'; break; + case 'b': cval = '\b'; break; + case 'r': cval = '\r'; break; + default: cval = *cp; + } + cp++; + } + else if (*cp == '^') /* expand control-character syntax */ + { + cval = (*++cp & 0x1f); + cp++; + } + else + cval = *cp++; + *tp++ = cval; + } + + return(tp - StartAddr); +} + +/* end */ + diff --git a/third_party/giflib/giflib/gifclrmp.c b/third_party/giflib/giflib/gifclrmp.c new file mode 100644 index 00000000..ffab0c94 --- /dev/null +++ b/third_party/giflib/giflib/gifclrmp.c @@ -0,0 +1,361 @@ +/***************************************************************************** + +gifclrmap - extract colormaps from GIF images + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "gifclrmp" + +static char + *VersionStr = + PROGRAM_NAME + VERSION_COOKIE + " Gershon Elber, " + __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char + *CtrlStr = + PROGRAM_NAME + " v%- s%- t%-TranslationFile!s l%-ColorMapFile!s g%-Gamma!F i%-Image#!d h%- GifFile!*s"; + +static bool + SaveFlag = false, + TranslateFlag = false, + LoadFlag = false, + GammaFlag = false; +static + double Gamma = 1.0; +static + FILE *ColorFile = NULL; + FILE *TranslateFile = NULL; +static + GifPixelType Translation[256]; + +static ColorMapObject *ModifyColorMap(ColorMapObject *ColorMap); +static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut); + +/****************************************************************************** + Interpret the command line and scan the given GIF file. +******************************************************************************/ +int main(int argc, char **argv) +{ + int NumFiles, ExtCode, CodeSize, ImageNum = 0, + ImageN, HasGIFOutput, ErrorCode; + bool Error, ImageNFlag = false, HelpFlag = false; + GifRecordType RecordType; + GifByteType *Extension, *CodeBlock; + char **FileName = NULL, *ColorFileName, *TranslateFileName; + GifFileType *GifFileIn = NULL, *GifFileOut = NULL; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &SaveFlag, + &TranslateFlag, &TranslateFileName, + &LoadFlag, &ColorFileName, + &GammaFlag, &Gamma, &ImageNFlag, &ImageN, + &HelpFlag, &NumFiles, &FileName)) != false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) + GAPrintErrMsg(Error); + else if (NumFiles > 1) + GIF_MESSAGE("Error in command line parsing - one GIF file please."); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + if (SaveFlag + LoadFlag + GammaFlag + TranslateFlag > 1) + GIF_EXIT("Can not handle more than one of -s -l, -t, or -g at the same time."); + + /* Default action is to dump colormaps */ + if (!SaveFlag && !LoadFlag && !GammaFlag && !TranslateFlag) + SaveFlag = true; + + if (NumFiles == 1) { + if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + else { + /* Use stdin instead: */ + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + + if (SaveFlag) { + /* We are dumping out the color map as text file to stdout: */ + ColorFile = stdout; + } + else { + if (TranslateFlag) { + /* We are loading new color map from specified file: */ + if ((TranslateFile = fopen(TranslateFileName, "rt")) == NULL) + GIF_EXIT("Failed to open specified color translation file."); + } + + if (LoadFlag) { + /* We are loading new color map from specified file: */ + if ((ColorFile = fopen(ColorFileName, "rt")) == NULL) + GIF_EXIT("Failed to open specified color map file."); + } + } + + if ((HasGIFOutput = (LoadFlag || TranslateFlag || GammaFlag)) != 0) { + /* Open stdout for GIF output file: */ + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + + if (!ImageNFlag) { + /* We are supposed to modify the screen color map, so do it: */ + if (!GifFileIn->SColorMap) + GIF_EXIT("No colormap to modify"); + GifFileIn->SColorMap = ModifyColorMap(GifFileIn->SColorMap); + if (!HasGIFOutput) { + /* We can quit here, as we have the color map: */ + DGifCloseFile(GifFileIn, NULL); + fclose(ColorFile); + exit(EXIT_SUCCESS); + } + } + /* And dump out its new possible repositioned screen information: */ + if (HasGIFOutput) + if (EGifPutScreenDesc(GifFileOut, + GifFileIn->SWidth, GifFileIn->SHeight, + GifFileIn->SColorResolution, GifFileIn->SBackGroundColor, + GifFileIn->SColorMap) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + /* Scan the content of the GIF file and load the image(s) in: */ + do { + if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if ((++ImageNum == ImageN) && ImageNFlag) { + /* We are suppose to modify this image color map, do it: */ + GifFileIn->SColorMap =ModifyColorMap(GifFileIn->SColorMap); + if (!HasGIFOutput) { + /* We can quit here, as we have the color map: */ + DGifCloseFile(GifFileIn, NULL); + fclose(ColorFile); + exit(EXIT_SUCCESS); + } + } + if (HasGIFOutput) + if (EGifPutImageDesc(GifFileOut, + GifFileIn->Image.Left, GifFileIn->Image.Top, + GifFileIn->Image.Width, GifFileIn->Image.Height, + GifFileIn->Image.Interlace, + GifFileIn->Image.ColorMap) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + if (!TranslateFlag || (ImageNFlag && (ImageN != ImageNum))) + { + /* Now read image itself in decoded form as we don't */ + /* really care what we have there, and this is much */ + /* faster. */ + if (DGifGetCode(GifFileIn, &CodeSize, &CodeBlock) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if (HasGIFOutput) + if (EGifPutCode(GifFileOut, CodeSize, CodeBlock) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + while (CodeBlock != NULL) { + if (DGifGetCodeNext(GifFileIn, &CodeBlock) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if (HasGIFOutput) + if (EGifPutCodeNext(GifFileOut, CodeBlock) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + } + } + else /* we need to mung pixels intices */ + { + int i; + register GifPixelType *cp; + + GifPixelType *Line + = (GifPixelType *) malloc(GifFileIn->Image.Width * + sizeof(GifPixelType)); + for (i = 0; i < GifFileIn->Image.Height; i++) { + if (DGifGetLine(GifFileIn, Line,GifFileIn->Image.Width) + == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + + /* translation step goes here */ + for (cp = Line; cp < Line+GifFileIn->Image.Width; cp++) + *cp = Translation[*cp]; + + if (EGifPutLine(GifFileOut, + Line, GifFileIn->Image.Width) + == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + } + free((char *) Line); + } + break; + case EXTENSION_RECORD_TYPE: + assert(GifFileOut != NULL); /* might pacify Coverity */ + /* pass through extension records */ + if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if (Extension == NULL) + break; + if (EGifPutExtensionLeader(GifFileOut, ExtCode) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if (EGifPutExtensionBlock(GifFileOut, + Extension[0], + Extension + 1) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + while (Extension != NULL) { + if (DGifGetExtensionNext(GifFileIn, &Extension)==GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if (Extension != NULL) + if (EGifPutExtensionBlock(GifFileOut, + Extension[0], + Extension + 1) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + } + if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType. */ + break; + } + } + while (RecordType != TERMINATE_RECORD_TYPE); + + if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) + { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + if (HasGIFOutput) + if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) + { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + return 0; +} + +/****************************************************************************** + Modify the given colormap according to global variables setting. +******************************************************************************/ +static ColorMapObject *ModifyColorMap(ColorMapObject *ColorMap) +{ + int i, Dummy, Red, Green, Blue; + + if (SaveFlag) { + /* Save this color map to ColorFile: */ + for (i = 0; i < ColorMap->ColorCount; i++) + fprintf(ColorFile, "%3d %3d %3d %3d\n", i, + ColorMap->Colors[i].Red, + ColorMap->Colors[i].Green, + ColorMap->Colors[i].Blue); + return(ColorMap); + } + else if (LoadFlag) { + /* Read the color map in ColorFile into this color map: */ + for (i = 0; i < ColorMap->ColorCount; i++) { + if (feof(ColorFile)) + GIF_EXIT("Color file to load color map from, too small."); + if (fscanf(ColorFile, "%3d %3d %3d %3d\n", &Dummy, &Red, &Green, &Blue) == 4) { + ColorMap->Colors[i].Red = Red; + ColorMap->Colors[i].Green = Green; + ColorMap->Colors[i].Blue = Blue; + } + } + return(ColorMap); + } + else if (GammaFlag) { + /* Apply gamma correction to this color map: */ + double Gamma1 = 1.0 / Gamma; + for (i = 0; i < ColorMap->ColorCount; i++) { + ColorMap->Colors[i].Red = + ((int) (255 * pow(ColorMap->Colors[i].Red / 255.0, Gamma1))); + ColorMap->Colors[i].Green = + ((int) (255 * pow(ColorMap->Colors[i].Green / 255.0, Gamma1))); + ColorMap->Colors[i].Blue = + ((int) (255 * pow(ColorMap->Colors[i].Blue / 255.0, Gamma1))); + } + return(ColorMap); + } + else if (TranslateFlag) { + ColorMapObject *NewMap; + int Max = 0; + + /* Read the translation table in TranslateFile: */ + for (i = 0; i < ColorMap->ColorCount; i++) { + int tmp; + if (feof(TranslateFile)) + GIF_EXIT("Color file to load color map from, too small."); + if (fscanf(TranslateFile, "%3d %3d\n", &Dummy, &tmp) == 2) { + Translation[i] = tmp & 0xff; + if (Translation[i] > Max) + Max = Translation[i]; + } + } + + if ((NewMap = GifMakeMapObject(1 << GifBitSize(Max+1), NULL)) == NULL) + GIF_EXIT("Out of memory while allocating color map!"); + + /* Apply the translation; we'll do it to the pixels, too */ + for (i = 0; i < ColorMap->ColorCount; i++) { + NewMap->Colors[i] = ColorMap->Colors[Translation[i]]; + } + + return(NewMap); + } + else + { + GIF_EXIT("Nothing to do!"); + return(ColorMap); + } +} + +/****************************************************************************** + Close both input and output file (if open), and exit. +******************************************************************************/ +static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) +{ + if (GifFileIn != NULL) { + PrintGifError(GifFileIn->Error); + EGifCloseFile(GifFileIn, NULL); + } + if (GifFileOut != NULL) { + PrintGifError(GifFileOut->Error); + EGifCloseFile(GifFileOut, NULL); + } + exit(EXIT_FAILURE); +} + +/* end */ diff --git a/third_party/giflib/giflib/gifcolor.c b/third_party/giflib/giflib/gifcolor.c new file mode 100644 index 00000000..a814d4d9 --- /dev/null +++ b/third_party/giflib/giflib/gifcolor.c @@ -0,0 +1,170 @@ +/***************************************************************************** + +gifcolor - generate color test-pattern GIFs + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "gifcolor" + +#define LINE_LEN 40 +#define IMAGEWIDTH LINE_LEN*GIF_FONT_WIDTH + +static char + *VersionStr = + PROGRAM_NAME + VERSION_COOKIE + " Gershon Elber, " + __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char + *CtrlStr = PROGRAM_NAME " v%- b%-Background!d h%-"; + +static int BackGround = 0; +static void QuitGifError(GifFileType *GifFile); +static void GenRasterTextLine(GifRowType *RasterBuffer, char *TextLine, + int BufferWidth, int ForeGroundIndex); + +/****************************************************************************** + Interpret the command line and generate the given GIF file. +******************************************************************************/ +int main(int argc, char **argv) +{ + int i, j, l, GifNoisyPrint, ColorMapSize, ErrorCode; + bool Error, BackGroundFlag = false, HelpFlag = false; + char Line[LINE_LEN]; + GifRowType RasterBuffer[GIF_FONT_HEIGHT]; + ColorMapObject *ColorMap; + GifFileType *GifFile; + GifColorType ScratchMap[256]; + int red, green, blue; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, + &GifNoisyPrint, + &BackGroundFlag, &BackGround, + &HelpFlag)) != false) { + GAPrintErrMsg(Error); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + /* Allocate the raster buffer for GIF_FONT_HEIGHT scan lines. */ + for (i = 0; i < GIF_FONT_HEIGHT; i++) + { + if ((RasterBuffer[i] = (GifRowType) malloc(sizeof(GifPixelType) * + IMAGEWIDTH)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + } + + /* Open stdout for the output file: */ + if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* Read the color map in ColorFile into this color map: */ + ColorMapSize = 0; + while (fscanf(stdin, + "%*3d %3d %3d %3d\n", + &red, &green, &blue) == 3) { + if (ColorMapSize < 256) { + ScratchMap[ColorMapSize].Red = red; + ScratchMap[ColorMapSize].Green = green; + ScratchMap[ColorMapSize].Blue = blue; + ColorMapSize++; + } else { + GIF_EXIT("Too many color map triples, aborting."); + } + } + + if ((ColorMap = GifMakeMapObject(1 << GifBitSize(ColorMapSize), ScratchMap)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + if (EGifPutScreenDesc(GifFile, + IMAGEWIDTH, ColorMapSize * GIF_FONT_HEIGHT, + GifBitSize(ColorMapSize), + BackGround, ColorMap) == GIF_ERROR) + QuitGifError(GifFile); + + /* Dump out the image descriptor: */ + if (EGifPutImageDesc(GifFile, + 0, 0, IMAGEWIDTH, ColorMapSize * GIF_FONT_HEIGHT, false, NULL) == GIF_ERROR) + QuitGifError(GifFile); + + GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top, + GifFile->Image.Width, GifFile->Image.Height); + + for (i = l = 0; i < ColorMap->ColorCount; i++) { + (void)snprintf(Line, sizeof(Line), + "Color %-3d: [%-3d, %-3d, %-3d] ", i, + ColorMap->Colors[i].Red, + ColorMap->Colors[i].Green, + ColorMap->Colors[i].Blue); + GenRasterTextLine(RasterBuffer, Line, IMAGEWIDTH, i); + for (j = 0; j < GIF_FONT_HEIGHT; j++) { + if (EGifPutLine(GifFile, RasterBuffer[j], IMAGEWIDTH) == GIF_ERROR) + QuitGifError(GifFile); + GifQprintf("\b\b\b\b%-4d", l++); + } + } + + if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) + { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + return 0; +} + +/****************************************************************************** + Close output file (if open), and exit. +******************************************************************************/ +static void GenRasterTextLine(GifRowType *RasterBuffer, char *TextLine, + int BufferWidth, int ForeGroundIndex) +{ + unsigned char Byte, Mask; + int i, j, k, CharPosX, Len = strlen(TextLine); + + for (i = 0; i < BufferWidth; i++) + for (j = 0; j < GIF_FONT_HEIGHT; j++) RasterBuffer[j][i] = BackGround; + + for (i = CharPosX = 0; i < Len; i++, CharPosX += GIF_FONT_WIDTH) { + unsigned char c = TextLine[i]; + for (j = 0; j < GIF_FONT_HEIGHT; j++) { + Byte = GifAsciiTable8x8[(unsigned short)c][j]; + for (k = 0, Mask = 128; k < GIF_FONT_WIDTH; k++, Mask >>= 1) + if (Byte & Mask) + RasterBuffer[j][CharPosX + k] = ForeGroundIndex; + } + } +} + +/****************************************************************************** + Close output file (if open), and exit. +******************************************************************************/ +static void QuitGifError(GifFileType *GifFile) +{ + if (GifFile != NULL) { + PrintGifError(GifFile->Error); + EGifCloseFile(GifFile, NULL); + } + exit(EXIT_FAILURE); +} diff --git a/third_party/giflib/giflib/gifecho.c b/third_party/giflib/giflib/gifecho.c new file mode 100644 index 00000000..f0518a94 --- /dev/null +++ b/third_party/giflib/giflib/gifecho.c @@ -0,0 +1,209 @@ +/***************************************************************************** + +gifecho - generate a GIF from ASCII text + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "gifecho" + +#define MAX_NUM_TEXT_LINES 100 /* Maximum number of lines in file. */ + +#define LINE_LEN 256 /* Maximum length of one text line. */ + +#define DEFAULT_FG_INDEX 1 /* Text foreground index. */ + +#define DEFAULT_COLOR_RED 255 /* Text foreground color. */ +#define DEFAULT_COLOR_GREEN 255 +#define DEFAULT_COLOR_BLUE 255 + +static char + *VersionStr = + PROGRAM_NAME + VERSION_COOKIE + " Gershon Elber, " + __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char + *CtrlStr = + PROGRAM_NAME + " v%- s%-ClrMapSize!d f%-FGClr!d c%-R|G|B!d!d!d t%-\"Text\"!s h%-"; + +static unsigned int + RedColor = DEFAULT_COLOR_RED, + GreenColor = DEFAULT_COLOR_GREEN, + BlueColor = DEFAULT_COLOR_BLUE; + +static void QuitGifError(GifFileType *GifFile); +static void GenRasterTextLine(GifRowType *RasterBuffer, char *TextLine, + int BufferWidth, int ForeGroundIndex); + +/****************************************************************************** + Interpret the command line and generate the given GIF file. +******************************************************************************/ +int main(int argc, char **argv) +{ + int i, j, l, ImageWidth, ImageHeight, NumOfLines, LogNumLevels, + ErrorCode, NumLevels, ColorMapSize = 1, + ForeGroundIndex = DEFAULT_FG_INDEX; + bool Error, ClrMapSizeFlag = false, ForeGroundFlag = false, + TextLineFlag = false, HelpFlag = false, ColorFlag = false; + char *TextLines[MAX_NUM_TEXT_LINES]; + GifRowType RasterBuffer[GIF_FONT_HEIGHT]; + ColorMapObject *ColorMap; + GifFileType *GifFile; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, + &GifNoisyPrint, &ClrMapSizeFlag, &ColorMapSize, + &ForeGroundFlag, &ForeGroundIndex, + &ColorFlag, &RedColor, &GreenColor, &BlueColor, + &TextLineFlag, &TextLines[0], + &HelpFlag)) != false) { + GAPrintErrMsg(Error); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + if (ForeGroundIndex > 255 || ForeGroundIndex < 1) + GIF_EXIT("Foregound (-f) should be in the range 1..255, aborted."); + + if (ColorMapSize > 8 || ColorMapSize < 1) + GIF_EXIT("ColorMapSize (-s) should be in the range 1..8, aborted."); + + if (TextLineFlag) { + NumOfLines = 1; + ImageHeight = GIF_FONT_HEIGHT; + ImageWidth = GIF_FONT_WIDTH * strlen(TextLines[0]); + } + else { + char Line[LINE_LEN]; + NumOfLines = l = 0; + while (fgets(Line, LINE_LEN - 1, stdin)) { + for (i = strlen(Line); i > 0 && Line[i-1] <= ' '; i--); + Line[i] = 0; + if (l < i) l = i; + TextLines[NumOfLines++] = strdup(Line); + if (NumOfLines == MAX_NUM_TEXT_LINES) + GIF_EXIT("Input file has too many lines, aborted."); + } + if (NumOfLines == 0) + GIF_EXIT("No input text, aborted."); + ImageHeight = GIF_FONT_HEIGHT * NumOfLines; + ImageWidth = GIF_FONT_WIDTH * l; + } + + /* Allocate the raster buffer for GIF_FONT_HEIGHT scan lines (one text line). */ + for (i = 0; i < GIF_FONT_HEIGHT; i++) + if ((RasterBuffer[i] = (GifRowType) malloc(sizeof(GifPixelType) * + ImageWidth)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + /* Open stdout for the output file: */ + if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* Dump out screen description with given size and generated color map: */ + for (LogNumLevels = 1, NumLevels = 2; + NumLevels < ForeGroundIndex; + LogNumLevels++, NumLevels <<= 1); + if (NumLevels < (1 << ColorMapSize)) { + NumLevels = (1 << ColorMapSize); + LogNumLevels = ColorMapSize; + } + + if ((ColorMap = GifMakeMapObject(NumLevels, NULL)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + for (i = 0; i < NumLevels; i++) + ColorMap->Colors[i].Red = ColorMap->Colors[i].Green = ColorMap->Colors[i].Blue = 0; + ColorMap->Colors[ForeGroundIndex].Red = RedColor; + ColorMap->Colors[ForeGroundIndex].Green = GreenColor; + ColorMap->Colors[ForeGroundIndex].Blue = BlueColor; + + if (EGifPutScreenDesc(GifFile, + ImageWidth, ImageHeight, LogNumLevels, 0, ColorMap) + == GIF_ERROR) + QuitGifError(GifFile); + + /* Dump out the image descriptor: */ + if (EGifPutImageDesc(GifFile, + 0, 0, ImageWidth, ImageHeight, false, NULL) == GIF_ERROR) + QuitGifError(GifFile); + + GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top, + GifFile->Image.Width, GifFile->Image.Height); + + for (i = l = 0; i < NumOfLines; i++) { + GenRasterTextLine(RasterBuffer, TextLines[i], ImageWidth, + ForeGroundIndex); + for (j = 0; j < GIF_FONT_HEIGHT; j++) { + if (EGifPutLine(GifFile, RasterBuffer[j], ImageWidth) == GIF_ERROR) + QuitGifError(GifFile); + GifQprintf("\b\b\b\b%-4d", l++); + } + } + + if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) + { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + return 0; +} + +/****************************************************************************** + Generate raster bits corresponding to given text +******************************************************************************/ +static void GenRasterTextLine(GifRowType *RasterBuffer, char *TextLine, + int BufferWidth, int ForeGroundIndex) +{ + unsigned char Byte, Mask; + int i, j, k, CharPosX, Len = strlen(TextLine); + + for (i = 0; i < BufferWidth; i++) + for (j = 0; j < GIF_FONT_HEIGHT; j++) RasterBuffer[j][i] = 0; + + for (i = CharPosX = 0; i < Len; i++, CharPosX += GIF_FONT_WIDTH) { + unsigned char c = TextLine[i]; + for (j = 0; j < GIF_FONT_HEIGHT; j++) { + Byte = GifAsciiTable8x8[(unsigned short)c][j]; + for (k = 0, Mask = 128; k < GIF_FONT_WIDTH; k++, Mask >>= 1) + if (Byte & Mask) + RasterBuffer[j][CharPosX + k] = ForeGroundIndex; + } + } +} + +/****************************************************************************** +* Close output file (if open), and exit. +******************************************************************************/ +static void QuitGifError(GifFileType *GifFile) +{ + if (GifFile != NULL) { + PrintGifError(GifFile->Error); + EGifCloseFile(GifFile, NULL); + } + exit(EXIT_FAILURE); +} + +/* end */ diff --git a/third_party/giflib/giflib/giffilter.c b/third_party/giflib/giflib/giffilter.c new file mode 100644 index 00000000..4764fc62 --- /dev/null +++ b/third_party/giflib/giflib/giffilter.c @@ -0,0 +1,154 @@ +/****************************************************************************** + +giffilter.c - skeleton file for generic GIF `filter' program + +Sequentially read GIF records from stdin, process them, send them out. +Most of the junk above `int main' isn't needed for the skeleton, but +is likely to be for what you'll do with it. + +If you compile this, it will turn into an expensive GIF copying routine; +stdin to stdout with no changes and minimal validation. Well, it's a +decent test of the low-level routines, anyway. + +Note: due to the vicissitudes of Lempel-Ziv compression, the output of this +copier may not be bitwise identical to its input. This can happen if you +copy an image from a much more (or much *less*) memory-limited system; your +compression may use more (or fewer) bits. The uncompressed rasters should, +however, be identical (you can check this with gifbuild -d). + +SPDX-License-Identifier: MIT + +******************************************************************************/ + +#include +#include +#include +#include + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "giffilter" + +/****************************************************************************** + Close both input and output file (if open), and exit. +******************************************************************************/ +static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) +{ + if (GifFileIn != NULL) { + PrintGifError(GifFileIn->Error); + EGifCloseFile(GifFileIn, NULL); + } + if (GifFileOut != NULL) { + PrintGifError(GifFileOut->Error); + EGifCloseFile(GifFileOut, NULL); + } + exit(EXIT_FAILURE); +} + +/****************************************************************************** + Main sequence +******************************************************************************/ +int main(int argc, char **argv) +{ + GifFileType *GifFileIn = NULL, *GifFileOut = NULL; + GifRecordType RecordType; + int CodeSize, ExtCode, ErrorCode; + GifByteType *CodeBlock, *Extension; + + /* + * Command-line processing goes here. + */ + + /* Use stdin as input (note this also read screen descriptor in: */ + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* Use the stdout as output: */ + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* And dump out its screen information: */ + if (EGifPutScreenDesc(GifFileOut, + GifFileIn->SWidth, GifFileIn->SHeight, + GifFileIn->SColorResolution, GifFileIn->SBackGroundColor, + GifFileIn->SColorMap) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + /* Scan the content of the input GIF file and load the image(s) in: */ + do { + if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + /* Put image descriptor to out file: */ + if (EGifPutImageDesc(GifFileOut, + GifFileIn->Image.Left, GifFileIn->Image.Top, + GifFileIn->Image.Width, GifFileIn->Image.Height, + GifFileIn->Image.Interlace, + GifFileIn->Image.ColorMap) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + /* Now read image itself in decoded form as we dont really */ + /* care what we have there, and this is much faster. */ + if (DGifGetCode(GifFileIn, &CodeSize, &CodeBlock) == GIF_ERROR || + EGifPutCode(GifFileOut, CodeSize, CodeBlock) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + while (CodeBlock != NULL) { + if (DGifGetCodeNext(GifFileIn, &CodeBlock) == GIF_ERROR || + EGifPutCodeNext(GifFileOut, CodeBlock) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + } + break; + case EXTENSION_RECORD_TYPE: + /* pass through extension records */ + if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == GIF_ERROR || Extension == NULL) + QuitGifError(GifFileIn, GifFileOut); + if (EGifPutExtensionLeader(GifFileOut, ExtCode) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if (EGifPutExtensionBlock(GifFileOut, + Extension[0], + Extension + 1) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + while (Extension != NULL) { + if (DGifGetExtensionNext(GifFileIn, &Extension)==GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if (Extension != NULL) + if (EGifPutExtensionBlock(GifFileOut, + Extension[0], + Extension + 1) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + } + if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } + while (RecordType != TERMINATE_RECORD_TYPE); + + if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) + { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) + { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + return 0; +} + +/* end */ diff --git a/third_party/giflib/giflib/giffix.c b/third_party/giflib/giflib/giffix.c new file mode 100644 index 00000000..8f18afe6 --- /dev/null +++ b/third_party/giflib/giflib/giffix.c @@ -0,0 +1,218 @@ +/***************************************************************************** + +giffix - attempt to fix a truncated GIF + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "giffix" + +static char + *VersionStr = + PROGRAM_NAME + VERSION_COOKIE + " Gershon Elber, " + __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char + *CtrlStr = + PROGRAM_NAME + " v%- h%- GifFile!*s"; + +static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut); + +/****************************************************************************** + Interpret the command line and scan the given GIF file. +******************************************************************************/ +int main(int argc, char **argv) +{ + int i, j, NumFiles, ExtCode, Row, Col, Width, Height, ErrorCode, + DarkestColor = 0, ColorIntens = 10000; + bool Error, HelpFlag = false; + GifRecordType RecordType; + GifByteType *Extension; + char **FileName = NULL; + GifRowType LineBuffer; + ColorMapObject *ColorMap; + GifFileType *GifFileIn = NULL, *GifFileOut = NULL; + int ImageNum = 0; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &HelpFlag, + &NumFiles, &FileName)) != false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) + GAPrintErrMsg(Error); + else if (NumFiles > 1) + GIF_MESSAGE("Error in command line parsing - one GIF file please."); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + if (NumFiles == 1) { + if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + else + { + /* Use stdin instead: */ + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + + /* Open stdout for the output file: */ + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* Dump out exactly same screen information: */ + /* coverity[var_deref_op] */ + if (EGifPutScreenDesc(GifFileOut, + GifFileIn->SWidth, GifFileIn->SHeight, + GifFileIn->SColorResolution, GifFileIn->SBackGroundColor, + GifFileIn->SColorMap) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + if ((LineBuffer = (GifRowType) malloc(GifFileIn->SWidth)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + /* Scan the content of the GIF file and load the image(s) in: */ + do { + if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if (GifFileIn->Image.Interlace) + GIF_EXIT("Cannot fix interlaced images."); + + Row = GifFileIn->Image.Top; /* Image Position relative to Screen. */ + Col = GifFileIn->Image.Left; + Width = GifFileIn->Image.Width; + Height = GifFileIn->Image.Height; + GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height); + if (Width > GifFileIn->SWidth) + GIF_EXIT("Image is wider than total"); + + /* Put the image descriptor to out file: */ + if (EGifPutImageDesc(GifFileOut, Col, Row, Width, Height, + false, GifFileIn->Image.ColorMap) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + /* Find the darkest color in color map to use as a filler. */ + ColorMap = (GifFileIn->Image.ColorMap ? GifFileIn->Image.ColorMap : + GifFileIn->SColorMap); + for (i = 0; i < ColorMap->ColorCount; i++) { + j = ((int) ColorMap->Colors[i].Red) * 30 + + ((int) ColorMap->Colors[i].Green) * 59 + + ((int) ColorMap->Colors[i].Blue) * 11; + if (j < ColorIntens) { + ColorIntens = j; + DarkestColor = i; + } + } + + /* Load the image, and dump it. */ + for (i = 0; i < Height; i++) { + GifQprintf("\b\b\b\b%-4d", i); + if (DGifGetLine(GifFileIn, LineBuffer, Width) + == GIF_ERROR) break; + if (EGifPutLine(GifFileOut, LineBuffer, Width) + == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); + } + + if (i < Height) { + fprintf(stderr,"\nFollowing error occurred (and ignored):"); + PrintGifError(GifFileIn->Error); + + /* Fill in with the darkest color in color map. */ + for (j = 0; j < Width; j++) + LineBuffer[j] = DarkestColor; + for (; i < Height; i++) + if (EGifPutLine(GifFileOut, LineBuffer, Width) + == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); + } + break; + case EXTENSION_RECORD_TYPE: + /* pass through extension records */ + if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if (EGifPutExtensionLeader(GifFileOut, ExtCode) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if (Extension != NULL) + if (EGifPutExtensionBlock(GifFileOut, + Extension[0], + Extension + 1) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + while (Extension != NULL) { + if (DGifGetExtensionNext(GifFileIn, &Extension)==GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + if (Extension != NULL) + if (EGifPutExtensionBlock(GifFileOut, + Extension[0], + Extension + 1) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + } + if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType. */ + break; + } + } + while (RecordType != TERMINATE_RECORD_TYPE); + + if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + return 0; +} + +/****************************************************************************** + Close both input and output file (if open), and exit. +******************************************************************************/ +static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) +{ + fprintf(stderr, "\nFollowing unrecoverable error occurred:"); + if (GifFileIn != NULL) { + PrintGifError(GifFileIn->Error); + EGifCloseFile(GifFileIn, NULL); + } + if (GifFileOut != NULL) { + PrintGifError(GifFileOut->Error); + EGifCloseFile(GifFileOut, NULL); + } + exit(EXIT_FAILURE); +} + +/* end */ diff --git a/third_party/giflib/giflib/gifhisto.c b/third_party/giflib/giflib/gifhisto.c new file mode 100644 index 00000000..77408948 --- /dev/null +++ b/third_party/giflib/giflib/gifhisto.c @@ -0,0 +1,259 @@ +/***************************************************************************** + +gifhisto - make a color histogram from image color frequencies + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "gifhisto" + +#define DEFAULT_HISTO_WIDTH 100 /* Histogram image diemnsions. */ +#define DEFAULT_HISTO_HEIGHT 256 +#define HISTO_BITS_PER_PIXEL 2 /* Size of bitmap for histogram GIF. */ + +static char + *VersionStr = + PROGRAM_NAME + VERSION_COOKIE + " Gershon Elber, " + __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char + *CtrlStr = + PROGRAM_NAME + " v%- t%- s%-Width|Height!d!d n%-ImageNumber!d b%- h%- GifFile!*s"; + +static int + ImageWidth = DEFAULT_HISTO_WIDTH, + ImageHeight = DEFAULT_HISTO_HEIGHT, + ImageN = 1; +static GifColorType + HistoColorMap[] = { /* Constant bit map for histograms: */ + { 0, 0, 0 }, + { 255, 0, 0 }, + { 0, 255, 0 }, + { 0, 0, 255 } + }; + +static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut); + +/****************************************************************************** + Interpret the command line and scan the given GIF file. +******************************************************************************/ +int main(int argc, char **argv) +{ + int i, j, ErrorCode, NumFiles, ExtCode, CodeSize, NumColors = 2, ImageNum = 0; + bool Error, TextFlag = false, SizeFlag = false, + ImageNFlag = false, BackGroundFlag = false, HelpFlag = false; + long Histogram[256]; + GifRecordType RecordType; + GifByteType *Extension, *CodeBlock; + char **FileName = NULL; + GifRowType Line; + GifFileType *GifFileIn = NULL, *GifFileOut = NULL; + + /* Same image dimension vars for both Image & ImageN as only one allowed */ + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, + &TextFlag, &SizeFlag, &ImageWidth, &ImageHeight, + &ImageNFlag, &ImageN, &BackGroundFlag, + &HelpFlag, &NumFiles, &FileName)) != false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) + GAPrintErrMsg(Error); + else if (NumFiles > 1) + GIF_MESSAGE("Error in command line parsing - one GIF file please."); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + if (NumFiles == 1) { + if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + else { + /* Use stdin instead: */ + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + + for (i = 0; i < 256; i++) Histogram[i] = 0; /* Reset counters. */ + + /* Scan the content of the GIF file and load the image(s) in: */ + do { + if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + if (GifFileIn->Image.ColorMap) + NumColors = GifFileIn->Image.ColorMap->ColorCount; + else if (GifFileIn->SColorMap) + NumColors = GifFileIn->SColorMap->ColorCount; + else + GIF_EXIT("Neither Screen nor Image color map exists."); + + if ((ImageHeight / NumColors) * NumColors != ImageHeight) + GIF_EXIT("Image height specified not dividable by #colors."); + + if (++ImageNum == ImageN) { + /* This is the image we should make histogram for: */ + Line = (GifRowType) malloc(GifFileIn->Image.Width * + sizeof(GifPixelType)); + GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, ImageNum, + GifFileIn->Image.Left, GifFileIn->Image.Top, + GifFileIn->Image.Width, GifFileIn->Image.Height); + + for (i = 0; i < GifFileIn->Image.Height; i++) { + if (DGifGetLine(GifFileIn, Line, GifFileIn->Image.Width) + == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + for (j = 0; j < GifFileIn->Image.Width; j++) + Histogram[Line[j]]++; + GifQprintf("\b\b\b\b%-4d", i); + } + + free((char *) Line); + } + else { + /* Skip the image: */ + /* Now read image itself in decoded form as we dont */ + /* really care what is there, and this is much faster. */ + if (DGifGetCode(GifFileIn, &CodeSize, &CodeBlock) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + while (CodeBlock != NULL) + if (DGifGetCodeNext(GifFileIn, &CodeBlock) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + } + break; + case EXTENSION_RECORD_TYPE: + /* Skip any extension blocks in file: */ + if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + while (Extension != NULL) { + if (DGifGetExtensionNext(GifFileIn, &Extension) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + } + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType. */ + break; + } + } + while (RecordType != TERMINATE_RECORD_TYPE); + + /* We requested suppression of the background count: */ + if (BackGroundFlag) Histogram[GifFileIn->SBackGroundColor] = 0; + + if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) + { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + + /* We may required to dump out the histogram as text file: */ + if (TextFlag) { + for (i = 0; i < NumColors; i++) + printf("%12ld %3d\n", Histogram[i], i); + } + else { + int Color, Count; + long Scaler; + /* Open stdout for the histogram output file: */ + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* Dump out screen descriptor to fit histogram dimensions: */ + if (EGifPutScreenDesc(GifFileOut, + ImageWidth, ImageHeight, HISTO_BITS_PER_PIXEL, 0, + GifMakeMapObject(4, HistoColorMap)) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + /* Dump out image descriptor to fit histogram dimensions: */ + if (EGifPutImageDesc(GifFileOut, + 0, 0, ImageWidth, ImageHeight, false, NULL) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + + /* Prepare scan line for histogram file, and find scaler to scale */ + /* histogram to be between 0 and ImageWidth: */ + Line = (GifRowType) malloc(ImageWidth * sizeof(GifPixelType)); + for (Scaler = 0, i = 0; i < NumColors; i++) if (Histogram[i] > Scaler) + Scaler = Histogram[i]; + Scaler /= ImageWidth; + if (Scaler == 0) Scaler = 1; /* In case maximum is less than width. */ + + /* Dump out the image itself: */ + for (Count = ImageHeight, i = 0, Color = 1; i < NumColors; i++) { + int Size; + if ((Size = Histogram[i] / Scaler) > ImageWidth) Size = ImageWidth; + for (j = 0; j < Size; j++) + Line[j] = Color; + for (j = Size; j < ImageWidth; j++) + Line[j] = GifFileOut->SBackGroundColor; + + /* Move to next color: */ + if (++Color >= (1 << HISTO_BITS_PER_PIXEL)) Color = 1; + + /* Dump this histogram entry as many times as required: */ + for (j = 0; j < ImageHeight / NumColors; j++) { + if (EGifPutLine(GifFileOut, Line, ImageWidth) == GIF_ERROR) + QuitGifError(GifFileIn, GifFileOut); + GifQprintf("\b\b\b\b%-4d", Count--); + } + } + + if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) + { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + + return 0; +} + +/****************************************************************************** + Close both input and output file (if open), and exit. +******************************************************************************/ +static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) +{ + if (GifFileIn != NULL) { + PrintGifError(GifFileIn->Error); + EGifCloseFile(GifFileIn, NULL); + } + if (GifFileOut != NULL) { + PrintGifError(GifFileOut->Error); + EGifCloseFile(GifFileOut, NULL); + } + exit(EXIT_FAILURE); +} + +/* end */ diff --git a/third_party/giflib/giflib/gifinto.c b/third_party/giflib/giflib/gifinto.c new file mode 100644 index 00000000..35faaf9e --- /dev/null +++ b/third_party/giflib/giflib/gifinto.c @@ -0,0 +1,191 @@ +/***************************************************************************** + +gifinto - save GIF on stdin to file if size over set threshold + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif /* _WIN32 */ + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "gifinto" + +#define STRLEN 512 + +#define DEFAULT_MIN_FILE_SIZE 14 /* More than GIF stamp + screen desc. */ +#define DEFAULT_OUT_NAME "GifInto.Gif" +#define DEFAULT_TMP_NAME "TempInto.XXXXXX" + +static char + *VersionStr = + PROGRAM_NAME + VERSION_COOKIE + " Gershon Elber, " + __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char + *CtrlStr = + PROGRAM_NAME + " v%- s%-MinFileSize!d h%- GifFile!*s"; + +static int + MinFileSize = DEFAULT_MIN_FILE_SIZE; + +#ifdef _WIN32 +#include +#include +int +mkstemp(char *tpl) +{ + int fd = -1; + char *p; + int e = errno; + + errno = 0; + p = _mktemp(tpl); + if (*p && errno == 0) + { + errno = e; + fd = _open(p, _O_RDWR | _O_CREAT | _O_EXCL | _O_BINARY, + _S_IREAD | _S_IWRITE); + } + return fd; +} +#endif + +/****************************************************************************** + This is simply: read until EOF, then close the output, test its length, and + if non zero then rename it. +******************************************************************************/ +int main(int argc, char **argv) +{ + int FD; + int NumFiles; + bool Error, MinSizeFlag = false, HelpFlag = false; + char **FileName = NULL, FoutTmpName[STRLEN+1], FullPath[STRLEN+1], *p; + FILE *Fin, *Fout; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, + &MinSizeFlag, &MinFileSize, &HelpFlag, + &NumFiles, &FileName)) != false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) + GAPrintErrMsg(Error); + else if (NumFiles != 1) + GIF_MESSAGE("Error in command line parsing - one GIF file please."); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + /* Open the stdin in binary mode and increase its buffer size: */ +#ifdef _WIN32 + _setmode(0, O_BINARY); /* Make sure it is in binary mode. */ +#endif + + Fin = fdopen(0, "rb"); /* Make it into a stream: */ + + if (Fin == NULL) + { + GIF_EXIT("Failed to open input."); + } + + /* Isolate the directory where our destination is, and set tmp file name */ + /* in the very same directory. This code is isecure because it creates */ + /* predictable names, but it's not worth the effort and risk to fix. */ + if ( *FileName == NULL ) GIF_EXIT("No valid Filename given."); + if ( strlen(*FileName) > STRLEN-1 ) GIF_EXIT("Filename too long."); + memset(FullPath, '\0', sizeof(FullPath)); + strncpy(FullPath, *FileName, STRLEN); + if ((p = strrchr(FullPath, '/')) != NULL || + (p = strrchr(FullPath, '\\')) != NULL) + p[1] = 0; + else if ((p = strrchr(FullPath, ':')) != NULL) + p[1] = 0; + else + FullPath[0] = 0; /* No directory or disk specified. */ + + if ( strlen(FullPath) > STRLEN-1 ) GIF_EXIT("Filename too long."); + strncpy(FoutTmpName, FullPath, STRLEN); /* First setup the Path */ + /* then add a name for the tempfile */ + if ( (strlen(FoutTmpName) + strlen(DEFAULT_TMP_NAME)) > STRLEN-1 ) GIF_EXIT("Filename too long."); + strcat(FoutTmpName, DEFAULT_TMP_NAME); +#ifdef _WIN32 + char *tmpFN = _mktemp(FoutTmpName); + if (tmpFN) + FD = open(tmpFN, O_CREAT | O_EXCL | O_WRONLY); + else + FD = -1; +#else + FD = mkstemp(FoutTmpName); /* returns filedescriptor */ +#endif + if (FD == -1 ) + { + GIF_EXIT("Failed to open output."); + } + Fout = fdopen(FD, "wb"); /* returns a stream with FD */ + if (Fout == NULL ) + { + GIF_EXIT("Failed to open output."); + } + + while (1) { + int c = getc(Fin); + + if (feof(Fin)) + break; + if (putc(c, Fout) == EOF) + GIF_EXIT("Failed to write output."); + } + + fclose(Fin); + if (ftell(Fout) >= (long) MinFileSize) { + fclose(Fout); + unlink(*FileName); + if (rename(FoutTmpName, *FileName) != 0) { + char DefaultName[STRLEN+1]; + memset(DefaultName, '\0', sizeof(DefaultName)); + if ( (strlen(FullPath) + strlen(DEFAULT_OUT_NAME)) > STRLEN-1 ) GIF_EXIT("Filename too long."); + strncpy(DefaultName, FullPath, STRLEN); + strcat(DefaultName, DEFAULT_OUT_NAME); + if (rename(FoutTmpName, DefaultName) == 0) { + char s[STRLEN]; + snprintf(s, STRLEN, "Failed to rename out file - left as %s.", + DefaultName); + GIF_MESSAGE(s); + } + else { + unlink(FoutTmpName); + GIF_MESSAGE("Failed to rename out file - deleted."); + } + } + } + else { + fclose(Fout); + unlink(FoutTmpName); + GIF_MESSAGE("File too small - not renamed."); + } + + return 0; +} + +/* end */ diff --git a/third_party/giflib/giflib/giflib.cbp b/third_party/giflib/giflib/giflib.cbp new file mode 100644 index 00000000..9c39f4e7 --- /dev/null +++ b/third_party/giflib/giflib/giflib.cbp @@ -0,0 +1,79 @@ + + + + + + diff --git a/third_party/giflib/giflib/gifsponge.c b/third_party/giflib/giflib/gifsponge.c new file mode 100644 index 00000000..e2fc8ec7 --- /dev/null +++ b/third_party/giflib/giflib/gifsponge.c @@ -0,0 +1,87 @@ +/**************************************************************************** + +gifsponge.c - skeleton file for generic GIF `sponge' program + +Slurp a GIF into core, operate on it, spew it out again. Most of the +junk above `int main' isn't needed for the skeleton, but is likely to +be for what you'll do with it. + +If you compile this, it will turn into an expensive GIF copying routine; +stdin to stdout with no changes and minimal validation. Well, it's a +decent test of DGifSlurp() and EGifSpew(), anyway. + +Note: due to the vicissitudes of Lempel-Ziv compression, the output of this +copier may not be bitwise identical to its input. This can happen if you +copy an image from a much more (or much *less*) memory-limited system; your +compression may use more (or fewer) bits. The uncompressed rasters should, +however, be identical (you can check this with gifbuild -d). + +SPDX-License-Identifier: MIT + +****************************************************************************/ + +#include +#include +#include +#include + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "gifsponge" + +int main(int argc, char **argv) +{ + int i, ErrorCode; + GifFileType *GifFileIn, *GifFileOut = (GifFileType *)NULL; + + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + if (DGifSlurp(GifFileIn) == GIF_ERROR) { + PrintGifError(GifFileIn->Error); + exit(EXIT_FAILURE); + } + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* + * Your operations on in-core structures go here. + * This code just copies the header and each image from the incoming file. + */ + GifFileOut->SWidth = GifFileIn->SWidth; + GifFileOut->SHeight = GifFileIn->SHeight; + GifFileOut->SColorResolution = GifFileIn->SColorResolution; + GifFileOut->SBackGroundColor = GifFileIn->SBackGroundColor; + if (GifFileIn->SColorMap) { + GifFileOut->SColorMap = GifMakeMapObject( + GifFileIn->SColorMap->ColorCount, + GifFileIn->SColorMap->Colors); + } else { + GifFileOut->SColorMap = NULL; + } + + for (i = 0; i < GifFileIn->ImageCount; i++) + (void) GifMakeSavedImage(GifFileOut, &GifFileIn->SavedImages[i]); + + /* + * Note: don't do DGifCloseFile early, as this will + * deallocate all the memory containing the GIF data! + * + * Further note: EGifSpew() doesn't try to validity-check any of this + * data; it's *your* responsibility to keep your changes consistent. + * Caveat hacker! + */ + if (EGifSpew(GifFileOut) == GIF_ERROR) + PrintGifError(GifFileOut->Error); + + if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) + PrintGifError(ErrorCode); + + return 0; +} + +/* end */ diff --git a/third_party/giflib/giflib/giftext.c b/third_party/giflib/giflib/giftext.c new file mode 100644 index 00000000..1360d474 --- /dev/null +++ b/third_party/giflib/giflib/giftext.c @@ -0,0 +1,466 @@ +/***************************************************************************** + +giftext - dump GIF pixels and metadata as text + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif /* _WIN32 */ + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "giftext" + +#define MAKE_PRINTABLE(c) (isprint(c) ? (c) : ' ') + +static char + *VersionStr = + PROGRAM_NAME + VERSION_COOKIE + " Gershon Elber, " + __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char + *CtrlStr = + PROGRAM_NAME + " v%- c%- e%- z%- p%- r%- h%- GifFile!*s"; + +static void PrintCodeBlock(GifFileType *GifFile, GifByteType *CodeBlock, bool Reset); +static void PrintPixelBlock(GifByteType *PixelBlock, int Len, bool Reset); +static void PrintExtBlock(GifByteType *Extension, bool Reset); +static void PrintLZCodes(GifFileType *GifFile); + +/****************************************************************************** + Interpret the command line and scan the given GIF file. +******************************************************************************/ +int main(int argc, char **argv) +{ + int i, j, ExtCode, ErrorCode, CodeSize, NumFiles, Len, ImageNum = 1; + bool Error, + ColorMapFlag = false, EncodedFlag = false, LZCodesFlag = false, + PixelFlag = false, HelpFlag = false, RawFlag = false; + char *GifFileName, **FileName = NULL; + GifPixelType *Line; + GifRecordType RecordType; + GifByteType *CodeBlock, *Extension; + GifFileType *GifFile; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, + &GifNoisyPrint, &ColorMapFlag, &EncodedFlag, + &LZCodesFlag, &PixelFlag, &RawFlag, &HelpFlag, + &NumFiles, &FileName)) != false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) + GAPrintErrMsg(Error); + else if (NumFiles > 1) + GIF_MESSAGE("Error in command line parsing - one GIF file please."); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + if (NumFiles == 1) { + GifFileName = *FileName; + if ((GifFile = DGifOpenFileName(*FileName, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + else { + /* Use stdin instead: */ + GifFileName = "Stdin"; + if ((GifFile = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } + + /* Because we write binary data - make sure no text will be written. */ + if (RawFlag) { + ColorMapFlag = EncodedFlag = LZCodesFlag = PixelFlag = false; +#ifdef _WIN32 + _setmode(1, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* _WIN32 */ + } + else { + printf("\n%s:\n\n\tScreen Size - Width = %d, Height = %d.\n", + GifFileName, GifFile->SWidth, GifFile->SHeight); + printf("\tColorResolution = %d, BitsPerPixel = %d, BackGround = %d, Aspect = %d.\n", + GifFile->SColorResolution, + GifFile->SColorMap ? GifFile->SColorMap->BitsPerPixel : 0, + GifFile->SBackGroundColor, + GifFile->AspectByte); + if (GifFile->SColorMap) + printf("\tHas Global Color Map.\n\n"); + else + printf("\tNo Global Color Map.\n\n"); + if (ColorMapFlag && GifFile->SColorMap) { + printf("\tGlobal Color Map:\n"); + Len = GifFile->SColorMap->ColorCount; + printf("\tSort Flag: %s\n", + GifFile->SColorMap->SortFlag ? "on":"off"); + for (i = 0; i < Len; i+=4) { + for (j = 0; j < 4 && j < Len; j++) { + printf("%3d: %02xh %02xh %02xh ", i + j, + GifFile->SColorMap->Colors[i + j].Red, + GifFile->SColorMap->Colors[i + j].Green, + GifFile->SColorMap->Colors[i + j].Blue); + } + printf("\n"); + } + } + } + + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + if (!RawFlag) { + printf("\nImage #%d:\n\n\tImage Size - Left = %d, Top = %d, Width = %d, Height = %d.\n", + ImageNum++, GifFile->Image.Left, GifFile->Image.Top, + GifFile->Image.Width, GifFile->Image.Height); + printf("\tImage is %s", + GifFile->Image.Interlace ? "Interlaced" : + "Non Interlaced"); + if (GifFile->Image.ColorMap != NULL) + printf(", BitsPerPixel = %d.\n", + GifFile->Image.ColorMap->BitsPerPixel); + else + printf(".\n"); + if (GifFile->Image.ColorMap) + printf("\tImage Has Color Map.\n"); + else + printf("\tNo Image Color Map.\n"); + if (ColorMapFlag && GifFile->Image.ColorMap) { + printf("\tSort Flag: %s\n", + GifFile->Image.ColorMap->SortFlag ? "on":"off"); + Len = 1 << GifFile->Image.ColorMap->BitsPerPixel; + for (i = 0; i < Len; i+=4) { + for (j = 0; j < 4 && j < Len; j++) { + printf("%3d: %02xh %02xh %02xh ", i + j, + GifFile->Image.ColorMap->Colors[i + j].Red, + GifFile->Image.ColorMap->Colors[i + j].Green, + GifFile->Image.ColorMap->Colors[i + j].Blue); + } + printf("\n"); + } + } + } + + if (EncodedFlag) { + if (DGifGetCode(GifFile, &CodeSize, &CodeBlock) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + printf("\nImage LZ compressed Codes (Code Size = %d):\n", + CodeSize); + PrintCodeBlock(GifFile, CodeBlock, true); + while (CodeBlock != NULL) { + if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + PrintCodeBlock(GifFile, CodeBlock, false); + } + } + else if (LZCodesFlag) { + PrintLZCodes(GifFile); + } + else if (PixelFlag) { + Line = (GifPixelType *) malloc(GifFile->Image.Width * + sizeof(GifPixelType)); + for (i = 0; i < GifFile->Image.Height; i++) { + if (DGifGetLine(GifFile, Line, GifFile->Image.Width) + == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + PrintPixelBlock(Line, GifFile->Image.Width, i == 0); + } + PrintPixelBlock(NULL, GifFile->Image.Width, false); + free((char *) Line); + } + else if (RawFlag) { + Line = (GifPixelType *) malloc(GifFile->Image.Width * + sizeof(GifPixelType)); + for (i = 0; i < GifFile->Image.Height; i++) { + if (DGifGetLine(GifFile, Line, GifFile->Image.Width) + == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + fwrite(Line, 1, GifFile->Image.Width, stdout); + } + free((char *) Line); + } + else { + /* Skip the image: */ + if (DGifGetCode(GifFile, &CodeSize, &CodeBlock) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + while (CodeBlock != NULL) { + if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + } + + } + break; + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + if (!RawFlag) { + putchar('\n'); + switch (ExtCode) + { + case COMMENT_EXT_FUNC_CODE: + printf("GIF89 comment"); + break; + case GRAPHICS_EXT_FUNC_CODE: + printf("GIF89 graphics control"); + break; + case PLAINTEXT_EXT_FUNC_CODE: + printf("GIF89 plaintext"); + break; + case APPLICATION_EXT_FUNC_CODE: + printf("GIF89 application block"); + break; + default: + printf("Extension record of unknown type"); + break; + } + printf(" (Ext Code = %d [%c]):\n", + ExtCode, MAKE_PRINTABLE(ExtCode)); + PrintExtBlock(Extension, true); + + if (ExtCode == GRAPHICS_EXT_FUNC_CODE) { + GraphicsControlBlock gcb; + if (Extension == NULL) { + printf("Invalid extension block\n"); + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + if (DGifExtensionToGCB(Extension[0], Extension+1, &gcb) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + printf("\tDisposal Mode: %d\n", gcb.DisposalMode); + printf("\tUser Input Flag: %d\n", gcb.UserInputFlag); + printf("\tTransparency on: %s\n", + gcb.TransparentColor != -1 ? "yes" : "no"); + printf("\tDelayTime: %d\n", gcb.DelayTime); + printf("\tTransparent Index: %d\n", gcb.TransparentColor); + } + } + for (;;) { + if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + if (Extension == NULL) + break; + PrintExtBlock(Extension, false); + } + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } + while (RecordType != TERMINATE_RECORD_TYPE); + + if (DGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + if (!RawFlag) printf("\nGIF file terminated normally.\n"); + + return 0; +} + +/****************************************************************************** + Print the given CodeBlock - a string in pascal notation (size in first + place). Save local information so printing can be performed continuously, + or reset to start state if Reset. If CodeBlock is NULL, output is flushed +******************************************************************************/ +static void PrintCodeBlock(GifFileType *GifFile, GifByteType *CodeBlock, bool Reset) +{ + static int CrntPlace = 0; + static long CodeCount = 0; + int i, Len; + + if (Reset || CodeBlock == NULL) { + if (CodeBlock == NULL) { + long NumBytes = 0; + if (CrntPlace > 0) { + printf("\n"); + CodeCount += CrntPlace - 16; + } + if (GifFile->Image.ColorMap) + NumBytes = ((((long) GifFile->Image.Width) * GifFile->Image.Height) + * GifFile->Image.ColorMap->BitsPerPixel) / 8; + else if (GifFile->SColorMap != NULL) + NumBytes = ((((long) GifFile->Image.Width) * GifFile->Image.Height) + * GifFile->SColorMap->BitsPerPixel) / 8; + /* FIXME: What should the compression ratio be if no color table? */ + if (NumBytes > 0) { + int Percent = 100 * CodeCount / NumBytes; + printf("\nCompression ratio: %ld/%ld (%d%%).\n", + CodeCount, NumBytes, Percent); + } + return; + } + CrntPlace = 0; + CodeCount = 0; + } + + Len = CodeBlock[0]; + for (i = 1; i <= Len; i++) { + if (CrntPlace == 0) { + printf("\n%05lxh: ", CodeCount); + CodeCount += 16; + } + (void)printf(" %02xh", CodeBlock[i]); + if (++CrntPlace >= 16) CrntPlace = 0; + } +} + +/****************************************************************************** + Print the given Extension - a string in pascal notation (size in first + place). Save local information so printing can be performed continuously, + or reset to start state if Reset. If Extension is NULL, output is flushed +******************************************************************************/ +static void PrintExtBlock(GifByteType *Extension, bool Reset) +{ + static int CrntPlace = 0; + static long ExtCount = 0; + static char HexForm[49], AsciiForm[17]; + + if (Reset || Extension == NULL) { + if (Extension == NULL) { + if (CrntPlace > 0) { + HexForm[CrntPlace * 3] = 0; + AsciiForm[CrntPlace] = 0; + printf("\n%05lx: %-49s %-17s\n", ExtCount, HexForm, AsciiForm); + return; + } + else + printf("\n"); + } + CrntPlace = 0; + ExtCount = 0; + } + + if (Extension != NULL) { + int i, Len; + Len = Extension[0]; + for (i = 1; i <= Len; i++) { + (void)snprintf(&HexForm[CrntPlace * 3], 3, + " %02x", Extension[i]); + (void)snprintf(&AsciiForm[CrntPlace], 3, + "%c", MAKE_PRINTABLE(Extension[i])); + if (++CrntPlace == 16) { + HexForm[CrntPlace * 3] = 0; + AsciiForm[CrntPlace] = 0; + printf("\n%05lx: %-49s %-17s", ExtCount, HexForm, AsciiForm); + ExtCount += 16; + CrntPlace = 0; + } + } + } +} + +/****************************************************************************** + Print the given PixelBlock of length Len. + Save local information so printing can be performed continuously, + or reset to start state if Reset. If PixelBlock is NULL, output is flushed +******************************************************************************/ +static void PrintPixelBlock(GifByteType *PixelBlock, int Len, bool Reset) +{ + static int CrntPlace = 0; + static long ExtCount = 0; + static char HexForm[49], AsciiForm[17]; + int i; + + if (Reset || PixelBlock == NULL) { + if (PixelBlock == NULL) { + if (CrntPlace > 0) { + HexForm[CrntPlace * 3] = 0; + AsciiForm[CrntPlace] = 0; + printf("\n%05lx: %-49s %-17s\n", ExtCount, HexForm, AsciiForm); + } + else + printf("\n"); + } + CrntPlace = 0; + ExtCount = 0; + if (PixelBlock == NULL) return; + } + + for (i = 0; i < Len; i++) { + (void)snprintf(&HexForm[CrntPlace * 3], 3, + " %02x", PixelBlock[i]); + (void)snprintf(&AsciiForm[CrntPlace], 3, + "%c", MAKE_PRINTABLE(PixelBlock[i])); + if (++CrntPlace == 16) { + HexForm[CrntPlace * 3] = 0; + AsciiForm[CrntPlace] = 0; + printf("\n%05lx: %-49s %-17s", ExtCount, HexForm, AsciiForm); + ExtCount += 16; + CrntPlace = 0; + } + } +} + +/****************************************************************************** + Print the image as LZ codes (each 12bits), until EOF marker is reached. +******************************************************************************/ +static void PrintLZCodes(GifFileType *GifFile) +{ + int Code, CrntPlace = 0; + long CodeCount = 0; + + do { + if (CrntPlace == 0) printf("\n%05lx:", CodeCount); + if (DGifGetLZCodes(GifFile, &Code) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + if (Code >= 0) + printf(" %03x", Code); /* EOF Code is returned as -1. */ + CodeCount++; + if (++CrntPlace >= 16) CrntPlace = 0; + } + while (Code >= 0); +} + +/* end */ diff --git a/third_party/giflib/giflib/giftool.c b/third_party/giflib/giflib/giftool.c new file mode 100644 index 00000000..77c75c02 --- /dev/null +++ b/third_party/giflib/giflib/giftool.c @@ -0,0 +1,579 @@ +/**************************************************************************** + +giftool.c - GIF transformation tool. + +SPDX-License-Identifier: MIT + +****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "getopt.h" +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "giftool" + +#define MAX_OPERATIONS 256 +#define MAX_IMAGES 2048 + +enum boolmode {numeric, onoff, tf, yesno}; + +char *putbool(bool flag, enum boolmode mode) +{ + if (flag) + switch (mode) { + case numeric: return "1"; break; + case onoff: return "on"; break; + case tf: return "true"; break; + case yesno: return "yes"; break; + } + else + switch (mode) { + case numeric: return "0"; break; + case onoff: return "off"; break; + case tf: return "false"; break; + case yesno: return "no"; break; + } + + return "FAIL"; /* should never happen */ +} + +bool getbool(char *from) +{ + struct valmap {char *name; bool val;} + boolnames[] = { + {"yes", true}, + {"on", true}, + {"1", true}, + {"t", true}, + {"no", false}, + {"off", false}, + {"0", false}, + {"f", false}, + {NULL, false}, + }, *sp; + + for (sp = boolnames; sp->name; sp++) + if (strcmp(sp->name, from) == 0) + return sp->val; + + (void)fprintf(stderr, + "giftool: %s is not a valid boolean argument.\n", + sp->name); + exit(EXIT_FAILURE); +} + +struct operation { + enum { + aspect, + delaytime, + background, + info, + interlace, + position, + screensize, + transparent, + userinput, + disposal, + } mode; + union { + GifByteType numerator; + int delay; + int color; + int dispose; + char *format; + bool flag; + struct { + int x, y; + } p; + }; +}; + +int main(int argc, char **argv) +{ + extern char *optarg; /* set by getopt */ + extern int optind; /* set by getopt */ + struct operation operations[MAX_OPERATIONS]; + struct operation *top = operations; + int selected[MAX_IMAGES], nselected = 0; + bool have_selection = false; + char *cp; + int i, status, ErrorCode; + GifFileType *GifFileIn, *GifFileOut = (GifFileType *)NULL; + struct operation *op; + + /* + * Gather operations from the command line. We use regular + * getopt(3) here rather than Gershom's argument getter because + * preserving the order of operations is important. + */ + while ((status = getopt(argc, argv, "a:b:d:f:i:n:p:s:u:x:")) != EOF) + { + if (top >= operations + MAX_OPERATIONS) { + (void)fprintf(stderr, "giftool: too many operations."); + exit(EXIT_FAILURE); + } + + switch (status) + { + case 'a': + top->mode = aspect; + top->numerator = (GifByteType)atoi(optarg); + break; + + case 'b': + top->mode = background; + top->color = atoi(optarg); + break; + + case 'd': + top->mode = delaytime; + top->delay = atoi(optarg); + break; + + case 'f': + top->mode = info; + top->format = optarg; + break; + + case 'i': + top->mode = interlace; + top->flag = getbool(optarg); + break; + + case 'n': + have_selection = true; + nselected = 0; + cp = optarg; + for (;;) + { + size_t span = strspn(cp, "0123456789"); + + if (span > 0) + { + selected[nselected++] = atoi(cp)-1; + cp += span; + if (*cp == '\0') + break; + else if (*cp == ',') + continue; + } + + (void) fprintf(stderr, "giftool: bad selection.\n"); + exit(EXIT_FAILURE); + } + break; + + case 'p': + case 's': + if (status == 'p') + top->mode = position; + else + top->mode = screensize; + cp = strchr(optarg, ','); + if (cp == NULL) + { + (void) fprintf(stderr, "giftool: missing comma in coordinate pair.\n"); + exit(EXIT_FAILURE); + } + top->p.x = atoi(optarg); + top->p.y = atoi(cp+1); + if (top->p.x < 0 || top->p.y < 0) + { + (void) fprintf(stderr, "giftool: negative coordinate.\n"); + exit(EXIT_FAILURE); + } + break; + + case 'u': + top->mode = userinput; + top->flag = getbool(optarg); + break; + + case 'x': + top->mode = disposal; + top->dispose = atoi(optarg); + break; + + default: + fprintf(stderr, "usage: giftool [-b color] [-d delay] [-iI] [-t color] -[uU] [-x disposal]\n"); + break; + } + + ++top; + } + + /* read in a GIF */ + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + if (DGifSlurp(GifFileIn) == GIF_ERROR) { + PrintGifError(GifFileIn->Error); + exit(EXIT_FAILURE); + } + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* if the selection is defaulted, compute it; otherwise bounds-check it */ + if (!have_selection) + for (i = nselected = 0; i < GifFileIn->ImageCount; i++) + selected[nselected++] = i; + else + for (i = 0; i < nselected; i++) + if (selected[i] >= GifFileIn->ImageCount || selected[i] < 0) + { + (void) fprintf(stderr, + "giftool: selection index out of bounds.\n"); + exit(EXIT_FAILURE); + } + + /* perform the operations we've gathered */ + for (op = operations; op < top; op++) + switch (op->mode) + { + case background: + GifFileIn->SBackGroundColor = op->color; + break; + + case delaytime: + for (i = 0; i < nselected; i++) + { + GraphicsControlBlock gcb; + + DGifSavedExtensionToGCB(GifFileIn, selected[i], &gcb); + gcb.DelayTime = op->delay; + EGifGCBToSavedExtension(&gcb, GifFileIn, selected[i]); + } + break; + + case info: + for (i = 0; i < nselected; i++) { + SavedImage *ip = &GifFileIn->SavedImages[selected[i]]; + GraphicsControlBlock gcb; + for (cp = op->format; *cp; cp++) { + if (*cp == '\\') + { + char c; + switch (*++cp) + { + case 'b': + (void)putchar('\b'); + break; + case 'e': + (void)putchar(0x1b); + break; + case 'f': + (void)putchar('\f'); + break; + case 'n': + (void)putchar('\n'); + break; + case 'r': + (void)putchar('\r'); + break; + case 't': + (void)putchar('\t'); + break; + case 'v': + (void)putchar('\v'); + break; + case 'x': + switch (*++cp) { + case '0': + c = (char)0x00; + break; + case '1': + c = (char)0x10; + break; + case '2': + c = (char)0x20; + break; + case '3': + c = (char)0x30; + break; + case '4': + c = (char)0x40; + break; + case '5': + c = (char)0x50; + break; + case '6': + c = (char)0x60; + break; + case '7': + c = (char)0x70; + break; + case '8': + c = (char)0x80; + break; + case '9': + c = (char)0x90; + break; + case 'A': + case 'a': + c = (char)0xa0; + break; + case 'B': + case 'b': + c = (char)0xb0; + break; + case 'C': + case 'c': + c = (char)0xc0; + break; + case 'D': + case 'd': + c = (char)0xd0; + break; + case 'E': + case 'e': + c = (char)0xe0; + break; + case 'F': + case 'f': + c = (char)0xf0; + break; + default: + return -1; + } + switch (*++cp) { + case '0': + c += 0x00; + break; + case '1': + c += 0x01; + break; + case '2': + c += 0x02; + break; + case '3': + c += 0x03; + break; + case '4': + c += 0x04; + break; + case '5': + c += 0x05; + break; + case '6': + c += 0x06; + break; + case '7': + c += 0x07; + break; + case '8': + c += 0x08; + break; + case '9': + c += 0x09; + break; + case 'A': + case 'a': + c += 0x0a; + break; + case 'B': + case 'b': + c += 0x0b; + break; + case 'C': + case 'c': + c += 0x0c; + break; + case 'D': + case 'd': + c += 0x0d; + break; + case 'E': + case 'e': + c += 0x0e; + break; + case 'F': + case 'f': + c += 0x0f; + break; + default: + return -2; + } + putchar(c); + break; + default: + putchar(*cp); + break; + } + } + else if (*cp == '%') + { + enum boolmode boolfmt; + SavedImage *sp = &GifFileIn->SavedImages[i]; + + if (cp[1] == 't') { + boolfmt = tf; + ++cp; + } else if (cp[1] == 'o') { + boolfmt = onoff; + ++cp; + } else if (cp[1] == 'y') { + boolfmt = yesno; + ++cp; + } else if (cp[1] == '1') { + boolfmt = numeric; + ++cp; + } else + boolfmt = numeric; + + switch (*++cp) + { + case '%': + putchar('%'); + break; + case 'a': + (void)printf("%d", GifFileIn->AspectByte); + break; + case 'b': + (void)printf("%d", GifFileIn->SBackGroundColor); + break; + case 'd': + DGifSavedExtensionToGCB(GifFileIn, + selected[i], + &gcb); + (void)printf("%d", gcb.DelayTime); + break; + case 'h': + (void)printf("%d", ip->ImageDesc.Height); + break; + case 'n': + (void)printf("%d", selected[i]+1); + break; + case 'p': + (void)printf("%d,%d", + ip->ImageDesc.Left, ip->ImageDesc.Top); + break; + case 's': + (void)printf("%d,%d", + GifFileIn->SWidth, + GifFileIn->SHeight); + break; + case 'w': + (void)printf("%d", ip->ImageDesc.Width); + break; + case 't': + DGifSavedExtensionToGCB(GifFileIn, + selected[i], + &gcb); + (void)printf("%d", gcb.TransparentColor); + break; + case 'u': + DGifSavedExtensionToGCB(GifFileIn, + selected[i], + &gcb); + (void)printf("%s", putbool(gcb.UserInputFlag, boolfmt)); + break; + case 'v': + fputs(EGifGetGifVersion(GifFileIn), stdout); + break; + case 'x': + DGifSavedExtensionToGCB(GifFileIn, + selected[i], + &gcb); + (void)printf("%d", gcb.DisposalMode); + break; + case 'z': + (void) printf("%s", putbool(sp->ImageDesc.ColorMap && sp->ImageDesc.ColorMap->SortFlag, boolfmt)); + break; + default: + (void)fprintf(stderr, + "giftool: bad format %%%c\n", *cp); + } + } + else + (void)putchar(*cp); + } + } + exit(EXIT_SUCCESS); + break; + + case interlace: + for (i = 0; i < nselected; i++) + GifFileIn->SavedImages[selected[i]].ImageDesc.Interlace = op->flag; + break; + + case position: + for (i = 0; i < nselected; i++) { + GifFileIn->SavedImages[selected[i]].ImageDesc.Left = op->p.x; + GifFileIn->SavedImages[selected[i]].ImageDesc.Top = op->p.y; + } + break; + + case screensize: + GifFileIn->SWidth = op->p.x; + GifFileIn->SHeight = op->p.y; + break; + + case transparent: + for (i = 0; i < nselected; i++) + { + GraphicsControlBlock gcb; + + DGifSavedExtensionToGCB(GifFileIn, selected[i], &gcb); + gcb.TransparentColor = op->color; + EGifGCBToSavedExtension(&gcb, GifFileIn, selected[i]); + } + break; + + case userinput: + for (i = 0; i < nselected; i++) + { + GraphicsControlBlock gcb; + + DGifSavedExtensionToGCB(GifFileIn, selected[i], &gcb); + gcb.UserInputFlag = op->flag; + EGifGCBToSavedExtension(&gcb, GifFileIn, selected[i]); + } + break; + + case disposal: + for (i = 0; i < nselected; i++) + { + GraphicsControlBlock gcb; + + DGifSavedExtensionToGCB(GifFileIn, selected[i], &gcb); + gcb.DisposalMode = op->dispose; + EGifGCBToSavedExtension(&gcb, GifFileIn, selected[i]); + } + break; + + default: + (void)fprintf(stderr, "giftool: unknown operation mode\n"); + exit(EXIT_FAILURE); + } + + /* write out the results */ + GifFileOut->SWidth = GifFileIn->SWidth; + GifFileOut->SHeight = GifFileIn->SHeight; + GifFileOut->SColorResolution = GifFileIn->SColorResolution; + GifFileOut->SBackGroundColor = GifFileIn->SBackGroundColor; + if (GifFileIn->SColorMap != NULL) + GifFileOut->SColorMap = GifMakeMapObject( + GifFileIn->SColorMap->ColorCount, + GifFileIn->SColorMap->Colors); + + for (i = 0; i < GifFileIn->ImageCount; i++) + (void) GifMakeSavedImage(GifFileOut, &GifFileIn->SavedImages[i]); + + if (EGifSpew(GifFileOut) == GIF_ERROR) + PrintGifError(GifFileOut->Error); + else if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) + PrintGifError(ErrorCode); + + return 0; +} + +/* end */ diff --git a/third_party/giflib/giflib/gifwedge.c b/third_party/giflib/giflib/gifwedge.c new file mode 100644 index 00000000..8d7f0a83 --- /dev/null +++ b/third_party/giflib/giflib/gifwedge.c @@ -0,0 +1,143 @@ +/***************************************************************************** + +gifwedge - create a GIF test pattern + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "gif_lib.h" +#include "getarg.h" + +#define PROGRAM_NAME "gifwedge" + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 350 + +#define DEFAULT_NUM_LEVELS 16 /* Number of colors to gen the image. */ + +static char + *VersionStr = + PROGRAM_NAME + VERSION_COOKIE + " Gershon Elber, " + __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char + *CtrlStr = + PROGRAM_NAME + " v%- l%-#Lvls!d s%-Width|Height!d!d h%-"; + +static int + NumLevels = DEFAULT_NUM_LEVELS, + ImageWidth = DEFAULT_WIDTH, + ImageHeight = DEFAULT_HEIGHT; + +/****************************************************************************** + Interpret the command line and scan the given GIF file. +******************************************************************************/ +int main(int argc, char **argv) +{ + int i, j, l, c, LevelStep, LogNumLevels, ErrorCode, Count = 0; + bool Error, LevelsFlag = false, SizeFlag = false, HelpFlag = false; + GifRowType Line; + ColorMapObject *ColorMap; + GifFileType *GifFile; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, + &GifNoisyPrint, &LevelsFlag, &NumLevels, + &SizeFlag, &ImageWidth, &ImageHeight, + &HelpFlag)) != false) { + GAPrintErrMsg(Error); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + /* Make sure the number of levels is power of 2 (up to 32 levels.). */ + for (i = 1; i < 6; i++) if (NumLevels == (1 << i)) break; + if (i == 6) GIF_EXIT("#Lvls (-l option) is not power of 2 up to 32."); + LogNumLevels = i + 3; /* Multiple by 8 (see below). */ + LevelStep = 256 / NumLevels; + + /* Make sure the image dimension is a multiple of NumLevels horizontally */ + /* and 7 (White, Red, Green, Blue and Yellow Cyan Magenta) vertically. */ + ImageWidth = (ImageWidth / NumLevels) * NumLevels; + ImageHeight = (ImageHeight / 7) * 7; + + /* Open stdout for the output file: */ + if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* Dump out screen description with given size and generated color map: */ + /* The color map has 7 NumLevels colors for White, Red, Green and then */ + /* The secondary colors Yellow Cyan and magenta. */ + if ((ColorMap = GifMakeMapObject(8 * NumLevels, NULL)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + for (i = 0; i < 8; i++) /* Set color map. */ + for (j = 0; j < NumLevels; j++) { + l = LevelStep * j; + c = i * NumLevels + j; + ColorMap->Colors[c].Red = (i == 0 || i == 1 || i == 4 || i == 6) * l; + ColorMap->Colors[c].Green = (i == 0 || i == 2 || i == 4 || i == 5) * l; + ColorMap->Colors[c].Blue = (i == 0 || i == 3 || i == 5 || i == 6) * l; + } + + if (EGifPutScreenDesc(GifFile, ImageWidth, ImageHeight, LogNumLevels, 0, ColorMap) == GIF_ERROR) { + PrintGifError(GifFile->Error); + } + + /* Dump out the image descriptor: */ + if (EGifPutImageDesc(GifFile, + 0, 0, ImageWidth, ImageHeight, + false, NULL) == GIF_ERROR) { + + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + + GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top, + GifFile->Image.Width, GifFile->Image.Height); + + /* Allocate one scan line to be used for all image. */ + if ((Line = (GifRowType) malloc(sizeof(GifPixelType) * ImageWidth)) == NULL) + GIF_EXIT("Failed to allocate memory required, aborted."); + + /* Dump the pixels: */ + for (c = 0; c < 7; c++) { + for (i = 0, l = 0; i < NumLevels; i++) + for (j = 0; j < ImageWidth / NumLevels; j++) + Line[l++] = i + NumLevels * c; + for (i = 0; i < ImageHeight / 7; i++) { + if (EGifPutLine(GifFile, Line, ImageWidth) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + GifQprintf("\b\b\b\b%-4d", Count++); + } + } + + if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + return 0; +} + +/* end */ diff --git a/third_party/giflib/giflib/openbsd-reallocarray.c b/third_party/giflib/giflib/openbsd-reallocarray.c new file mode 100644 index 00000000..7004f3ec --- /dev/null +++ b/third_party/giflib/giflib/openbsd-reallocarray.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2008 Otto Moerbeek + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include + +#ifndef SIZE_MAX + #define SIZE_MAX UINTPTR_MAX +#endif + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +openbsd_reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + /* + * Head off variations in realloc behavior on different + * platforms (reported by MarkR ) + * + * The behaviour of reallocarray is implementation-defined if + * nmemb or size is zero. It can return NULL or non-NULL + * depending on the platform. + * https://www.securecoding.cert.org/confluence/display/c/MEM04-C.Beware+of+zero-lengthallocations + * + * Here are some extracts from realloc man pages on different platforms. + * + * void realloc( void memblock, size_t size ); + * + * Windows: + * + * If there is not enough available memory to expand the block + * to the given size, the original block is left unchanged, + * and NULL is returned. If size is zero, then the block + * pointed to by memblock is freed; the return value is NULL, + * and memblock is left pointing at a freed block. + * + * OpenBSD: + * + * If size or nmemb is equal to 0, a unique pointer to an + * access protected, zero sized object is returned. Access via + * this pointer will generate a SIGSEGV exception. + * + * Linux: + * + * If size was equal to 0, either NULL or a pointer suitable + * to be passed to free() is returned. + * + * OS X: + * + * If size is zero and ptr is not NULL, a new, minimum sized + * object is allocated and the original object is freed. + * + * It looks like images with zero width or height can trigger + * this, and fuzzing behaviour will differ by platform, so + * fuzzing on one platform may not detect zero-size allocation + * problems on other platforms. + */ + if (size == 0 || nmemb == 0) + return NULL; + return realloc(optr, size * nmemb); +} diff --git a/third_party/giflib/giflib/qprintf.c b/third_party/giflib/giflib/qprintf.c new file mode 100644 index 00000000..e5420fdd --- /dev/null +++ b/third_party/giflib/giflib/qprintf.c @@ -0,0 +1,48 @@ +/***************************************************************************** + + qprintf.c - module to emulate a printf with a possible quiet (disable mode.) + + A global variable GifNoisyPrint controls the printing of this routine + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + + +#include +#include +#include + +#include "gif_lib.h" + +bool GifNoisyPrint = false; + +/***************************************************************************** + Same as fprintf to stderr but with optional print. +******************************************************************************/ +void +GifQprintf(char *Format, ...) { + va_list ArgPtr; + + va_start(ArgPtr, Format); + + if (GifNoisyPrint) { + char Line[128]; + (void)vsnprintf(Line, sizeof(Line), Format, ArgPtr); + (void)fputs(Line, stderr); + } + + va_end(ArgPtr); +} + +void +PrintGifError(int ErrorCode) { + const char *Err = GifErrorString(ErrorCode); + + if (Err != NULL) + fprintf(stderr, "GIF-LIB error: %s.\n", Err); + else + fprintf(stderr, "GIF-LIB undefined error %d.\n", ErrorCode); +} + +/* end */ diff --git a/third_party/giflib/giflib/quantize.c b/third_party/giflib/giflib/quantize.c new file mode 100644 index 00000000..94152ba7 --- /dev/null +++ b/third_party/giflib/giflib/quantize.c @@ -0,0 +1,332 @@ +/***************************************************************************** + + quantize.c - quantize a high resolution image into lower one + + Based on: "Color Image Quantization for frame buffer Display", by + Paul Heckbert SIGGRAPH 1982 page 297-307. + + This doesn't really belong in the core library, was undocumented, + and was removed in 4.2. Then it turned out some client apps were + actually using it, so it was restored in 5.0. + +SPDX-License-Identifier: MIT + +******************************************************************************/ + +#include +#include +#include "gif_lib.h" +#include "gif_lib_private.h" + +#define ABS(x) ((x) > 0 ? (x) : (-(x))) + +#define COLOR_ARRAY_SIZE 32768 +#define BITS_PER_PRIM_COLOR 5 +#define MAX_PRIM_COLOR 0x1f + +static int SortRGBAxis; + +typedef struct QuantizedColorType { + GifByteType RGB[3]; + GifByteType NewColorIndex; + long Count; + struct QuantizedColorType *Pnext; +} QuantizedColorType; + +typedef struct NewColorMapType { + GifByteType RGBMin[3], RGBWidth[3]; + unsigned int NumEntries; /* # of QuantizedColorType in linked list below */ + unsigned long Count; /* Total number of pixels in all the entries */ + QuantizedColorType *QuantizedColors; +} NewColorMapType; + +static int SubdivColorMap(NewColorMapType * NewColorSubdiv, + unsigned int ColorMapSize, + unsigned int *NewColorMapSize); +static int SortCmpRtn(const void *Entry1, const void *Entry2); + +/****************************************************************************** + Quantize high resolution image into lower one. Input image consists of a + 2D array for each of the RGB colors with size Width by Height. There is no + Color map for the input. Output is a quantized image with 2D array of + indexes into the output color map. + Note input image can be 24 bits at the most (8 for red/green/blue) and + the output has 256 colors at the most (256 entries in the color map.). + ColorMapSize specifies size of color map up to 256 and will be updated to + real size before returning. + Also non of the parameter are allocated by this routine. + This function returns GIF_OK if successful, GIF_ERROR otherwise. +******************************************************************************/ +int +GifQuantizeBuffer(unsigned int Width, + unsigned int Height, + int *ColorMapSize, + GifByteType * RedInput, + GifByteType * GreenInput, + GifByteType * BlueInput, + GifByteType * OutputBuffer, + GifColorType * OutputColorMap) { + + unsigned int Index, NumOfEntries; + int i, j, MaxRGBError[3]; + unsigned int NewColorMapSize; + long Red, Green, Blue; + NewColorMapType NewColorSubdiv[256]; + QuantizedColorType *ColorArrayEntries, *QuantizedColor; + + ColorArrayEntries = (QuantizedColorType *)malloc( + sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE); + if (ColorArrayEntries == NULL) { + return GIF_ERROR; + } + + for (i = 0; i < COLOR_ARRAY_SIZE; i++) { + ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR); + ColorArrayEntries[i].RGB[1] = (i >> BITS_PER_PRIM_COLOR) & + MAX_PRIM_COLOR; + ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR; + ColorArrayEntries[i].Count = 0; + } + + /* Sample the colors and their distribution: */ + for (i = 0; i < (int)(Width * Height); i++) { + Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << + (2 * BITS_PER_PRIM_COLOR)) + + ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << + BITS_PER_PRIM_COLOR) + + (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); + ColorArrayEntries[Index].Count++; + } + + /* Put all the colors in the first entry of the color map, and call the + * recursive subdivision process. */ + for (i = 0; i < 256; i++) { + NewColorSubdiv[i].QuantizedColors = NULL; + NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0; + for (j = 0; j < 3; j++) { + NewColorSubdiv[i].RGBMin[j] = 0; + NewColorSubdiv[i].RGBWidth[j] = 255; + } + } + + /* Find the non empty entries in the color table and chain them: */ + for (i = 0; i < COLOR_ARRAY_SIZE; i++) + if (ColorArrayEntries[i].Count > 0) + break; + QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i]; + NumOfEntries = 1; + while (++i < COLOR_ARRAY_SIZE) + if (ColorArrayEntries[i].Count > 0) { + QuantizedColor->Pnext = &ColorArrayEntries[i]; + QuantizedColor = &ColorArrayEntries[i]; + NumOfEntries++; + } + QuantizedColor->Pnext = NULL; + + NewColorSubdiv[0].NumEntries = NumOfEntries; /* Different sampled colors */ + NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */ + NewColorMapSize = 1; + if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) != + GIF_OK) { + free((char *)ColorArrayEntries); + return GIF_ERROR; + } + if (NewColorMapSize < *ColorMapSize) { + /* And clear rest of color map: */ + for (i = NewColorMapSize; i < *ColorMapSize; i++) + OutputColorMap[i].Red = OutputColorMap[i].Green = + OutputColorMap[i].Blue = 0; + } + + /* Average the colors in each entry to be the color to be used in the + * output color map, and plug it into the output color map itself. */ + for (i = 0; i < NewColorMapSize; i++) { + if ((j = NewColorSubdiv[i].NumEntries) > 0) { + QuantizedColor = NewColorSubdiv[i].QuantizedColors; + Red = Green = Blue = 0; + while (QuantizedColor) { + QuantizedColor->NewColorIndex = i; + Red += QuantizedColor->RGB[0]; + Green += QuantizedColor->RGB[1]; + Blue += QuantizedColor->RGB[2]; + QuantizedColor = QuantizedColor->Pnext; + } + OutputColorMap[i].Red = (Red << (8 - BITS_PER_PRIM_COLOR)) / j; + OutputColorMap[i].Green = (Green << (8 - BITS_PER_PRIM_COLOR)) / j; + OutputColorMap[i].Blue = (Blue << (8 - BITS_PER_PRIM_COLOR)) / j; + } + } + + /* Finally scan the input buffer again and put the mapped index in the + * output buffer. */ + MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0; + for (i = 0; i < (int)(Width * Height); i++) { + Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << + (2 * BITS_PER_PRIM_COLOR)) + + ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << + BITS_PER_PRIM_COLOR) + + (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); + Index = ColorArrayEntries[Index].NewColorIndex; + OutputBuffer[i] = Index; + if (MaxRGBError[0] < ABS(OutputColorMap[Index].Red - RedInput[i])) + MaxRGBError[0] = ABS(OutputColorMap[Index].Red - RedInput[i]); + if (MaxRGBError[1] < ABS(OutputColorMap[Index].Green - GreenInput[i])) + MaxRGBError[1] = ABS(OutputColorMap[Index].Green - GreenInput[i]); + if (MaxRGBError[2] < ABS(OutputColorMap[Index].Blue - BlueInput[i])) + MaxRGBError[2] = ABS(OutputColorMap[Index].Blue - BlueInput[i]); + } + +#ifdef DEBUG + fprintf(stderr, + "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n", + MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]); +#endif /* DEBUG */ + + free((char *)ColorArrayEntries); + + *ColorMapSize = NewColorMapSize; + + return GIF_OK; +} + +/****************************************************************************** + Routine to subdivide the RGB space recursively using median cut in each + axes alternatingly until ColorMapSize different cubes exists. + The biggest cube in one dimension is subdivide unless it has only one entry. + Returns GIF_ERROR if failed, otherwise GIF_OK. +*******************************************************************************/ +static int +SubdivColorMap(NewColorMapType * NewColorSubdiv, + unsigned int ColorMapSize, + unsigned int *NewColorMapSize) { + + unsigned int i, j, Index = 0; + QuantizedColorType *QuantizedColor, **SortArray; + + while (ColorMapSize > *NewColorMapSize) { + /* Find candidate for subdivision: */ + long Sum, Count; + int MaxSize = -1; + unsigned int NumEntries, MinColor, MaxColor; + for (i = 0; i < *NewColorMapSize; i++) { + for (j = 0; j < 3; j++) { + if ((((int)NewColorSubdiv[i].RGBWidth[j]) > MaxSize) && + (NewColorSubdiv[i].NumEntries > 1)) { + MaxSize = NewColorSubdiv[i].RGBWidth[j]; + Index = i; + SortRGBAxis = j; + } + } + } + + if (MaxSize == -1) + return GIF_OK; + + /* Split the entry Index into two along the axis SortRGBAxis: */ + + /* Sort all elements in that entry along the given axis and split at + * the median. */ + SortArray = (QuantizedColorType **)malloc( + sizeof(QuantizedColorType *) * + NewColorSubdiv[Index].NumEntries); + if (SortArray == NULL) + return GIF_ERROR; + for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors; + j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL; + j++, QuantizedColor = QuantizedColor->Pnext) + SortArray[j] = QuantizedColor; + + /* + * Because qsort isn't stable, this can produce differing + * results for the order of tuples depending on platform + * details of how qsort() is implemented. + * + * We mitigate this problem by sorting on all three axes rather + * than only the one specied by SortRGBAxis; that way the instability + * can only become an issue if there are multiple color indices + * referring to identical RGB tuples. Older versions of this + * sorted on only the one axis. + */ + qsort(SortArray, NewColorSubdiv[Index].NumEntries, + sizeof(QuantizedColorType *), SortCmpRtn); + + /* Relink the sorted list into one: */ + for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++) + SortArray[j]->Pnext = SortArray[j + 1]; + SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL; + NewColorSubdiv[Index].QuantizedColors = QuantizedColor = SortArray[0]; + free((char *)SortArray); + + /* Now simply add the Counts until we have half of the Count: */ + Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count; + NumEntries = 1; + Count = QuantizedColor->Count; + while (QuantizedColor->Pnext != NULL && + (Sum -= QuantizedColor->Pnext->Count) >= 0 && + QuantizedColor->Pnext->Pnext != NULL) { + QuantizedColor = QuantizedColor->Pnext; + NumEntries++; + Count += QuantizedColor->Count; + } + /* Save the values of the last color of the first half, and first + * of the second half so we can update the Bounding Boxes later. + * Also as the colors are quantized and the BBoxes are full 0..255, + * they need to be rescaled. + */ + MaxColor = QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */ + /* coverity[var_deref_op] */ + MinColor = QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */ + MaxColor <<= (8 - BITS_PER_PRIM_COLOR); + MinColor <<= (8 - BITS_PER_PRIM_COLOR); + + /* Partition right here: */ + NewColorSubdiv[*NewColorMapSize].QuantizedColors = + QuantizedColor->Pnext; + QuantizedColor->Pnext = NULL; + NewColorSubdiv[*NewColorMapSize].Count = Count; + NewColorSubdiv[Index].Count -= Count; + NewColorSubdiv[*NewColorMapSize].NumEntries = + NewColorSubdiv[Index].NumEntries - NumEntries; + NewColorSubdiv[Index].NumEntries = NumEntries; + for (j = 0; j < 3; j++) { + NewColorSubdiv[*NewColorMapSize].RGBMin[j] = + NewColorSubdiv[Index].RGBMin[j]; + NewColorSubdiv[*NewColorMapSize].RGBWidth[j] = + NewColorSubdiv[Index].RGBWidth[j]; + } + NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] = + NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] + + NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - MinColor; + NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor; + + NewColorSubdiv[Index].RGBWidth[SortRGBAxis] = + MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis]; + + (*NewColorMapSize)++; + } + + return GIF_OK; +} + +/**************************************************************************** + Routine called by qsort to compare two entries. +*****************************************************************************/ + +static int +SortCmpRtn(const void *Entry1, + const void *Entry2) { + QuantizedColorType *entry1 = (*((QuantizedColorType **) Entry1)); + QuantizedColorType *entry2 = (*((QuantizedColorType **) Entry2)); + + /* sort on all axes of the color space! */ + int hash1 = entry1->RGB[SortRGBAxis] * 256 * 256 + + entry1->RGB[(SortRGBAxis+1) % 3] * 256 + + entry1->RGB[(SortRGBAxis+2) % 3]; + int hash2 = entry2->RGB[SortRGBAxis] * 256 * 256 + + entry2->RGB[(SortRGBAxis+1) % 3] * 256 + + entry2->RGB[(SortRGBAxis+2) % 3]; + + return hash1 - hash2; +} + +/* end */ diff --git a/third_party/giflib/uos/amd64/include/gif_lib.h b/third_party/giflib/uos/amd64/include/gif_lib.h new file mode 100644 index 00000000..ebdbd3cd --- /dev/null +++ b/third_party/giflib/uos/amd64/include/gif_lib.h @@ -0,0 +1,303 @@ +/****************************************************************************** + +gif_lib.h - service library for decoding and encoding GIF images + +SPDX-License-Identifier: MIT + +*****************************************************************************/ + +#ifndef _GIF_LIB_H_ +#define _GIF_LIB_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GIFLIB_MAJOR 5 +#define GIFLIB_MINOR 2 +#define GIFLIB_RELEASE 1 + +#define GIF_ERROR 0 +#define GIF_OK 1 + +#include +#include + +#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */ +#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1 +#define GIF_VERSION_POS 3 /* Version first character in stamp. */ +#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */ +#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */ + +typedef unsigned char GifPixelType; +typedef unsigned char *GifRowType; +typedef unsigned char GifByteType; +typedef unsigned int GifPrefixType; +typedef int GifWord; + +typedef struct GifColorType { + GifByteType Red, Green, Blue; +} GifColorType; + +typedef struct ColorMapObject { + int ColorCount; + int BitsPerPixel; + bool SortFlag; + GifColorType *Colors; /* on malloc(3) heap */ +} ColorMapObject; + +typedef struct GifImageDesc { + GifWord Left, Top, Width, Height; /* Current image dimensions. */ + bool Interlace; /* Sequential/Interlaced lines. */ + ColorMapObject *ColorMap; /* The local color map */ +} GifImageDesc; + +typedef struct ExtensionBlock { + int ByteCount; + GifByteType *Bytes; /* on malloc(3) heap */ + int Function; /* The block function code */ +#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */ +#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */ +#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */ +#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */ +#define APPLICATION_EXT_FUNC_CODE 0xff /* application block (GIF89) */ +} ExtensionBlock; + +typedef struct SavedImage { + GifImageDesc ImageDesc; + GifByteType *RasterBits; /* on malloc(3) heap */ + int ExtensionBlockCount; /* Count of extensions before image */ + ExtensionBlock *ExtensionBlocks; /* Extensions before image */ +} SavedImage; + +typedef struct GifFileType { + GifWord SWidth, SHeight; /* Size of virtual canvas */ + GifWord SColorResolution; /* How many colors can we generate? */ + GifWord SBackGroundColor; /* Background color for virtual canvas */ + GifByteType AspectByte; /* Used to compute pixel aspect ratio */ + ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */ + int ImageCount; /* Number of current image (both APIs) */ + GifImageDesc Image; /* Current image (low-level API) */ + SavedImage *SavedImages; /* Image sequence (high-level API) */ + int ExtensionBlockCount; /* Count extensions past last image */ + ExtensionBlock *ExtensionBlocks; /* Extensions past last image */ + int Error; /* Last error condition reported */ + void *UserData; /* hook to attach user data (TVT) */ + void *Private; /* Don't mess with this! */ +} GifFileType; + +#define GIF_ASPECT_RATIO(n) ((n)+15.0/64.0) + +typedef enum { + UNDEFINED_RECORD_TYPE, + SCREEN_DESC_RECORD_TYPE, + IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ + EXTENSION_RECORD_TYPE, /* Begin with '!' */ + TERMINATE_RECORD_TYPE /* Begin with ';' */ +} GifRecordType; + +/* func type to read gif data from arbitrary sources (TVT) */ +typedef int (*InputFunc) (GifFileType *, GifByteType *, int); + +/* func type to write gif data to arbitrary targets. + * Returns count of bytes written. (MRB) + */ +typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int); + +/****************************************************************************** + GIF89 structures +******************************************************************************/ + +typedef struct GraphicsControlBlock { + int DisposalMode; +#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */ +#define DISPOSE_DO_NOT 1 /* Leave image in place */ +#define DISPOSE_BACKGROUND 2 /* Set area too background color */ +#define DISPOSE_PREVIOUS 3 /* Restore to previous content */ + bool UserInputFlag; /* User confirmation required before disposal */ + int DelayTime; /* pre-display delay in 0.01sec units */ + int TransparentColor; /* Palette index for transparency, -1 if none */ +#define NO_TRANSPARENT_COLOR -1 +} GraphicsControlBlock; + +/****************************************************************************** + GIF encoding routines +******************************************************************************/ + +/* Main entry points */ +GifFileType *EGifOpenFileName(const char *GifFileName, + const bool GifTestExistence, int *Error); +GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error); +GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error); +int EGifSpew(GifFileType * GifFile); +const char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */ +int EGifCloseFile(GifFileType *GifFile, int *ErrorCode); + +#define E_GIF_SUCCEEDED 0 +#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */ +#define E_GIF_ERR_WRITE_FAILED 2 +#define E_GIF_ERR_HAS_SCRN_DSCR 3 +#define E_GIF_ERR_HAS_IMAG_DSCR 4 +#define E_GIF_ERR_NO_COLOR_MAP 5 +#define E_GIF_ERR_DATA_TOO_BIG 6 +#define E_GIF_ERR_NOT_ENOUGH_MEM 7 +#define E_GIF_ERR_DISK_IS_FULL 8 +#define E_GIF_ERR_CLOSE_FAILED 9 +#define E_GIF_ERR_NOT_WRITEABLE 10 + +/* These are legacy. You probably do not want to call them directly */ +int EGifPutScreenDesc(GifFileType *GifFile, + const int GifWidth, const int GifHeight, + const int GifColorRes, + const int GifBackGround, + const ColorMapObject *GifColorMap); +int EGifPutImageDesc(GifFileType *GifFile, + const int GifLeft, const int GifTop, + const int GifWidth, const int GifHeight, + const bool GifInterlace, + const ColorMapObject *GifColorMap); +void EGifSetGifVersion(GifFileType *GifFile, const bool gif89); +int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine, + int GifLineLen); +int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel); +int EGifPutComment(GifFileType *GifFile, const char *GifComment); +int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode); +int EGifPutExtensionBlock(GifFileType *GifFile, + const int GifExtLen, const void *GifExtension); +int EGifPutExtensionTrailer(GifFileType *GifFile); +int EGifPutExtension(GifFileType *GifFile, const int GifExtCode, + const int GifExtLen, + const void *GifExtension); +int EGifPutCode(GifFileType *GifFile, int GifCodeSize, + const GifByteType *GifCodeBlock); +int EGifPutCodeNext(GifFileType *GifFile, + const GifByteType *GifCodeBlock); + +/****************************************************************************** + GIF decoding routines +******************************************************************************/ + +/* Main entry points */ +GifFileType *DGifOpenFileName(const char *GifFileName, int *Error); +GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error); +int DGifSlurp(GifFileType * GifFile); +GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */ + int DGifCloseFile(GifFileType * GifFile, int *ErrorCode); + +#define D_GIF_SUCCEEDED 0 +#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */ +#define D_GIF_ERR_READ_FAILED 102 +#define D_GIF_ERR_NOT_GIF_FILE 103 +#define D_GIF_ERR_NO_SCRN_DSCR 104 +#define D_GIF_ERR_NO_IMAG_DSCR 105 +#define D_GIF_ERR_NO_COLOR_MAP 106 +#define D_GIF_ERR_WRONG_RECORD 107 +#define D_GIF_ERR_DATA_TOO_BIG 108 +#define D_GIF_ERR_NOT_ENOUGH_MEM 109 +#define D_GIF_ERR_CLOSE_FAILED 110 +#define D_GIF_ERR_NOT_READABLE 111 +#define D_GIF_ERR_IMAGE_DEFECT 112 +#define D_GIF_ERR_EOF_TOO_SOON 113 + +/* These are legacy. You probably do not want to call them directly */ +int DGifGetScreenDesc(GifFileType *GifFile); +int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType); +int DGifGetImageHeader(GifFileType *GifFile); +int DGifGetImageDesc(GifFileType *GifFile); +int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen); +int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel); +int DGifGetExtension(GifFileType *GifFile, int *GifExtCode, + GifByteType **GifExtension); +int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension); +int DGifGetCode(GifFileType *GifFile, int *GifCodeSize, + GifByteType **GifCodeBlock); +int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock); +int DGifGetLZCodes(GifFileType *GifFile, int *GifCode); +const char *DGifGetGifVersion(GifFileType *GifFile); + + +/****************************************************************************** + Error handling and reporting. +******************************************************************************/ +extern const char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */ + +/***************************************************************************** + Everything below this point is new after version 1.2, supporting `slurp + mode' for doing I/O in two big belts with all the image-bashing in core. +******************************************************************************/ + +/****************************************************************************** + Color map handling from gif_alloc.c +******************************************************************************/ + +extern ColorMapObject *GifMakeMapObject(int ColorCount, + const GifColorType *ColorMap); +extern void GifFreeMapObject(ColorMapObject *Object); +extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1, + const ColorMapObject *ColorIn2, + GifPixelType ColorTransIn2[]); +extern int GifBitSize(int n); + +/****************************************************************************** + Support for the in-core structures allocation (slurp mode). +******************************************************************************/ + +extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]); +extern int GifAddExtensionBlock(int *ExtensionBlock_Count, + ExtensionBlock **ExtensionBlocks, + int Function, + unsigned int Len, unsigned char ExtData[]); +extern void GifFreeExtensions(int *ExtensionBlock_Count, + ExtensionBlock **ExtensionBlocks); +extern SavedImage *GifMakeSavedImage(GifFileType *GifFile, + const SavedImage *CopyFrom); +extern void GifFreeSavedImages(GifFileType *GifFile); + +/****************************************************************************** + 5.x functions for GIF89 graphics control blocks +******************************************************************************/ + +int DGifExtensionToGCB(const size_t GifExtensionLength, + const GifByteType *GifExtension, + GraphicsControlBlock *GCB); +size_t EGifGCBToExtension(const GraphicsControlBlock *GCB, + GifByteType *GifExtension); + +int DGifSavedExtensionToGCB(GifFileType *GifFile, + int ImageIndex, + GraphicsControlBlock *GCB); +int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, + GifFileType *GifFile, + int ImageIndex); + +/****************************************************************************** + The library's internal utility font +******************************************************************************/ + +#define GIF_FONT_WIDTH 8 +#define GIF_FONT_HEIGHT 8 +extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH]; + +extern void GifDrawText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, const int color); + +extern void GifDrawBox(SavedImage *Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void GifDrawRectangle(SavedImage *Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void GifDrawBoxedText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, + const int border, const int bg, const int fg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _GIF_LIB_H */ + +/* end */ diff --git a/third_party/giflib/uos/amd64/lib/libgiflib.a b/third_party/giflib/uos/amd64/lib/libgiflib.a new file mode 100644 index 0000000000000000000000000000000000000000..9f87d8a478d9e85941c3066fd58cd72e33b57ff7 GIT binary patch literal 67272 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%0ABK{yZXp&HWPCY!~2` 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