Menu

#570 GIF write for Tk

obsolete: 8.1
closed-fixed
nobody
2
2015-02-25
2000-10-31
No

OriginalBugID: 2039 RFE
Version: 8.1
SubmitDate: '1999-05-10'
LastModified: '1999-11-29'
Severity: MED
Status: Released
Submitter: pat
ChangedBy: hobbs
OS: Linux-Red Hat
OSVersion: 4.2
Machine: Other
FixedDate: '1999-11-29'
FixedInVersion: 8.3b1

Name:

Jan Nijtmans

Comments:

The reason that I submit 2 enancements (GIF-write and "<imageName>

data")

in one patch, is that they are highly related. At this moment there is

no way to write image data to a (base-64 encoded) string, only to a

file. This is a serious limitation, which only can be solved by a patch.

However, there is currently also no photo format handler for which the

StringWrite function is implemented. Therefore, there is no way to

demonstrate this new feature without enhancing at least one format

handler as well. GIF was simply the most useful candidate.

DesiredBehavior:

This enhancement request is essentially the same as:

http://www.scriptics.com/live/showurl?i=software%2epatches%2etk803&url=%2

fpatches%2ftk8%2e0%2e3%2fgif%2dwrite%2etxt

,only it is upgraded for Tk8.1, and contains a small additional bug-fix

in the GIF-write function.

* GIF write function, for example:

imageName write fileName -format GIF

This will save the content of the photo in a file, in the popular GIF

format. In stead of the lzw compression, miGIF compression is used,

which is a kind of runlength encoding. This compression method is not as

good as lzw, but it can be decoded by any lzw decompressor and it can

freely be used without patent problems. (see also the comments in

tkImgGIF.c)

* New "imageName data" command. This command extracts data from the

photo and transforms it to a string. For example:

imageName data

This will return the content of the image in the same format as accepted

by "imageName put"

imageName data -format GIF

This returns the content of the image in base-64 encoded GIF format.

Extensions can add other formats in the usual way. The "imageName data"

command accepts the "-to" option in the same way as the "imageName

write" command.

* The photo man page is adapted according to these enhancements.

* Tests are added executing all possible branches in the C-code.

Known limitations:

* Only images containing less than 255 colors can be written, otherwise

an error is generated. (color quantisation is not easy to

implement......)

* The GIF-write function is not thread-safe yet.

The "imageName data" command and the GIF write function are already in

use by the Img extension for about two years. The miGIF compression

method is more recent: I extracted it from the popular GD library, the

core library behind Gdtcl.

Patch:

*** doc/photo.n.orig Thu Apr 29 21:37:42 1999

--- doc/photo.n Sun May 9 20:09:42 1999

***************

*** 186,191 ****

--- 186,214 ----

default value is the same as \fIx\fR.

.RE

.TP

+ \fIimageName \fBdata ?\fIoption value(s) ...\fR?

+ returns image data in the form of a string.

+ The following options may be specified:

+ .RS

+ .TP

+ \fB\-format\fI format-name\fR

+ Specifies the name of the image file format handler to be used to

+ convert the data. Specifically, this subcommand searches

+ for the first handler whose name matches a initial substring of

+ \fIformat-name\fR and which has the capability to write an string.

+ If this option is not given, the data is returned in the default

+ format as accepted by \fIimageName \fBput\fR?.

+ .TP

+ \fB\-from \fIx1 y1 x2 y2\fR

+ Specifies a rectangular region of \fIimageName\fR to be written to the

+ string. If only \fIx1\fR and \fIy1\fR are specified, the region

+ extends from \fI(x1,y1)\fR to the bottom-right corner of

+ \fIimageName\fR. If all four coordinates are given, they specify

+ diagonally opposite corners of the rectangular region. The default,

+ if this option is not given, is the whole image.

+ .RE

+ .TP

+

\fIimageName \fBget\fR \fIx y\fR

Returns the color of the pixel at coordinates (\fIx\fR,\fIy\fR) in the

image as a list of three integers between 0 and 255, representing the

*** generic/tkImgGIF.c.orig Thu Apr 29 21:37:52 1999

--- generic/tkImgGIF.c Sun May 9 21:19:20 1999

***************

*** 30,35 ****

--- 30,37 ----

* RCS: @(#) $Id: tkImgGIF.c,v 1.3 1999/04/16 01:51:15 stanton Exp $

*/

+ #include "tcl.h"

+

/*

* GIF's are represented as data in base64 format.

* base64 strings consist of 4 6-bit characters -> 3 8 bit bytes.

***************

*** 44,49 ****

--- 46,52 ----

#define GIF_SPACE (GIF_SPECIAL+2)

#define GIF_BAD (GIF_SPECIAL+3)

#define GIF_DONE (GIF_SPECIAL+4)

+ #define GIF_CHAN (GIF_SPECIAL+5)

/*

* structure to "mimic" FILE for Mread, so we can look like fread.

***************

*** 55,60 ****

--- 58,65 ----

unsigned char *data; /* mmencoded source string */

int c; /* bits left over from previous character */

int state; /* decoder state (0-4 or GIF_DONE) */

+ Tcl_DString *buffer; /* pointer to dynamical string */

+ int length; /* length of phisical line already written */

} MFile;

#include "tkInt.h"

***************

*** 87,92 ****

--- 92,106 ----

char *formatString, Tk_PhotoHandle imageHandle,

int destX, int destY, int width, int height,

int srcX, int srcY));

+ static int FileWriteGIF _ANSI_ARGS_(( Tcl_Interp *interp,

+ char *filename, char *formatString,

+ Tk_PhotoImageBlock *blockPtr));

+ static int StringWriteGIF _ANSI_ARGS_((Tcl_Interp *interp,

+ Tcl_DString *dataPtr, char *formatString,

+ Tk_PhotoImageBlock *blockPtr));

+ static int CommonWriteGIF _ANSI_ARGS_((Tcl_Interp *interp,

+ MFile *handle, char *formatString,

+ Tk_PhotoImageBlock *blockPtr));

Tk_PhotoImageFormat tkImgFmtGIF = {

"GIF", /* name */

***************

*** 94,101 ****

StringMatchGIF, /* stringMatchProc */

FileReadGIF, /* fileReadProc */

StringReadGIF, /* stringReadProc */

! NULL, /* fileWriteProc */

! NULL, /* stringWriteProc */

};

#define INTERLACE 0x40

--- 108,115 ----

StringMatchGIF, /* stringMatchProc */

FileReadGIF, /* fileReadProc */

StringReadGIF, /* stringReadProc */

! FileWriteGIF, /* fileWriteProc */

! StringWriteGIF, /* stringWriteProc */

};

#define INTERLACE 0x40

***************

*** 145,150 ****

--- 159,170 ----

static int char64 _ANSI_ARGS_((int c));

static void mInit _ANSI_ARGS_((unsigned char *string,

MFile *handle));

+ static int Mputc _ANSI_ARGS_((int c, MFile *handle));

+ static int Mwrite _ANSI_ARGS_((MFile *handle,

+ CONST char *src, int count));

+ static void mWriteInit _ANSI_ARGS_((Tcl_DString *buffer,

+ MFile *handle));

+

/*

*----------------------------------------------------------------------

***************

*** 1070,1072 ****

--- 1090,2013 ----

return Tcl_Read(chan, (char *) dst, (int) (hunk * count));

}

}

+

+

+ /*

+ * ChanWriteGIF - writes a image in GIF format.

+ *-------------------------------------------------------------------------

+ * Author: Lolo

+ * Engeneering Projects Area

+ * Department of Mining

+ * University of Oviedo

+ * e-mail zz11425958@zeus.etsimo.uniovi.es

+ * lolo@pcsig22.etsimo.uniovi.es

+ * Date: Fri September 20 1996

+ *----------------------------------------------------------------------

+ * FileWriteGIF-

+ *

+ * This procedure is called by the photo image type to write

+ * GIF format 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 interp->result.

+ *

+ *----------------------------------------------------------------------

+ */

+

+ /*

+ * Types, defines and variables needed to write and compress a GIF.

+ */

+

+ typedef int (* ifunptr) _ANSI_ARGS_((void));

+

+ #define LSB(a) ((unsigned char) (((short)(a)) & 0x00FF))

+ #define MSB(a) ((unsigned char) (((short)(a)) >> 8))

+

+ #define GIFBITS 12

+ #define HSIZE 5003 /* 80% occupancy */

+

+ static int ssize;

+ static int csize;

+ static int rsize;

+ static unsigned char *pixelo;

+ static int pixelSize;

+ static int pixelPitch;

+ static int greenOffset;

+ static int blueOffset;

+ static int alphaOffset;

+ static int num;

+ static unsigned char mapa[MAXCOLORMAPSIZE][3];

+

+ /*

+ * Definition of new functions to write GIFs

+ */

+

+ static int color _ANSI_ARGS_((int red,int green, int blue));

+ static void compress _ANSI_ARGS_((int init_bits, MFile *handle,

+ ifunptr readValue));

+ static int nuevo _ANSI_ARGS_((int red, int green ,int blue,

+ unsigned char mapa[MAXCOLORMAPSIZE][3]));

+ static int savemap _ANSI_ARGS_((Tk_PhotoImageBlock *blockPtr,

+ unsigned char mapa[MAXCOLORMAPSIZE][3]));

+ static int ReadValue _ANSI_ARGS_((void));

+ static int no_bits _ANSI_ARGS_((int colors));

+

+ static int

+ FileWriteGIF (interp, filename, formatString, blockPtr)

+ Tcl_Interp *interp; /* Interpreter to use for reporting errors. */

+ char *filename;

+ char *formatString;

+ Tk_PhotoImageBlock *blockPtr;

+ {

+ Tcl_Channel chan = NULL;

+ MFile handle;

+ int result;

+

+ chan = Tcl_OpenFileChannel(interp, filename, "w", 0644);

+ if (!chan) {

+ return TCL_ERROR;

+ }

+ if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") != TCL_OK) {

+ return TCL_ERROR;

+ }

+

+ handle.data = (char *) chan;

+ handle.state = GIF_CHAN;

+

+ result = CommonWriteGIF(interp, &handle, formatString, blockPtr);

+ if (Tcl_Close(interp, chan) == TCL_ERROR) {

+ return TCL_ERROR;

+ }

+ return result;

+ }

+

+ static int

+ StringWriteGIF(interp, dataPtr, formatString, blockPtr)

+ Tcl_Interp *interp;

+ Tcl_DString *dataPtr;

+ char *formatString;

+ Tk_PhotoImageBlock *blockPtr;

+ {

+ int result;

+ MFile handle;

+

+ Tcl_DStringSetLength(dataPtr, 1024);

+ mWriteInit(dataPtr, &handle);

+

+ result = CommonWriteGIF(interp, &handle, formatString, blockPtr);

+ Mputc(GIF_DONE, &handle);

+

+ return(result);

+ }

+

+ static int

+ CommonWriteGIF(interp, handle, formatString, blockPtr)

+ Tcl_Interp *interp;

+ MFile *handle;

+ char *formatString;

+ Tk_PhotoImageBlock *blockPtr;

+ {

+ int resolution;

+ long numcolormap;

+

+ long width,height,x;

+ unsigned char c;

+ unsigned int top,left;

+ int num;

+

+ top = 0;

+ left = 0;

+

+ pixelSize=blockPtr->pixelSize;

+ greenOffset=blockPtr->offset[1]-blockPtr->offset[0];

+ blueOffset=blockPtr->offset[2]-blockPtr->offset[0];

+ alphaOffset = blockPtr->offset[0];

+ if (alphaOffset < blockPtr->offset[2]) {

+ alphaOffset = blockPtr->offset[2];

+ }

+ if (++alphaOffset < pixelSize) {

+ alphaOffset -= blockPtr->offset[0];

+ } else {

+ alphaOffset = 0;

+ }

+

+ Mwrite(handle, (CONST char *) (alphaOffset ? "GIF89a":"GIF87a"), 6);

+

+ for (x=0;x<MAXCOLORMAPSIZE;x++) {

+ mapa[x][CM_RED] = 255;

+ mapa[x][CM_GREEN] = 255;

+ mapa[x][CM_BLUE] = 255;

+ }

+

+

+ width=blockPtr->width;

+ height=blockPtr->height;

+ pixelo=blockPtr->pixelPtr + blockPtr->offset[0];

+ pixelPitch=blockPtr->pitch;

+ if ((num=savemap(blockPtr,mapa))<0) {

+ Tcl_AppendResult(interp, "too many colors", (char *) NULL);

+ return TCL_ERROR;

+ }

+ if (num<3) num=3;

+ c=LSB(width);

+ Mputc(c,handle);

+ c=MSB(width);

+ Mputc(c,handle);

+ c=LSB(height);

+ Mputc(c,handle);

+ c=MSB(height);

+ Mputc(c,handle);

+

+ c= (1 << 7) | (no_bits(num) << 4) | (no_bits(num));

+ Mputc(c,handle);

+ resolution = no_bits(num)+1;

+

+ numcolormap=1 << resolution;

+

+ /* background color */

+

+ Mputc(0,handle);

+

+ /* zero for future expansion */

+

+ Mputc(0,handle);

+

+ for (x=0; x<numcolormap ;x++) {

+ Mputc(mapa[x][CM_RED],handle);

+ Mputc(mapa[x][CM_GREEN],handle);

+ Mputc(mapa[x][CM_BLUE],handle);

+ }

+

+ /*

+ * Write out extension for transparent colour index, if necessary.

+ */

+

+ if (alphaOffset) {

+ Mwrite(handle, "!\371\4\1\0\0\0", 8);

+ }

+

+ Mputc(',',handle);

+ c=LSB(top);

+ Mputc(c,handle);

+ c=MSB(top);

+ Mputc(c,handle);

+ c=LSB(left);

+ Mputc(c,handle);

+ c=MSB(left);

+ Mputc(c,handle);

+

+ c=LSB(width);

+ Mputc(c,handle);

+ c=MSB(width);

+ Mputc(c,handle);

+

+ c=LSB(height);

+ Mputc(c,handle);

+ c=MSB(height);

+ Mputc(c,handle);

+

+ c=0;

+ Mputc(c,handle);

+ c=resolution;

+ Mputc(c,handle);

+

+ ssize = rsize = blockPtr->width;

+ csize = blockPtr->height;

+ compress(resolution+1, handle, ReadValue);

+

+ Mputc(0,handle);

+ Mputc(';',handle);

+

+ return TCL_OK;

+ }

+

+ static int

+ color(red, green, blue)

+ int red;

+ int green;

+ int blue;

+ {

+ int x;

+ for (x=(alphaOffset != 0);x<=MAXCOLORMAPSIZE;x++) {

+ if ((mapa[x][CM_RED]==red) && (mapa[x][CM_GREEN]==green) &&

+ (mapa[x][CM_BLUE]==blue)) {

+ return x;

+ }

+ }

+ return -1;

+ }

+

+

+ static int

+ nuevo(red, green, blue, mapa)

+ int red,green,blue;

+ unsigned char mapa[MAXCOLORMAPSIZE][3];

+ {

+ int x;

+ for (x=(alphaOffset != 0);x<num;x++) {

+ if ((mapa[x][CM_RED]==red) && (mapa[x][CM_GREEN]==green) &&

+ (mapa[x][CM_BLUE]==blue)) {

+ return 0;

+ }

+ }

+ return 1;

+ }

+

+ static int

+ savemap(blockPtr,mapa)

+ Tk_PhotoImageBlock *blockPtr;

+ unsigned char mapa[MAXCOLORMAPSIZE][3];

+ {

+ unsigned char *colores;

+ int x,y;

+ unsigned char red,green,blue;

+

+ if (alphaOffset) {

+ num = 1;

+ mapa[0][CM_RED] = 0xd9;

+ mapa[0][CM_GREEN] = 0xd9;

+ mapa[0][CM_BLUE] = 0xd9;

+ } else {

+ num = 0;

+ }

+

+ for(y=0;y<blockPtr->height;y++) {

+ colores=blockPtr->pixelPtr + blockPtr->offset[0]

+ + y * blockPtr->pitch;

+ for(x=0;x<blockPtr->width;x++) {

+ if (!alphaOffset || (colores[alphaOffset] != 0)) {

+ red = colores[0];

+ green = colores[greenOffset];

+ blue = colores[blueOffset];

+ if (nuevo(red,green,blue,mapa)) {

+ if (num>255)

+ return -1;

+

+ mapa[num][CM_RED]=red;

+ mapa[num][CM_GREEN]=green;

+ mapa[num][CM_BLUE]=blue;

+ num++;

+ }

+ }

+ colores += pixelSize;

+ }

+ }

+ return num-1;

+ }

+

+ static int

+ ReadValue()

+ {

+ unsigned int col;

+

+ if (csize == 0) {

+ return EOF;

+ }

+ if (alphaOffset && (pixelo[alphaOffset]==0)) {

+ col = 0;

+ } else {

+ col = color(pixelo[0],pixelo[greenOffset],pixelo[blueOffset]);

+ }

+ pixelo += pixelSize;

+ if (--ssize <= 0) {

+ ssize = rsize;

+ csize--;

+ pixelo += pixelPitch - (rsize * pixelSize);

+ }

+

+ return col;

+ }

+

+ /*

+ * Return the number of bits ( -1 ) to represent a given

+ * number of colors ( ex: 256 colors => 7 ).

+ */

+ static int

+ no_bits( colors )

+ int colors;

+ {

+ register int bits = 0;

+

+ colors--;

+ while ( colors >> bits ) {

+ bits++;

+ }

+

+ return (bits-1);

+ }

+

+ /*

+ *-----------------------------------------------------------------------

+ * Mwrite --

+ *

+ * This procedure is invoked to put imaged data into a stream

+ * using Mputc.

+ *

+ * Results:

+ * The return value is the number of characters "written"

+ *

+ * Side effects:

+ * The base64 handle will change state.

+ *

+ *-----------------------------------------------------------------------

+ */

+

+ int

+ Mwrite(handle, src, count)

+ MFile *handle; /* mmencode "file" handle */

+ CONST char *src; /* where to get the data */

+ int count; /* number of bytes */

+ {

+ register int i;

+ int curcount, bufcount;

+

+ if (handle->state == GIF_CHAN) {

+ return Tcl_Write((Tcl_Channel) handle->data, (char *) src, count);

+ }

+ curcount = (char *) handle->data - Tcl_DStringValue(handle->buffer);

+ bufcount = curcount + count + count/3 + count/52 + 1024;

+

+ /* make sure that the DString contains enough space */

+ if (bufcount >= (handle->buffer->spaceAvl)) {

+ Tcl_DStringSetLength(handle->buffer, bufcount + 4096);

+ handle->data = Tcl_DStringValue(handle->buffer) + curcount;

+ }

+ /* write the data */

+ for (i=0; (i<count) && (Mputc(*src++, handle) != GIF_DONE); i++) {

+ /* empty loop body */

+ }

+ return i;

+ }

+ /*

+ *-----------------------------------------------------------------------

+ *

+ * Mputc --

+ *

+ * This procedure encodes and writes the next byte to a base64

+ * encoded string.

+ *

+ * Results:

+ * The written byte is returned.

+ *

+ * Side effects:

+ * the base64 handle will change state.

+ *

+ *-----------------------------------------------------------------------

+ */

+

+ static char base64_table[64] = {

+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',

+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',

+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',

+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',

+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',

+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',

+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',

+ '4', '5', '6', '7', '8', '9', '+', '/'

+ };

+

+ int

+ Mputc(c, handle)

+ register int c; /* character to be written */

+ register MFile *handle; /* handle containing decoder data and state */

+ {

+ /* In fact, here should be checked first if the dynamic

+ * string contains enough space for the next character.

+ * This would be very expensive to do for each character.

+ * Therefore we just allocate 1024 bytes immediately in

+ * the beginning and also take a 1024 bytes margin inside

+ * every Mwrite. At least this check is done then only

+ * every 256 bytes, which is much faster. Because the GIF

+ * header is less than 1024 bytes and pixel data is

+ * written in 256 byte portions, this should be safe.

+ */

+

+ if (c == GIF_DONE) {

+ switch(handle->state) {

+ case 0:

+ break;

+ case 1:

+ *handle->data++ = base64_table[(handle->c<<4)&63];

+ *handle->data++ = '='; *handle->data++ = '='; break;

+ case 2:

+ *handle->data++ = base64_table[(handle->c<<2)&63];

+ *handle->data++ = '='; break;

+ default:

+ handle->state = GIF_DONE;

+ return GIF_DONE;

+ }

+ Tcl_DStringSetLength(handle->buffer,

+ (char *) handle->data - Tcl_DStringValue(handle->buffer));

+ handle->state = GIF_DONE;

+ return GIF_DONE;

+ }

+

+ if (handle->state == GIF_CHAN) {

+ char ch = (char) c;

+ return (Tcl_Write((Tcl_Channel) handle->data, &ch, 1)>0) ? c : GIF_DONE;

+ }

+

+ c &= 0xff;

+ switch (handle->state++) {

+ case 0:

+ *handle->data++ = base64_table[(c>>2)&63]; break;

+ case 1:

+ c |= handle->c << 8;

+ *handle->data++ = base64_table[(c>>4)&63]; break;

+ case 2:

+ handle->state = 0;

+ c |= handle->c << 8;

+ *handle->data++ = base64_table[(c>>6)&63];

+ *handle->data++ = base64_table[c&63]; break;

+ }

+ handle->c = c;

+ if (handle->length++ > 52) {

+ handle->length = 0;

+ *handle->data++ = '\n';

+ }

+ return c & 0xff;

+ };

+

+ /*

+ *-------------------------------------------------------------------------

+ * mWriteInit --

+ * This procedure initializes a base64 decoder handle for writing

+ *

+ * Results:

+ * none

+ *

+ * Side effects:

+ * the base64 handle is initialized

+ *

+ *-------------------------------------------------------------------------

+ */

+

+ void

+ mWriteInit(buffer, handle)

+ Tcl_DString *buffer;

+ MFile *handle; /* mmencode "file" handle */

+ {

+ Tcl_DStringSetLength(buffer, buffer->spaceAvl);

+ handle->buffer = buffer;

+ handle->data = Tcl_DStringValue(buffer);

+ handle->state = 0;

+ handle->length = 0;

+ }

+

+

+

+ /*-----------------------------------------------------------------------

+ *

+ * miGIF Compression - mouse and ivo's GIF-compatible compression

+ *

+ * -run length encoding compression routines-

+ *

+ * Copyright (C) 1998 Hutchison Avenue Software Corporation

+ * http://www.hasc.com

+ * info@hasc.com

+ *

+ * Permission to use, copy, modify, and distribute this software and its

+ * documentation for any purpose and without fee is hereby granted, provided

+ * that the above copyright notice appear in all copies and that both that

+ * copyright notice and this permission notice appear in supporting

+ * documentation. This software is provided "AS IS." The Hutchison Avenue

+ * Software Corporation disclaims all warranties, either express or implied,

+ * including but not limited to implied warranties of merchantability and

+ * fitness for a particular purpose, with respect to this code and accompanying

+ * documentation.

+ *

+ * The miGIF compression routines do not, strictly speaking, generate files

+ * conforming to the GIF spec, since the image data is not LZW-compressed

+ * (this is the point: in order to avoid transgression of the Unisys patent

+ * on the LZW algorithm.) However, miGIF generates data streams that any

+ * reasonably sane LZW decompresser will decompress to what we want.

+ *

+ * miGIF compression uses run length encoding. It compresses horizontal runs

+ * of pixels of the same color. This type of compression gives good results

+ * on images with many runs, for example images with lines, text and solid

+ * shapes on a solid-colored background. It gives little or no compression

+ * on images with few runs, for example digital or scanned photos.

+ *

+ * der Mouse

+ * mouse@rodents.montreal.qc.ca

+ * 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B

+ *

+ * ivo@hasc.com

+ *

+ * The Graphics Interchange Format(c) is the Copyright property of

+ * CompuServe Incorporated. GIF(sm) is a Service Mark property of

+ * CompuServe Incorporated.

+ *

+ */

+

+ static int rl_pixel;

+ static int rl_basecode;

+ static int rl_count;

+ static int rl_table_pixel;

+ static int rl_table_max;

+ static int just_cleared;

+ static int out_bits;

+ static int out_bits_init;

+ static int out_count;

+ static int out_bump;

+ static int out_bump_init;

+ static int out_clear;

+ static int out_clear_init;

+ static int max_ocodes;

+ static int code_clear;

+ static int code_eof;

+ static unsigned int obuf;

+ static int obits;

+ static MFile *ofile;

+ static unsigned char oblock[256];

+ static int oblen;

+

+ /* Used only when debugging GIF compression code */

+ /* #define DEBUGGING_ENVARS */

+

+ #ifdef DEBUGGING_ENVARS

+

+ static int verbose_set = 0;

+ static int verbose;

+ #define VERBOSE (verbose_set?verbose:set_verbose())

+

+ static int set_verbose(void)

+ {

+ verbose = !!getenv("GIF_VERBOSE");

+ verbose_set = 1;

+ return(verbose);

+ }

+

+ #else

+

+ #define VERBOSE 0

+

+ #endif

+

+

+ static const char *

+ binformat(v, nbits)

+ unsigned int v;

+ int nbits;

+ {

+ static char bufs[8][64];

+ static int bhand = 0;

+ unsigned int bit;

+ int bno;

+ char *bp;

+

+ bhand --;

+ if (bhand < 0) bhand = (sizeof(bufs)/sizeof(bufs[0]))-1;

+ bp = &bufs[bhand][0];

+ for (bno=nbits-1,bit=1U<<bno;bno>=0;bno--,bit>>=1)

+ { *bp++ = (v & bit) ? '1' : '0';

+ if (((bno&3) == 0) && (bno != 0)) *bp++ = '.';

+ }

+ *bp = '\0';

+ return(&bufs[bhand][0]);

+ }

+

+ static void write_block()

+ {

+ int i;

+ unsigned char c;

+

+ if (VERBOSE)

+ { printf("write_block %d:",oblen);

+ for (i=0;i<oblen;i++) printf(" %02x",oblock[i]);

+ printf("\n");

+ }

+ c = oblen;

+ Mwrite(ofile, (CONST char *) &c, 1);

+ Mwrite(ofile, &oblock[0], oblen);

+ oblen = 0;

+ }

+

+ static void

+ block_out(c)

+ unsigned char c;

+ {

+ if (VERBOSE) printf("block_out %s\n",binformat(c,8));

+ oblock[oblen++] = c;

+ if (oblen >= 255) write_block();

+ }

+

+ static void block_flush()

+ {

+ if (VERBOSE) printf("block_flush\n");

+ if (oblen > 0) write_block();

+ }

+

+ static void output(val)

+ int val;

+ {

+ if (VERBOSE) printf("output %s [%s %d %d]\n",binformat(val,out_bits),binformat(obuf,obits),obits,out_bits);

+ obuf |= val << obits;

+ obits += out_bits;

+ while (obits >= 8)

+ { block_out(obuf&0xff);

+ obuf >>= 8;

+ obits -= 8;

+ }

+ if (VERBOSE) printf("output leaving [%s %d]\n",binformat(obuf,obits),obits);

+ }

+

+ static void output_flush()

+ {

+ if (VERBOSE) printf("output_flush\n");

+ if (obits > 0) block_out(obuf);

+ block_flush();

+ }

+

+ static void did_clear()

+ {

+ if (VERBOSE) printf("did_clear\n");

+ out_bits = out_bits_init;

+ out_bump = out_bump_init;

+ out_clear = out_clear_init;

+ out_count = 0;

+ rl_table_max = 0;

+ just_cleared = 1;

+ }

+

+ static void

+ output_plain(c)

+ int c;

+ {

+ if (VERBOSE) printf("output_plain %s\n",binformat(c,out_bits));

+ just_cleared = 0;

+ output(c);

+ out_count ++;

+ if (out_count >= out_bump)

+ { out_bits ++;

+ out_bump += 1 << (out_bits - 1);

+ }

+ if (out_count >= out_clear)

+ { output(code_clear);

+ did_clear();

+ }

+ }

+

+ static unsigned int isqrt(x)

+ unsigned int x;

+ {

+ unsigned int r;

+ unsigned int v;

+

+ if (x < 2) return(x);

+ for (v=x,r=1;v;v>>=2,r<<=1) ;

+ while (1)

+ { v = ((x / r) + r) / 2;

+ if ((v == r) || (v == r+1)) return(r);

+ r = v;

+ }

+ }

+

+ static unsigned int

+ compute_triangle_count(count, nrepcodes)

+ unsigned int count;

+ unsigned int nrepcodes;

+ {

+ unsigned int perrep;

+ unsigned int cost;

+

+ cost = 0;

+ perrep = (nrepcodes * (nrepcodes+1)) / 2;

+ while (count >= perrep)

+ { cost += nrepcodes;

+ count -= perrep;

+ }

+ if (count > 0)

+ { unsigned int n;

+ n = isqrt(count);

+ while ((n*(n+1)) >= 2*count) n --;

+ while ((n*(n+1)) < 2*count) n ++;

+ cost += n;

+ }

+ return(cost);

+ }

+

+ static void max_out_clear()

+ {

+ out_clear = max_ocodes;

+ }

+

+ static void reset_out_clear()

+ {

+ out_clear = out_clear_init;

+ if (out_count >= out_clear)

+ { output(code_clear);

+ did_clear();

+ }

+ }

+

+ static void

+ rl_flush_fromclear(count)

+ int count;

+ {

+ int n;

+

+ if (VERBOSE) printf("rl_flush_fromclear %d\n",count);

+ max_out_clear();

+ rl_table_pixel = rl_pixel;

+ n = 1;

+ while (count > 0)

+ { if (n == 1)

+ { rl_table_max = 1;

+ output_plain(rl_pixel);

+ count --;

+ }

+ else if (count >= n)

+ { rl_table_max = n;

+ output_plain(rl_basecode+n-2);

+ count -= n;

+ }

+ else if (count == 1)

+ { rl_table_max ++;

+ output_plain(rl_pixel);

+ count = 0;

+ }

+ else

+ { rl_table_max ++;

+ output_plain(rl_basecode+count-2);

+ count = 0;

+ }

+ if (out_count == 0) n = 1; else n ++;

+ }

+ reset_out_clear();

+ if (VERBOSE) printf("rl_flush_fromclear leaving table_max=%d\n",rl_table_max);

+ }

+

+ static void rl_flush_clearorrep(count)

+ int count;

+ {

+ int withclr;

+

+ if (VERBOSE) printf("rl_flush_clearorrep %d\n",count);

+ withclr = 1 + compute_triangle_count(count,max_ocodes);

+ if (withclr < count)

+ { output(code_clear);

+ did_clear();

+ rl_flush_fromclear(count);

+ }

+ else

+ { for (;count>0;count--) output_plain(rl_pixel);

+ }

+ }

+

+ static void rl_flush_withtable(count)

+ int count;

+ {

+ int repmax;

+ int repleft;

+ int leftover;

+

+ if (VERBOSE) printf("rl_flush_withtable %d\n",count);

+ repmax = count / rl_table_max;

+ leftover = count % rl_table_max;

+ repleft = (leftover ? 1 : 0);

+ if (out_count+repmax+repleft > max_ocodes)

+ { repmax = max_ocodes - out_count;

+ leftover = count - (repmax * rl_table_max);

+ repleft = 1 + compute_triangle_count(leftover,max_ocodes);

+ }

+ if (VERBOSE) printf("rl_flush_withtable repmax=%d leftover=%d repleft=%d\n",repmax,leftover,repleft);

+ if (1+compute_triangle_count(count,max_ocodes) < repmax+repleft)

+ { output(code_clear);

+ did_clear();

+ rl_flush_fromclear(count);

+ return;

+ }

+ max_out_clear();

+ for (;repmax>0;repmax--) output_plain(rl_basecode+rl_table_max-2);

+ if (leftover)

+ { if (just_cleared)

+ { rl_flush_fromclear(leftover);

+ }

+ else if (leftover == 1)

+ { output_plain(rl_pixel);

+ }

+ else

+ { output_plain(rl_basecode+leftover-2);

+ }

+ }

+ reset_out_clear();

+ }

+

+ static void rl_flush()

+ {

+ if (VERBOSE) printf("rl_flush [ %d %d\n",rl_count,rl_pixel);

+ if (rl_count == 1)

+ { output_plain(rl_pixel);

+ rl_count = 0;

+ if (VERBOSE) printf("rl_flush ]\n");

+ return;

+ }

+ if (just_cleared)

+ { rl_flush_fromclear(rl_count);

+ }

+ else if ((rl_table_max < 2) || (rl_table_pixel != rl_pixel))

+ { rl_flush_clearorrep(rl_count);

+ }

+ else

+ { rl_flush_withtable(rl_count);

+ }

+ if (VERBOSE) printf("rl_flush ]\n");

+ rl_count = 0;

+ }

+

+

+ static void compress( init_bits, handle, readValue )

+ int init_bits;

+ MFile *handle;

+ ifunptr readValue;

+ {

+ int c;

+

+ ofile = handle;

+ obuf = 0;

+ obits = 0;

+ oblen = 0;

+ code_clear = 1 << (init_bits - 1);

+ code_eof = code_clear + 1;

+ rl_basecode = code_eof + 1;

+ out_bump_init = (1 << (init_bits - 1)) - 1;

+ /* for images with a lot of runs, making out_clear_init larger will

+ give better compression. */

+ out_clear_init = (init_bits <= 3) ? 9 : (out_bump_init-1);

+ #ifdef DEBUGGING_ENVARS

+ { const char *ocienv;

+ ocienv = getenv("GIF_OUT_CLEAR_INIT");

+ if (ocienv)

+ { out_clear_init = atoi(ocienv);

+ if (VERBOSE) printf("[overriding out_clear_init to %d]\n",out_clear_init);

+ }

+ }

+ #endif

+ out_bits_init = init_bits;

+ max_ocodes = (1 << GIFBITS) - ((1 << (out_bits_init - 1)) + 3);

+ did_clear();

+ output(code_clear);

+ rl_count = 0;

+ while (1)

+ { c = readValue();

+ if ((rl_count > 0) && (c != rl_pixel)) rl_flush();

+ if (c == EOF) break;

+ if (rl_pixel == c)

+ { rl_count ++;

+ }

+ else

+ { rl_pixel = c;

+ rl_count = 1;

+ }

+ }

+ output(code_eof);

+ output_flush();

+ }

+

+ /*-----------------------------------------------------------------------

+ *

+ * End of miGIF section - See copyright notice at start of section.

+ *

+ *-----------------------------------------------------------------------*/

*** generic/tkImgPhoto.c.orig Thu Apr 29 21:37:53 1999

--- generic/tkImgPhoto.c Sun May 9 20:28:01 1999

***************

*** 364,369 ****

--- 364,372 ----

int width, int height));

static void ImgPhotoInstanceSetSize _ANSI_ARGS_((

PhotoInstance *instancePtr));

+ static int ImgStringWrite _ANSI_ARGS_((Tcl_Interp *interp,

+ Tcl_DString *dataPtr, char *formatString,

+ Tk_PhotoImageBlock *blockPtr));

static int IsValidPalette _ANSI_ARGS_((PhotoInstance *instancePtr,

char *palette));

static int CountBits _ANSI_ARGS_((pixel mask));

***************

*** 684,689 ****

--- 687,781 ----

options.toY2 - options.toY, options.zoomX, options.zoomY,

options.subsampleX, options.subsampleY);

+ } else if ((c == 'd') && (strncmp(argv[1], "data", length) == 0)) {

+ /*

+ * photo data command - first parse and check parameters.

+ */

+ Tk_PhotoImageBlock block;

+ Tcl_DString buffer;

+

+ Tk_ImageStringWriteProc *stringWriteProc = NULL;

+

+ index = 2;

+ memset((VOID *) &options, 0, sizeof(options));

+ options.name = NULL;

+ options.format = NULL;

+ options.

These are integrated with 8.3.
-- 11/29/1999 hobbs

Discussion

  • Brent B. Welch

    Brent B. Welch - 2000-10-31
    • priority: 5 --> 2
    • status: open --> closed-fixed
     
  • Don Porter

    Don Porter - 2001-03-22
    • labels: 104340 --> 41. Photo Images