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