summaryrefslogtreecommitdiff
path: root/support/mimetex/gifsave.c
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/mimetex/gifsave.c
Initial commit
Diffstat (limited to 'support/mimetex/gifsave.c')
-rw-r--r--support/mimetex/gifsave.c1129
1 files changed, 1129 insertions, 0 deletions
diff --git a/support/mimetex/gifsave.c b/support/mimetex/gifsave.c
new file mode 100644
index 0000000000..a0727c9375
--- /dev/null
+++ b/support/mimetex/gifsave.c
@@ -0,0 +1,1129 @@
+/* $Id: gifsave.c,v 1.2 1998/07/05 16:29:56 sverrehu Exp $ */
+/**************************************************************************
+ *
+ * FILE gifsave.c
+ *
+ * DESCRIPTION Routines to create a GIF-file. See README for
+ * a description.
+ *
+ * The functions were originally written using Borland's
+ * C-compiler on an IBM PC -compatible computer, but they
+ * are compiled and tested on Linux and SunOS as well.
+ *
+ * WRITTEN BY Sverre H. Huseby <sverrehu@online.no>
+ *
+ **************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+/* #include <unistd.h> */ /* (added by j.forkosh) to get STDOUT_FILENO*/
+#include <string.h> /* " */
+/* --- windows-specific header info --- */
+#ifndef WINDOWS /* -DWINDOWS not supplied by user */
+ #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
+ || defined(DJGPP) /* try to recognize windows compilers */ \
+ || defined(_USRDLL) /* must be WINDOWS if compiling for DLL */
+ #define WINDOWS /* signal windows */
+ #endif
+#endif
+#ifdef WINDOWS /* " if filename=NULL passed to GIF_Create()*/
+ #include <fcntl.h> /* " OutFile=stdout used. But Windows opens*/
+ #include <io.h> /* " stdout in char mode, and precedes every*/
+ /* " 0x0A with spurious 0x0D. */
+ #if defined(_O_BINARY) && !defined(O_BINARY) /* only have _O_BINARY */
+ #define O_BINARY _O_BINARY /* make O_BINARY available, etc... */
+ #define setmode _setmode
+ #define fileno _fileno
+ #endif
+ #if defined(_O_BINARY) || defined(O_BINARY) /* setmode() now available */
+ #define HAVE_SETMODE /* so we'll use setmode() */
+ #endif
+#endif
+
+/* #include "gifsave.h" */ /* (j.forkosh) explcitly include header */
+enum GIF_Code {
+ GIF_OK = 0,
+ GIF_ERRCREATE,
+ GIF_ERRWRITE,
+ GIF_OUTMEM
+};
+
+int GIF_Create(const char *filename, int width, int height,
+ int numcolors, int colorres);
+void GIF_SetColor(int colornum, int red, int green, int blue);
+void GIF_SetTransparent(int colornum); /* (added by j.forkosh) */
+int GIF_CompressImage(int left, int top, int width, int height,
+ int (*getpixel)(int x, int y));
+int GIF_Close(void);
+/* --- end-of-header gifsave.h --- */
+
+
+/**************************************************************************
+ * *
+ * P R I V A T E D A T A *
+ * *
+ **************************************************************************/
+
+typedef unsigned Word; /* at least two bytes (16 bits) */
+typedef unsigned char Byte; /* exactly one byte (8 bits) */
+
+/* used by IO-routines */
+static FILE *OutFile = NULL; /* file to write to */
+static Byte *OutBuffer = NULL; /* (added by j.forkosh) */
+static int isCloseOutFile = 0; /* " */
+#if !defined(MAXGIFSZ) /* " */
+ #define MAXGIFSZ 131072 /* " max #bytes comprising gif image */
+#endif /* " */
+int gifSize = 0; /* " #bytes comprising gif */
+int maxgifSize = MAXGIFSZ; /* " max #bytes written to OutBuffer */
+extern int iscachecontenttype; /* " true to cache mime content-type */
+extern char contenttype[2048]; /* " content-type:, etc. buffer */
+
+/* used when writing to a file bitwise */
+static Byte Buffer[256]; /* there must be one more than `needed' */
+static int Index, /* current byte in buffer */
+ BitsLeft; /* bits left to fill in current byte. These
+ * are right-justified */
+
+/* used by routines maintaining an LZW string table */
+#define RES_CODES 2
+
+#define HASH_FREE 0xFFFF
+#define NEXT_FIRST 0xFFFF
+
+#define MAXBITS 12
+#define MAXSTR (1 << MAXBITS)
+
+#define HASHSIZE 9973
+#define HASHSTEP 2039
+
+#define HASH(index, lastbyte) (((lastbyte << 8) ^ index) % HASHSIZE)
+
+static Byte *StrChr = NULL;
+static Word *StrNxt = NULL,
+ *StrHsh = NULL,
+ NumStrings;
+
+/* used in the main routines */
+typedef struct {
+ Word LocalScreenWidth,
+ LocalScreenHeight;
+ Byte GlobalColorTableSize : 3,
+ SortFlag : 1,
+ ColorResolution : 3,
+ GlobalColorTableFlag : 1;
+ Byte BackgroundColorIndex;
+ Byte PixelAspectRatio;
+} ScreenDescriptor;
+
+typedef struct {
+ Byte Separator;
+ Word LeftPosition,
+ TopPosition;
+ Word Width,
+ Height;
+ Byte LocalColorTableSize : 3,
+ Reserved : 2,
+ SortFlag : 1,
+ InterlaceFlag : 1,
+ LocalColorTableFlag : 1;
+} ImageDescriptor;
+
+static int BitsPrPrimColor, /* bits pr primary color */
+ NumColors; /* number of colors in color table */
+static int TransparentColorIndex=(-1); /* (added by j.forkosh) */
+static Byte *ColorTable = NULL;
+static Word ScreenHeight,
+ ScreenWidth,
+ ImageHeight,
+ ImageWidth,
+ ImageLeft,
+ ImageTop,
+ RelPixX, RelPixY; /* used by InputByte() -function */
+static int (*GetPixel)(int x, int y);
+
+
+
+/**************************************************************************
+ * *
+ * P R I V A T E F U N C T I O N S *
+ * *
+ **************************************************************************/
+
+/*========================================================================*
+ = Routines to do file IO =
+ *========================================================================*/
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME Create
+ *
+ * DESCRIPTION Creates a new file, and enables referencing using the
+ * global variable OutFile. This variable is only used
+ * by these IO-functions, making it relatively simple to
+ * rewrite file IO.
+ *
+ * INPUT filename
+ * name of file to create,
+ * or NULL for stdout,
+ * or if *filename='\000' then it's the address of
+ * a memory buffer to which gif will be written
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_ERRWRITE - Error opening the file
+ */
+static int
+Create(const char *filename)
+{
+ OutBuffer = NULL; /* (added by j.forkosh) */
+ isCloseOutFile = 0; /* " */
+ gifSize = 0; /* " */
+ if ( filename == NULL ) /* " */
+ { OutFile = stdout; /* " */
+ /*OutFile = fdopen(STDOUT_FILENO,"wb");*/ /* " doesn't work, */
+ #ifdef WINDOWS /* " so instead... */
+ #ifdef HAVE_SETMODE /* " try to use setmode()*/
+ if ( setmode ( fileno (stdout), O_BINARY) /* to set stdout */
+ == -1 ) ; /* handle error */ /* " to binary mode */
+ #else /* " setmode not available */
+ #if 1 /* " */
+ freopen ("CON", "wb", stdout); /* " freopen stdout binary */
+ #else /* " */
+ stdout = fdopen (STDOUT_FILENO, "wb"); /*fdopen stdout binary*/
+ #endif /* " */
+ #endif /* " */
+ #endif /* " */
+ } /* " */
+ else /* " */
+ if ( *filename != '\000' ) /* " */
+ { if ((OutFile = fopen(filename, "wb")) == NULL)
+ return GIF_ERRCREATE;
+ isCloseOutFile = 1; /* (added by j.forkosh) */
+ if ( iscachecontenttype ) /* " cache headers in file */
+ if ( *contenttype != '\000' ) /* " have headers in buffer*/
+ fputs(contenttype,OutFile); } /* " write buffered headers*/
+ else /* " */
+ OutBuffer = (Byte *)filename; /* " */
+ return GIF_OK;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME Write
+ *
+ * DESCRIPTION Output bytes to the current OutFile.
+ *
+ * INPUT buf pointer to buffer to write
+ * len number of bytes to write
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_ERRWRITE - Error writing to the file
+ */
+static int
+Write(const void *buf, unsigned len)
+{
+ if ( OutBuffer == NULL ) /* (added by j.forkosh) */
+ { if (fwrite(buf, sizeof(Byte), len, OutFile) < len)
+ return GIF_ERRWRITE; }
+ else /* (added by j.forkosh) */
+ { if ( gifSize+len <= maxgifSize ) /* " */
+ memcpy(OutBuffer+gifSize,buf,len); } /* " */
+ gifSize += len; /* " */
+ return GIF_OK;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME WriteByte
+ *
+ * DESCRIPTION Output one byte to the current OutFile.
+ *
+ * INPUT b byte to write
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_ERRWRITE - Error writing to the file
+ */
+static int
+WriteByte(Byte b)
+{
+ if ( OutBuffer == NULL ) /* (added by j.forkosh) */
+ { if (putc(b, OutFile) == EOF)
+ return GIF_ERRWRITE; }
+ else /* (added by j.forkosh) */
+ { if ( gifSize < maxgifSize ) /* " */
+ OutBuffer[gifSize] = b; } /* " */
+ gifSize++; /* " */
+ return GIF_OK;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME WriteWord
+ *
+ * DESCRIPTION Output one word (2 bytes with byte-swapping, like on
+ * the IBM PC) to the current OutFile.
+ *
+ * INPUT w word to write
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_ERRWRITE - Error writing to the file
+ */
+static int
+WriteWord(Word w)
+{
+ if ( OutBuffer == NULL ) /* (added by j.forkosh) */
+ { if (putc(w & 0xFF, OutFile) == EOF)
+ return GIF_ERRWRITE;
+ if (putc((w >> 8), OutFile) == EOF)
+ return GIF_ERRWRITE; }
+ else /* (added by j.forkosh) */
+ if ( gifSize+1 < maxgifSize ) /* " */
+ { OutBuffer[gifSize] = (Byte)(w & 0xFF); /* " */
+ OutBuffer[gifSize+1] = (Byte)(w >> 8); } /* " */
+ gifSize += 2; /* " */
+ return GIF_OK;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME Close
+ *
+ * DESCRIPTION Close current OutFile.
+ */
+static void
+Close(void)
+{
+ if ( isCloseOutFile ) /* (added by j.forkosh) */
+ fclose(OutFile);
+ OutBuffer = NULL; /* (added by j.forkosh) */
+ isCloseOutFile = 0; /* " */
+}
+
+
+
+
+
+/*========================================================================*
+ = =
+ = Routines to write a bit-file =
+ = =
+ *========================================================================*/
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME InitBitFile
+ *
+ * DESCRIPTION Initiate for using a bitfile. All output is sent to
+ * the current OutFile using the I/O-routines above.
+ */
+static void
+InitBitFile(void)
+{
+ Buffer[Index = 0] = 0;
+ BitsLeft = 8;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME ResetOutBitFile
+ *
+ * DESCRIPTION Tidy up after using a bitfile
+ *
+ * RETURNS 0 - OK, -1 - error
+ */
+static int
+ResetOutBitFile(void)
+{
+ Byte numbytes;
+
+ /* how much is in the buffer? */
+ numbytes = Index + (BitsLeft == 8 ? 0 : 1);
+
+ /* write whatever is in the buffer to the file */
+ if (numbytes) {
+ if (WriteByte(numbytes) != GIF_OK)
+ return -1;
+
+ if (Write(Buffer, numbytes) != GIF_OK)
+ return -1;
+
+ Buffer[Index = 0] = 0;
+ BitsLeft = 8;
+ }
+ return 0;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME WriteBits
+ *
+ * DESCRIPTION Put the given number of bits to the outfile.
+ *
+ * INPUT bits bits to write from (right justified)
+ * numbits number of bits to write
+ *
+ * RETURNS bits written, or -1 on error.
+ *
+ */
+static int
+WriteBits(int bits, int numbits)
+{
+ int bitswritten = 0;
+ Byte numbytes = 255;
+
+ do {
+ /* if the buffer is full, write it */
+ if ((Index == 254 && !BitsLeft) || Index > 254) {
+ if (WriteByte(numbytes) != GIF_OK)
+ return -1;
+
+ if (Write(Buffer, numbytes) != GIF_OK)
+ return -1;
+
+ Buffer[Index = 0] = 0;
+ BitsLeft = 8;
+ }
+
+ /* now take care of the two specialcases */
+ if (numbits <= BitsLeft) {
+ Buffer[Index] |= (bits & ((1 << numbits) - 1)) << (8 - BitsLeft);
+ bitswritten += numbits;
+ BitsLeft -= numbits;
+ numbits = 0;
+ } else {
+ Buffer[Index] |= (bits & ((1 << BitsLeft) - 1)) << (8 - BitsLeft);
+ bitswritten += BitsLeft;
+ bits >>= BitsLeft;
+ numbits -= BitsLeft;
+
+ Buffer[++Index] = 0;
+ BitsLeft = 8;
+ }
+ } while (numbits);
+
+ return bitswritten;
+}
+
+
+
+/*========================================================================*
+ = Routines to maintain an LZW-string table =
+ *========================================================================*/
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME FreeStrtab
+ *
+ * DESCRIPTION Free arrays used in string table routines
+ */
+static void
+FreeStrtab(void)
+{
+ if (StrHsh) {
+ free(StrHsh);
+ StrHsh = NULL;
+ }
+ if (StrNxt) {
+ free(StrNxt);
+ StrNxt = NULL;
+ }
+ if (StrChr) {
+ free(StrChr);
+ StrChr = NULL;
+ }
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME AllocStrtab
+ *
+ * DESCRIPTION Allocate arrays used in string table routines
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_OUTMEM - Out of memory
+ */
+static int
+AllocStrtab(void)
+{
+ /* just in case */
+ FreeStrtab();
+
+ if ((StrChr = (Byte *) malloc(MAXSTR * sizeof(Byte))) == 0) {
+ FreeStrtab();
+ return GIF_OUTMEM;
+ }
+ if ((StrNxt = (Word *) malloc(MAXSTR * sizeof(Word))) == 0) {
+ FreeStrtab();
+ return GIF_OUTMEM;
+ }
+ if ((StrHsh = (Word *) malloc(HASHSIZE * sizeof(Word))) == 0) {
+ FreeStrtab();
+ return GIF_OUTMEM;
+ }
+ return GIF_OK;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME AddCharString
+ *
+ * DESCRIPTION Add a string consisting of the string of index plus
+ * the byte b.
+ *
+ * If a string of length 1 is wanted, the index should
+ * be 0xFFFF.
+ *
+ * INPUT index index to first part of string, or 0xFFFF is
+ * only 1 byte is wanted
+ * b last byte in new string
+ *
+ * RETURNS Index to new string, or 0xFFFF if no more room
+ */
+static Word
+AddCharString(Word index, Byte b)
+{
+ Word hshidx;
+
+ /* check if there is more room */
+ if (NumStrings >= MAXSTR)
+ return 0xFFFF;
+
+ /* search the string table until a free position is found */
+ hshidx = HASH(index, b);
+ while (StrHsh[hshidx] != 0xFFFF)
+ hshidx = (hshidx + HASHSTEP) % HASHSIZE;
+
+ /* insert new string */
+ StrHsh[hshidx] = NumStrings;
+ StrChr[NumStrings] = b;
+ StrNxt[NumStrings] = (index != 0xFFFF) ? index : NEXT_FIRST;
+
+ return NumStrings++;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME FindCharString
+ *
+ * DESCRIPTION Find index of string consisting of the string of index
+ * plus the byte b.
+ *
+ * If a string of length 1 is wanted, the index should
+ * be 0xFFFF.
+ *
+ * INPUT index index to first part of string, or 0xFFFF is
+ * only 1 byte is wanted
+ * b last byte in string
+ *
+ * RETURNS Index to string, or 0xFFFF if not found
+ */
+static Word
+FindCharString(Word index, Byte b)
+{
+ Word hshidx, nxtidx;
+
+ /* check if index is 0xFFFF. in that case we need only return b,
+ * since all one-character strings has their bytevalue as their
+ * index */
+ if (index == 0xFFFF)
+ return b;
+
+ /* search the string table until the string is found, or we find
+ * HASH_FREE. in that case the string does not exist. */
+ hshidx = HASH(index, b);
+ while ((nxtidx = StrHsh[hshidx]) != 0xFFFF) {
+ if (StrNxt[nxtidx] == index && StrChr[nxtidx] == b)
+ return nxtidx;
+ hshidx = (hshidx + HASHSTEP) % HASHSIZE;
+ }
+
+ /* no match is found */
+ return 0xFFFF;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME ClearStrtab
+ *
+ * DESCRIPTION Mark the entire table as free, enter the 2**codesize
+ * one-byte strings, and reserve the RES_CODES reserved
+ * codes.
+ *
+ * INPUT codesize
+ * number of bits to encode one pixel
+ */
+static void
+ClearStrtab(int codesize)
+{
+ int q, w;
+ Word *wp;
+
+ /* no strings currently in the table */
+ NumStrings = 0;
+
+ /* mark entire hashtable as free */
+ wp = StrHsh;
+ for (q = 0; q < HASHSIZE; q++)
+ *wp++ = HASH_FREE;
+
+ /* insert 2**codesize one-character strings, and reserved codes */
+ w = (1 << codesize) + RES_CODES;
+ for (q = 0; q < w; q++)
+ AddCharString(0xFFFF, q);
+}
+
+
+
+/*========================================================================*
+ = LZW compression routine =
+ *========================================================================*/
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME LZW_Compress
+ *
+ * DESCRIPTION Perform LZW compression as specified in the
+ * GIF-standard.
+ *
+ * INPUT codesize
+ * number of bits needed to represent
+ * one pixelvalue.
+ * inputbyte
+ * function that fetches each byte to compress.
+ * must return -1 when no more bytes.
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_OUTMEM - Out of memory
+ */
+static int
+LZW_Compress(int codesize, int (*inputbyte)(void))
+{
+ register int c;
+ register Word index;
+ int clearcode, endofinfo, numbits, limit, errcode;
+ Word prefix = 0xFFFF;
+
+ /* set up the given outfile */
+ InitBitFile();
+
+ /* set up variables and tables */
+ clearcode = 1 << codesize;
+ endofinfo = clearcode + 1;
+
+ numbits = codesize + 1;
+ limit = (1 << numbits) - 1;
+
+ if ((errcode = AllocStrtab()) != GIF_OK)
+ return errcode;
+ ClearStrtab(codesize);
+
+ /* first send a code telling the unpacker to clear the stringtable */
+ WriteBits(clearcode, numbits);
+
+ /* pack image */
+ while ((c = inputbyte()) != -1) {
+ /* now perform the packing. check if the prefix + the new
+ * character is a string that exists in the table */
+ if ((index = FindCharString(prefix, c)) != 0xFFFF) {
+ /* the string exists in the table. make this string the
+ * new prefix. */
+ prefix = index;
+ } else {
+ /* the string does not exist in the table. first write
+ * code of the old prefix to the file. */
+ WriteBits(prefix, numbits);
+
+ /* add the new string (the prefix + the new character) to
+ * the stringtable */
+ if (AddCharString(prefix, c) > limit) {
+ if (++numbits > 12) {
+ WriteBits(clearcode, numbits - 1);
+ ClearStrtab(codesize);
+ numbits = codesize + 1;
+ }
+ limit = (1 << numbits) - 1;
+ }
+
+ /* set prefix to a string containing only the character
+ * read. since all possible one-character strings exists
+ * int the table, there's no need to check if it is found. */
+ prefix = c;
+ }
+ }
+
+ /* end of info is reached. write last prefix. */
+ if (prefix != 0xFFFF)
+ WriteBits(prefix, numbits);
+
+ /* erite end of info -mark, flush the buffer, and tidy up */
+ WriteBits(endofinfo, numbits);
+ ResetOutBitFile();
+ FreeStrtab();
+
+ return GIF_OK;
+}
+
+
+
+/*========================================================================*
+ = Other routines =
+ *========================================================================*/
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME BitsNeeded
+ *
+ * DESCRIPTION Calculates number of bits needed to store numbers
+ * between 0 and n - 1
+ *
+ * INPUT n number of numbers to store (0 to n - 1)
+ *
+ * RETURNS Number of bits needed
+ */
+static int
+BitsNeeded(Word n)
+{
+ int ret = 1;
+
+ if (!n--)
+ return 0;
+ while (n >>= 1)
+ ++ret;
+ return ret;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME InputByte
+ *
+ * DESCRIPTION Get next pixel from image. Called by the
+ * LZW_Compress()-function
+ *
+ * RETURNS Next pixelvalue, or -1 if no more pixels
+ */
+static int
+InputByte(void)
+{
+ int ret;
+
+ if (RelPixY >= ImageHeight)
+ return -1;
+ ret = GetPixel(ImageLeft + RelPixX, ImageTop + RelPixY);
+ if (++RelPixX >= ImageWidth) {
+ RelPixX = 0;
+ ++RelPixY;
+ }
+ return ret;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME WriteScreenDescriptor
+ *
+ * DESCRIPTION Output a screen descriptor to the current GIF-file
+ *
+ * INPUT sd pointer to screen descriptor to output
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_ERRWRITE - Error writing to the file
+ */
+static int
+WriteScreenDescriptor(ScreenDescriptor *sd)
+{
+ Byte tmp;
+
+ if (WriteWord(sd->LocalScreenWidth) != GIF_OK)
+ return GIF_ERRWRITE;
+ if (WriteWord(sd->LocalScreenHeight) != GIF_OK)
+ return GIF_ERRWRITE;
+ tmp = (sd->GlobalColorTableFlag << 7)
+ | (sd->ColorResolution << 4)
+ | (sd->SortFlag << 3)
+ | sd->GlobalColorTableSize;
+ if (WriteByte(tmp) != GIF_OK)
+ return GIF_ERRWRITE;
+ if (WriteByte(sd->BackgroundColorIndex) != GIF_OK)
+ return GIF_ERRWRITE;
+ if (WriteByte(sd->PixelAspectRatio) != GIF_OK)
+ return GIF_ERRWRITE;
+
+ return GIF_OK;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME WriteTransparentColorIndex (added by j.forkosh)
+ *
+ * DESCRIPTION Output a graphic extension block setting transparent
+ * colormap index
+ *
+ * INPUT colornum colormap index of color to be transparent
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_ERRWRITE - Error writing to the file
+ */
+static int
+WriteTransparentColorIndex(int colornum)
+{
+ if ( colornum < 0 ) return GIF_OK; /*no transparent color set*/
+ if (WriteByte((Byte)(0x21)) != GIF_OK) /*magic:Extension Introducer*/
+ return GIF_ERRWRITE;
+ if (WriteByte((Byte)(0xf9)) != GIF_OK) /*magic:Graphic Control Label*/
+ return GIF_ERRWRITE;
+ if (WriteByte((Byte)(4)) != GIF_OK) /* #bytes in block */
+ return GIF_ERRWRITE;
+ if (WriteByte((Byte)(1)) != GIF_OK) /*transparent index indicator*/
+ return GIF_ERRWRITE;
+ if (WriteWord((Word)(0)) != GIF_OK) /* delay time */
+ return GIF_ERRWRITE;
+ if (WriteByte((Byte)(colornum)) != GIF_OK) /* transparent color index */
+ return GIF_ERRWRITE;
+ if (WriteByte((Byte)(0)) != GIF_OK) /* terminator */
+ return GIF_ERRWRITE;
+
+ return GIF_OK;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME WriteImageDescriptor
+ *
+ * DESCRIPTION Output an image descriptor to the current GIF-file
+ *
+ * INPUT id pointer to image descriptor to output
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_ERRWRITE - Error writing to the file
+ */
+static int
+WriteImageDescriptor(ImageDescriptor *id)
+{
+ Byte tmp;
+
+ if (WriteByte(id->Separator) != GIF_OK)
+ return GIF_ERRWRITE;
+ if (WriteWord(id->LeftPosition) != GIF_OK)
+ return GIF_ERRWRITE;
+ if (WriteWord(id->TopPosition) != GIF_OK)
+ return GIF_ERRWRITE;
+ if (WriteWord(id->Width) != GIF_OK)
+ return GIF_ERRWRITE;
+ if (WriteWord(id->Height) != GIF_OK)
+ return GIF_ERRWRITE;
+ tmp = (id->LocalColorTableFlag << 7)
+ | (id->InterlaceFlag << 6)
+ | (id->SortFlag << 5)
+ | (id->Reserved << 3)
+ | id->LocalColorTableSize;
+ if (WriteByte(tmp) != GIF_OK)
+ return GIF_ERRWRITE;
+
+ return GIF_OK;
+}
+
+
+
+/**************************************************************************
+ * *
+ * P U B L I C F U N C T I O N S *
+ * *
+ **************************************************************************/
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME GIF_Create
+ *
+ * DESCRIPTION Create a GIF-file, and write headers for both screen
+ * and image.
+ *
+ * INPUT filename
+ * name of file to create (including extension)
+ * width number of horisontal pixels on screen
+ * height number of vertical pixels on screen
+ * numcolors
+ * number of colors in the colormaps
+ * colorres
+ * color resolution. Number of bits for each
+ * primary color
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_ERRCREATE - Couldn't create file
+ * GIF_ERRWRITE - Error writing to the file
+ * GIF_OUTMEM - Out of memory allocating color table
+ */
+int
+GIF_Create(const char *filename, int width, int height,
+ int numcolors, int colorres)
+{
+ int q, tabsize;
+ Byte *bp;
+ ScreenDescriptor SD;
+
+ /* initiate variables for new GIF-file */
+ NumColors = numcolors ? (1 << BitsNeeded(numcolors)) : 0;
+ BitsPrPrimColor = colorres;
+ ScreenHeight = height;
+ ScreenWidth = width;
+
+ /* create file specified */
+ if (Create(filename) != GIF_OK)
+ return GIF_ERRCREATE;
+
+ /* write GIF signature */
+ if ((Write("GIF87a", 6)) != GIF_OK)
+ return GIF_ERRWRITE;
+
+ /* initiate and write screen descriptor */
+ SD.LocalScreenWidth = width;
+ SD.LocalScreenHeight = height;
+ if (NumColors) {
+ SD.GlobalColorTableSize = BitsNeeded(NumColors) - 1;
+ SD.GlobalColorTableFlag = 1;
+ } else {
+ SD.GlobalColorTableSize = 0;
+ SD.GlobalColorTableFlag = 0;
+ }
+ SD.SortFlag = 0;
+ SD.ColorResolution = colorres - 1;
+ SD.BackgroundColorIndex = 0;
+ SD.PixelAspectRatio = 0;
+ if (WriteScreenDescriptor(&SD) != GIF_OK)
+ return GIF_ERRWRITE;
+
+ /* allocate color table */
+ if (ColorTable) {
+ free(ColorTable);
+ ColorTable = NULL;
+ }
+ if (NumColors) {
+ tabsize = NumColors * 3;
+ if ((ColorTable = (Byte *) malloc(tabsize * sizeof(Byte))) == NULL)
+ return GIF_OUTMEM;
+ else {
+ bp = ColorTable;
+ for (q = 0; q < tabsize; q++)
+ *bp++ = 0;
+ }
+ }
+ return 0;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME GIF_SetColor
+ *
+ * DESCRIPTION Set red, green and blue components of one of the
+ * colors. The color components are all in the range
+ * [0, (1 << BitsPrPrimColor) - 1]
+ *
+ * INPUT colornum
+ * color number to set. [0, NumColors - 1]
+ * red red component of color
+ * green green component of color
+ * blue blue component of color
+ */
+void
+GIF_SetColor(int colornum, int red, int green, int blue)
+{
+ long maxcolor;
+ Byte *p;
+
+ maxcolor = (1L << BitsPrPrimColor) - 1L;
+ p = ColorTable + colornum * 3;
+ *p++ = (Byte) ((red * 255L) / maxcolor);
+ *p++ = (Byte) ((green * 255L) / maxcolor);
+ *p++ = (Byte) ((blue * 255L) / maxcolor);
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME GIF_SetTransparent (added by j.forkosh)
+ *
+ * DESCRIPTION Set colormap index of color to be transparent
+ *
+ * INPUT colornum
+ * color number to set transparent. [0, NumColors - 1]
+ */
+void
+GIF_SetTransparent(int colornum)
+{
+ TransparentColorIndex = colornum;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME GIF_CompressImage
+ *
+ * DESCRIPTION Compress an image into the GIF-file previousely
+ * created using GIF_Create(). All color values should
+ * have been specified before this function is called.
+ *
+ * The pixels are retrieved using a user defined callback
+ * function. This function should accept two parameters,
+ * x and y, specifying which pixel to retrieve. The pixel
+ * values sent to this function are as follows:
+ *
+ * x : [ImageLeft, ImageLeft + ImageWidth - 1]
+ * y : [ImageTop, ImageTop + ImageHeight - 1]
+ *
+ * The function should return the pixel value for the
+ * point given, in the interval [0, NumColors - 1]
+ *
+ * INPUT left screen-relative leftmost pixel x-coordinate
+ * of the image
+ * top screen-relative uppermost pixel y-coordinate
+ * of the image
+ * width width of the image, or -1 if as wide as
+ * the screen
+ * height height of the image, or -1 if as high as
+ * the screen
+ * getpixel
+ * address of user defined callback function.
+ * (see above)
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_OUTMEM - Out of memory
+ * GIF_ERRWRITE - Error writing to the file
+ */
+int
+GIF_CompressImage(int left, int top, int width, int height,
+ int (*getpixel)(int x, int y))
+{
+ int codesize, errcode;
+ ImageDescriptor ID;
+
+ if (width < 0) {
+ width = ScreenWidth;
+ left = 0;
+ }
+ if (height < 0) {
+ height = ScreenHeight;
+ top = 0;
+ }
+ if (left < 0)
+ left = 0;
+ if (top < 0)
+ top = 0;
+
+ /* write global colortable if any */
+ if (NumColors)
+ if ((Write(ColorTable, NumColors * 3)) != GIF_OK)
+ return GIF_ERRWRITE;
+
+ /* write graphic extension block with transparent color index */
+ if ( TransparentColorIndex >= 0 ) /* (added by j.forkosh) */
+ if ( WriteTransparentColorIndex(TransparentColorIndex)
+ != GIF_OK ) return GIF_ERRWRITE;
+
+ /* initiate and write image descriptor */
+ ID.Separator = ',';
+ ID.LeftPosition = ImageLeft = left;
+ ID.TopPosition = ImageTop = top;
+ ID.Width = ImageWidth = width;
+ ID.Height = ImageHeight = height;
+ ID.LocalColorTableSize = 0;
+ ID.Reserved = 0;
+ ID.SortFlag = 0;
+ ID.InterlaceFlag = 0;
+ ID.LocalColorTableFlag = 0;
+
+ if (WriteImageDescriptor(&ID) != GIF_OK)
+ return GIF_ERRWRITE;
+
+ /* write code size */
+ codesize = BitsNeeded(NumColors);
+ if (codesize == 1)
+ ++codesize;
+ if (WriteByte(codesize) != GIF_OK)
+ return GIF_ERRWRITE;
+
+ /* perform compression */
+ RelPixX = RelPixY = 0;
+ GetPixel = getpixel;
+ if ((errcode = LZW_Compress(codesize, InputByte)) != GIF_OK)
+ return errcode;
+
+ /* write terminating 0-byte */
+ if (WriteByte(0) != GIF_OK)
+ return GIF_ERRWRITE;
+
+ return GIF_OK;
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * NAME GIF_Close
+ *
+ * DESCRIPTION Close the GIF-file
+ *
+ * RETURNS GIF_OK - OK
+ * GIF_ERRWRITE - Error writing to file
+ */
+int
+GIF_Close(void)
+{
+ ImageDescriptor ID;
+
+ /* initiate and write ending image descriptor */
+ ID.Separator = ';';
+ ID.LeftPosition = 0; /* (added by j.forkosh) */
+ ID.TopPosition = 0; /* " initialize entire ID structure */
+ ID.Width = 0; /* " and ditto for other ID.x=0; below */
+ ID.Height = 0;
+ ID.LocalColorTableSize = 0;
+ ID.Reserved = 0;
+ ID.SortFlag = 0;
+ ID.InterlaceFlag = 0;
+ ID.LocalColorTableFlag = 0;
+
+ if (WriteImageDescriptor(&ID) != GIF_OK)
+ return GIF_ERRWRITE;
+
+ /* close file */
+ Close();
+
+ /* release color table */
+ if (ColorTable) {
+ free(ColorTable);
+ ColorTable = NULL;
+ }
+
+ return GIF_OK;
+}
+/* --- end-of-file gifsave.c --- */