/[dtapublic]/projs/trunk/shared_source/tk_base/tkimggif.c
ViewVC logotype

Contents of /projs/trunk/shared_source/tk_base/tkimggif.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (show annotations) (download)
Fri Oct 14 01:50:00 2016 UTC (7 years, 5 months ago) by dashley
File MIME type: text/plain
File size: 55991 byte(s)
Move shared source code to commonize.
1 /* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkimggif.c,v 1.1.1.1 2001/06/13 05:02:40 dtashley Exp $ */
2
3 /*
4 * tkImgGIF.c --
5 *
6 * A photo image file handler for GIF files. Reads 87a and 89a GIF
7 * files. At present, there only is a file write function. GIF images may be
8 * read using the -data option of the photo image. The data may be
9 * given as a binary string in a Tcl_Obj or by representing
10 * the data as BASE64 encoded ascii. Derived from the giftoppm code
11 * found in the pbmplus package and tkImgFmtPPM.c in the tk4.0b2
12 * distribution.
13 *
14 * Copyright (c) Reed Wade (wade@cs.utk.edu), University of Tennessee
15 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
16 * Copyright (c) 1997 Australian National University
17 *
18 * See the file "license.terms" for information on usage and redistribution
19 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
20 *
21 * This file also contains code from the giftoppm program, which is
22 * copyrighted as follows:
23 *
24 * +-------------------------------------------------------------------+
25 * | Copyright 1990, David Koblas. |
26 * | Permission to use, copy, modify, and distribute this software |
27 * | and its documentation for any purpose and without fee is hereby |
28 * | granted, provided that the above copyright notice appear in all |
29 * | copies and that both that copyright notice and this permission |
30 * | notice appear in supporting documentation. This software is |
31 * | provided "as is" without express or implied warranty. |
32 * +-------------------------------------------------------------------+
33 *
34 * RCS: @(#) $Id: tkimggif.c,v 1.1.1.1 2001/06/13 05:02:40 dtashley Exp $
35 */
36
37 /*
38 * GIF's are represented as data in base64 format.
39 * base64 strings consist of 4 6-bit characters -> 3 8 bit bytes.
40 * A-Z, a-z, 0-9, + and / represent the 64 values (in order).
41 * '=' is a trailing padding char when the un-encoded data is not a
42 * multiple of 3 bytes. We'll ignore white space when encountered.
43 * Any other invalid character is treated as an EOF
44 */
45
46 #define GIF_SPECIAL (256)
47 #define GIF_PAD (GIF_SPECIAL+1)
48 #define GIF_SPACE (GIF_SPECIAL+2)
49 #define GIF_BAD (GIF_SPECIAL+3)
50 #define GIF_DONE (GIF_SPECIAL+4)
51
52 /*
53 * structure to "mimic" FILE for Mread, so we can look like fread.
54 * The decoder state keeps track of which byte we are about to read,
55 * or EOF.
56 */
57
58 typedef struct mFile {
59 unsigned char *data; /* mmencoded source string */
60 int c; /* bits left over from previous character */
61 int state; /* decoder state (0-4 or GIF_DONE) */
62 } MFile;
63
64 #include "tkInt.h"
65 #include "tkPort.h"
66
67 /*
68 * Non-ASCII encoding support:
69 * Most data in a GIF image is binary and is treated as such. However,
70 * a few key bits are stashed in ASCII. If we try to compare those pieces
71 * to the char they represent, it will fail on any non-ASCII (eg, EBCDIC)
72 * system. To accomodate these systems, we test against the numeric value
73 * of the ASCII characters instead of the characters themselves. This is
74 * encoding independant.
75 */
76
77 # define GIF87a "\x47\x49\x46\x38\x37\x61" /* ASCII GIF87a */
78 # define GIF89a "\x47\x49\x46\x38\x39\x61" /* ASCII GIF89a */
79 # define GIF_TERMINATOR 0x3b /* ASCII ; */
80 # define GIF_EXTENSION 0x21 /* ASCII ! */
81 # define GIF_START 0x2c /* ASCII , */
82
83 /*
84 * HACK ALERT!! HACK ALERT!! HACK ALERT!!
85 * This code is hard-wired for reading from files. In order to read
86 * from a data stream, we'll trick fread so we can reuse the same code.
87 * 0==from file; 1==from base64 encoded data; 2==from binary data
88 */
89
90 typedef struct ThreadSpecificData {
91 int fromData;
92 } ThreadSpecificData;
93 static Tcl_ThreadDataKey dataKey;
94
95 /*
96 * The format record for the GIF file format:
97 */
98
99 static int FileMatchGIF _ANSI_ARGS_((Tcl_Channel chan, CONST char *fileName,
100 Tcl_Obj *format, int *widthPtr, int *heightPtr,
101 Tcl_Interp *interp));
102 static int FileReadGIF _ANSI_ARGS_((Tcl_Interp *interp,
103 Tcl_Channel chan, CONST char *fileName, Tcl_Obj *format,
104 Tk_PhotoHandle imageHandle, int destX, int destY,
105 int width, int height, int srcX, int srcY));
106 static int StringMatchGIF _ANSI_ARGS_(( Tcl_Obj *dataObj,
107 Tcl_Obj *format, int *widthPtr, int *heightPtr,
108 Tcl_Interp *interp));
109 static int StringReadGIF _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *dataObj,
110 Tcl_Obj *format, Tk_PhotoHandle imageHandle,
111 int destX, int destY, int width, int height,
112 int srcX, int srcY));
113 static int FileWriteGIF _ANSI_ARGS_((Tcl_Interp *interp,
114 CONST char *filename, Tcl_Obj *format,
115 Tk_PhotoImageBlock *blockPtr));
116 static int CommonWriteGIF _ANSI_ARGS_((Tcl_Interp *interp,
117 Tcl_Channel handle, Tcl_Obj *format,
118 Tk_PhotoImageBlock *blockPtr));
119
120 Tk_PhotoImageFormat tkImgFmtGIF = {
121 "gif", /* name */
122 FileMatchGIF, /* fileMatchProc */
123 StringMatchGIF, /* stringMatchProc */
124 FileReadGIF, /* fileReadProc */
125 StringReadGIF, /* stringReadProc */
126 FileWriteGIF, /* fileWriteProc */
127 NULL, /* stringWriteProc */
128 };
129
130 #define INTERLACE 0x40
131 #define LOCALCOLORMAP 0x80
132 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
133 #define MAXCOLORMAPSIZE 256
134 #define CM_RED 0
135 #define CM_GREEN 1
136 #define CM_BLUE 2
137 #define CM_ALPHA 3
138 #define MAX_LWZ_BITS 12
139 #define LM_to_uint(a,b) (((b)<<8)|(a))
140 #define ReadOK(file,buffer,len) (Fread(buffer, len, 1, file) != 0)
141
142 /*
143 * Prototypes for local procedures defined in this file:
144 */
145
146 static int DoExtension _ANSI_ARGS_((Tcl_Channel chan, int label,
147 int *transparent));
148 static int GetCode _ANSI_ARGS_((Tcl_Channel chan, int code_size,
149 int flag));
150 static int GetDataBlock _ANSI_ARGS_((Tcl_Channel chan,
151 unsigned char *buf));
152 static int ReadColorMap _ANSI_ARGS_((Tcl_Channel chan, int number,
153 unsigned char buffer[MAXCOLORMAPSIZE][4]));
154 static int ReadGIFHeader _ANSI_ARGS_((Tcl_Channel chan,
155 int *widthPtr, int *heightPtr));
156 static int ReadImage _ANSI_ARGS_((Tcl_Interp *interp,
157 char *imagePtr, Tcl_Channel chan,
158 int len, int rows,
159 unsigned char cmap[MAXCOLORMAPSIZE][4],
160 int width, int height, int srcX, int srcY,
161 int interlace, int transparent));
162
163 /*
164 * these are for the BASE64 image reader code only
165 */
166
167 static int Fread _ANSI_ARGS_((unsigned char *dst, size_t size,
168 size_t count, Tcl_Channel chan));
169 static int Mread _ANSI_ARGS_((unsigned char *dst, size_t size,
170 size_t count, MFile *handle));
171 static int Mgetc _ANSI_ARGS_((MFile *handle));
172 static int char64 _ANSI_ARGS_((int c));
173 static void mInit _ANSI_ARGS_((unsigned char *string,
174 MFile *handle));
175
176
177 /*
178 *----------------------------------------------------------------------
179 *
180 * FileMatchGIF --
181 *
182 * This procedure is invoked by the photo image type to see if
183 * a file contains image data in GIF format.
184 *
185 * Results:
186 * The return value is 1 if the first characters in file f look
187 * like GIF data, and 0 otherwise.
188 *
189 * Side effects:
190 * The access position in f may change.
191 *
192 *----------------------------------------------------------------------
193 */
194
195 static int
196 FileMatchGIF(chan, fileName, format, widthPtr, heightPtr, interp)
197 Tcl_Channel chan; /* The image file, open for reading. */
198 CONST char *fileName; /* The name of the image file. */
199 Tcl_Obj *format; /* User-specified format object, or NULL. */
200 int *widthPtr, *heightPtr; /* The dimensions of the image are
201 * returned here if the file is a valid
202 * raw GIF file. */
203 Tcl_Interp *interp; /* not used */
204 {
205 return ReadGIFHeader(chan, widthPtr, heightPtr);
206 }
207
208 /*
209 *----------------------------------------------------------------------
210 *
211 * FileReadGIF --
212 *
213 * This procedure is called by the photo image type to read
214 * GIF format data from a file and write it into a given
215 * photo image.
216 *
217 * Results:
218 * A standard TCL completion code. If TCL_ERROR is returned
219 * then an error message is left in the interp's result.
220 *
221 * Side effects:
222 * The access position in file f is changed, and new data is
223 * added to the image given by imageHandle.
224 *
225 *----------------------------------------------------------------------
226 */
227
228 static int
229 FileReadGIF(interp, chan, fileName, format, imageHandle, destX, destY,
230 width, height, srcX, srcY)
231 Tcl_Interp *interp; /* Interpreter to use for reporting errors. */
232 Tcl_Channel chan; /* The image file, open for reading. */
233 CONST char *fileName; /* The name of the image file. */
234 Tcl_Obj *format; /* User-specified format object, or NULL. */
235 Tk_PhotoHandle imageHandle; /* The photo image to write into. */
236 int destX, destY; /* Coordinates of top-left pixel in
237 * photo image to be written to. */
238 int width, height; /* Dimensions of block of photo image to
239 * be written to. */
240 int srcX, srcY; /* Coordinates of top-left pixel to be used
241 * in image being read. */
242 {
243 int fileWidth, fileHeight;
244 int nBytes, index = 0, argc = 0, i;
245 Tcl_Obj **objv;
246 Tk_PhotoImageBlock block;
247 unsigned char buf[100];
248 unsigned char *trashBuffer = NULL;
249 int bitPixel;
250 unsigned char colorMap[MAXCOLORMAPSIZE][4];
251 int transparent = -1;
252 static char *optionStrings[] = {
253 "-index", NULL
254 };
255
256 if (format && Tcl_ListObjGetElements(interp, format,
257 &argc, &objv) != TCL_OK) {
258 return TCL_ERROR;
259 }
260 for (i = 1; i < argc; i++) {
261 if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings, "option name", 0,
262 &nBytes) != TCL_OK) {
263 return TCL_ERROR;
264 }
265 if (i == (argc-1)) {
266 Tcl_AppendResult(interp, "no value given for \"",
267 Tcl_GetStringFromObj(objv[i], NULL),
268 "\" option", (char *) NULL);
269 return TCL_ERROR;
270 }
271 if (Tcl_GetIntFromObj(interp, objv[++i], &index) != TCL_OK) {
272 return TCL_ERROR;
273 }
274 }
275 if (!ReadGIFHeader(chan, &fileWidth, &fileHeight)) {
276 Tcl_AppendResult(interp, "couldn't read GIF header from file \"",
277 fileName, "\"", NULL);
278 return TCL_ERROR;
279 }
280 if ((fileWidth <= 0) || (fileHeight <= 0)) {
281 Tcl_AppendResult(interp, "GIF image file \"", fileName,
282 "\" has dimension(s) <= 0", (char *) NULL);
283 return TCL_ERROR;
284 }
285
286 if (Fread(buf, 1, 3, chan) != 3) {
287 return TCL_OK;
288 }
289 bitPixel = 2<<(buf[0]&0x07);
290
291 if (BitSet(buf[0], LOCALCOLORMAP)) { /* Global Colormap */
292 if (!ReadColorMap(chan, bitPixel, colorMap)) {
293 Tcl_AppendResult(interp, "error reading color map",
294 (char *) NULL);
295 return TCL_ERROR;
296 }
297 }
298
299 if ((srcX + width) > fileWidth) {
300 width = fileWidth - srcX;
301 }
302 if ((srcY + height) > fileHeight) {
303 height = fileHeight - srcY;
304 }
305 if ((width <= 0) || (height <= 0)
306 || (srcX >= fileWidth) || (srcY >= fileHeight)) {
307 return TCL_OK;
308 }
309
310 Tk_PhotoExpand(imageHandle, destX + width, destY + height);
311
312 block.width = width;
313 block.height = height;
314 block.pixelSize = 4;
315 block.pitch = block.pixelSize * block.width;
316 block.offset[0] = 0;
317 block.offset[1] = 1;
318 block.offset[2] = 2;
319 block.offset[3] = 3;
320 block.pixelPtr = NULL;
321
322 while (1) {
323 if (Fread(buf, 1, 1, chan) != 1) {
324 /*
325 * Premature end of image. We should really notify
326 * the user, but for now just show garbage.
327 */
328
329 break;
330 }
331
332 if (buf[0] == GIF_TERMINATOR) {
333 /*
334 * GIF terminator.
335 */
336
337 Tcl_AppendResult(interp,"no image data for this index",
338 (char *) NULL);
339 goto error;
340 }
341
342 if (buf[0] == GIF_EXTENSION) {
343 /*
344 * This is a GIF extension.
345 */
346
347 if (Fread(buf, 1, 1, chan) != 1) {
348 Tcl_SetResult(interp,
349 "error reading extension function code in GIF image",
350 TCL_STATIC);
351 goto error;
352 }
353 if (DoExtension(chan, buf[0], &transparent) < 0) {
354 Tcl_SetResult(interp, "error reading extension in GIF image",
355 TCL_STATIC);
356 goto error;
357 }
358 continue;
359 }
360
361 if (buf[0] != GIF_START) {
362 /*
363 * Not a valid start character; ignore it.
364 */
365 continue;
366 }
367
368 if (Fread(buf, 1, 9, chan) != 9) {
369 Tcl_SetResult(interp,
370 "couldn't read left/top/width/height in GIF image",
371 TCL_STATIC);
372 goto error;
373 }
374
375 fileWidth = LM_to_uint(buf[4],buf[5]);
376 fileHeight = LM_to_uint(buf[6],buf[7]);
377
378 bitPixel = 1<<((buf[8]&0x07)+1);
379
380 if (index--) {
381 /* this is not the image we want to read: skip it. */
382 if (BitSet(buf[8], LOCALCOLORMAP)) {
383 if (!ReadColorMap(chan, bitPixel, colorMap)) {
384 Tcl_AppendResult(interp,
385 "error reading color map", (char *) NULL);
386 goto error;
387 }
388 }
389
390 /* If we've not yet allocated a trash buffer, do so now */
391 if (trashBuffer == NULL) {
392 nBytes = fileWidth * fileHeight * 3;
393 trashBuffer =
394 (unsigned char *) ckalloc((unsigned int) nBytes);
395 }
396
397 /*
398 * Slurp! Process the data for this image and stuff it in a
399 * trash buffer.
400 *
401 * Yes, it might be more efficient here to *not* store the data
402 * (we're just going to throw it away later). However, I elected
403 * to implement it this way for good reasons. First, I wanted to
404 * avoid duplicating the (fairly complex) LWZ decoder in ReadImage.
405 * Fine, you say, why didn't you just modify it to allow the use of
406 * a NULL specifier for the output buffer? I tried that, but it
407 * negatively impacted the performance of what I think will be the
408 * common case: reading the first image in the file. Rather than
409 * marginally improve the speed of the less frequent case, I chose
410 * to maintain high performance for the common case.
411 */
412 if (ReadImage(interp, (char *) trashBuffer, chan, fileWidth,
413 fileHeight, colorMap, 0, 0, 0, 0, 0, -1) != TCL_OK) {
414 goto error;
415 }
416 continue;
417 }
418
419 /* If a trash buffer has been allocated, free it now */
420 if (trashBuffer != NULL) {
421 ckfree((char *)trashBuffer);
422 trashBuffer = NULL;
423 }
424 if (BitSet(buf[8], LOCALCOLORMAP)) {
425 if (!ReadColorMap(chan, bitPixel, colorMap)) {
426 Tcl_AppendResult(interp, "error reading color map",
427 (char *) NULL);
428 goto error;
429 }
430 }
431
432 index = LM_to_uint(buf[0],buf[1]);
433 srcX -= index;
434 if (srcX<0) {
435 destX -= srcX; width += srcX;
436 srcX = 0;
437 }
438
439 if (width > fileWidth) {
440 width = fileWidth;
441 }
442
443 index = LM_to_uint(buf[2],buf[3]);
444 srcY -= index;
445 if (index > srcY) {
446 destY -= srcY; height += srcY;
447 srcY = 0;
448 }
449 if (height > fileHeight) {
450 height = fileHeight;
451 }
452
453 if ((width <= 0) || (height <= 0)) {
454 block.pixelPtr = 0;
455 goto noerror;
456 }
457
458 block.width = width;
459 block.height = height;
460 block.pixelSize = (transparent>=0) ? 4 : 3;
461 block.offset[3] = (transparent>=0) ? 3 : 0;
462 block.pitch = block.pixelSize * width;
463 nBytes = block.pitch * height;
464 block.pixelPtr = (unsigned char *) ckalloc((unsigned) nBytes);
465
466 if (ReadImage(interp, (char *) block.pixelPtr, chan, width,
467 height, colorMap, fileWidth, fileHeight, srcX, srcY,
468 BitSet(buf[8], INTERLACE), transparent) != TCL_OK) {
469 goto error;
470 }
471 break;
472 }
473
474 Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, height);
475
476 noerror:
477 if (block.pixelPtr) {
478 ckfree((char *) block.pixelPtr);
479 }
480 Tcl_AppendResult(interp, tkImgFmtGIF.name, (char *) NULL);
481 return TCL_OK;
482
483 error:
484 if (block.pixelPtr) {
485 ckfree((char *) block.pixelPtr);
486 }
487 return TCL_ERROR;
488
489 }
490
491 /*
492 *----------------------------------------------------------------------
493 *
494 * StringMatchGIF --
495 *
496 * This procedure is invoked by the photo image type to see if
497 * an object contains image data in GIF format.
498 *
499 * Results:
500 * The return value is 1 if the first characters in the data are
501 * like GIF data, and 0 otherwise.
502 *
503 * Side effects:
504 * the size of the image is placed in widthPre and heightPtr.
505 *
506 *----------------------------------------------------------------------
507 */
508
509 static int
510 StringMatchGIF(dataObj, format, widthPtr, heightPtr, interp)
511 Tcl_Obj *dataObj; /* the object containing the image data */
512 Tcl_Obj *format; /* the image format object, or NULL */
513 int *widthPtr; /* where to put the string width */
514 int *heightPtr; /* where to put the string height */
515 Tcl_Interp *interp; /* not used */
516 {
517 unsigned char *data, header[10];
518 int got, length;
519 MFile handle;
520
521 data = Tcl_GetByteArrayFromObj(dataObj, &length);
522
523 /* Header is a minimum of 10 bytes */
524 if (length < 10) {
525 return 0;
526 }
527
528 /* Check whether the data is Base64 encoded */
529
530 if ((strncmp(GIF87a, (char *) data, 6) != 0) &&
531 (strncmp(GIF89a, (char *) data, 6) != 0)) {
532 /* Try interpreting the data as Base64 encoded */
533 mInit((unsigned char *) data, &handle);
534 got = Mread(header, 10, 1, &handle);
535 if (got != 10
536 || ((strncmp(GIF87a, (char *) header, 6) != 0)
537 && (strncmp(GIF89a, (char *) header, 6) != 0))) {
538 return 0;
539 }
540 } else {
541 memcpy((VOID *) header, (VOID *) data, 10);
542 }
543 *widthPtr = LM_to_uint(header[6],header[7]);
544 *heightPtr = LM_to_uint(header[8],header[9]);
545 return 1;
546 }
547
548 /*
549 *----------------------------------------------------------------------
550 *
551 * StringReadGif -- --
552 *
553 * This procedure is called by the photo image type to read
554 * GIF format data from an object, optionally base64 encoded,
555 * and give it to the photo image.
556 *
557 * Results:
558 * A standard TCL completion code. If TCL_ERROR is returned
559 * then an error message is left in the interp's result.
560 *
561 * Side effects:
562 * new data is added to the image given by imageHandle. This
563 * procedure calls FileReadGif by redefining the operation of
564 * fprintf temporarily.
565 *
566 *----------------------------------------------------------------------
567 */
568
569 static int
570 StringReadGIF(interp, dataObj, format, imageHandle,
571 destX, destY, width, height, srcX, srcY)
572 Tcl_Interp *interp; /* interpreter for reporting errors in */
573 Tcl_Obj *dataObj; /* object containing the image */
574 Tcl_Obj *format; /* format object, or NULL */
575 Tk_PhotoHandle imageHandle; /* the image to write this data into */
576 int destX, destY; /* The rectangular region of the */
577 int width, height; /* image to copy */
578 int srcX, srcY;
579 {
580 int result;
581 MFile handle;
582 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
583 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
584 Tcl_Channel dataSrc;
585 char *data;
586
587 /*
588 * Check whether the data is Base64 encoded
589 */
590 data = (char *) Tcl_GetByteArrayFromObj(dataObj, NULL);
591 if ((strncmp(GIF87a, data, 6) != 0) &&
592 (strncmp(GIF89a, data, 6) != 0)) {
593 mInit((unsigned char *)data, &handle);
594 tsdPtr->fromData = 1;
595 dataSrc = (Tcl_Channel) &handle;
596 } else {
597 tsdPtr->fromData = 2;
598 mInit((unsigned char *)data, &handle);
599 dataSrc = (Tcl_Channel) &handle;
600 }
601 result = FileReadGIF(interp, dataSrc, "inline data",
602 format, imageHandle, destX, destY, width, height, srcX, srcY);
603 tsdPtr->fromData = 0;
604 return(result);
605 }
606
607 /*
608 *----------------------------------------------------------------------
609 *
610 * ReadGIFHeader --
611 *
612 * This procedure reads the GIF header from the beginning of a
613 * GIF file and returns the dimensions of the image.
614 *
615 * Results:
616 * The return value is 1 if file "f" appears to start with
617 * a valid GIF header, 0 otherwise. If the header is valid,
618 * then *widthPtr and *heightPtr are modified to hold the
619 * dimensions of the image.
620 *
621 * Side effects:
622 * The access position in f advances.
623 *
624 *----------------------------------------------------------------------
625 */
626
627 static int
628 ReadGIFHeader(chan, widthPtr, heightPtr)
629 Tcl_Channel chan; /* Image file to read the header from */
630 int *widthPtr, *heightPtr; /* The dimensions of the image are
631 * returned here. */
632 {
633 unsigned char buf[7];
634
635 if ((Fread(buf, 1, 6, chan) != 6)
636 || ((strncmp(GIF87a, (char *) buf, 6) != 0)
637 && (strncmp(GIF89a, (char *) buf, 6) != 0))) {
638 return 0;
639 }
640
641 if (Fread(buf, 1, 4, chan) != 4) {
642 return 0;
643 }
644
645 *widthPtr = LM_to_uint(buf[0],buf[1]);
646 *heightPtr = LM_to_uint(buf[2],buf[3]);
647 return 1;
648 }
649
650 /*
651 *-----------------------------------------------------------------
652 * The code below is copied from the giftoppm program and modified
653 * just slightly.
654 *-----------------------------------------------------------------
655 */
656
657 static int
658 ReadColorMap(chan, number, buffer)
659 Tcl_Channel chan;
660 int number;
661 unsigned char buffer[MAXCOLORMAPSIZE][4];
662 {
663 int i;
664 unsigned char rgb[3];
665
666 for (i = 0; i < number; ++i) {
667 if (! ReadOK(chan, rgb, sizeof(rgb))) {
668 return 0;
669 }
670
671 if (buffer) {
672 buffer[i][CM_RED] = rgb[0] ;
673 buffer[i][CM_GREEN] = rgb[1] ;
674 buffer[i][CM_BLUE] = rgb[2] ;
675 buffer[i][CM_ALPHA] = 255 ;
676 }
677 }
678 return 1;
679 }
680
681
682
683 static int
684 DoExtension(chan, label, transparent)
685 Tcl_Channel chan;
686 int label;
687 int *transparent;
688 {
689 static unsigned char buf[256];
690 int count;
691
692 switch (label) {
693 case 0x01: /* Plain Text Extension */
694 break;
695
696 case 0xff: /* Application Extension */
697 break;
698
699 case 0xfe: /* Comment Extension */
700 do {
701 count = GetDataBlock(chan, (unsigned char*) buf);
702 } while (count > 0);
703 return count;
704
705 case 0xf9: /* Graphic Control Extension */
706 count = GetDataBlock(chan, (unsigned char*) buf);
707 if (count < 0) {
708 return 1;
709 }
710 if ((buf[0] & 0x1) != 0) {
711 *transparent = buf[3];
712 }
713
714 do {
715 count = GetDataBlock(chan, (unsigned char*) buf);
716 } while (count > 0);
717 return count;
718 }
719
720 do {
721 count = GetDataBlock(chan, (unsigned char*) buf);
722 } while (count > 0);
723 return count;
724 }
725
726 static int ZeroDataBlock = 0;
727
728 static int
729 GetDataBlock(chan, buf)
730 Tcl_Channel chan;
731 unsigned char *buf;
732 {
733 unsigned char count;
734
735 if (! ReadOK(chan, &count,1)) {
736 return -1;
737 }
738
739 ZeroDataBlock = count == 0;
740
741 if ((count != 0) && (! ReadOK(chan, buf, count))) {
742 return -1;
743 }
744
745 return count;
746 }
747
748
749
750 /*
751 *----------------------------------------------------------------------
752 *
753 * ReadImage --
754 *
755 * Process a GIF image from a given source, with a given height,
756 * width, transparency, etc.
757 *
758 * This code is based on the code found in the ImageMagick GIF decoder,
759 * which is (c) 2000 ImageMagick Studio.
760 *
761 * Some thoughts on our implementation:
762 * It sure would be nice if ReadImage didn't take 11 parameters! I think
763 * that if we were smarter, we could avoid doing that.
764 *
765 * Possible further optimizations: we could pull the GetCode function
766 * directly into ReadImage, which would improve our speed.
767 *
768 * Results:
769 * Processes a GIF image and loads the pixel data into a memory array.
770 *
771 * Side effects:
772 * None.
773 *
774 *----------------------------------------------------------------------
775 */
776
777 static int
778 ReadImage(interp, imagePtr, chan, len, rows, cmap,
779 width, height, srcX, srcY, interlace, transparent)
780 Tcl_Interp *interp;
781 char *imagePtr;
782 Tcl_Channel chan;
783 int len, rows;
784 unsigned char cmap[MAXCOLORMAPSIZE][4];
785 int width, height;
786 int srcX, srcY;
787 int interlace;
788 int transparent;
789 {
790 unsigned char initialCodeSize;
791 int v;
792 int xpos = 0, ypos = 0, pass = 0, i;
793 register char *pixelPtr;
794 CONST static int interlaceStep[] = { 8, 8, 4, 2 };
795 CONST static int interlaceStart[] = { 0, 4, 2, 1 };
796 unsigned short prefix[(1 << MAX_LWZ_BITS)];
797 unsigned char append[(1 << MAX_LWZ_BITS)];
798 unsigned char stack[(1 << MAX_LWZ_BITS)*2];
799 register unsigned char *top;
800 int codeSize, clearCode, inCode, endCode, oldCode, maxCode,
801 code, firstCode;
802
803 /*
804 * Initialize the decoder
805 */
806 if (! ReadOK(chan, &initialCodeSize, 1)) {
807 Tcl_AppendResult(interp, "error reading GIF image: ",
808 Tcl_PosixError(interp), (char *) NULL);
809 return TCL_ERROR;
810 }
811 if (transparent!=-1) {
812 cmap[transparent][CM_RED] = 0;
813 cmap[transparent][CM_GREEN] = 0;
814 cmap[transparent][CM_BLUE] = 0;
815 cmap[transparent][CM_ALPHA] = 0;
816 }
817
818 pixelPtr = imagePtr;
819
820 /* Initialize the decoder */
821 /* Set values for "special" numbers:
822 * clear code reset the decoder
823 * end code stop decoding
824 * code size size of the next code to retrieve
825 * max code next available table position
826 */
827 clearCode = 1 << (int) initialCodeSize;
828 endCode = clearCode + 1;
829 codeSize = (int) initialCodeSize + 1;
830 maxCode = clearCode + 2;
831 oldCode = -1;
832 firstCode = -1;
833
834 memset((void *)prefix, 0, (1 << MAX_LWZ_BITS) * sizeof(short));
835 memset((void *)append, 0, (1 << MAX_LWZ_BITS) * sizeof(char));
836 for (i = 0; i < clearCode; i++) {
837 append[i] = i;
838 }
839 top = stack;
840
841 GetCode(chan, 0, 1);
842
843 /* Read until we finish the image */
844 for (i = 0, ypos = 0; i < rows; i++) {
845 for (xpos = 0; xpos < len; ) {
846
847 if (top == stack) {
848 /* Bummer -- our stack is empty. Now we have to work! */
849 code = GetCode(chan, codeSize, 0);
850 if (code < 0) {
851 return TCL_OK;
852 }
853
854 if (code > maxCode || code == endCode) {
855 /*
856 * If we're doing things right, we should never
857 * receive a code that is greater than our current
858 * maximum code. If we do, bail, because our decoder
859 * does not yet have that code set up.
860 *
861 * If the code is the magic endCode value, quit.
862 */
863 return TCL_OK;
864 }
865
866 if (code == clearCode) {
867 /* Reset the decoder */
868 codeSize = initialCodeSize + 1;
869 maxCode = clearCode + 2;
870 oldCode = -1;
871 continue;
872 }
873
874 if (oldCode == -1) {
875 /*
876 * Last pass reset the decoder, so the first code we
877 * see must be a singleton. Seed the stack with it,
878 * and set up the old/first code pointers for
879 * insertion into the string table. We can't just
880 * roll this into the clearCode test above, because
881 * at that point we have not yet read the next code.
882 */
883 *top++=append[code];
884 oldCode = code;
885 firstCode = code;
886 continue;
887 }
888
889 inCode = code;
890
891 if (code == maxCode) {
892 /*
893 * maxCode is always one bigger than our highest assigned
894 * code. If the code we see is equal to maxCode, then
895 * we are about to add a new string to the table. ???
896 */
897 *top++ = firstCode;
898 code = oldCode;
899 }
900
901 while (code > clearCode) {
902 /*
903 * Populate the stack by tracing the string in the
904 * string table from its tail to its head
905 */
906 *top++ = append[code];
907 code = prefix[code];
908 }
909 firstCode = append[code];
910
911 /*
912 * If there's no more room in our string table, quit.
913 * Otherwise, add a new string to the table
914 */
915 if (maxCode >= (1 << MAX_LWZ_BITS)) {
916 return TCL_OK;
917 }
918
919 /* Push the head of the string onto the stack */
920 *top++ = firstCode;
921
922 /* Add a new string to the string table */
923 prefix[maxCode] = oldCode;
924 append[maxCode] = firstCode;
925 maxCode++;
926
927 /* maxCode tells us the maximum code value we can accept.
928 * If we see that we need more bits to represent it than
929 * we are requesting from the unpacker, we need to increase
930 * the number we ask for.
931 */
932 if ((maxCode >= (1 << codeSize))
933 && (maxCode < (1<<MAX_LWZ_BITS))) {
934 codeSize++;
935 }
936 oldCode = inCode;
937 }
938
939 /* Pop the next color index off the stack */
940 v = *(--top);
941 if (v < 0) {
942 return TCL_OK;
943 }
944
945 /*
946 * If pixelPtr is null, we're skipping this image (presumably
947 * there are more in the file and we will be called to read
948 * one of them later)
949 */
950 *pixelPtr++ = cmap[v][CM_RED];
951 *pixelPtr++ = cmap[v][CM_GREEN];
952 *pixelPtr++ = cmap[v][CM_BLUE];
953 if (transparent >= 0) {
954 *pixelPtr++ = cmap[v][CM_ALPHA];
955 }
956 xpos++;
957
958 }
959
960 /* If interlacing, the next ypos is not just +1 */
961 if (interlace) {
962 ypos += interlaceStep[pass];
963 while (ypos >= height) {
964 pass++;
965 if (pass > 3) {
966 return TCL_OK;
967 }
968 ypos = interlaceStart[pass];
969 }
970 } else {
971 ypos++;
972 }
973 pixelPtr = imagePtr + (ypos) * len * ((transparent>=0)?4:3);
974 }
975 return TCL_OK;
976 }
977
978
979 /*
980 *----------------------------------------------------------------------
981 *
982 * GetCode --
983 *
984 * Extract the next compression code from the file. In GIF's, the
985 * compression codes are between 3 and 12 bits long and are then
986 * packed into 8 bit bytes, left to right, for example:
987 * bbbaaaaa
988 * dcccccbb
989 * eeeedddd
990 * ...
991 * We use a byte buffer read from the file and a sliding window
992 * to unpack the bytes. Thanks to ImageMagick for the sliding window
993 * idea.
994 * args: chan the channel to read from
995 * code_size size of the code to extract
996 * flag boolean indicating whether the extractor
997 * should be reset or not
998 *
999 * Results:
1000 * code the next compression code
1001 *
1002 * Side effects:
1003 * May consume more input from chan.
1004 *
1005 *----------------------------------------------------------------------
1006 */
1007
1008 static int
1009 GetCode(chan, code_size, flag)
1010 Tcl_Channel chan;
1011 int code_size;
1012 int flag;
1013 {
1014 static unsigned char buf[280];
1015 static int bytes = 0, done;
1016 static unsigned char *c;
1017
1018 static unsigned int window;
1019 static int bitsInWindow = 0;
1020 int ret;
1021
1022 if (flag) {
1023 /* Initialize the decoder */
1024 bitsInWindow = 0;
1025 bytes = 0;
1026 window = 0;
1027 done = 0;
1028 c = NULL;
1029 return 0;
1030 }
1031
1032 while (bitsInWindow < code_size) {
1033 /* Not enough bits in our window to cover the request */
1034 if (done) {
1035 return -1;
1036 }
1037 if (bytes == 0) {
1038 /* Not enough bytes in our buffer to add to the window */
1039 bytes = GetDataBlock(chan, buf);
1040 c = buf;
1041 if (bytes <= 0) {
1042 done = 1;
1043 break;
1044 }
1045 }
1046 /* Tack another byte onto the window, see if that's enough */
1047 window += (*c) << bitsInWindow;
1048 c++;
1049 bitsInWindow += 8;
1050 bytes--;
1051 }
1052
1053
1054 /* The next code will always be the last code_size bits of the window */
1055 ret = window & ((1 << code_size) - 1);
1056
1057 /* Shift data in the window to put the next code at the end */
1058 window >>= code_size;
1059 bitsInWindow -= code_size;
1060 return ret;
1061 }
1062
1063 /*
1064 *----------------------------------------------------------------------
1065 *
1066 * Minit -- --
1067 *
1068 * This procedure initializes a base64 decoder handle
1069 *
1070 * Results:
1071 * none
1072 *
1073 * Side effects:
1074 * the base64 handle is initialized
1075 *
1076 *----------------------------------------------------------------------
1077 */
1078
1079 static void
1080 mInit(string, handle)
1081 unsigned char *string; /* string containing initial mmencoded data */
1082 MFile *handle; /* mmdecode "file" handle */
1083 {
1084 handle->data = string;
1085 handle->state = 0;
1086 }
1087
1088 /*
1089 *----------------------------------------------------------------------
1090 *
1091 * Mread --
1092 *
1093 * This procedure is invoked by the GIF file reader as a
1094 * temporary replacement for "fread", to get GIF data out
1095 * of a string (using Mgetc).
1096 *
1097 * Results:
1098 * The return value is the number of characters "read"
1099 *
1100 * Side effects:
1101 * The base64 handle will change state.
1102 *
1103 *----------------------------------------------------------------------
1104 */
1105
1106 static int
1107 Mread(dst, chunkSize, numChunks, handle)
1108 unsigned char *dst; /* where to put the result */
1109 size_t chunkSize; /* size of each transfer */
1110 size_t numChunks; /* number of chunks */
1111 MFile *handle; /* mmdecode "file" handle */
1112 {
1113 register int i, c;
1114 int count = chunkSize * numChunks;
1115
1116 for(i=0; i<count && (c=Mgetc(handle)) != GIF_DONE; i++) {
1117 *dst++ = c;
1118 }
1119 return i;
1120 }
1121
1122 /*
1123 * get the next decoded character from an mmencode handle
1124 * This causes at least 1 character to be "read" from the encoded string
1125 */
1126
1127 /*
1128 *----------------------------------------------------------------------
1129 *
1130 * Mgetc --
1131 *
1132 * This procedure decodes and returns the next byte from a base64
1133 * encoded string.
1134 *
1135 * Results:
1136 * The next byte (or GIF_DONE) is returned.
1137 *
1138 * Side effects:
1139 * The base64 handle will change state.
1140 *
1141 *----------------------------------------------------------------------
1142 */
1143
1144 static int
1145 Mgetc(handle)
1146 MFile *handle; /* Handle containing decoder data and state. */
1147 {
1148 int c;
1149 int result = 0; /* Initialization needed only to prevent
1150 * gcc compiler warning. */
1151
1152 if (handle->state == GIF_DONE) {
1153 return(GIF_DONE);
1154 }
1155
1156 do {
1157 c = char64(*handle->data);
1158 handle->data++;
1159 } while (c==GIF_SPACE);
1160
1161 if (c>GIF_SPECIAL) {
1162 handle->state = GIF_DONE;
1163 return(handle->state ? handle->c : GIF_DONE);
1164 }
1165
1166 switch (handle->state++) {
1167 case 0:
1168 handle->c = c<<2;
1169 result = Mgetc(handle);
1170 break;
1171 case 1:
1172 result = handle->c | (c>>4);
1173 handle->c = (c&0xF)<<4;
1174 break;
1175 case 2:
1176 result = handle->c | (c>>2);
1177 handle->c = (c&0x3) << 6;
1178 break;
1179 case 3:
1180 result = handle->c | c;
1181 handle->state = 0;
1182 break;
1183 }
1184 return(result);
1185 }
1186
1187 /*
1188 *----------------------------------------------------------------------
1189 *
1190 * char64 --
1191 *
1192 * This procedure converts a base64 ascii character into its binary
1193 * equivalent. This code is a slightly modified version of the
1194 * char64 proc in N. Borenstein's metamail decoder.
1195 *
1196 * Results:
1197 * The binary value, or an error code.
1198 *
1199 * Side effects:
1200 * None.
1201 *----------------------------------------------------------------------
1202 */
1203
1204 static int
1205 char64(c)
1206 int c;
1207 {
1208 switch(c) {
1209 case 'A': return(0); case 'B': return(1); case 'C': return(2);
1210 case 'D': return(3); case 'E': return(4); case 'F': return(5);
1211 case 'G': return(6); case 'H': return(7); case 'I': return(8);
1212 case 'J': return(9); case 'K': return(10); case 'L': return(11);
1213 case 'M': return(12); case 'N': return(13); case 'O': return(14);
1214 case 'P': return(15); case 'Q': return(16); case 'R': return(17);
1215 case 'S': return(18); case 'T': return(19); case 'U': return(20);
1216 case 'V': return(21); case 'W': return(22); case 'X': return(23);
1217 case 'Y': return(24); case 'Z': return(25); case 'a': return(26);
1218 case 'b': return(27); case 'c': return(28); case 'd': return(29);
1219 case 'e': return(30); case 'f': return(31); case 'g': return(32);
1220 case 'h': return(33); case 'i': return(34); case 'j': return(35);
1221 case 'k': return(36); case 'l': return(37); case 'm': return(38);
1222 case 'n': return(39); case 'o': return(40); case 'p': return(41);
1223 case 'q': return(42); case 'r': return(43); case 's': return(44);
1224 case 't': return(45); case 'u': return(46); case 'v': return(47);
1225 case 'w': return(48); case 'x': return(49); case 'y': return(50);
1226 case 'z': return(51); case '0': return(52); case '1': return(53);
1227 case '2': return(54); case '3': return(55); case '4': return(56);
1228 case '5': return(57); case '6': return(58); case '7': return(59);
1229 case '8': return(60); case '9': return(61); case '+': return(62);
1230 case '/': return(63);
1231
1232 case ' ': case '\t': case '\n': case '\r': case '\f': return(GIF_SPACE);
1233 case '=': return(GIF_PAD);
1234 case '\0': return(GIF_DONE);
1235 default: return(GIF_BAD);
1236 }
1237 }
1238
1239 /*
1240 *----------------------------------------------------------------------
1241 *
1242 * Fread --
1243 *
1244 * This procedure calls either fread or Mread to read data
1245 * from a file or a base64 encoded string.
1246 *
1247 * Results: - same as fread
1248 *
1249 *----------------------------------------------------------------------
1250 */
1251
1252 static int
1253 Fread(dst, hunk, count, chan)
1254 unsigned char *dst; /* where to put the result */
1255 size_t hunk,count; /* how many */
1256 Tcl_Channel chan;
1257 {
1258 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1259 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1260 MFile *handle;
1261
1262 switch (tsdPtr->fromData) {
1263 case 1:
1264 return(Mread(dst, hunk, count, (MFile *) chan));
1265 case 2:
1266 handle = (MFile *) chan;
1267 memcpy((VOID *)dst, (VOID *) handle->data, (size_t) (hunk * count));
1268 handle->data += hunk * count;
1269 return((int) (hunk * count));
1270 default:
1271 return Tcl_Read(chan, (char *) dst, (int) (hunk * count));
1272 }
1273 }
1274
1275
1276 /*
1277 * ChanWriteGIF - writes a image in GIF format.
1278 *-------------------------------------------------------------------------
1279 * Author: Lolo
1280 * Engeneering Projects Area
1281 * Department of Mining
1282 * University of Oviedo
1283 * e-mail zz11425958@zeus.etsimo.uniovi.es
1284 * lolo@pcsig22.etsimo.uniovi.es
1285 * Date: Fri September 20 1996
1286 *
1287 * Modified for transparency handling (gif89a) and miGIF compression
1288 * by Jan Nijtmans <j.nijtmans@chello.nl>
1289 *
1290 *----------------------------------------------------------------------
1291 * FileWriteGIF-
1292 *
1293 * This procedure is called by the photo image type to write
1294 * GIF format data from a photo image into a given file
1295 *
1296 * Results:
1297 * A standard TCL completion code. If TCL_ERROR is returned
1298 * then an error message is left in interp->result.
1299 *
1300 *----------------------------------------------------------------------
1301 */
1302
1303 /*
1304 * Types, defines and variables needed to write and compress a GIF.
1305 */
1306
1307 typedef int (* ifunptr) _ANSI_ARGS_((void));
1308
1309 #define LSB(a) ((unsigned char) (((short)(a)) & 0x00FF))
1310 #define MSB(a) ((unsigned char) (((short)(a)) >> 8))
1311
1312 #define GIFBITS 12
1313 #define HSIZE 5003 /* 80% occupancy */
1314
1315 static int ssize;
1316 static int csize;
1317 static int rsize;
1318 static unsigned char *pixelo;
1319 static int pixelSize;
1320 static int pixelPitch;
1321 static int greenOffset;
1322 static int blueOffset;
1323 static int alphaOffset;
1324 static int num;
1325 static unsigned char mapa[MAXCOLORMAPSIZE][3];
1326
1327 /*
1328 * Definition of new functions to write GIFs
1329 */
1330
1331 static int color _ANSI_ARGS_((int red,int green, int blue));
1332 static void compress _ANSI_ARGS_((int init_bits, Tcl_Channel handle,
1333 ifunptr readValue));
1334 static int nuevo _ANSI_ARGS_((int red, int green ,int blue,
1335 unsigned char mapa[MAXCOLORMAPSIZE][3]));
1336 static int savemap _ANSI_ARGS_((Tk_PhotoImageBlock *blockPtr,
1337 unsigned char mapa[MAXCOLORMAPSIZE][3]));
1338 static int ReadValue _ANSI_ARGS_((void));
1339 static int no_bits _ANSI_ARGS_((int colors));
1340
1341 static int
1342 FileWriteGIF (interp, filename, format, blockPtr)
1343 Tcl_Interp *interp; /* Interpreter to use for reporting errors. */
1344 CONST char *filename;
1345 Tcl_Obj *format;
1346 Tk_PhotoImageBlock *blockPtr;
1347 {
1348 Tcl_Channel chan = NULL;
1349 int result;
1350
1351 chan = Tcl_OpenFileChannel(interp, (char *) filename, "w", 0644);
1352 if (!chan) {
1353 return TCL_ERROR;
1354 }
1355 if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") != TCL_OK) {
1356 return TCL_ERROR;
1357 }
1358 if (Tcl_SetChannelOption(interp, chan, "-encoding", "binary") != TCL_OK) {
1359 return TCL_ERROR;
1360 }
1361
1362 result = CommonWriteGIF(interp, chan, format, blockPtr);
1363 if (Tcl_Close(interp, chan) == TCL_ERROR) {
1364 return TCL_ERROR;
1365 }
1366 return result;
1367 }
1368
1369 #define Mputc(c,handle) Tcl_Write(handle,(char *) &c,1)
1370
1371 static int
1372 CommonWriteGIF(interp, handle, format, blockPtr)
1373 Tcl_Interp *interp;
1374 Tcl_Channel handle;
1375 Tcl_Obj *format;
1376 Tk_PhotoImageBlock *blockPtr;
1377 {
1378 int resolution;
1379 long numcolormap;
1380
1381 long width,height,x;
1382 unsigned char c;
1383 unsigned int top,left;
1384 int num;
1385
1386 top = 0;
1387 left = 0;
1388
1389 pixelSize=blockPtr->pixelSize;
1390 greenOffset=blockPtr->offset[1]-blockPtr->offset[0];
1391 blueOffset=blockPtr->offset[2]-blockPtr->offset[0];
1392 alphaOffset = blockPtr->offset[0];
1393 if (alphaOffset < blockPtr->offset[2]) {
1394 alphaOffset = blockPtr->offset[2];
1395 }
1396 if (++alphaOffset < pixelSize) {
1397 alphaOffset -= blockPtr->offset[0];
1398 } else {
1399 alphaOffset = 0;
1400 }
1401
1402 Tcl_Write(handle, (char *) (alphaOffset ? GIF89a : GIF87a), 6);
1403
1404 for (x=0;x<MAXCOLORMAPSIZE;x++) {
1405 mapa[x][CM_RED] = 255;
1406 mapa[x][CM_GREEN] = 255;
1407 mapa[x][CM_BLUE] = 255;
1408 }
1409
1410
1411 width=blockPtr->width;
1412 height=blockPtr->height;
1413 pixelo=blockPtr->pixelPtr + blockPtr->offset[0];
1414 pixelPitch=blockPtr->pitch;
1415 if ((num=savemap(blockPtr,mapa))<0) {
1416 Tcl_AppendResult(interp, "too many colors", (char *) NULL);
1417 return TCL_ERROR;
1418 }
1419 if (num<3) num=3;
1420 c=LSB(width);
1421 Mputc(c,handle);
1422 c=MSB(width);
1423 Mputc(c,handle);
1424 c=LSB(height);
1425 Mputc(c,handle);
1426 c=MSB(height);
1427 Mputc(c,handle);
1428
1429 c= (1 << 7) | (no_bits(num) << 4) | (no_bits(num));
1430 Mputc(c,handle);
1431 resolution = no_bits(num)+1;
1432
1433 numcolormap=1 << resolution;
1434
1435 /* background color */
1436
1437 c = 0;
1438 Mputc(c,handle);
1439
1440 /* zero for future expansion */
1441
1442 Mputc(c,handle);
1443
1444 for (x=0; x<numcolormap ;x++) {
1445 c = mapa[x][CM_RED];
1446 Mputc(c,handle);
1447 c = mapa[x][CM_GREEN];
1448 Mputc(c,handle);
1449 c = mapa[x][CM_BLUE];
1450 Mputc(c,handle);
1451 }
1452
1453 /*
1454 * Write out extension for transparent colour index, if necessary.
1455 */
1456
1457 if (alphaOffset) {
1458 c = GIF_EXTENSION;
1459 Mputc(c, handle);
1460 Tcl_Write(handle, "\371\4\1\0\0\0", 7);
1461 }
1462
1463 c = GIF_START;
1464 Mputc(c,handle);
1465 c=LSB(top);
1466 Mputc(c,handle);
1467 c=MSB(top);
1468 Mputc(c,handle);
1469 c=LSB(left);
1470 Mputc(c,handle);
1471 c=MSB(left);
1472 Mputc(c,handle);
1473
1474 c=LSB(width);
1475 Mputc(c,handle);
1476 c=MSB(width);
1477 Mputc(c,handle);
1478
1479 c=LSB(height);
1480 Mputc(c,handle);
1481 c=MSB(height);
1482 Mputc(c,handle);
1483
1484 c=0;
1485 Mputc(c,handle);
1486 c=resolution;
1487 Mputc(c,handle);
1488
1489 ssize = rsize = blockPtr->width;
1490 csize = blockPtr->height;
1491 compress(resolution+1, handle, ReadValue);
1492
1493 c = 0;
1494 Mputc(c,handle);
1495 c = GIF_TERMINATOR;
1496 Mputc(c,handle);
1497
1498 return TCL_OK;
1499 }
1500
1501 static int
1502 color(red, green, blue)
1503 int red;
1504 int green;
1505 int blue;
1506 {
1507 int x;
1508 for (x=(alphaOffset != 0);x<=MAXCOLORMAPSIZE;x++) {
1509 if ((mapa[x][CM_RED]==red) && (mapa[x][CM_GREEN]==green) &&
1510 (mapa[x][CM_BLUE]==blue)) {
1511 return x;
1512 }
1513 }
1514 return -1;
1515 }
1516
1517
1518 static int
1519 nuevo(red, green, blue, mapa)
1520 int red,green,blue;
1521 unsigned char mapa[MAXCOLORMAPSIZE][3];
1522 {
1523 int x;
1524 for (x=(alphaOffset != 0);x<num;x++) {
1525 if ((mapa[x][CM_RED]==red) && (mapa[x][CM_GREEN]==green) &&
1526 (mapa[x][CM_BLUE]==blue)) {
1527 return 0;
1528 }
1529 }
1530 return 1;
1531 }
1532
1533 static int
1534 savemap(blockPtr,mapa)
1535 Tk_PhotoImageBlock *blockPtr;
1536 unsigned char mapa[MAXCOLORMAPSIZE][3];
1537 {
1538 unsigned char *colores;
1539 int x,y;
1540 unsigned char red,green,blue;
1541
1542 if (alphaOffset) {
1543 num = 1;
1544 mapa[0][CM_RED] = 0xd9;
1545 mapa[0][CM_GREEN] = 0xd9;
1546 mapa[0][CM_BLUE] = 0xd9;
1547 } else {
1548 num = 0;
1549 }
1550
1551 for(y=0;y<blockPtr->height;y++) {
1552 colores=blockPtr->pixelPtr + blockPtr->offset[0]
1553 + y * blockPtr->pitch;
1554 for(x=0;x<blockPtr->width;x++) {
1555 if (!alphaOffset || (colores[alphaOffset] != 0)) {
1556 red = colores[0];
1557 green = colores[greenOffset];
1558 blue = colores[blueOffset];
1559 if (nuevo(red,green,blue,mapa)) {
1560 if (num>255)
1561 return -1;
1562
1563 mapa[num][CM_RED]=red;
1564 mapa[num][CM_GREEN]=green;
1565 mapa[num][CM_BLUE]=blue;
1566 num++;
1567 }
1568 }
1569 colores += pixelSize;
1570 }
1571 }
1572 return num-1;
1573 }
1574
1575 static int
1576 ReadValue()
1577 {
1578 unsigned int col;
1579
1580 if (csize == 0) {
1581 return EOF;
1582 }
1583 if (alphaOffset && (pixelo[alphaOffset]==0)) {
1584 col = 0;
1585 } else {
1586 col = color(pixelo[0],pixelo[greenOffset],pixelo[blueOffset]);
1587 }
1588 pixelo += pixelSize;
1589 if (--ssize <= 0) {
1590 ssize = rsize;
1591 csize--;
1592 pixelo += pixelPitch - (rsize * pixelSize);
1593 }
1594
1595 return col;
1596 }
1597
1598 /*
1599 * Return the number of bits ( -1 ) to represent a given
1600 * number of colors ( ex: 256 colors => 7 ).
1601 */
1602
1603 static int
1604 no_bits( colors )
1605 int colors;
1606 {
1607 register int bits = 0;
1608
1609 colors--;
1610 while ( colors >> bits ) {
1611 bits++;
1612 }
1613
1614 return (bits-1);
1615 }
1616
1617
1618
1619 /*-----------------------------------------------------------------------
1620 *
1621 * miGIF Compression - mouse and ivo's GIF-compatible compression
1622 *
1623 * -run length encoding compression routines-
1624 *
1625 * Copyright (C) 1998 Hutchison Avenue Software Corporation
1626 * http://www.hasc.com
1627 * info@hasc.com
1628 *
1629 * Permission to use, copy, modify, and distribute this software and its
1630 * documentation for any purpose and without fee is hereby granted, provided
1631 * that the above copyright notice appear in all copies and that both that
1632 * copyright notice and this permission notice appear in supporting
1633 * documentation. This software is provided "AS IS." The Hutchison Avenue
1634 * Software Corporation disclaims all warranties, either express or implied,
1635 * including but not limited to implied warranties of merchantability and
1636 * fitness for a particular purpose, with respect to this code and accompanying
1637 * documentation.
1638 *
1639 * The miGIF compression routines do not, strictly speaking, generate files
1640 * conforming to the GIF spec, since the image data is not LZW-compressed
1641 * (this is the point: in order to avoid transgression of the Unisys patent
1642 * on the LZW algorithm.) However, miGIF generates data streams that any
1643 * reasonably sane LZW decompresser will decompress to what we want.
1644 *
1645 * miGIF compression uses run length encoding. It compresses horizontal runs
1646 * of pixels of the same color. This type of compression gives good results
1647 * on images with many runs, for example images with lines, text and solid
1648 * shapes on a solid-colored background. It gives little or no compression
1649 * on images with few runs, for example digital or scanned photos.
1650 *
1651 * der Mouse
1652 * mouse@rodents.montreal.qc.ca
1653 * 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B
1654 *
1655 * ivo@hasc.com
1656 *
1657 * The Graphics Interchange Format(c) is the Copyright property of
1658 * CompuServe Incorporated. GIF(sm) is a Service Mark property of
1659 * CompuServe Incorporated.
1660 *
1661 */
1662
1663 static int rl_pixel;
1664 static int rl_basecode;
1665 static int rl_count;
1666 static int rl_table_pixel;
1667 static int rl_table_max;
1668 static int just_cleared;
1669 static int out_bits;
1670 static int out_bits_init;
1671 static int out_count;
1672 static int out_bump;
1673 static int out_bump_init;
1674 static int out_clear;
1675 static int out_clear_init;
1676 static int max_ocodes;
1677 static int code_clear;
1678 static int code_eof;
1679 static unsigned int obuf;
1680 static int obits;
1681 static Tcl_Channel ofile;
1682 static unsigned char oblock[256];
1683 static int oblen;
1684
1685 /* Used only when debugging GIF compression code */
1686 /* #define DEBUGGING_ENVARS */
1687
1688 #ifdef DEBUGGING_ENVARS
1689
1690 static int verbose_set = 0;
1691 static int verbose;
1692 #define VERBOSE (verbose_set?verbose:set_verbose())
1693
1694 static int set_verbose(void)
1695 {
1696 verbose = !!getenv("GIF_VERBOSE");
1697 verbose_set = 1;
1698 return(verbose);
1699 }
1700
1701 #else
1702
1703 #define VERBOSE 0
1704
1705 #endif
1706
1707
1708 static CONST char *
1709 binformat(v, nbits)
1710 unsigned int v;
1711 int nbits;
1712 {
1713 static char bufs[8][64];
1714 static int bhand = 0;
1715 unsigned int bit;
1716 int bno;
1717 char *bp;
1718
1719 bhand --;
1720 if (bhand < 0) bhand = (sizeof(bufs)/sizeof(bufs[0]))-1;
1721 bp = &bufs[bhand][0];
1722 for (bno=nbits-1,bit=((unsigned int)1)<<bno;bno>=0;bno--,bit>>=1)
1723 { *bp++ = (v & bit) ? '1' : '0';
1724 if (((bno&3) == 0) && (bno != 0)) *bp++ = '.';
1725 }
1726 *bp = '\0';
1727 return(&bufs[bhand][0]);
1728 }
1729
1730 static void write_block()
1731 {
1732 int i;
1733 unsigned char c;
1734
1735 if (VERBOSE)
1736 { printf("write_block %d:",oblen);
1737 for (i=0;i<oblen;i++) printf(" %02x",oblock[i]);
1738 printf("\n");
1739 }
1740 c = oblen;
1741 Tcl_Write(ofile, (char *) &c, 1);
1742 Tcl_Write(ofile, (char *) &oblock[0], oblen);
1743 oblen = 0;
1744 }
1745
1746 static void
1747 block_out(c)
1748 unsigned char c;
1749 {
1750 if (VERBOSE) printf("block_out %s\n",binformat(c,8));
1751 oblock[oblen++] = c;
1752 if (oblen >= 255) write_block();
1753 }
1754
1755 static void block_flush()
1756 {
1757 if (VERBOSE) printf("block_flush\n");
1758 if (oblen > 0) write_block();
1759 }
1760
1761 static void output(val)
1762 int val;
1763 {
1764 if (VERBOSE) printf("output %s [%s %d %d]\n",binformat(val,out_bits),binformat(obuf,obits),obits,out_bits);
1765 obuf |= val << obits;
1766 obits += out_bits;
1767 while (obits >= 8)
1768 { block_out(obuf&0xff);
1769 obuf >>= 8;
1770 obits -= 8;
1771 }
1772 if (VERBOSE) printf("output leaving [%s %d]\n",binformat(obuf,obits),obits);
1773 }
1774
1775 static void output_flush()
1776 {
1777 if (VERBOSE) printf("output_flush\n");
1778 if (obits > 0) block_out(obuf);
1779 block_flush();
1780 }
1781
1782 static void did_clear()
1783 {
1784 if (VERBOSE) printf("did_clear\n");
1785 out_bits = out_bits_init;
1786 out_bump = out_bump_init;
1787 out_clear = out_clear_init;
1788 out_count = 0;
1789 rl_table_max = 0;
1790 just_cleared = 1;
1791 }
1792
1793 static void
1794 output_plain(c)
1795 int c;
1796 {
1797 if (VERBOSE) printf("output_plain %s\n",binformat(c,out_bits));
1798 just_cleared = 0;
1799 output(c);
1800 out_count ++;
1801 if (out_count >= out_bump)
1802 { out_bits ++;
1803 out_bump += 1 << (out_bits - 1);
1804 }
1805 if (out_count >= out_clear)
1806 { output(code_clear);
1807 did_clear();
1808 }
1809 }
1810
1811 static unsigned int isqrt(x)
1812 unsigned int x;
1813 {
1814 unsigned int r;
1815 unsigned int v;
1816
1817 if (x < 2) return(x);
1818 for (v=x,r=1;v;v>>=2,r<<=1) ;
1819 while (1)
1820 { v = ((x / r) + r) / 2;
1821 if ((v == r) || (v == r+1)) return(r);
1822 r = v;
1823 }
1824 }
1825
1826 static unsigned int
1827 compute_triangle_count(count, nrepcodes)
1828 unsigned int count;
1829 unsigned int nrepcodes;
1830 {
1831 unsigned int perrep;
1832 unsigned int cost;
1833
1834 cost = 0;
1835 perrep = (nrepcodes * (nrepcodes+1)) / 2;
1836 while (count >= perrep)
1837 { cost += nrepcodes;
1838 count -= perrep;
1839 }
1840 if (count > 0)
1841 { unsigned int n;
1842 n = isqrt(count);
1843 while ((n*(n+1)) >= 2*count) n --;
1844 while ((n*(n+1)) < 2*count) n ++;
1845 cost += n;
1846 }
1847 return(cost);
1848 }
1849
1850 static void max_out_clear()
1851 {
1852 out_clear = max_ocodes;
1853 }
1854
1855 static void reset_out_clear()
1856 {
1857 out_clear = out_clear_init;
1858 if (out_count >= out_clear)
1859 { output(code_clear);
1860 did_clear();
1861 }
1862 }
1863
1864 static void
1865 rl_flush_fromclear(count)
1866 int count;
1867 {
1868 int n;
1869
1870 if (VERBOSE) printf("rl_flush_fromclear %d\n",count);
1871 max_out_clear();
1872 rl_table_pixel = rl_pixel;
1873 n = 1;
1874 while (count > 0)
1875 { if (n == 1)
1876 { rl_table_max = 1;
1877 output_plain(rl_pixel);
1878 count --;
1879 }
1880 else if (count >= n)
1881 { rl_table_max = n;
1882 output_plain(rl_basecode+n-2);
1883 count -= n;
1884 }
1885 else if (count == 1)
1886 { rl_table_max ++;
1887 output_plain(rl_pixel);
1888 count = 0;
1889 }
1890 else
1891 { rl_table_max ++;
1892 output_plain(rl_basecode+count-2);
1893 count = 0;
1894 }
1895 if (out_count == 0) n = 1; else n ++;
1896 }
1897 reset_out_clear();
1898 if (VERBOSE) printf("rl_flush_fromclear leaving table_max=%d\n",rl_table_max);
1899 }
1900
1901 static void rl_flush_clearorrep(count)
1902 int count;
1903 {
1904 int withclr;
1905
1906 if (VERBOSE) printf("rl_flush_clearorrep %d\n",count);
1907 withclr = 1 + compute_triangle_count(count,max_ocodes);
1908 if (withclr < count)
1909 { output(code_clear);
1910 did_clear();
1911 rl_flush_fromclear(count);
1912 }
1913 else
1914 { for (;count>0;count--) output_plain(rl_pixel);
1915 }
1916 }
1917
1918 static void rl_flush_withtable(count)
1919 int count;
1920 {
1921 int repmax;
1922 int repleft;
1923 int leftover;
1924
1925 if (VERBOSE) printf("rl_flush_withtable %d\n",count);
1926 repmax = count / rl_table_max;
1927 leftover = count % rl_table_max;
1928 repleft = (leftover ? 1 : 0);
1929 if (out_count+repmax+repleft > max_ocodes)
1930 { repmax = max_ocodes - out_count;
1931 leftover = count - (repmax * rl_table_max);
1932 repleft = 1 + compute_triangle_count(leftover,max_ocodes);
1933 }
1934 if (VERBOSE) printf("rl_flush_withtable repmax=%d leftover=%d repleft=%d\n",repmax,leftover,repleft);
1935 if (1+(int)compute_triangle_count(count,max_ocodes) < repmax+repleft)
1936 { output(code_clear);
1937 did_clear();
1938 rl_flush_fromclear(count);
1939 return;
1940 }
1941 max_out_clear();
1942 for (;repmax>0;repmax--) output_plain(rl_basecode+rl_table_max-2);
1943 if (leftover)
1944 { if (just_cleared)
1945 { rl_flush_fromclear(leftover);
1946 }
1947 else if (leftover == 1)
1948 { output_plain(rl_pixel);
1949 }
1950 else
1951 { output_plain(rl_basecode+leftover-2);
1952 }
1953 }
1954 reset_out_clear();
1955 }
1956
1957 static void rl_flush()
1958 {
1959 if (VERBOSE) printf("rl_flush [ %d %d\n",rl_count,rl_pixel);
1960 if (rl_count == 1)
1961 { output_plain(rl_pixel);
1962 rl_count = 0;
1963 if (VERBOSE) printf("rl_flush ]\n");
1964 return;
1965 }
1966 if (just_cleared)
1967 { rl_flush_fromclear(rl_count);
1968 }
1969 else if ((rl_table_max < 2) || (rl_table_pixel != rl_pixel))
1970 { rl_flush_clearorrep(rl_count);
1971 }
1972 else
1973 { rl_flush_withtable(rl_count);
1974 }
1975 if (VERBOSE) printf("rl_flush ]\n");
1976 rl_count = 0;
1977 }
1978
1979
1980 static void compress( init_bits, handle, readValue )
1981 int init_bits;
1982 Tcl_Channel handle;
1983 ifunptr readValue;
1984 {
1985 int c;
1986
1987 ofile = handle;
1988 obuf = 0;
1989 obits = 0;
1990 oblen = 0;
1991 code_clear = 1 << (init_bits - 1);
1992 code_eof = code_clear + 1;
1993 rl_basecode = code_eof + 1;
1994 out_bump_init = (1 << (init_bits - 1)) - 1;
1995 /* for images with a lot of runs, making out_clear_init larger will
1996 give better compression. */
1997 out_clear_init = (init_bits <= 3) ? 9 : (out_bump_init-1);
1998 #ifdef DEBUGGING_ENVARS
1999 { const char *ocienv;
2000 ocienv = getenv("GIF_OUT_CLEAR_INIT");
2001 if (ocienv)
2002 { out_clear_init = atoi(ocienv);
2003 if (VERBOSE) printf("[overriding out_clear_init to %d]\n",out_clear_init);
2004 }
2005 }
2006 #endif
2007 out_bits_init = init_bits;
2008 max_ocodes = (1 << GIFBITS) - ((1 << (out_bits_init - 1)) + 3);
2009 did_clear();
2010 output(code_clear);
2011 rl_count = 0;
2012 while (1)
2013 { c = readValue();
2014 if ((rl_count > 0) && (c != rl_pixel)) rl_flush();
2015 if (c == EOF) break;
2016 if (rl_pixel == c)
2017 { rl_count ++;
2018 }
2019 else
2020 { rl_pixel = c;
2021 rl_count = 1;
2022 }
2023 }
2024 output(code_eof);
2025 output_flush();
2026 }
2027
2028 /*-----------------------------------------------------------------------
2029 *
2030 * End of miGIF section - See copyright notice at start of section.
2031 *
2032 *-----------------------------------------------------------------------*/
2033
2034
2035 /* $History: tkImgGIF.c $
2036 *
2037 * ***************** Version 1 *****************
2038 * User: Dtashley Date: 1/02/01 Time: 2:54a
2039 * Created in $/IjuScripter, IjuConsole/Source/Tk Base
2040 * Initial check-in.
2041 */
2042
2043 /* End of TKIMGGIF.C */

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25