summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordkf <donal.k.fellows@manchester.ac.uk>2010-07-18 23:58:17 (GMT)
committerdkf <donal.k.fellows@manchester.ac.uk>2010-07-18 23:58:17 (GMT)
commitaa15eba7248d4f511d831e0536eb937cedb885d9 (patch)
treecb07b940dd34677f3356dd9b5b1b50091b05d66f
parenta5b009796383e0a829c96ed6ac24c3b035f55fde (diff)
downloadtk-aa15eba7248d4f511d831e0536eb937cedb885d9.zip
tk-aa15eba7248d4f511d831e0536eb937cedb885d9.tar.gz
tk-aa15eba7248d4f511d831e0536eb937cedb885d9.tar.bz2
* generic/tkImgGIF.c (StringWriteGIF): Added ability to write a GIF to
a string (as a byte array, of course) following comments on c.l.t by Aric Bills. Also improved readability of some of the function and field names in this file.
-rw-r--r--ChangeLog56
-rw-r--r--generic/tkImgGIF.c688
2 files changed, 402 insertions, 342 deletions
diff --git a/ChangeLog b/ChangeLog
index 06cf416..4c6e993 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,36 +1,44 @@
+2010-07-19 Donal K. Fellows <dkf@users.sf.net>
+
+ * generic/tkImgGIF.c (StringWriteGIF): Added ability to write a GIF to
+ a string (as a byte array, of course) following comments on c.l.t by
+ Aric Bills. Also improved readability of some of the function and
+ field names in this file.
+
2010-07-16 Jan Nijtmans <nijtmans@users.sf.net>
- * generic/tkDecls.h [Tcl Bug 3029891] Functions that don't belong in the
- stub table (Tk part, not really removed from the stub table, just disabled)
- * generic/tkMain.c [Bug 3027438] Tk_Main calls Tcl_CreateInterp before
- * generic/tk.h Tcl_FindExecutable
+ * generic/tkDecls.h: [Tcl Bug 3029891]: Functions that don't belong in
+ the stub table (Tk part, not really removed from the stub table, just
+ disabled)
+ * generic/tkMain.c: [Bug 3027438]: Tk_Main calls Tcl_CreateInterp
+ * generic/tk.h: before Tcl_FindExecutable
2010-07-06 Andreas Kupries <andreask@activestate.com>
- * doc/text.n: Fixed minor typo in the description of 'text
- delete', as reported by <eee@users.sf.net> on the chat.
+ * doc/text.n: Fixed minor typo in the description of 'text delete', as
+ reported by <eee@users.sf.net> on the chat.
2010-07-01 Jan Nijtmans <nijtmans@users.sf.net>
- * win/rules.vc [Bugs #3020677] wish can't link reg1.2
+ * win/rules.vc: [Bug 3020677]: wish can't link reg1.2
2010-06-22 Anton Kovalenko <a_kovalenko@users.sf.net>
- * generic/tkPlace.c: [Patch #3019624] modify "place" command,
- making it get main window through ClientData (like grid or pack
- do), instead of calling Tk_MainWindow(interp).
- * generic/tkWindow.c: modify "place" entry in commands[],
- turn on passMainWindow flag.
+ * generic/tkPlace.c: [Patch 3019624]: modify "place" command, making
+ it get main window through ClientData (like grid or pack do), instead
+ of calling Tk_MainWindow(interp).
+ * generic/tkWindow.c: modify "place" entry in commands[], turn on
+ passMainWindow flag.
2010-06-22 Jan Nijtmans <nijtmans@users.sf.net>
- * generic/ttk/ttkGenStubs.tcl [Bug #3019363] "make genstubs" failure
- * generic/ttk/ttkDecls.h (regenerated)
- * generic/ttk/ttkTheme.c Unnecessary type cast
+ * generic/ttk/ttkGenStubs.tcl: [Bug 3019363]: "make genstubs" failure
+ * generic/ttk/ttkDecls.h: (regenerated)
+ * generic/ttk/ttkTheme.c: Unnecessary type cast
2010-06-21 Jan Nijtmans <nijtmans@users.sf.net>
- * generic/tkInt.decls [Patch #2999889] TkCopyAndGlobalEval obsolete
+ * generic/tkInt.decls: [Patch 2999889]: TkCopyAndGlobalEval obsolete
* generic/tkIntDecls.h
* generic/tkBind.c
* generic/tkStubInit.c
@@ -38,21 +46,21 @@
2010-06-19 Joe English <jenglish@users.sourceforge.net>
* win/tkWinScrlbr.c, carbon/tkMacOSXScrlbr.c: Replace binding
- procedures with ordinary event handlers [Patch 3009998].
+ procedures with ordinary event handlers [Patch 3009998].
* generic/tkBind.c, generic/tk.h, generic/tkInt.h,
- generic/tkInt.decls: Simplifications enabled by previous change:
- TkCreateBindingProcedure() and associated machinery
- no longer needed; TkBindDeadWindow() no longer needed;
- TK_DEFER_MODAL_LOOP and associated machinery no longer needed.
+ * generic/tkInt.decls: Simplifications enabled by previous change:
+ TkCreateBindingProcedure() and associated machinery no longer needed;
+ TkBindDeadWindow() no longer needed; TK_DEFER_MODAL_LOOP and
+ associated machinery no longer needed.
* generic/tkTest.c, tests/bind.test: Tests related to C binding
- procedures no longer needed.
+ procedures no longer needed.
* generic/tkWindow.c: TkBindDeadWindow() no longer needed.
* generic/tkIntDecls.h, generic/tkStubInit.c: Regenerated.
2010-06-15 Joe English <jenglish@users.sourceforge.net>
- * library/ttk/ttk.tcl: Bump dummy [package ifneeded tile] version
- to 0.8.6; see [Bug 3016598].
+ * library/ttk/ttk.tcl: Bump dummy [package ifneeded tile] version to
+ 0.8.6; see [Bug 3016598].
2010-06-15 Donal K. Fellows <dkf@users.sf.net>
diff --git a/generic/tkImgGIF.c b/generic/tkImgGIF.c
index b7bb956..0ece066 100644
--- a/generic/tkImgGIF.c
+++ b/generic/tkImgGIF.c
@@ -11,7 +11,7 @@
* Copyright (c) Reed Wade (wade@cs.utk.edu), University of Tennessee
* Copyright (c) 1995-1997 Sun Microsystems, Inc.
* Copyright (c) 1997 Australian National University
- * Copyright (c) 2005 Donal K. Fellows
+ * Copyright (c) 2005-2010 Donal K. Fellows
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -29,7 +29,7 @@
* | provided "as is" without express or implied warranty. |
* +--------------------------------------------------------------------+
*
- * RCS: @(#) $Id: tkImgGIF.c,v 1.47 2010/04/23 08:32:56 nijtmans Exp $
+ * RCS: @(#) $Id: tkImgGIF.c,v 1.48 2010/07/18 23:58:17 dkf Exp $
*/
#include "tkInt.h"
@@ -109,6 +109,14 @@ typedef struct {
} GIFImageConfig;
/*
+ * Type of a function used to do the writing to a file or buffer when
+ * serializing in the GIF format.
+ */
+
+typedef int (WriteBytesFunc) (ClientData clientData, const char *bytes,
+ int byteCount);
+
+/*
* The format record for the GIF file format:
*/
@@ -127,8 +135,11 @@ static int StringReadGIF(Tcl_Interp *interp, Tcl_Obj *dataObj,
int srcX, int srcY);
static int FileWriteGIF(Tcl_Interp *interp, const char *filename,
Tcl_Obj *format, Tk_PhotoImageBlock *blockPtr);
-static int CommonWriteGIF(Tcl_Interp *interp, Tcl_Channel handle,
- Tcl_Obj *format, Tk_PhotoImageBlock *blockPtr);
+static int StringWriteGIF(Tcl_Interp *interp, Tcl_Obj *format,
+ Tk_PhotoImageBlock *blockPtr);
+static int CommonWriteGIF(Tcl_Interp *interp, ClientData clientData,
+ WriteBytesFunc *writeProc, Tcl_Obj *format,
+ Tk_PhotoImageBlock *blockPtr);
Tk_PhotoImageFormat tkImgFmtGIF = {
"gif", /* name */
@@ -137,7 +148,7 @@ Tk_PhotoImageFormat tkImgFmtGIF = {
FileReadGIF, /* fileReadProc */
StringReadGIF, /* stringReadProc */
FileWriteGIF, /* fileWriteProc */
- NULL, /* stringWriteProc */
+ StringWriteGIF, /* stringWriteProc */
NULL
};
@@ -186,6 +197,135 @@ static int Mgetc(MFile *handle);
static int char64(int c);
static void mInit(unsigned char *string, MFile *handle,
int length);
+
+/*
+ * Types, defines and variables needed to write and compress a GIF.
+ */
+
+#define LSB(a) ((unsigned char) (((short)(a)) & 0x00FF))
+#define MSB(a) ((unsigned char) (((short)(a)) >> 8))
+
+#define GIFBITS 12
+#define HSIZE 5003 /* 80% occupancy */
+
+#define DEFAULT_BACKGROUND_VALUE 0xD9
+
+typedef struct {
+ int ssize;
+ int csize;
+ int rsize;
+ unsigned char *pixelOffset;
+ int pixelSize;
+ int pixelPitch;
+ int greenOffset;
+ int blueOffset;
+ int alphaOffset;
+ int num;
+ unsigned char mapa[MAXCOLORMAPSIZE][3];
+} GifWriterState;
+
+typedef int (* ifunptr) (GifWriterState *statePtr);
+
+/*
+ * Support for compression of GIFs.
+ */
+
+#define MAXCODE(numBits) (((long) 1 << (numBits)) - 1)
+
+#ifdef SIGNED_COMPARE_SLOW
+#define U(x) ((unsigned) (x))
+#else
+#define U(x) (x)
+#endif
+
+typedef struct {
+ int numBits; /* Number of bits/code. */
+ long maxCode; /* Maximum code, given numBits. */
+ int hashTable[HSIZE];
+ unsigned int codeTable[HSIZE];
+ long hSize; /* For dynamic table sizing. */
+
+ /*
+ * To save much memory, we overlay the table used by compress() with those
+ * used by decompress(). The tab_prefix table is the same size and type as
+ * the codeTable. The tab_suffix table needs 2**GIFBITS characters. We get
+ * this from the beginning of hashTable. The output stack uses the rest of
+ * hashTable, and contains characters. There is plenty of room for any
+ * possible stack (stack used to be 8000 characters).
+ */
+
+ int freeEntry; /* First unused entry. */
+
+ /*
+ * Block compression parameters. After all codes are used up, and
+ * compression rate changes, start over.
+ */
+
+ int clearFlag;
+
+ int offset;
+ unsigned int inCount; /* Length of input */
+ unsigned int outCount; /* # of codes output (for debugging) */
+
+ /*
+ * Algorithm: use open addressing double hashing (no chaining) on the
+ * prefix code / next character combination. We do a variant of Knuth's
+ * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+ * secondary probe. Here, the modular division first probe is gives way to
+ * a faster exclusive-or manipulation. Also do block compression with an
+ * adaptive reset, whereby the code table is cleared when the compression
+ * ratio decreases, but after the table fills. The variable-length output
+ * codes are re-sized at this point, and a special CLEAR code is generated
+ * for the decompressor. Late addition: construct the table according to
+ * file size for noticeable speed improvement on small files. Please
+ * direct questions about this implementation to ames!jaw.
+ */
+
+ int initialBits;
+ ClientData destination;
+ WriteBytesFunc *writeProc;
+
+ int clearCode;
+ int eofCode;
+
+ unsigned long currentAccumulated;
+ int currentBits;
+
+ /*
+ * Number of characters so far in this 'packet'
+ */
+
+ int accumulatedByteCount;
+
+ /*
+ * Define the storage for the packet accumulator
+ */
+
+ unsigned char packetAccumulator[256];
+} GIFState_t;
+
+/*
+ * Definition of new functions to write GIFs
+ */
+
+static int ColorNumber(GifWriterState *statePtr,
+ int red, int green, int blue);
+static void Compress(int initBits, ClientData handle,
+ WriteBytesFunc *writeProc, ifunptr readValue,
+ GifWriterState *statePtr);
+static int IsNewColor(GifWriterState *statePtr,
+ int red, int green, int blue);
+static void SaveMap(GifWriterState *statePtr,
+ Tk_PhotoImageBlock *blockPtr);
+static int ReadValue(GifWriterState *statePtr);
+static WriteBytesFunc WriteToChannel;
+static WriteBytesFunc WriteToByteArray;
+static void Output(GIFState_t *statePtr, long code);
+static void ClearForBlock(GIFState_t *statePtr);
+static void ClearHashTable(GIFState_t *statePtr, int hSize);
+static void CharInit(GIFState_t *statePtr);
+static void CharOut(GIFState_t *statePtr, int c);
+static void FlushChar(GIFState_t *statePtr);
/*
*----------------------------------------------------------------------
@@ -1424,51 +1564,12 @@ Fread(
* data from a photo image into a given file
*
* Results:
- * A standard TCL completion code. If TCL_ERROR is returned
- * then an error message is left in the interp's result.
+ * A standard TCL completion code. If TCL_ERROR is returned then an
+ * error message is left in the interp's result.
*
*----------------------------------------------------------------------
*/
-/*
- * Types, defines and variables needed to write and compress a GIF.
- */
-
-#define LSB(a) ((unsigned char) (((short)(a)) & 0x00FF))
-#define MSB(a) ((unsigned char) (((short)(a)) >> 8))
-
-#define GIFBITS 12
-#define HSIZE 5003 /* 80% occupancy */
-
-typedef struct {
- int ssize;
- int csize;
- int rsize;
- unsigned char *pixelo;
- int pixelSize;
- int pixelPitch;
- int greenOffset;
- int blueOffset;
- int alphaOffset;
- int num;
- unsigned char mapa[MAXCOLORMAPSIZE][3];
-} GifWriterState;
-
-typedef int (* ifunptr) (GifWriterState *statePtr);
-
-/*
- * Definition of new functions to write GIFs
- */
-
-static int color(GifWriterState *statePtr,
- int red, int green, int blue);
-static void compress(int initBits, Tcl_Channel handle,
- ifunptr readValue, GifWriterState *statePtr);
-static int nuevo(GifWriterState *statePtr,
- int red, int green, int blue);
-static void savemap(GifWriterState *statePtr,
- Tk_PhotoImageBlock *blockPtr);
-static int ReadValue(GifWriterState *statePtr);
static int
FileWriteGIF(
@@ -1490,18 +1591,65 @@ FileWriteGIF(
return TCL_ERROR;
}
- result = CommonWriteGIF(interp, chan, format, blockPtr);
+ result = CommonWriteGIF(interp, chan, WriteToChannel, format, blockPtr);
if (Tcl_Close(interp, chan) == TCL_ERROR) {
return TCL_ERROR;
}
return result;
}
+
+static int
+StringWriteGIF(
+ Tcl_Interp *interp, /* Interpreter to use for reporting errors and
+ * returning the GIF data. */
+ Tcl_Obj *format,
+ Tk_PhotoImageBlock *blockPtr)
+{
+ int result;
+ Tcl_Obj *objPtr = Tcl_NewObj();
+
+ Tcl_IncrRefCount(objPtr);
+ result = CommonWriteGIF(interp, objPtr, WriteToByteArray, format,
+ blockPtr);
+ if (result == TCL_OK) {
+ Tcl_SetObjResult(interp, objPtr);
+ }
+ Tcl_DecrRefCount(objPtr);
+ return result;
+}
+
+static int
+WriteToChannel(
+ ClientData clientData,
+ const char *bytes,
+ int byteCount)
+{
+ Tcl_Channel handle = clientData;
+
+ return Tcl_Write(handle, bytes, byteCount);
+}
+
+static int
+WriteToByteArray(
+ ClientData clientData,
+ const char *bytes,
+ int byteCount)
+{
+ Tcl_Obj *objPtr = clientData;
+ Tcl_Obj *tmpObj = Tcl_NewByteArrayObj((unsigned char *) bytes, byteCount);
+
+ Tcl_IncrRefCount(tmpObj);
+ Tcl_AppendObjToObj(objPtr, tmpObj);
+ Tcl_DecrRefCount(tmpObj);
+ return byteCount;
+}
static int
CommonWriteGIF(
Tcl_Interp *interp,
- Tcl_Channel handle,
+ ClientData handle,
+ WriteBytesFunc *writeProc,
Tcl_Obj *format,
Tk_PhotoImageBlock *blockPtr)
{
@@ -1529,7 +1677,7 @@ CommonWriteGIF(
state.alphaOffset = 0;
}
- Tcl_Write(handle, (char *) (state.alphaOffset ? GIF89a : GIF87a), 6);
+ writeProc(handle, (char *) (state.alphaOffset ? GIF89a : GIF87a), 6);
for (x = 0; x < MAXCOLORMAPSIZE ;x++) {
state.mapa[x][CM_RED] = 255;
@@ -1539,9 +1687,9 @@ CommonWriteGIF(
width = blockPtr->width;
height = blockPtr->height;
- state.pixelo = blockPtr->pixelPtr + blockPtr->offset[0];
+ state.pixelOffset = blockPtr->pixelPtr + blockPtr->offset[0];
state.pixelPitch = blockPtr->pitch;
- savemap(&state, blockPtr);
+ SaveMap(&state, blockPtr);
if (state.num >= MAXCOLORMAPSIZE) {
Tcl_AppendResult(interp, "too many colors", NULL);
return TCL_ERROR;
@@ -1550,20 +1698,20 @@ CommonWriteGIF(
state.num = 2;
}
c = LSB(width);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = MSB(width);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = LSB(height);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = MSB(height);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
resolution = 0;
while (state.num >> resolution) {
resolution++;
}
c = 111 + resolution * 17;
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
state.num = 1 << resolution;
@@ -1572,21 +1720,21 @@ CommonWriteGIF(
*/
c = 0;
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
/*
* Zero for future expansion.
*/
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
for (x = 0; x < state.num; x++) {
c = state.mapa[x][CM_RED];
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = state.mapa[x][CM_GREEN];
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = state.mapa[x][CM_BLUE];
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
}
/*
@@ -1595,50 +1743,50 @@ CommonWriteGIF(
if (state.alphaOffset) {
c = GIF_EXTENSION;
- Tcl_Write(handle, (char *) &c, 1);
- Tcl_Write(handle, "\371\4\1\0\0\0", 7);
+ writeProc(handle, (char *) &c, 1);
+ writeProc(handle, "\371\4\1\0\0\0", 7);
}
c = GIF_START;
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = LSB(top);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = MSB(top);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = LSB(left);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = MSB(left);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = LSB(width);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = MSB(width);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = LSB(height);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = MSB(height);
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = 0;
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = resolution;
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
state.ssize = state.rsize = blockPtr->width;
state.csize = blockPtr->height;
- compress(resolution+1, handle, ReadValue, &state);
+ Compress(resolution+1, handle, writeProc, ReadValue, &state);
c = 0;
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
c = GIF_TERMINATOR;
- Tcl_Write(handle, (char *) &c, 1);
+ writeProc(handle, (char *) &c, 1);
return TCL_OK;
}
static int
-color(
+ColorNumber(
GifWriterState *statePtr,
int red, int green, int blue)
{
@@ -1655,7 +1803,7 @@ color(
}
static int
-nuevo(
+IsNewColor(
GifWriterState *statePtr,
int red, int green, int blue)
{
@@ -1672,7 +1820,7 @@ nuevo(
}
static void
-savemap(
+SaveMap(
GifWriterState *statePtr,
Tk_PhotoImageBlock *blockPtr)
{
@@ -1682,9 +1830,9 @@ savemap(
if (statePtr->alphaOffset) {
statePtr->num = 0;
- statePtr->mapa[0][CM_RED] = 0xd9;
- statePtr->mapa[0][CM_GREEN] = 0xd9;
- statePtr->mapa[0][CM_BLUE] = 0xd9;
+ statePtr->mapa[0][CM_RED] = DEFAULT_BACKGROUND_VALUE;
+ statePtr->mapa[0][CM_GREEN] = DEFAULT_BACKGROUND_VALUE;
+ statePtr->mapa[0][CM_BLUE] = DEFAULT_BACKGROUND_VALUE;
} else {
statePtr->num = -1;
}
@@ -1696,7 +1844,7 @@ savemap(
red = colores[0];
green = colores[statePtr->greenOffset];
blue = colores[statePtr->blueOffset];
- if (nuevo(statePtr, red, green, blue)) {
+ if (IsNewColor(statePtr, red, green, blue)) {
statePtr->num++;
if (statePtr->num >= MAXCOLORMAPSIZE) {
return;
@@ -1720,18 +1868,19 @@ ReadValue(
if (statePtr->csize == 0) {
return EOF;
}
- if (statePtr->alphaOffset && (statePtr->pixelo[statePtr->alphaOffset]==0)) {
+ if (statePtr->alphaOffset
+ && (statePtr->pixelOffset[statePtr->alphaOffset]==0)) {
col = 0;
} else {
- col = color(statePtr, statePtr->pixelo[0],
- statePtr->pixelo[statePtr->greenOffset],
- statePtr->pixelo[statePtr->blueOffset]);
+ col = ColorNumber(statePtr, statePtr->pixelOffset[0],
+ statePtr->pixelOffset[statePtr->greenOffset],
+ statePtr->pixelOffset[statePtr->blueOffset]);
}
- statePtr->pixelo += statePtr->pixelSize;
+ statePtr->pixelOffset += statePtr->pixelSize;
if (--statePtr->ssize <= 0) {
statePtr->ssize = statePtr->rsize;
statePtr->csize--;
- statePtr->pixelo += statePtr->pixelPitch
+ statePtr->pixelOffset += statePtr->pixelPitch
- (statePtr->rsize * statePtr->pixelSize);
}
@@ -1739,7 +1888,7 @@ ReadValue(
}
/*
- * GIF Image compression - modified 'compress'
+ * GIF Image compression - modified 'Compress'
*
* Based on: compress.c - File compression ala IEEE Computer, June 1984.
*
@@ -1750,192 +1899,98 @@ ReadValue(
* James A. Woods (decvax!ihnp4!ames!jaw)
* Joe Orost (decvax!vax135!petsd!joe)
*/
-
-#define MAXCODE(n_bits) (((long) 1 << (n_bits)) - 1)
-
-typedef struct {
- int n_bits; /* number of bits/code */
- long maxcode; /* maximum code, given n_bits */
- int htab[HSIZE];
- unsigned int codetab[HSIZE];
- long hsize; /* for dynamic table sizing */
-
- /*
- * To save much memory, we overlay the table used by compress() with those
- * used by decompress(). The tab_prefix table is the same size and type as
- * the codetab. The tab_suffix table needs 2**GIFBITS characters. We get
- * this from the beginning of htab. The output stack uses the rest of
- * htab, and contains characters. There is plenty of room for any possible
- * stack (stack used to be 8000 characters).
- */
-
- int free_ent; /* first unused entry */
-
- /*
- * block compression parameters -- after all codes are used up,
- * and compression rate changes, start over.
- */
-
- int clear_flg;
-
- int offset;
- unsigned int in_count; /* length of input */
- unsigned int out_count; /* # of codes output (for debugging) */
-
- /*
- * compress stdin to stdout
- *
- * Algorithm: use open addressing double hashing (no chaining) on the
- * prefix code / next character combination. We do a variant of Knuth's
- * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
- * secondary probe. Here, the modular division first probe is gives way to
- * a faster exclusive-or manipulation. Also do block compression with an
- * adaptive reset, whereby the code table is cleared when the compression
- * ratio decreases, but after the table fills. The variable-length output
- * codes are re-sized at this point, and a special CLEAR code is generated
- * for the decompressor. Late addition: construct the table according to
- * file size for noticeable speed improvement on small files. Please
- * direct questions about this implementation to ames!jaw.
- */
-
- int g_init_bits;
- Tcl_Channel g_outfile;
-
- int ClearCode;
- int EOFCode;
-
- unsigned long cur_accum;
- int cur_bits;
-
- /*
- * Number of characters so far in this 'packet'
- */
-
- int a_count;
-
- /*
- * Define the storage for the packet accumulator
- */
-
- unsigned char accum[256];
-} GIFState_t;
-
-static void output(GIFState_t *statePtr, long code);
-static void cl_block(GIFState_t *statePtr);
-static void cl_hash(GIFState_t *statePtr, int hsize);
-static void char_init(GIFState_t *statePtr);
-static void char_out(GIFState_t *statePtr, int c);
-static void flush_char(GIFState_t *statePtr);
-static void compress(int initBits, Tcl_Channel handle,
- ifunptr readValue, GifWriterState *statePtr);
static void
-compress(
- int init_bits,
- Tcl_Channel handle,
+Compress(
+ int initialBits,
+ ClientData handle,
+ WriteBytesFunc *writeProc,
ifunptr readValue,
GifWriterState *statePtr)
{
- register long fcode;
- register long i = 0;
- register int c;
- register long ent;
- register long disp;
- register long hsize_reg;
- register int hshift;
+ long fcode, ent, disp, hSize, i = 0;
+ int c, hshift;
GIFState_t state;
memset(&state, 0, sizeof(state));
/*
- * Set up the globals: g_init_bits - initial number of bits
- * g_outfile - pointer to output file
+ * Set up the globals: initialBits - initial number of bits
+ * outChannel - pointer to output file
*/
- state.g_init_bits = init_bits;
- state.g_outfile = handle;
+ state.initialBits = initialBits;
+ state.destination = handle;
+ state.writeProc = writeProc;
/*
* Set up the necessary values.
*/
state.offset = 0;
- state.hsize = HSIZE;
- state.out_count = 0;
- state.clear_flg = 0;
- state.in_count = 1;
- state.maxcode = MAXCODE(state.n_bits = state.g_init_bits);
-
- state.ClearCode = (1 << (init_bits - 1));
- state.EOFCode = state.ClearCode + 1;
- state.free_ent = state.ClearCode + 2;
-
- char_init(&state);
+ state.hSize = HSIZE;
+ state.outCount = 0;
+ state.clearFlag = 0;
+ state.inCount = 1;
+ state.maxCode = MAXCODE(state.numBits = state.initialBits);
+ state.clearCode = 1 << (initialBits - 1);
+ state.eofCode = state.clearCode + 1;
+ state.freeEntry = state.clearCode + 2;
+ CharInit(&state);
ent = readValue(statePtr);
hshift = 0;
- for (fcode = (long) state.hsize; fcode < 65536L; fcode *= 2L) {
+ for (fcode = (long) state.hSize; fcode < 65536L; fcode *= 2L) {
hshift++;
}
- hshift = 8 - hshift; /* set hash code range bound */
+ hshift = 8 - hshift; /* Set hash code range bound */
- hsize_reg = state.hsize;
- cl_hash(&state, (int) hsize_reg); /* clear hash table */
+ hSize = state.hSize;
+ ClearHashTable(&state, (int) hSize); /* Clear hash table */
- output(&state, (long) state.ClearCode);
+ Output(&state, (long) state.clearCode);
-#ifdef SIGNED_COMPARE_SLOW
- while ((c = readValue(statePtr)) != (unsigned) EOF)
-#else
- while ((c = readValue(statePtr)) != EOF)
-#endif
- {
- state.in_count++;
+ while (U(c = readValue(statePtr)) != U(EOF)) {
+ state.inCount++;
fcode = (long) (((long) c << GIFBITS) + ent);
- i = (((long)c << hshift) ^ ent); /* xor hashing */
+ i = ((long)c << hshift) ^ ent; /* XOR hashing */
- if (state.htab[i] == fcode) {
- ent = state.codetab[i];
+ if (state.hashTable[i] == fcode) {
+ ent = state.codeTable[i];
continue;
- } else if ((long) state.htab[i] < 0) { /* empty slot */
+ } else if ((long) state.hashTable[i] < 0) { /* Empty slot */
goto nomatch;
}
- disp = hsize_reg - i; /* secondary hash (after G.
- * Knott) */
+ disp = hSize - i; /* Secondary hash (after G. Knott) */
if (i == 0) {
disp = 1;
}
probe:
if ((i -= disp) < 0) {
- i += hsize_reg;
+ i += hSize;
}
- if (state.htab[i] == fcode) {
- ent = state.codetab[i];
+ if (state.hashTable[i] == fcode) {
+ ent = state.codeTable[i];
continue;
}
- if ((long) state.htab[i] > 0) {
+ if ((long) state.hashTable[i] > 0) {
goto probe;
}
nomatch:
- output(&state, (long) ent);
- state.out_count++;
+ Output(&state, (long) ent);
+ state.outCount++;
ent = c;
-#ifdef SIGNED_COMPARE_SLOW
- if ((unsigned) free_ent < (unsigned) ((long)1 << GIFBITS))
-#else
- if (state.free_ent < ((long)1 << GIFBITS))
-#endif
- {
- state.codetab[i] = state.free_ent++; /* code -> hashtable */
- state.htab[i] = fcode;
+ if (U(state.freeEntry) < U((long)1 << GIFBITS)) {
+ state.codeTable[i] = state.freeEntry++; /* code -> hashtable */
+ state.hashTable[i] = fcode;
} else {
- cl_block(&state);
+ ClearForBlock(&state);
}
}
@@ -1943,91 +1998,87 @@ compress(
* Put out the final code.
*/
- output(&state, (long)ent);
- state.out_count++;
- output(&state, (long) state.EOFCode);
-
- return;
+ Output(&state, (long) ent);
+ state.outCount++;
+ Output(&state, (long) state.eofCode);
}
/*****************************************************************
- * TAG( output )
+ * Output --
+ * Output the given code.
*
- * Output the given code.
* Inputs:
- * code: A n_bits-bit integer. If == -1, then EOF. This assumes that
- * n_bits =< (long) wordsize - 1.
+ * code: A numBits-bit integer. If == -1, then EOF. This assumes that
+ * numBits =< (long) wordsize - 1.
* Outputs:
* Outputs code to the file.
* Assumptions:
* Chars are 8 bits long.
* Algorithm:
- * Maintain a GIFBITS character long buffer (so that 8 codes will
- * fit in it exactly). Use the VAX insv instruction to insert each
- * code in turn. When the buffer fills up empty it and start over.
+ * Maintain a GIFBITS character long buffer (so that 8 codes will fit in
+ * it exactly). Use the VAX insv instruction to insert each code in turn.
+ * When the buffer fills up empty it and start over.
*/
-static const
-unsigned long masks[] = {
- 0x0000,
- 0x0001, 0x0003, 0x0007, 0x000F,
- 0x001F, 0x003F, 0x007F, 0x00FF,
- 0x01FF, 0x03FF, 0x07FF, 0x0FFF,
- 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
-};
-
static void
-output(
+Output(
GIFState_t *statePtr,
long code)
{
- statePtr->cur_accum &= masks[statePtr->cur_bits];
+ static const unsigned long masks[] = {
+ 0x0000,
+ 0x0001, 0x0003, 0x0007, 0x000F,
+ 0x001F, 0x003F, 0x007F, 0x00FF,
+ 0x01FF, 0x03FF, 0x07FF, 0x0FFF,
+ 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
+ };
- if (statePtr->cur_bits > 0) {
- statePtr->cur_accum |= ((long) code << statePtr->cur_bits);
+ statePtr->currentAccumulated &= masks[statePtr->currentBits];
+ if (statePtr->currentBits > 0) {
+ statePtr->currentAccumulated |= ((long) code << statePtr->currentBits);
} else {
- statePtr->cur_accum = code;
+ statePtr->currentAccumulated = code;
}
+ statePtr->currentBits += statePtr->numBits;
- statePtr->cur_bits += statePtr->n_bits;
-
- while (statePtr->cur_bits >= 8 ) {
- char_out(statePtr, (unsigned) (statePtr->cur_accum & 0xff));
- statePtr->cur_accum >>= 8;
- statePtr->cur_bits -= 8;
+ while (statePtr->currentBits >= 8) {
+ CharOut(statePtr, (unsigned) (statePtr->currentAccumulated & 0xff));
+ statePtr->currentAccumulated >>= 8;
+ statePtr->currentBits -= 8;
}
/*
- * If the next entry is going to be too big for the code size,
- * then increase it, if possible.
+ * If the next entry is going to be too big for the code size, then
+ * increase it, if possible.
*/
- if ((statePtr->free_ent > statePtr->maxcode)|| statePtr->clear_flg ) {
- if (statePtr->clear_flg) {
- statePtr->maxcode = MAXCODE(
- statePtr->n_bits = statePtr->g_init_bits);
- statePtr->clear_flg = 0;
+ if ((statePtr->freeEntry > statePtr->maxCode) || statePtr->clearFlag) {
+ if (statePtr->clearFlag) {
+ statePtr->maxCode = MAXCODE(
+ statePtr->numBits = statePtr->initialBits);
+ statePtr->clearFlag = 0;
} else {
- statePtr->n_bits++;
- if (statePtr->n_bits == GIFBITS) {
- statePtr->maxcode = (long)1 << GIFBITS;
+ statePtr->numBits++;
+ if (statePtr->numBits == GIFBITS) {
+ statePtr->maxCode = (long)1 << GIFBITS;
} else {
- statePtr->maxcode = MAXCODE(statePtr->n_bits);
+ statePtr->maxCode = MAXCODE(statePtr->numBits);
}
}
}
- if (code == statePtr->EOFCode) {
+ if (code == statePtr->eofCode) {
/*
* At EOF, write the rest of the buffer.
*/
- while (statePtr->cur_bits > 0) {
- char_out(statePtr, (unsigned) (statePtr->cur_accum & 0xff));
- statePtr->cur_accum >>= 8;
- statePtr->cur_bits -= 8;
+ while (statePtr->currentBits > 0) {
+ CharOut(statePtr,
+ (unsigned) (statePtr->currentAccumulated & 0xff));
+ statePtr->currentAccumulated >>= 8;
+ statePtr->currentBits -= 8;
}
- flush_char(statePtr);
+ FlushChar(statePtr);
}
}
@@ -2036,48 +2087,48 @@ output(
*/
static void
-cl_block( /* table clear for block compress */
+ClearForBlock( /* Table clear for block compress. */
GIFState_t *statePtr)
{
- cl_hash(statePtr, (int) statePtr->hsize);
- statePtr->free_ent = statePtr->ClearCode + 2;
- statePtr->clear_flg = 1;
+ ClearHashTable(statePtr, (int) statePtr->hSize);
+ statePtr->freeEntry = statePtr->clearCode + 2;
+ statePtr->clearFlag = 1;
- output(statePtr, (long) statePtr->ClearCode);
+ Output(statePtr, (long) statePtr->clearCode);
}
static void
-cl_hash( /* reset code table */
+ClearHashTable( /* Reset code table. */
GIFState_t *statePtr,
- int hsize)
+ int hSize)
{
- register int *htab_p = statePtr->htab + hsize;
+ register int *hashTablePtr = statePtr->hashTable + hSize;
register long i;
register long m1 = -1;
- i = hsize - 16;
+ i = hSize - 16;
do { /* might use Sys V memset(3) here */
- *(htab_p-16) = m1;
- *(htab_p-15) = m1;
- *(htab_p-14) = m1;
- *(htab_p-13) = m1;
- *(htab_p-12) = m1;
- *(htab_p-11) = m1;
- *(htab_p-10) = m1;
- *(htab_p-9) = m1;
- *(htab_p-8) = m1;
- *(htab_p-7) = m1;
- *(htab_p-6) = m1;
- *(htab_p-5) = m1;
- *(htab_p-4) = m1;
- *(htab_p-3) = m1;
- *(htab_p-2) = m1;
- *(htab_p-1) = m1;
- htab_p -= 16;
+ *(hashTablePtr-16) = m1;
+ *(hashTablePtr-15) = m1;
+ *(hashTablePtr-14) = m1;
+ *(hashTablePtr-13) = m1;
+ *(hashTablePtr-12) = m1;
+ *(hashTablePtr-11) = m1;
+ *(hashTablePtr-10) = m1;
+ *(hashTablePtr-9) = m1;
+ *(hashTablePtr-8) = m1;
+ *(hashTablePtr-7) = m1;
+ *(hashTablePtr-6) = m1;
+ *(hashTablePtr-5) = m1;
+ *(hashTablePtr-4) = m1;
+ *(hashTablePtr-3) = m1;
+ *(hashTablePtr-2) = m1;
+ *(hashTablePtr-1) = m1;
+ hashTablePtr -= 16;
} while ((i -= 16) >= 0);
for (i += 16; i > 0; i--) {
- *--htab_p = m1;
+ *--hashTablePtr = m1;
}
}
@@ -2094,12 +2145,12 @@ cl_hash( /* reset code table */
*/
static void
-char_init(
+CharInit(
GIFState_t *statePtr)
{
- statePtr->a_count = 0;
- statePtr->cur_accum = 0;
- statePtr->cur_bits = 0;
+ statePtr->accumulatedByteCount = 0;
+ statePtr->currentAccumulated = 0;
+ statePtr->currentBits = 0;
}
/*
@@ -2108,13 +2159,13 @@ char_init(
*/
static void
-char_out(
+CharOut(
GIFState_t *statePtr,
int c)
{
- statePtr->accum[statePtr->a_count++] = c;
- if (statePtr->a_count >= 254) {
- flush_char(statePtr);
+ statePtr->packetAccumulator[statePtr->accumulatedByteCount++] = c;
+ if (statePtr->accumulatedByteCount >= 254) {
+ FlushChar(statePtr);
}
}
@@ -2123,17 +2174,18 @@ char_out(
*/
static void
-flush_char(
+FlushChar(
GIFState_t *statePtr)
{
unsigned char c;
- if (statePtr->a_count > 0) {
- c = statePtr->a_count;
- Tcl_Write(statePtr->g_outfile, (const char *) &c, 1);
- Tcl_Write(statePtr->g_outfile, (const char *) statePtr->accum,
- statePtr->a_count);
- statePtr->a_count = 0;
+ if (statePtr->accumulatedByteCount > 0) {
+ c = statePtr->accumulatedByteCount;
+ statePtr->writeProc(statePtr->destination, (const char *) &c, 1);
+ statePtr->writeProc(statePtr->destination,
+ (const char *) statePtr->packetAccumulator,
+ statePtr->accumulatedByteCount);
+ statePtr->accumulatedByteCount = 0;
}
}