home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkImgGIF.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  27.0 KB  |  1,060 lines  |  [TEXT/CWIE]

  1. /*
  2.  * tkImgGIF.c --
  3.  *
  4.  *    A photo image file handler for GIF files. Reads 87a and 89a GIF
  5.  *    files. At present there is no write function.  GIF images may be
  6.  *    read using the -data option of the photo image by representing
  7.  *    the data as BASE64 encoded ascii.  Derived from the giftoppm code
  8.  *    found in the pbmplus package and tkImgFmtPPM.c in the tk4.0b2
  9.  *    distribution.
  10.  *
  11.  * Copyright (c) Reed Wade (wade@cs.utk.edu), University of Tennessee
  12.  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  13.  *
  14.  * See the file "license.terms" for information on usage and redistribution
  15.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  16.  *
  17.  * This file also contains code from the giftoppm program, which is
  18.  * copyrighted as follows:
  19.  *
  20.  * +-------------------------------------------------------------------+
  21.  * | Copyright 1990, David Koblas.                                     |
  22.  * |   Permission to use, copy, modify, and distribute this software   |
  23.  * |   and its documentation for any purpose and without fee is hereby |
  24.  * |   granted, provided that the above copyright notice appear in all |
  25.  * |   copies and that both that copyright notice and this permission  |
  26.  * |   notice appear in supporting documentation.  This software is    |
  27.  * |   provided "as is" without express or implied warranty.           |
  28.  * +-------------------------------------------------------------------+
  29.  *
  30.  * SCCS: @(#) tkImgGIF.c 1.19 97/08/13 15:23:45
  31.  */
  32.  
  33. /*
  34.  * GIF's are represented as data in base64 format.
  35.  * base64 strings consist of 4 6-bit characters -> 3 8 bit bytes.
  36.  * A-Z, a-z, 0-9, + and / represent the 64 values (in order).
  37.  * '=' is a trailing padding char when the un-encoded data is not a
  38.  * multiple of 3 bytes.  We'll ignore white space when encountered.
  39.  * Any other invalid character is treated as an EOF
  40.  */
  41.  
  42. #define GIF_SPECIAL     (256)
  43. #define GIF_PAD        (GIF_SPECIAL+1)
  44. #define GIF_SPACE    (GIF_SPECIAL+2)
  45. #define GIF_BAD        (GIF_SPECIAL+3)
  46. #define GIF_DONE    (GIF_SPECIAL+4)
  47.  
  48. /*
  49.  * structure to "mimic" FILE for Mread, so we can look like fread.
  50.  * The decoder state keeps track of which byte we are about to read,
  51.  * or EOF.
  52.  */
  53.  
  54. typedef struct mFile {
  55.     unsigned char *data;    /* mmencoded source string */
  56.     int c;            /* bits left over from previous character */
  57.     int state;            /* decoder state (0-4 or GIF_DONE) */
  58. } MFile;
  59.  
  60. #include "tkInt.h"
  61. #include "tkPort.h"
  62.  
  63. /*
  64.  * The format record for the GIF file format:
  65.  */
  66.  
  67. static int      FileMatchGIF _ANSI_ARGS_((Tcl_Channel chan, char *fileName,
  68.             char *formatString, int *widthPtr, int *heightPtr));
  69. static int      FileReadGIF  _ANSI_ARGS_((Tcl_Interp *interp,
  70.             Tcl_Channel chan, char *fileName, char *formatString,
  71.             Tk_PhotoHandle imageHandle, int destX, int destY,
  72.             int width, int height, int srcX, int srcY));
  73. static int    StringMatchGIF _ANSI_ARGS_(( char *string,
  74.             char *formatString, int *widthPtr, int *heightPtr));
  75. static int    StringReadGIF _ANSI_ARGS_((Tcl_Interp *interp, char *string,
  76.             char *formatString, Tk_PhotoHandle imageHandle,
  77.             int destX, int destY, int width, int height,
  78.             int srcX, int srcY));
  79.  
  80. Tk_PhotoImageFormat tkImgFmtGIF = {
  81.     "GIF",            /* name */
  82.     FileMatchGIF,   /* fileMatchProc */
  83.     StringMatchGIF, /* stringMatchProc */
  84.     FileReadGIF,    /* fileReadProc */
  85.     StringReadGIF,  /* stringReadProc */
  86.     NULL,           /* fileWriteProc */
  87.     NULL,           /* stringWriteProc */
  88. };
  89.  
  90. #define INTERLACE        0x40
  91. #define LOCALCOLORMAP        0x80
  92. #define BitSet(byte, bit)    (((byte) & (bit)) == (bit))
  93. #define MAXCOLORMAPSIZE        256
  94. #define CM_RED            0
  95. #define CM_GREEN        1
  96. #define CM_BLUE            2
  97. #define CM_ALPHA        3
  98. #define MAX_LWZ_BITS        12
  99. #define LM_to_uint(a,b)         (((b)<<8)|(a))
  100. #define ReadOK(file,buffer,len)    (Fread(buffer, len, 1, file) != 0)
  101.  
  102. /*
  103.  *              HACK ALERT!!  HACK ALERT!!  HACK ALERT!!
  104.  * This code is hard-wired for reading from files.  In order to read
  105.  * from a data stream, we'll trick fread so we can reuse the same code
  106.  */
  107.  
  108. static int fromData=0;
  109.  
  110. /*
  111.  * Prototypes for local procedures defined in this file:
  112.  */
  113.  
  114. static int        DoExtension _ANSI_ARGS_((Tcl_Channel chan, int label,
  115.                 int *transparent));
  116. static int        GetCode _ANSI_ARGS_((Tcl_Channel chan, int code_size,
  117.                 int flag));
  118. static int        GetDataBlock _ANSI_ARGS_((Tcl_Channel chan,
  119.                 unsigned char *buf));
  120. static int        LWZReadByte _ANSI_ARGS_((Tcl_Channel chan, int flag,
  121.                 int input_code_size));
  122. static int        ReadColorMap _ANSI_ARGS_((Tcl_Channel chan, int number,
  123.                 unsigned char buffer[MAXCOLORMAPSIZE][4]));
  124. static int        ReadGIFHeader _ANSI_ARGS_((Tcl_Channel chan,
  125.                 int *widthPtr, int *heightPtr));
  126. static int        ReadImage _ANSI_ARGS_((Tcl_Interp *interp,
  127.                 char *imagePtr, Tcl_Channel chan,
  128.                 int len, int rows,
  129.                 unsigned char cmap[MAXCOLORMAPSIZE][4],
  130.                 int width, int height, int srcX, int srcY,
  131.                 int interlace, int transparent));
  132.  
  133. /*
  134.  * these are for the BASE64 image reader code only
  135.  */
  136.  
  137. static int        Fread _ANSI_ARGS_((unsigned char *dst, size_t size,
  138.                 size_t count, Tcl_Channel chan));
  139. static int        Mread _ANSI_ARGS_((unsigned char *dst, size_t size,
  140.                 size_t count, MFile *handle));
  141. static int        Mgetc _ANSI_ARGS_((MFile *handle));
  142. static int        char64 _ANSI_ARGS_((int c));
  143. static void        mInit _ANSI_ARGS_((unsigned char *string,
  144.                 MFile *handle));
  145.  
  146. /*
  147.  *----------------------------------------------------------------------
  148.  *
  149.  * FileMatchGIF --
  150.  *
  151.  *    This procedure is invoked by the photo image type to see if
  152.  *    a file contains image data in GIF format.
  153.  *
  154.  * Results:
  155.  *    The return value is 1 if the first characters in file f look
  156.  *    like GIF data, and 0 otherwise.
  157.  *
  158.  * Side effects:
  159.  *    The access position in f may change.
  160.  *
  161.  *----------------------------------------------------------------------
  162.  */
  163.  
  164. static int
  165. FileMatchGIF(chan, fileName, formatString, widthPtr, heightPtr)
  166.     Tcl_Channel chan;        /* The image file, open for reading. */
  167.     char *fileName;        /* The name of the image file. */
  168.     char *formatString;        /* User-specified format string, or NULL. */
  169.     int *widthPtr, *heightPtr;    /* The dimensions of the image are
  170.                  * returned here if the file is a valid
  171.                  * raw GIF file. */
  172. {
  173.     return ReadGIFHeader(chan, widthPtr, heightPtr);
  174. }
  175.  
  176. /*
  177.  *----------------------------------------------------------------------
  178.  *
  179.  * FileReadGIF --
  180.  *
  181.  *    This procedure is called by the photo image type to read
  182.  *    GIF format data from a file and write it into a given
  183.  *    photo image.
  184.  *
  185.  * Results:
  186.  *    A standard TCL completion code.  If TCL_ERROR is returned
  187.  *    then an error message is left in interp->result.
  188.  *
  189.  * Side effects:
  190.  *    The access position in file f is changed, and new data is
  191.  *    added to the image given by imageHandle.
  192.  *
  193.  *----------------------------------------------------------------------
  194.  */
  195.  
  196. static int
  197. FileReadGIF(interp, chan, fileName, formatString, imageHandle, destX, destY,
  198.     width, height, srcX, srcY)
  199.     Tcl_Interp *interp;        /* Interpreter to use for reporting errors. */
  200.     Tcl_Channel chan;        /* The image file, open for reading. */
  201.     char *fileName;        /* The name of the image file. */
  202.     char *formatString;        /* User-specified format string, or NULL. */
  203.     Tk_PhotoHandle imageHandle;    /* The photo image to write into. */
  204.     int destX, destY;        /* Coordinates of top-left pixel in
  205.                  * photo image to be written to. */
  206.     int width, height;        /* Dimensions of block of photo image to
  207.                  * be written to. */
  208.     int srcX, srcY;        /* Coordinates of top-left pixel to be used
  209.                  * in image being read. */
  210. {
  211.     int fileWidth, fileHeight;
  212.     int nBytes;
  213.     Tk_PhotoImageBlock block;
  214.     unsigned char buf[100];
  215.     int bitPixel;
  216.     unsigned char colorMap[MAXCOLORMAPSIZE][4];
  217.     int transparent = -1;
  218.  
  219.     if (!ReadGIFHeader(chan, &fileWidth, &fileHeight)) {
  220.     Tcl_AppendResult(interp, "couldn't read GIF header from file \"",
  221.         fileName, "\"", NULL);
  222.     return TCL_ERROR;
  223.     }
  224.     if ((fileWidth <= 0) || (fileHeight <= 0)) {
  225.     Tcl_AppendResult(interp, "GIF image file \"", fileName,
  226.         "\" has dimension(s) <= 0", (char *) NULL);
  227.     return TCL_ERROR;
  228.     }
  229.  
  230.     if (Fread(buf, 1, 3, chan) != 3) {
  231.     return TCL_OK;
  232.     }
  233.     bitPixel = 2<<(buf[0]&0x07);
  234.  
  235.     if (BitSet(buf[0], LOCALCOLORMAP)) {    /* Global Colormap */
  236.     if (!ReadColorMap(chan, bitPixel, colorMap)) {
  237.         Tcl_AppendResult(interp, "error reading color map",
  238.             (char *) NULL);
  239.         return TCL_ERROR;
  240.     }
  241.     }
  242.  
  243.     if ((srcX + width) > fileWidth) {
  244.     width = fileWidth - srcX;
  245.     }
  246.     if ((srcY + height) > fileHeight) {
  247.     height = fileHeight - srcY;
  248.     }
  249.     if ((width <= 0) || (height <= 0)
  250.         || (srcX >= fileWidth) || (srcY >= fileHeight)) {
  251.     return TCL_OK;
  252.     }
  253.  
  254.     Tk_PhotoExpand(imageHandle, destX + width, destY + height);
  255.  
  256.     block.width = width;
  257.     block.height = height;
  258.     block.pixelSize = 4;
  259.     block.pitch = block.pixelSize * block.width;
  260.     block.offset[0] = 0;
  261.     block.offset[1] = 1;
  262.     block.offset[2] = 2;
  263.     nBytes = height * block.pitch;
  264.     block.pixelPtr = (unsigned char *) ckalloc((unsigned) nBytes);
  265.  
  266.     while (1) {
  267.     if (Fread(buf, 1, 1, chan) != 1) {
  268.         /*
  269.          * Premature end of image.  We should really notify
  270.          * the user, but for now just show garbage.
  271.          */
  272.  
  273.         break;
  274.     }
  275.  
  276.     if (buf[0] == ';') {
  277.         /*
  278.          * GIF terminator.
  279.          */
  280.  
  281.         break;
  282.     }
  283.  
  284.     if (buf[0] == '!') {
  285.         /*
  286.          * This is a GIF extension.
  287.          */
  288.  
  289.         if (Fread(buf, 1, 1, chan) != 1) {
  290.         interp->result =
  291.             "error reading extension function code in GIF image";
  292.         goto error;
  293.         }
  294.         if (DoExtension(chan, buf[0], &transparent) < 0) {
  295.         interp->result = "error reading extension in GIF image";
  296.         goto error;
  297.         }
  298.         continue;
  299.     }
  300.  
  301.     if (buf[0] != ',') {
  302.         /*
  303.          * Not a valid start character; ignore it.
  304.          */
  305.         continue;
  306.     }
  307.  
  308.     if (Fread(buf, 1, 9, chan) != 9) {
  309.         interp->result = "couldn't read left/top/width/height in GIF image";
  310.         goto error;
  311.     }
  312.  
  313.     bitPixel = 1<<((buf[8]&0x07)+1);
  314.  
  315.     if (BitSet(buf[8], LOCALCOLORMAP)) {
  316.         if (!ReadColorMap(chan, bitPixel, colorMap)) {
  317.             Tcl_AppendResult(interp, "error reading color map", 
  318.                 (char *) NULL);
  319.             goto error;
  320.         }
  321.     }
  322.     if (ReadImage(interp, (char *) block.pixelPtr, chan, width,
  323.         height, colorMap, fileWidth, fileHeight, srcX, srcY,
  324.         BitSet(buf[8], INTERLACE), transparent) != TCL_OK) {
  325.         goto error;
  326.     }
  327.     break;
  328.    }
  329.  
  330.     if (transparent == -1) {
  331.     Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, height);
  332.     } else {
  333.     int x, y, end;
  334.     unsigned char *imagePtr, *rowPtr, *pixelPtr;
  335.  
  336.     imagePtr = rowPtr = block.pixelPtr;
  337.     for (y = 0; y < height; y++) {
  338.         x = 0;
  339.         pixelPtr = rowPtr;
  340.         while(x < width) {
  341.         /* search for first non-transparent pixel */
  342.         while ((x < width) && !(pixelPtr[CM_ALPHA])) {
  343.             x++; pixelPtr += 4;
  344.         }
  345.         end = x;
  346.         /* search for first transparent pixel */
  347.         while ((end < width) && pixelPtr[CM_ALPHA]) {
  348.             end++; pixelPtr += 4;
  349.         }
  350.         if (end > x) {
  351.             block.pixelPtr = rowPtr + 4 * x;
  352.             Tk_PhotoPutBlock(imageHandle, &block, destX+x,
  353.                 destY+y, end-x, 1);
  354.         }
  355.         x = end;
  356.         }
  357.         rowPtr += block.pitch;
  358.     }
  359.     block.pixelPtr = imagePtr;
  360.     }
  361.     ckfree((char *) block.pixelPtr);
  362.     return TCL_OK;
  363.  
  364.     error:
  365.     ckfree((char *) block.pixelPtr);
  366.     return TCL_ERROR;
  367.  
  368. }
  369.  
  370. /*
  371.  *----------------------------------------------------------------------
  372.  *
  373.  * StringMatchGIF --
  374.  *
  375.  *  This procedure is invoked by the photo image type to see if
  376.  *  a string contains image data in GIF format.
  377.  *
  378.  * Results:
  379.  *  The return value is 1 if the first characters in the string
  380.  *  like GIF data, and 0 otherwise.
  381.  *
  382.  * Side effects:
  383.  *  the size of the image is placed in widthPre and heightPtr.
  384.  *
  385.  *----------------------------------------------------------------------
  386.  */
  387.  
  388. static int
  389. StringMatchGIF(string, formatString, widthPtr, heightPtr)
  390.     char *string;        /* the string containing the image data */
  391.     char *formatString;        /* the image format string */
  392.     int *widthPtr;        /* where to put the string width */
  393.     int *heightPtr;        /* where to put the string height */
  394. {
  395.     unsigned char header[10];
  396.     int got;
  397.     MFile handle;
  398.     mInit((unsigned char *) string, &handle);
  399.     got = Mread(header, 10, 1, &handle);
  400.     if (got != 10
  401.         || ((strncmp("GIF87a", (char *) header, 6) != 0)
  402.         && (strncmp("GIF89a", (char *) header, 6) != 0))) {
  403.     return 0;
  404.     }
  405.     *widthPtr = LM_to_uint(header[6],header[7]);
  406.     *heightPtr = LM_to_uint(header[8],header[9]);
  407.     return 1;
  408. }
  409.  
  410. /*
  411.  *----------------------------------------------------------------------
  412.  *
  413.  * StringReadGif -- --
  414.  *
  415.  *    This procedure is called by the photo image type to read
  416.  *    GIF format data from a base64 encoded string, and give it to
  417.  *    the photo image.
  418.  *
  419.  * Results:
  420.  *    A standard TCL completion code.  If TCL_ERROR is returned
  421.  *    then an error message is left in interp->result.
  422.  *
  423.  * Side effects:
  424.  *    new data is added to the image given by imageHandle.  This
  425.  *    procedure calls FileReadGif by redefining the operation of
  426.  *    fprintf temporarily.
  427.  *
  428.  *----------------------------------------------------------------------
  429.  */
  430.  
  431. static int
  432. StringReadGIF(interp,string,formatString,imageHandle,
  433.     destX, destY, width, height, srcX, srcY)
  434.     Tcl_Interp *interp;        /* interpreter for reporting errors in */
  435.     char *string;        /* string containing the image */
  436.     char *formatString;        /* format string if any */
  437.     Tk_PhotoHandle imageHandle;    /* the image to write this data into */
  438.     int destX, destY;        /* The rectangular region of the  */
  439.     int  width, height;        /*   image to copy */
  440.     int srcX, srcY;
  441. {
  442.     int result;
  443.     MFile handle;
  444.     mInit((unsigned char *)string,&handle);
  445.     fromData = 1;
  446.     result = FileReadGIF(interp, (Tcl_Channel) &handle, "inline data",
  447.         formatString, imageHandle, destX, destY, width, height,
  448.         srcX, srcY);
  449.     fromData = 0;
  450.     return(result);
  451. }
  452.  
  453. /*
  454.  *----------------------------------------------------------------------
  455.  *
  456.  * ReadGIFHeader --
  457.  *
  458.  *    This procedure reads the GIF header from the beginning of a
  459.  *    GIF file and returns the dimensions of the image.
  460.  *
  461.  * Results:
  462.  *    The return value is 1 if file "f" appears to start with
  463.  *    a valid GIF header, 0 otherwise.  If the header is valid,
  464.  *    then *widthPtr and *heightPtr are modified to hold the
  465.  *    dimensions of the image.
  466.  *
  467.  * Side effects:
  468.  *    The access position in f advances.
  469.  *
  470.  *----------------------------------------------------------------------
  471.  */
  472.  
  473. static int
  474. ReadGIFHeader(chan, widthPtr, heightPtr)
  475.     Tcl_Channel chan;        /* Image file to read the header from */
  476.     int *widthPtr, *heightPtr;    /* The dimensions of the image are
  477.                  * returned here. */
  478. {
  479.     unsigned char buf[7];
  480.  
  481.     if ((Fread(buf, 1, 6, chan) != 6)
  482.         || ((strncmp("GIF87a", (char *) buf, 6) != 0)
  483.         && (strncmp("GIF89a", (char *) buf, 6) != 0))) {
  484.     return 0;
  485.     }
  486.  
  487.     if (Fread(buf, 1, 4, chan) != 4) {
  488.     return 0;
  489.     }
  490.  
  491.     *widthPtr = LM_to_uint(buf[0],buf[1]);
  492.     *heightPtr = LM_to_uint(buf[2],buf[3]);
  493.     return 1;
  494. }
  495.  
  496. /*
  497.  *-----------------------------------------------------------------
  498.  * The code below is copied from the giftoppm program and modified
  499.  * just slightly.
  500.  *-----------------------------------------------------------------
  501.  */
  502.  
  503. static int
  504. ReadColorMap(chan, number, buffer)
  505.      Tcl_Channel chan;
  506.      int number;
  507.      unsigned char buffer[MAXCOLORMAPSIZE][4];
  508. {
  509.     int i;
  510.     unsigned char rgb[3];
  511.  
  512.     for (i = 0; i < number; ++i) {
  513.         if (! ReadOK(chan, rgb, sizeof(rgb))) {
  514.         return 0;
  515.         }
  516.         
  517.         buffer[i][CM_RED] = rgb[0] ;
  518.         buffer[i][CM_GREEN] = rgb[1] ;
  519.         buffer[i][CM_BLUE] = rgb[2] ;
  520.         buffer[i][CM_ALPHA] = 255 ;
  521.     }
  522.     return 1;
  523. }
  524.  
  525.  
  526.  
  527. static int
  528. DoExtension(chan, label, transparent)
  529.      Tcl_Channel chan;
  530.      int label;
  531.      int *transparent;
  532. {
  533.     static unsigned char buf[256];
  534.     int count;
  535.  
  536.     switch (label) {
  537.     case 0x01:      /* Plain Text Extension */
  538.         break;
  539.         
  540.     case 0xff:      /* Application Extension */
  541.         break;
  542.  
  543.     case 0xfe:      /* Comment Extension */
  544.         do {
  545.         count = GetDataBlock(chan, (unsigned char*) buf);
  546.         } while (count > 0);
  547.         return count;
  548.  
  549.     case 0xf9:      /* Graphic Control Extension */
  550.         count = GetDataBlock(chan, (unsigned char*) buf);
  551.         if (count < 0) {
  552.         return 1;
  553.         }
  554.         if ((buf[0] & 0x1) != 0) {
  555.         *transparent = buf[3];
  556.         }
  557.  
  558.         do {
  559.         count = GetDataBlock(chan, (unsigned char*) buf);
  560.         } while (count > 0);
  561.         return count;
  562.     }
  563.  
  564.     do {
  565.     count = GetDataBlock(chan, (unsigned char*) buf);
  566.     } while (count > 0);
  567.     return count;
  568. }
  569.  
  570. static int ZeroDataBlock = 0;
  571.  
  572. static int
  573. GetDataBlock(chan, buf)
  574.      Tcl_Channel chan;
  575.      unsigned char *buf;
  576. {
  577.     unsigned char count;
  578.  
  579.     if (! ReadOK(chan, &count,1)) {
  580.     return -1;
  581.     }
  582.  
  583.     ZeroDataBlock = count == 0;
  584.  
  585.     if ((count != 0) && (! ReadOK(chan, buf, count))) {
  586.     return -1;
  587.     }
  588.  
  589.     return count;
  590. }
  591.  
  592.  
  593. static int
  594. ReadImage(interp, imagePtr, chan, len, rows, cmap,
  595.     width, height, srcX, srcY, interlace, transparent)
  596.      Tcl_Interp *interp;
  597.      char *imagePtr;
  598.      Tcl_Channel chan;
  599.      int len, rows;
  600.      unsigned char cmap[MAXCOLORMAPSIZE][4];
  601.      int width, height;
  602.      int srcX, srcY;
  603.      int interlace;
  604.      int transparent;
  605. {
  606.     unsigned char c;
  607.     int v;
  608.     int xpos = 0, ypos = 0, pass = 0;
  609.     char *pixelPtr;
  610.  
  611.  
  612.     /*
  613.      *  Initialize the Compression routines
  614.      */
  615.     if (! ReadOK(chan, &c, 1))  {
  616.     Tcl_AppendResult(interp, "error reading GIF image: ",
  617.         Tcl_PosixError(interp), (char *) NULL);
  618.     return TCL_ERROR;
  619.     }
  620.  
  621.     if (LWZReadByte(chan, 1, c) < 0) {
  622.     interp->result = "format error in GIF image";
  623.     return TCL_ERROR;
  624.     }
  625.  
  626.     if (transparent!=-1) {
  627.     cmap[transparent][CM_RED] = 0;
  628.     cmap[transparent][CM_GREEN] = 0;
  629.     cmap[transparent][CM_BLUE] = 0;
  630.     cmap[transparent][CM_ALPHA] = 0;
  631.     }
  632.  
  633.     pixelPtr = imagePtr;
  634.     while ((v = LWZReadByte(chan, 0, c)) >= 0 ) {
  635.  
  636.     if ((xpos>=srcX) && (xpos<srcX+len) &&
  637.         (ypos>=srcY) && (ypos<srcY+rows)) {
  638.         *pixelPtr++ = cmap[v][CM_RED];
  639.         *pixelPtr++ = cmap[v][CM_GREEN];
  640.         *pixelPtr++ = cmap[v][CM_BLUE];
  641.         *pixelPtr++ = cmap[v][CM_ALPHA];
  642.     }
  643.  
  644.     ++xpos;
  645.     if (xpos == width) {
  646.         xpos = 0;
  647.         if (interlace) {
  648.         switch (pass) {
  649.             case 0:
  650.             case 1:
  651.             ypos += 8; break;
  652.             case 2:
  653.             ypos += 4; break;
  654.             case 3:
  655.             ypos += 2; break;
  656.         }
  657.         
  658.         while (ypos >= height) {
  659.             ++pass;
  660.             switch (pass) {
  661.             case 1:
  662.                 ypos = 4; break;
  663.             case 2:
  664.                 ypos = 2; break;
  665.             case 3:
  666.                 ypos = 1; break;
  667.             default:
  668.                 return TCL_OK;
  669.             }
  670.         }
  671.         } else {
  672.         ++ypos;
  673.         }
  674.         pixelPtr = imagePtr + (ypos-srcY) * len * 4;
  675.     }
  676.     if (ypos >= height)
  677.         break;
  678.     }
  679.     return TCL_OK;
  680. }
  681.  
  682. static int
  683. LWZReadByte(chan, flag, input_code_size)
  684.      Tcl_Channel chan;
  685.      int flag;
  686.      int input_code_size;
  687. {
  688.     static int  fresh = 0;
  689.     int code, incode;
  690.     static int code_size, set_code_size;
  691.     static int max_code, max_code_size;
  692.     static int firstcode, oldcode;
  693.     static int clear_code, end_code;
  694.     static int table[2][(1<< MAX_LWZ_BITS)];
  695.     static int stack[(1<<(MAX_LWZ_BITS))*2], *sp;
  696.     register int    i;
  697.  
  698.     if (flag) {
  699.     set_code_size = input_code_size;
  700.     code_size = set_code_size+1;
  701.     clear_code = 1 << set_code_size ;
  702.     end_code = clear_code + 1;
  703.     max_code_size = 2*clear_code;
  704.     max_code = clear_code+2;
  705.  
  706.     GetCode(chan, 0, 1);
  707.  
  708.     fresh = 1;
  709.  
  710.     for (i = 0; i < clear_code; ++i) {
  711.         table[0][i] = 0;
  712.         table[1][i] = i;
  713.     }
  714.     for (; i < (1<<MAX_LWZ_BITS); ++i) {
  715.         table[0][i] = table[1][0] = 0;
  716.     }
  717.  
  718.     sp = stack;
  719.  
  720.     return 0;
  721.     } else if (fresh) {
  722.     fresh = 0;
  723.     do {
  724.         firstcode = oldcode = GetCode(chan, code_size, 0);
  725.     } while (firstcode == clear_code);
  726.     return firstcode;
  727.     }
  728.  
  729.     if (sp > stack) {
  730.     return *--sp;
  731.     }
  732.  
  733.     while ((code = GetCode(chan, code_size, 0)) >= 0) {
  734.     if (code == clear_code) {
  735.         for (i = 0; i < clear_code; ++i) {
  736.         table[0][i] = 0;
  737.         table[1][i] = i;
  738.         }
  739.         
  740.         for (; i < (1<<MAX_LWZ_BITS); ++i) {
  741.         table[0][i] = table[1][i] = 0;
  742.         }
  743.  
  744.         code_size = set_code_size+1;
  745.         max_code_size = 2*clear_code;
  746.         max_code = clear_code+2;
  747.         sp = stack;
  748.         firstcode = oldcode = GetCode(chan, code_size, 0);
  749.         return firstcode;
  750.  
  751.     } else if (code == end_code) {
  752.         int count;
  753.         unsigned char buf[260];
  754.  
  755.         if (ZeroDataBlock) {
  756.         return -2;
  757.         }
  758.         
  759.         while ((count = GetDataBlock(chan, buf)) > 0)
  760.         /* Empty body */;
  761.  
  762.         if (count != 0) {
  763.         return -2;
  764.         }
  765.     }
  766.  
  767.     incode = code;
  768.  
  769.     if (code >= max_code) {
  770.         *sp++ = firstcode;
  771.         code = oldcode;
  772.     }
  773.  
  774.     while (code >= clear_code) {
  775.         *sp++ = table[1][code];
  776.         if (code == table[0][code]) {
  777.         return -2;
  778.  
  779.         /*
  780.          * Used to be this instead, Steve Ball suggested
  781.          * the change to just return.
  782.          printf("circular table entry BIG ERROR\n");
  783.          */
  784.         }
  785.         code = table[0][code];
  786.     }
  787.  
  788.     *sp++ = firstcode = table[1][code];
  789.  
  790.     if ((code = max_code) <(1<<MAX_LWZ_BITS)) {
  791.         table[0][code] = oldcode;
  792.         table[1][code] = firstcode;
  793.         ++max_code;
  794.         if ((max_code>=max_code_size) && (max_code_size < (1<<MAX_LWZ_BITS))) {
  795.         max_code_size *= 2;
  796.         ++code_size;
  797.         }
  798.     }
  799.  
  800.     oldcode = incode;
  801.  
  802.     if (sp > stack)
  803.         return *--sp;
  804.     }
  805.     return code;
  806. }
  807.  
  808.  
  809. static int
  810. GetCode(chan, code_size, flag)
  811.      Tcl_Channel chan;
  812.      int code_size;
  813.      int flag;
  814. {
  815.     static unsigned char buf[280];
  816.     static int curbit, lastbit, done, last_byte;
  817.     int i, j, ret;
  818.     unsigned char count;
  819.  
  820.     if (flag) {
  821.     curbit = 0;
  822.     lastbit = 0;
  823.     done = 0;
  824.     return 0;
  825.     }
  826.  
  827.  
  828.     if ( (curbit+code_size) >= lastbit) {
  829.     if (done) {
  830.         /* ran off the end of my bits */
  831.         return -1;
  832.     }
  833.     if (last_byte >= 2) {
  834.         buf[0] = buf[last_byte-2];
  835.     }
  836.     if (last_byte >= 1) {
  837.         buf[1] = buf[last_byte-1];
  838.     }
  839.  
  840.     if ((count = GetDataBlock(chan, &buf[2])) == 0) {
  841.         done = 1;
  842.     }
  843.  
  844.     last_byte = 2 + count;
  845.     curbit = (curbit - lastbit) + 16;
  846.     lastbit = (2+count)*8 ;
  847.     }
  848.  
  849.     ret = 0;
  850.     for (i = curbit, j = 0; j < code_size; ++i, ++j) {
  851.     ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
  852.     }
  853.  
  854.     curbit += code_size;
  855.  
  856.     return ret;
  857. }
  858.  
  859. /*
  860.  *----------------------------------------------------------------------
  861.  *
  862.  * Minit -- --
  863.  *
  864.  *  This procedure initializes a base64 decoder handle
  865.  *
  866.  * Results:
  867.  *  none
  868.  *
  869.  * Side effects:
  870.  *  the base64 handle is initialized
  871.  *
  872.  *----------------------------------------------------------------------
  873.  */
  874.  
  875. static void
  876. mInit(string, handle)
  877.    unsigned char *string;    /* string containing initial mmencoded data */
  878.    MFile *handle;        /* mmdecode "file" handle */
  879. {
  880.    handle->data = string;
  881.    handle->state = 0;
  882. }
  883.  
  884. /*
  885.  *----------------------------------------------------------------------
  886.  *
  887.  * Mread --
  888.  *
  889.  *    This procedure is invoked by the GIF file reader as a 
  890.  *    temporary replacement for "fread", to get GIF data out
  891.  *    of a string (using Mgetc).
  892.  *
  893.  * Results:
  894.  *    The return value is the number of characters "read"
  895.  *
  896.  * Side effects:
  897.  *    The base64 handle will change state.
  898.  *
  899.  *----------------------------------------------------------------------
  900.  */
  901.  
  902. static int
  903. Mread(dst, chunkSize, numChunks, handle)  
  904.    unsigned char *dst;    /* where to put the result */
  905.    size_t chunkSize;    /* size of each transfer */
  906.    size_t numChunks;    /* number of chunks */
  907.    MFile *handle;    /* mmdecode "file" handle */
  908. {
  909.    register int i, c;
  910.    int count = chunkSize * numChunks;
  911.  
  912.    for(i=0; i<count && (c=Mgetc(handle)) != GIF_DONE; i++) {
  913.     *dst++ = c;
  914.    }
  915.    return i;
  916. }
  917.  
  918. /*
  919.  * get the next decoded character from an mmencode handle
  920.  * This causes at least 1 character to be "read" from the encoded string
  921.  */
  922.  
  923. /*
  924.  *----------------------------------------------------------------------
  925.  *
  926.  * Mgetc --
  927.  *
  928.  *  This procedure decodes and returns the next byte from a base64
  929.  *  encoded string.
  930.  *
  931.  * Results:
  932.  *  The next byte (or GIF_DONE) is returned.
  933.  *
  934.  * Side effects:
  935.  *  The base64 handle will change state.
  936.  *
  937.  *----------------------------------------------------------------------
  938.  */
  939.  
  940. static int
  941. Mgetc(handle)
  942.    MFile *handle;        /* Handle containing decoder data and state. */
  943. {
  944.     int c;
  945.     int result = 0;        /* Initialization needed only to prevent
  946.                  * gcc compiler warning. */
  947.      
  948.     if (handle->state == GIF_DONE) {
  949.     return(GIF_DONE);
  950.     }
  951.  
  952.     do {
  953.     c = char64(*handle->data);
  954.     handle->data++;
  955.     } while (c==GIF_SPACE);
  956.  
  957.     if (c>GIF_SPECIAL) {
  958.     handle->state = GIF_DONE;
  959.     return(handle->state ? handle->c : GIF_DONE);
  960.     }
  961.  
  962.     switch (handle->state++) {
  963.     case 0:
  964.        handle->c = c<<2;
  965.        result = Mgetc(handle);
  966.        break;
  967.     case 1:
  968.        result = handle->c | (c>>4);
  969.        handle->c = (c&0xF)<<4;
  970.        break;
  971.     case 2:
  972.        result = handle->c | (c>>2);
  973.        handle->c = (c&0x3) << 6;
  974.        break;
  975.     case 3:
  976.        result = handle->c | c;
  977.        handle->state = 0;
  978.        break;
  979.     }
  980.     return(result);
  981. }
  982.  
  983. /*
  984.  *----------------------------------------------------------------------
  985.  *
  986.  * char64 --
  987.  *
  988.  *    This procedure converts a base64 ascii character into its binary
  989.  *    equivalent.  This code is a slightly modified version of the
  990.  *    char64 proc in N. Borenstein's metamail decoder.
  991.  *
  992.  * Results:
  993.  *    The binary value, or an error code.
  994.  *
  995.  * Side effects:
  996.  *    None.
  997.  *----------------------------------------------------------------------
  998.  */
  999.  
  1000. static int
  1001. char64(c)
  1002. int c;
  1003. {
  1004.     switch(c) {
  1005.         case 'A': return(0);  case 'B': return(1);  case 'C': return(2);
  1006.         case 'D': return(3);  case 'E': return(4);  case 'F': return(5);
  1007.         case 'G': return(6);  case 'H': return(7);  case 'I': return(8);
  1008.         case 'J': return(9);  case 'K': return(10); case 'L': return(11);
  1009.         case 'M': return(12); case 'N': return(13); case 'O': return(14);
  1010.         case 'P': return(15); case 'Q': return(16); case 'R': return(17);
  1011.         case 'S': return(18); case 'T': return(19); case 'U': return(20);
  1012.         case 'V': return(21); case 'W': return(22); case 'X': return(23);
  1013.         case 'Y': return(24); case 'Z': return(25); case 'a': return(26);
  1014.         case 'b': return(27); case 'c': return(28); case 'd': return(29);
  1015.         case 'e': return(30); case 'f': return(31); case 'g': return(32);
  1016.         case 'h': return(33); case 'i': return(34); case 'j': return(35);
  1017.         case 'k': return(36); case 'l': return(37); case 'm': return(38);
  1018.         case 'n': return(39); case 'o': return(40); case 'p': return(41);
  1019.         case 'q': return(42); case 'r': return(43); case 's': return(44);
  1020.         case 't': return(45); case 'u': return(46); case 'v': return(47);
  1021.         case 'w': return(48); case 'x': return(49); case 'y': return(50);
  1022.         case 'z': return(51); case '0': return(52); case '1': return(53);
  1023.         case '2': return(54); case '3': return(55); case '4': return(56);
  1024.         case '5': return(57); case '6': return(58); case '7': return(59);
  1025.         case '8': return(60); case '9': return(61); case '+': return(62);
  1026.         case '/': return(63);
  1027.  
  1028.     case ' ': case '\t': case '\n': case '\r': case '\f': return(GIF_SPACE);
  1029.     case '=':  return(GIF_PAD);
  1030.     case '\0': return(GIF_DONE);
  1031.     default: return(GIF_BAD);
  1032.     }
  1033. }
  1034.  
  1035. /*
  1036.  *----------------------------------------------------------------------
  1037.  *
  1038.  * Fread --
  1039.  *
  1040.  *  This procedure calls either fread or Mread to read data
  1041.  *  from a file or a base64 encoded string.
  1042.  *
  1043.  * Results: - same as fread
  1044.  *
  1045.  *----------------------------------------------------------------------
  1046.  */
  1047.  
  1048. static int
  1049. Fread(dst, hunk, count, chan)
  1050.     unsigned char *dst;        /* where to put the result */
  1051.     size_t hunk,count;        /* how many */
  1052.     Tcl_Channel chan;
  1053. {
  1054.     if (fromData) {
  1055.     return(Mread(dst, hunk, count, (MFile *) chan));
  1056.     } else {
  1057.     return Tcl_Read(chan, (char *) dst, (int) (hunk * count));
  1058.     }
  1059. }
  1060.