/******************************************************************************

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
// SPDX-FileCopyrightText: Copyright (C) Eric S. Raymond <esr@thyrsus.com>

#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#include <io.h>
#else
#include <sys/types.h>
#include <unistd.h>
#endif /* _WIN32 */
#include <sys/stat.h>

#include "gif_lib.h"
#include "gif_lib_private.h"

/* Masks given codes to BitsPerPixel, to make sure all codes are in range: */
/*@+charint@*/
static const GifPixelType CodeMask[] = {0x00, 0x01, 0x03, 0x07, 0x0f,
                                        0x1f, 0x3f, 0x7f, 0xff};
/*@-charint@*/

static int EGifPutWord(int Word, GifFileType *GifFile);
static int EGifSetupCompress(GifFileType *GifFile);
static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line,
                            const int LineLen);
static int EGifCompressOutput(GifFileType *GifFile, int Code);
static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c);

/* extract bytes from an unsigned word */
#define LOBYTE(x) ((x)&0xff)
#define HIBYTE(x) (((x) >> 8) & 0xff)

#ifndef S_IREAD
#define S_IREAD S_IRUSR
#endif

#ifndef S_IWRITE
#define S_IWRITE S_IWUSR
#endif
/******************************************************************************
 Open a new GIF file for write, specified by name. If TestExistance then
 if the file exists this routines fails (returns NULL).
 Returns a dynamically allocated GifFileType pointer which serves as the GIF
 info record. The Error member is cleared if successful.
******************************************************************************/
GifFileType *EGifOpenFileName(const char *FileName, const bool TestExistence,
                              int *Error) {

	int FileHandle;
	GifFileType *GifFile;

	if (TestExistence) {
		FileHandle = open(FileName, O_WRONLY | O_CREAT | O_EXCL,
		                  S_IREAD | S_IWRITE);
	} else {
		FileHandle = open(FileName, O_WRONLY | O_CREAT | O_TRUNC,
		                  S_IREAD | S_IWRITE);
	}

	if (FileHandle == -1) {
		if (Error != NULL) {
			*Error = E_GIF_ERR_OPEN_FAILED;
		}
		return NULL;
	}
	GifFile = EGifOpenFileHandle(FileHandle, Error);
	if (GifFile == (GifFileType *)NULL) {
		(void)close(FileHandle);
	}
	return GifFile;
}

/******************************************************************************
 Update a new GIF file, given its file handle, which must be opened for
 write in binary mode.
 Returns dynamically allocated a GifFileType pointer which serves as the GIF
 info record.
 Only fails on a memory allocation error.
******************************************************************************/
GifFileType *EGifOpenFileHandle(const int FileHandle, int *Error) {
	GifFileType *GifFile;
	GifFilePrivateType *Private;
	FILE *f;

	GifFile = (GifFileType *)malloc(sizeof(GifFileType));
	if (GifFile == NULL) {
		return NULL;
	}

	memset(GifFile, '\0', sizeof(GifFileType));

	Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
	if (Private == NULL) {
		free(GifFile);
		if (Error != NULL) {
			*Error = E_GIF_ERR_NOT_ENOUGH_MEM;
		}
		return NULL;
	}
	/*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType));
	if ((Private->HashTable = _InitHashTable()) == NULL) {
		free(GifFile);
		free(Private);
		if (Error != NULL) {
			*Error = E_GIF_ERR_NOT_ENOUGH_MEM;
		}
		return NULL;
	}

#ifdef _WIN32
	_setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */
#endif                                  /* _WIN32 */

	f = fdopen(FileHandle, "wb"); /* Make it into a stream: */

	GifFile->Private = (void *)Private;
	Private->FileHandle = FileHandle;
	Private->File = f;
	Private->FileState = FILE_STATE_WRITE;
	Private->gif89 = false;

	Private->Write = (OutputFunc)0;   /* No user write routine (MRB) */
	GifFile->UserData = (void *)NULL; /* No user write handle (MRB) */

	GifFile->Error = 0;

	return GifFile;
}

/******************************************************************************
 Output constructor that takes user supplied output function.
 Basically just a copy of EGifOpenFileHandle. (MRB)
******************************************************************************/
GifFileType *EGifOpen(void *userData, OutputFunc writeFunc, int *Error) {
	GifFileType *GifFile;
	GifFilePrivateType *Private;

	GifFile = (GifFileType *)malloc(sizeof(GifFileType));
	if (GifFile == NULL) {
		if (Error != NULL) {
			*Error = E_GIF_ERR_NOT_ENOUGH_MEM;
		}
		return NULL;
	}

	memset(GifFile, '\0', sizeof(GifFileType));

	Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
	if (Private == NULL) {
		free(GifFile);
		if (Error != NULL) {
			*Error = E_GIF_ERR_NOT_ENOUGH_MEM;
		}
		return NULL;
	}

	memset(Private, '\0', sizeof(GifFilePrivateType));

	Private->HashTable = _InitHashTable();
	if (Private->HashTable == NULL) {
		free(GifFile);
		free(Private);
		if (Error != NULL) {
			*Error = E_GIF_ERR_NOT_ENOUGH_MEM;
		}
		return NULL;
	}

	GifFile->Private = (void *)Private;
	Private->FileHandle = 0;
	Private->File = (FILE *)0;
	Private->FileState = FILE_STATE_WRITE;

	Private->Write = writeFunc;   /* User write routine (MRB) */
	GifFile->UserData = userData; /* User write handle (MRB) */

	Private->gif89 = false; /* initially, write GIF87 */

	GifFile->Error = 0;

	return GifFile;
}

/******************************************************************************
 Routine to compute the GIF version that will be written on output.
******************************************************************************/
const char *EGifGetGifVersion(GifFileType *GifFile) {
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
	int i, j;

	/*
	 * Bulletproofing - always write GIF89 if we need to.
	 * Note, we don't clear the gif89 flag here because
	 * users of the sequential API might have called EGifSetGifVersion()
	 * in order to set that flag.
	 */
	for (i = 0; i < GifFile->ImageCount; i++) {
		for (j = 0; j < GifFile->SavedImages[i].ExtensionBlockCount;
		     j++) {
			int function =
			    GifFile->SavedImages[i].ExtensionBlocks[j].Function;

			if (function == COMMENT_EXT_FUNC_CODE ||
			    function == GRAPHICS_EXT_FUNC_CODE ||
			    function == PLAINTEXT_EXT_FUNC_CODE ||
			    function == APPLICATION_EXT_FUNC_CODE) {
				Private->gif89 = true;
			}
		}
	}
	for (i = 0; i < GifFile->ExtensionBlockCount; i++) {
		int function = GifFile->ExtensionBlocks[i].Function;

		if (function == COMMENT_EXT_FUNC_CODE ||
		    function == GRAPHICS_EXT_FUNC_CODE ||
		    function == PLAINTEXT_EXT_FUNC_CODE ||
		    function == APPLICATION_EXT_FUNC_CODE) {
			Private->gif89 = true;
		}
	}

	if (Private->gif89) {
		return GIF89_STAMP;
	} else {
		return GIF87_STAMP;
	}
}

/******************************************************************************
 Set the GIF version. In the extremely unlikely event that there is ever
 another version, replace the bool argument with an enum in which the
 GIF87 value is 0 (numerically the same as bool false) and the GIF89 value
 is 1 (numerically the same as bool true).  That way we'll even preserve
 object-file compatibility!
******************************************************************************/
void EGifSetGifVersion(GifFileType *GifFile, const bool gif89) {
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	Private->gif89 = gif89;
}

/******************************************************************************
 All writes to the GIF should go through this.
******************************************************************************/
static int InternalWrite(GifFileType *GifFileOut, const unsigned char *buf,
                         size_t len) {
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFileOut->Private;
	if (Private->Write) {
		return Private->Write(GifFileOut, buf, len);
	} else {
		return fwrite(buf, 1, len, Private->File);
	}
}

/******************************************************************************
 This routine should be called before any other EGif calls, immediately
 following the GIF file opening.
******************************************************************************/
int EGifPutScreenDesc(GifFileType *GifFile, const int Width, const int Height,
                      const int ColorRes, const int BackGround,
                      const ColorMapObject *ColorMap) {
	GifByteType Buf[3];
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
	const char *write_version;
	GifFile->SColorMap = NULL;

	if (Private->FileState & FILE_STATE_SCREEN) {
		/* If already has screen descriptor - something is wrong! */
		GifFile->Error = E_GIF_ERR_HAS_SCRN_DSCR;
		return GIF_ERROR;
	}
	if (!IS_WRITEABLE(Private)) {
		/* This file was NOT open for writing: */
		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
		return GIF_ERROR;
	}

	write_version = EGifGetGifVersion(GifFile);

	/* First write the version prefix into the file. */
	if (InternalWrite(GifFile, (unsigned char *)write_version,
	                  strlen(write_version)) != strlen(write_version)) {
		GifFile->Error = E_GIF_ERR_WRITE_FAILED;
		return GIF_ERROR;
	}

	GifFile->SWidth = Width;
	GifFile->SHeight = Height;
	GifFile->SColorResolution = ColorRes;
	GifFile->SBackGroundColor = BackGround;
	if (ColorMap) {
		GifFile->SColorMap =
		    GifMakeMapObject(ColorMap->ColorCount, ColorMap->Colors);
		if (GifFile->SColorMap == NULL) {
			GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM;
			return GIF_ERROR;
		}
	} else {
		GifFile->SColorMap = NULL;
	}

	/*
	 * Put the logical screen descriptor into the file:
	 */
	/* Logical Screen Descriptor: Dimensions */
	(void)EGifPutWord(Width, GifFile);
	(void)EGifPutWord(Height, GifFile);

	/* Logical Screen Descriptor: Packed Fields */
	/* Note: We have actual size of the color table default to the largest
	 * possible size (7+1 == 8 bits) because the decoder can use it to
	 * decide how to display the files.
	 */
	Buf[0] =
	    (ColorMap ? 0x80 : 0x00) | /* Yes/no global colormap */
	    ((ColorRes - 1) << 4) | /* Bits allocated to each primary color */
	    (ColorMap ? ColorMap->BitsPerPixel - 1
	              : 0x07); /* Actual size of the
	                          color table. */
	if (ColorMap != NULL && ColorMap->SortFlag) {
		Buf[0] |= 0x08;
	}
	Buf[1] =
	    BackGround; /* Index into the ColorTable for background color */
	Buf[2] = GifFile->AspectByte; /* Pixel Aspect Ratio */
	InternalWrite(GifFile, Buf, 3);

	/* If we have Global color map - dump it also: */
	if (ColorMap != NULL) {
		int i;
		for (i = 0; i < ColorMap->ColorCount; i++) {
			/* Put the ColorMap out also: */
			Buf[0] = ColorMap->Colors[i].Red;
			Buf[1] = ColorMap->Colors[i].Green;
			Buf[2] = ColorMap->Colors[i].Blue;
			if (InternalWrite(GifFile, Buf, 3) != 3) {
				GifFile->Error = E_GIF_ERR_WRITE_FAILED;
				return GIF_ERROR;
			}
		}
	}

	/* Mark this file as has screen descriptor, and no pixel written yet: */
	Private->FileState |= FILE_STATE_SCREEN;

	return GIF_OK;
}

/******************************************************************************
 This routine should be called before any attempt to dump an image - any
 call to any of the pixel dump routines.
******************************************************************************/
int EGifPutImageDesc(GifFileType *GifFile, const int Left, const int Top,
                     const int Width, const int Height, const bool Interlace,
                     const ColorMapObject *ColorMap) {
	GifByteType Buf[3];
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	if (Private->FileState & FILE_STATE_IMAGE &&
	    Private->PixelCount > 0xffff0000UL) {
		/* If already has active image descriptor - something is wrong!
		 */
		GifFile->Error = E_GIF_ERR_HAS_IMAG_DSCR;
		return GIF_ERROR;
	}
	if (!IS_WRITEABLE(Private)) {
		/* This file was NOT open for writing: */
		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
		return GIF_ERROR;
	}
	GifFile->Image.Left = Left;
	GifFile->Image.Top = Top;
	GifFile->Image.Width = Width;
	GifFile->Image.Height = Height;
	GifFile->Image.Interlace = Interlace;
	if (ColorMap != GifFile->Image.ColorMap) {
		if (ColorMap) {
			if (GifFile->Image.ColorMap != NULL) {
				GifFreeMapObject(GifFile->Image.ColorMap);
				GifFile->Image.ColorMap = NULL;
			}
			GifFile->Image.ColorMap = GifMakeMapObject(
			    ColorMap->ColorCount, ColorMap->Colors);
			if (GifFile->Image.ColorMap == NULL) {
				GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM;
				return GIF_ERROR;
			}
		} else {
			GifFile->Image.ColorMap = NULL;
		}
	}

	/* Put the image descriptor into the file: */
	Buf[0] = DESCRIPTOR_INTRODUCER; /* Image separator character. */
	InternalWrite(GifFile, Buf, 1);
	(void)EGifPutWord(Left, GifFile);
	(void)EGifPutWord(Top, GifFile);
	(void)EGifPutWord(Width, GifFile);
	(void)EGifPutWord(Height, GifFile);
	Buf[0] = (ColorMap ? 0x80 : 0x00) | (Interlace ? 0x40 : 0x00) |
	         (ColorMap ? ColorMap->BitsPerPixel - 1 : 0);
	InternalWrite(GifFile, Buf, 1);

	/* If we have Global color map - dump it also: */
	if (ColorMap != NULL) {
		int i;
		for (i = 0; i < ColorMap->ColorCount; i++) {
			/* Put the ColorMap out also: */
			Buf[0] = ColorMap->Colors[i].Red;
			Buf[1] = ColorMap->Colors[i].Green;
			Buf[2] = ColorMap->Colors[i].Blue;
			if (InternalWrite(GifFile, Buf, 3) != 3) {
				GifFile->Error = E_GIF_ERR_WRITE_FAILED;
				return GIF_ERROR;
			}
		}
	}
	if (GifFile->SColorMap == NULL && GifFile->Image.ColorMap == NULL) {
		GifFile->Error = E_GIF_ERR_NO_COLOR_MAP;
		return GIF_ERROR;
	}

	/* Mark this file as has screen descriptor: */
	Private->FileState |= FILE_STATE_IMAGE;
	Private->PixelCount = (long)Width * (long)Height;

	/* Reset compress algorithm parameters. */
	(void)EGifSetupCompress(GifFile);

	return GIF_OK;
}

/******************************************************************************
 Put one full scanned line (Line) of length LineLen into GIF file.
******************************************************************************/
int EGifPutLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) {
	int i;
	GifPixelType Mask;
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	if (!IS_WRITEABLE(Private)) {
		/* This file was NOT open for writing: */
		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
		return GIF_ERROR;
	}

	if (!LineLen) {
		LineLen = GifFile->Image.Width;
	}
	if (Private->PixelCount < (unsigned)LineLen) {
		GifFile->Error = E_GIF_ERR_DATA_TOO_BIG;
		return GIF_ERROR;
	}
	Private->PixelCount -= LineLen;

	/* Make sure the codes are not out of bit range, as we might generate
	 * wrong code (because of overflow when we combine them) in this case:
	 */
	Mask = CodeMask[Private->BitsPerPixel];
	for (i = 0; i < LineLen; i++) {
		Line[i] &= Mask;
	}

	return EGifCompressLine(GifFile, Line, LineLen);
}

/******************************************************************************
 Put one pixel (Pixel) into GIF file.
******************************************************************************/
int EGifPutPixel(GifFileType *GifFile, GifPixelType Pixel) {
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	if (!IS_WRITEABLE(Private)) {
		/* This file was NOT open for writing: */
		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
		return GIF_ERROR;
	}

	if (Private->PixelCount == 0) {
		GifFile->Error = E_GIF_ERR_DATA_TOO_BIG;
		return GIF_ERROR;
	}
	--Private->PixelCount;

	/* Make sure the code is not out of bit range, as we might generate
	 * wrong code (because of overflow when we combine them) in this case:
	 */
	Pixel &= CodeMask[Private->BitsPerPixel];

	return EGifCompressLine(GifFile, &Pixel, 1);
}

/******************************************************************************
 Put a comment into GIF file using the GIF89 comment extension block.
******************************************************************************/
int EGifPutComment(GifFileType *GifFile, const char *Comment) {
	unsigned int length;
	char *buf;

	length = strlen(Comment);
	if (length <= 255) {
		return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, length,
		                        Comment);
	} else {
		buf = (char *)Comment;
		if (EGifPutExtensionLeader(GifFile, COMMENT_EXT_FUNC_CODE) ==
		    GIF_ERROR) {
			return GIF_ERROR;
		}

		/* Break the comment into 255 byte sub blocks */
		while (length > 255) {
			if (EGifPutExtensionBlock(GifFile, 255, buf) ==
			    GIF_ERROR) {
				return GIF_ERROR;
			}
			buf = buf + 255;
			length -= 255;
		}
		/* Output any partial block and the clear code. */
		if (length > 0) {
			if (EGifPutExtensionBlock(GifFile, length, buf) ==
			    GIF_ERROR) {
				return GIF_ERROR;
			}
		}
		if (EGifPutExtensionTrailer(GifFile) == GIF_ERROR) {
			return GIF_ERROR;
		}
	}
	return GIF_OK;
}

/******************************************************************************
 Begin an extension block (see GIF manual).  More
 extensions can be dumped using EGifPutExtensionBlock until
 EGifPutExtensionTrailer is invoked.
******************************************************************************/
int EGifPutExtensionLeader(GifFileType *GifFile, const int ExtCode) {
	GifByteType Buf[3];
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	if (!IS_WRITEABLE(Private)) {
		/* This file was NOT open for writing: */
		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
		return GIF_ERROR;
	}

	Buf[0] = EXTENSION_INTRODUCER;
	Buf[1] = ExtCode;
	InternalWrite(GifFile, Buf, 2);

	return GIF_OK;
}

/******************************************************************************
 Put extension block data (see GIF manual) into a GIF file.
******************************************************************************/
int EGifPutExtensionBlock(GifFileType *GifFile, const int ExtLen,
                          const void *Extension) {
	GifByteType Buf;
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	if (!IS_WRITEABLE(Private)) {
		/* This file was NOT open for writing: */
		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
		return GIF_ERROR;
	}

	Buf = ExtLen;
	InternalWrite(GifFile, &Buf, 1);
	InternalWrite(GifFile, Extension, ExtLen);

	return GIF_OK;
}

/******************************************************************************
 Put a terminating block (see GIF manual) into a GIF file.
******************************************************************************/
int EGifPutExtensionTrailer(GifFileType *GifFile) {

	GifByteType Buf;
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	if (!IS_WRITEABLE(Private)) {
		/* This file was NOT open for writing: */
		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
		return GIF_ERROR;
	}

	/* Write the block terminator */
	Buf = 0;
	InternalWrite(GifFile, &Buf, 1);

	return GIF_OK;
}

/******************************************************************************
 Put an extension block (see GIF manual) into a GIF file.
 Warning: This function is only useful for Extension blocks that have at
 most one subblock.  Extensions with more than one subblock need to use the
 EGifPutExtension{Leader,Block,Trailer} functions instead.
******************************************************************************/
int EGifPutExtension(GifFileType *GifFile, const int ExtCode, const int ExtLen,
                     const void *Extension) {

	GifByteType Buf[3];
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	if (!IS_WRITEABLE(Private)) {
		/* This file was NOT open for writing: */
		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
		return GIF_ERROR;
	}

	if (ExtCode == 0) {
		InternalWrite(GifFile, (GifByteType *)&ExtLen, 1);
	} else {
		Buf[0] = EXTENSION_INTRODUCER;
		Buf[1] = ExtCode; /* Extension Label */
		Buf[2] = ExtLen;  /* Extension length */
		InternalWrite(GifFile, Buf, 3);
	}
	InternalWrite(GifFile, Extension, ExtLen);
	Buf[0] = 0;
	InternalWrite(GifFile, Buf, 1);

	return GIF_OK;
}

/******************************************************************************
 Render a Graphics Control Block as raw extension data
******************************************************************************/

size_t EGifGCBToExtension(const GraphicsControlBlock *GCB,
                          GifByteType *GifExtension) {
	GifExtension[0] = 0;
	GifExtension[0] |=
	    (GCB->TransparentColor == NO_TRANSPARENT_COLOR) ? 0x00 : 0x01;
	GifExtension[0] |= GCB->UserInputFlag ? 0x02 : 0x00;
	GifExtension[0] |= ((GCB->DisposalMode & 0x07) << 2);
	GifExtension[1] = LOBYTE(GCB->DelayTime);
	GifExtension[2] = HIBYTE(GCB->DelayTime);
	GifExtension[3] = (char)GCB->TransparentColor;
	return 4;
}

/******************************************************************************
 Replace the Graphics Control Block for a saved image, if it exists.
******************************************************************************/

int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB,
                            GifFileType *GifFile, int ImageIndex) {
	int i;
	size_t Len;
	GifByteType buf[sizeof(GraphicsControlBlock)]; /* a bit dodgy... */

	if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) {
		return GIF_ERROR;
	}

	for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount;
	     i++) {
		ExtensionBlock *ep =
		    &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
		if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
			EGifGCBToExtension(GCB, ep->Bytes);
			return GIF_OK;
		}
	}

	Len = EGifGCBToExtension(GCB, (GifByteType *)buf);
	if (GifAddExtensionBlock(
	        &GifFile->SavedImages[ImageIndex].ExtensionBlockCount,
	        &GifFile->SavedImages[ImageIndex].ExtensionBlocks,
	        GRAPHICS_EXT_FUNC_CODE, Len,
	        (unsigned char *)buf) == GIF_ERROR) {
		return (GIF_ERROR);
	}

	return (GIF_OK);
}

/******************************************************************************
 Put the image code in compressed form. This routine can be called if the
 information needed to be piped out as is. Obviously this is much faster
 than decoding and encoding again. This routine should be followed by calls
 to EGifPutCodeNext, until NULL block is given.
 The block should NOT be freed by the user (not dynamically allocated).
******************************************************************************/
int EGifPutCode(GifFileType *GifFile, int CodeSize,
                const GifByteType *CodeBlock) {
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	if (!IS_WRITEABLE(Private)) {
		/* This file was NOT open for writing: */
		GifFile->Error = E_GIF_ERR_NOT_WRITEABLE;
		return GIF_ERROR;
	}

	/* No need to dump code size as Compression set up does any for us: */
	/*
	 * Buf = CodeSize;
	 * if (InternalWrite(GifFile, &Buf, 1) != 1) {
	 *      GifFile->Error = E_GIF_ERR_WRITE_FAILED;
	 *      return GIF_ERROR;
	 * }
	 */

	return EGifPutCodeNext(GifFile, CodeBlock);
}

/******************************************************************************
 Continue to put the image code in compressed form. This routine should be
 called with blocks of code as read via DGifGetCode/DGifGetCodeNext. If
 given buffer pointer is NULL, empty block is written to mark end of code.
******************************************************************************/
int EGifPutCodeNext(GifFileType *GifFile, const GifByteType *CodeBlock) {
	GifByteType Buf;
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	if (CodeBlock != NULL) {
		if (InternalWrite(GifFile, CodeBlock, CodeBlock[0] + 1) !=
		    (unsigned)(CodeBlock[0] + 1)) {
			GifFile->Error = E_GIF_ERR_WRITE_FAILED;
			return GIF_ERROR;
		}
	} else {
		Buf = 0;
		if (InternalWrite(GifFile, &Buf, 1) != 1) {
			GifFile->Error = E_GIF_ERR_WRITE_FAILED;
			return GIF_ERROR;
		}
		Private->PixelCount =
		    0; /* And local info. indicate image read. */
	}

	return GIF_OK;
}

/******************************************************************************
 This routine should be called last, to close the GIF file.
******************************************************************************/
int EGifCloseFile(GifFileType *GifFile, int *ErrorCode) {
	GifByteType Buf;
	GifFilePrivateType *Private;
	FILE *File;

	if (GifFile == NULL) {
		return GIF_ERROR;
	}

	Private = (GifFilePrivateType *)GifFile->Private;
	if (Private == NULL) {
		return GIF_ERROR;
	} else if (!IS_WRITEABLE(Private)) {
		/* This file was NOT open for writing: */
		if (ErrorCode != NULL) {
			*ErrorCode = E_GIF_ERR_NOT_WRITEABLE;
		}
		free(GifFile);
		return GIF_ERROR;
	} else {
		File = Private->File;

		Buf = TERMINATOR_INTRODUCER;
		InternalWrite(GifFile, &Buf, 1);

		if (GifFile->Image.ColorMap) {
			GifFreeMapObject(GifFile->Image.ColorMap);
			GifFile->Image.ColorMap = NULL;
		}
		if (GifFile->SColorMap) {
			GifFreeMapObject(GifFile->SColorMap);
			GifFile->SColorMap = NULL;
		}
		if (Private->HashTable) {
			free((char *)Private->HashTable);
		}
		free((char *)Private);

		if (File && fclose(File) != 0) {
			if (ErrorCode != NULL) {
				*ErrorCode = E_GIF_ERR_CLOSE_FAILED;
			}
			free(GifFile);
			return GIF_ERROR;
		}

		free(GifFile);
		if (ErrorCode != NULL) {
			*ErrorCode = E_GIF_SUCCEEDED;
		}
	}
	return GIF_OK;
}

/******************************************************************************
 Put 2 bytes (a word) into the given file in little-endian order:
******************************************************************************/
static int EGifPutWord(int Word, GifFileType *GifFile) {
	unsigned char c[2];

	c[0] = LOBYTE(Word);
	c[1] = HIBYTE(Word);
	if (InternalWrite(GifFile, c, 2) == 2) {
		return GIF_OK;
	} else {
		return GIF_ERROR;
	}
}

/******************************************************************************
 Setup the LZ compression for this image:
******************************************************************************/
static int EGifSetupCompress(GifFileType *GifFile) {
	int BitsPerPixel;
	GifByteType Buf;
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	/* Test and see what color map to use, and from it # bits per pixel: */
	if (GifFile->Image.ColorMap) {
		BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel;
	} else if (GifFile->SColorMap) {
		BitsPerPixel = GifFile->SColorMap->BitsPerPixel;
	} else {
		GifFile->Error = E_GIF_ERR_NO_COLOR_MAP;
		return GIF_ERROR;
	}

	Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel);
	InternalWrite(GifFile, &Buf, 1); /* Write the Code size to file. */

	Private->Buf[0] = 0; /* Nothing was output yet. */
	Private->BitsPerPixel = BitsPerPixel;
	Private->ClearCode = (1 << BitsPerPixel);
	Private->EOFCode = Private->ClearCode + 1;
	Private->RunningCode = Private->EOFCode + 1;
	Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */
	Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */
	Private->CrntCode = FIRST_CODE; /* Signal that this is first one! */
	Private->CrntShiftState = 0;    /* No information in CrntShiftDWord. */
	Private->CrntShiftDWord = 0;

	/* Clear hash table and send Clear to make sure the decoder do the same.
	 */
	_ClearHashTable(Private->HashTable);

	if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) {
		GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
		return GIF_ERROR;
	}
	return GIF_OK;
}

/******************************************************************************
 The LZ compression routine:
 This version compresses the given buffer Line of length LineLen.
 This routine can be called a few times (one per scan line, for example), in
 order to complete the whole image.
******************************************************************************/
static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line,
                            const int LineLen) {
	int i = 0, CrntCode;
	GifHashTableType *HashTable;
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;

	HashTable = Private->HashTable;

	if (Private->CrntCode == FIRST_CODE) { /* Its first time! */
		CrntCode = Line[i++];
	} else {
		CrntCode =
		    Private->CrntCode; /* Get last code in compression. */
	}
	while (i < LineLen) { /* Decode LineLen items. */
		GifPixelType Pixel =
		    Line[i++]; /* Get next pixel from stream. */
		/* Form a new unique key to search hash table for the code
		 * combines CrntCode as Prefix string with Pixel as postfix
		 * char.
		 */
		int NewCode;
		unsigned long NewKey = (((uint32_t)CrntCode) << 8) + Pixel;
		if ((NewCode = _ExistsHashTable(HashTable, NewKey)) >= 0) {
			/* This Key is already there, or the string is old one,
			 * so simple take new code as our CrntCode:
			 */
			CrntCode = NewCode;
		} else {
			/* Put it in hash table, output the prefix code, and
			 * make our CrntCode equal to Pixel.
			 */
			if (EGifCompressOutput(GifFile, CrntCode) ==
			    GIF_ERROR) {
				GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
				return GIF_ERROR;
			}
			CrntCode = Pixel;

			/* If however the HashTable if full, we send a clear
			 * first and Clear the hash table.
			 */
			if (Private->RunningCode >= LZ_MAX_CODE) {
				/* Time to do some clearance: */
				if (EGifCompressOutput(GifFile,
				                       Private->ClearCode) ==
				    GIF_ERROR) {
					GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
					return GIF_ERROR;
				}
				Private->RunningCode = Private->EOFCode + 1;
				Private->RunningBits =
				    Private->BitsPerPixel + 1;
				Private->MaxCode1 = 1 << Private->RunningBits;
				_ClearHashTable(HashTable);
			} else {
				/* Put this unique key with its relative Code in
				 * hash table: */
				_InsertHashTable(HashTable, NewKey,
				                 Private->RunningCode++);
			}
		}
	}

	/* Preserve the current state of the compression algorithm: */
	Private->CrntCode = CrntCode;

	if (Private->PixelCount == 0) {
		/* We are done - output last Code and flush output buffers: */
		if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) {
			GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
			return GIF_ERROR;
		}
		if (EGifCompressOutput(GifFile, Private->EOFCode) ==
		    GIF_ERROR) {
			GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
			return GIF_ERROR;
		}
		if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) {
			GifFile->Error = E_GIF_ERR_DISK_IS_FULL;
			return GIF_ERROR;
		}
	}

	return GIF_OK;
}

/******************************************************************************
 The LZ compression output routine:
 This routine is responsible for the compression of the bit stream into
 8 bits (bytes) packets.
 Returns GIF_OK if written successfully.
******************************************************************************/
static int EGifCompressOutput(GifFileType *GifFile, const int Code) {
	GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
	int retval = GIF_OK;

	if (Code == FLUSH_OUTPUT) {
		while (Private->CrntShiftState > 0) {
			/* Get Rid of what is left in DWord, and flush it. */
			if (EGifBufferedOutput(GifFile, Private->Buf,
			                       Private->CrntShiftDWord &
			                           0xff) == GIF_ERROR) {
				retval = GIF_ERROR;
			}
			Private->CrntShiftDWord >>= 8;
			Private->CrntShiftState -= 8;
		}
		Private->CrntShiftState = 0; /* For next time. */
		if (EGifBufferedOutput(GifFile, Private->Buf, FLUSH_OUTPUT) ==
		    GIF_ERROR) {
			retval = GIF_ERROR;
		}
	} else {
		Private->CrntShiftDWord |= ((long)Code)
		                           << Private->CrntShiftState;
		Private->CrntShiftState += Private->RunningBits;
		while (Private->CrntShiftState >= 8) {
			/* Dump out full bytes: */
			if (EGifBufferedOutput(GifFile, Private->Buf,
			                       Private->CrntShiftDWord &
			                           0xff) == GIF_ERROR) {
				retval = GIF_ERROR;
			}
			Private->CrntShiftDWord >>= 8;
			Private->CrntShiftState -= 8;
		}
	}

	/* If code cannot fit into RunningBits bits, must raise its size. Note */
	/* however that codes above 4095 are used for special signaling.      */
	if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) {
		Private->MaxCode1 = 1 << ++Private->RunningBits;
	}

	return retval;
}

/******************************************************************************
 This routines buffers the given characters until 255 characters are ready
 to be output. If Code is equal to -1 the buffer is flushed (EOF).
 The buffer is Dumped with first byte as its size, as GIF format requires.
 Returns GIF_OK if written successfully.
******************************************************************************/
static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c) {
	if (c == FLUSH_OUTPUT) {
		/* Flush everything out. */
		if (Buf[0] != 0 && InternalWrite(GifFile, Buf, Buf[0] + 1) !=
		                       (unsigned)(Buf[0] + 1)) {
			GifFile->Error = E_GIF_ERR_WRITE_FAILED;
			return GIF_ERROR;
		}
		/* Mark end of compressed data, by an empty block (see GIF doc):
		 */
		Buf[0] = 0;
		if (InternalWrite(GifFile, Buf, 1) != 1) {
			GifFile->Error = E_GIF_ERR_WRITE_FAILED;
			return GIF_ERROR;
		}
	} else {
		if (Buf[0] == 255) {
			/* Dump out this buffer - it is full: */
			if (InternalWrite(GifFile, Buf, Buf[0] + 1) !=
			    (unsigned)(Buf[0] + 1)) {
				GifFile->Error = E_GIF_ERR_WRITE_FAILED;
				return GIF_ERROR;
			}
			Buf[0] = 0;
		}
		Buf[++Buf[0]] = c;
	}

	return GIF_OK;
}

/******************************************************************************
 This routine writes to disk an in-core representation of a GIF previously
 created by DGifSlurp().
******************************************************************************/

static int EGifWriteExtensions(GifFileType *GifFileOut,
                               ExtensionBlock *ExtensionBlocks,
                               int ExtensionBlockCount) {
	if (ExtensionBlocks) {
		int j;

		for (j = 0; j < ExtensionBlockCount; j++) {
			ExtensionBlock *ep = &ExtensionBlocks[j];
			if (ep->Function != CONTINUE_EXT_FUNC_CODE) {
				if (EGifPutExtensionLeader(GifFileOut,
				                           ep->Function) ==
				    GIF_ERROR) {
					return (GIF_ERROR);
				}
			}
			if (EGifPutExtensionBlock(GifFileOut, ep->ByteCount,
			                          ep->Bytes) == GIF_ERROR) {
				return (GIF_ERROR);
			}
			if (j == ExtensionBlockCount - 1 ||
			    (ep + 1)->Function != CONTINUE_EXT_FUNC_CODE) {
				if (EGifPutExtensionTrailer(GifFileOut) ==
				    GIF_ERROR) {
					return (GIF_ERROR);
				}
			}
		}
	}

	return (GIF_OK);
}

int EGifSpew(GifFileType *GifFileOut) {
	int i, j;

	if (EGifPutScreenDesc(GifFileOut, GifFileOut->SWidth,
	                      GifFileOut->SHeight, GifFileOut->SColorResolution,
	                      GifFileOut->SBackGroundColor,
	                      GifFileOut->SColorMap) == GIF_ERROR) {
		return (GIF_ERROR);
	}

	for (i = 0; i < GifFileOut->ImageCount; i++) {
		SavedImage *sp = &GifFileOut->SavedImages[i];
		int SavedHeight = sp->ImageDesc.Height;
		int SavedWidth = sp->ImageDesc.Width;

		/* this allows us to delete images by nuking their rasters */
		if (sp->RasterBits == NULL) {
			continue;
		}

		if (EGifWriteExtensions(GifFileOut, sp->ExtensionBlocks,
		                        sp->ExtensionBlockCount) == GIF_ERROR) {
			return (GIF_ERROR);
		}

		if (EGifPutImageDesc(GifFileOut, sp->ImageDesc.Left,
		                     sp->ImageDesc.Top, SavedWidth, SavedHeight,
		                     sp->ImageDesc.Interlace,
		                     sp->ImageDesc.ColorMap) == GIF_ERROR) {
			return (GIF_ERROR);
		}

		if (sp->ImageDesc.Interlace) {
			/*
			 * The way an interlaced image should be written -
			 * offsets and jumps...
			 */
			static const int InterlacedOffset[] = {0, 4, 2, 1};
			static const int InterlacedJumps[] = {8, 8, 4, 2};
			int k;
			/* Need to perform 4 passes on the images: */
			for (k = 0; k < 4; k++) {
				for (j = InterlacedOffset[k]; j < SavedHeight;
				     j += InterlacedJumps[k]) {
					if (EGifPutLine(
					        GifFileOut,
					        sp->RasterBits + j * SavedWidth,
					        SavedWidth) == GIF_ERROR) {
						return (GIF_ERROR);
					}
				}
			}
		} else {
			for (j = 0; j < SavedHeight; j++) {
				if (EGifPutLine(GifFileOut,
				                sp->RasterBits + j * SavedWidth,
				                SavedWidth) == GIF_ERROR) {
					return (GIF_ERROR);
				}
			}
		}
	}

	if (EGifWriteExtensions(GifFileOut, GifFileOut->ExtensionBlocks,
	                        GifFileOut->ExtensionBlockCount) == GIF_ERROR) {
		return (GIF_ERROR);
	}

	if (EGifCloseFile(GifFileOut, NULL) == GIF_ERROR) {
		return (GIF_ERROR);
	}

	return (GIF_OK);
}

/* end */
