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 00000000..9f87d8a4
Binary files /dev/null and b/third_party/giflib/uos/amd64/lib/libgiflib.a differ