home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1995 November / PCWK1195.iso / inne / win95 / sieciowe / hotja32.lzh / hotjava / classsrc / awt / gifimage.java < prev    next >
Text File  |  1995-08-11  |  12KB  |  489 lines

  1. /*
  2.  * @(#)GifImage.java    1.24 95/05/11 Patrick Naughton, Arthur van Hoff
  3.  *
  4.  * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * Permission to use, copy, modify, and distribute this software
  7.  * and its documentation for NON-COMMERCIAL purposes and without
  8.  * fee is hereby granted provided that this copyright notice
  9.  * appears in all copies. Please refer to the file "copyright.html"
  10.  * for further important copyright and licensing information.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  13.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  14.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  15.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  16.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  17.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  18.  */
  19.  
  20. /*-
  21.  *    Reads GIF images into a DIBitmap structure.
  22.  *
  23.  * The algorithm is copyright of CompuServe.
  24.  */
  25. package awt;
  26.  
  27. import java.io.*;
  28. import browser.Observer;
  29.  
  30. /**
  31.  * Gif Image converter
  32.  * 
  33.  * @version 1.24 11 May 1995
  34.  * @author Arthur van Hoff
  35.  */
  36. public
  37. class GifImage extends DIBitmap {
  38.     private final boolean verbose = false;
  39.  
  40.     private static final int IMAGESEP         = 0x2c;
  41.     private static final int EXBLOCK         = 0x21;
  42.     private static final int EX_GRAPHICS_CONTROL= 0xf9;
  43.     private static final int EX_COMMENT     = 0xfe;
  44.     private static final int EX_APPLICATION     = 0xff;
  45.     private static final int TERMINATOR     = 0x3b;
  46.     private static final int INTERLACEMASK     = 0x40;
  47.     private static final int COLORMAPMASK     = 0x80;
  48.     private static final int BUFSIZE         = 2048;
  49.  
  50.     /**
  51.      * An error has occurred. Throw an exception.
  52.      */
  53.     private static void error(String s1) {
  54.     throw new Exception(s1);
  55.     }
  56.  
  57.     /**
  58.      * Read bytes
  59.      */
  60.     boolean readBytes(InputStream in, byte buf[], int off, int len) {
  61.     while (len > 0) {
  62.         int n = in.read(buf, off, len);
  63.         if (n < 0) {
  64.         return false;
  65.         }
  66.         off += n;
  67.         len -= n;
  68.     }
  69.     return true;
  70.     }
  71.  
  72.     /**
  73.      * Constructor, read the image from a file
  74.      */
  75.     public GifImage(String fname) {
  76.     // Don't buffer this stream, most data is read in chunks anyway...
  77.     this(new FileInputStream(fname), null);
  78.     }
  79.  
  80.     public GifImage(InputStream in) {
  81.     this(in, null);
  82.     }
  83.  
  84.     /**
  85.      * Constructor, read image from stream.
  86.      */
  87.     public GifImage(InputStream in, Observer o) {
  88.     if (o != null) {
  89.         addObserver(o);
  90.     }
  91.     try {
  92.         readHeader(in);
  93.  
  94.         while (true) {
  95.         int code;
  96.  
  97.         switch (code = in.read()) {
  98.           case EXBLOCK:
  99.             switch (code = in.read()) {
  100.               case EX_GRAPHICS_CONTROL: {
  101.             byte buf[] = new byte[6];
  102.             if (!readBytes(in, buf, 0, 6)) {
  103.                 return;//error("corrupt GIF file");
  104.             }
  105.             if ((buf[0] != 4) || (buf[5] != 0)) {
  106.                 return;//error("corrupt GIF file (GCE size)");
  107.             }
  108.             // Get the index of the transparent color
  109.             trans_index = buf[4] & 0xFF;
  110.             break;
  111.               }
  112.  
  113.               case EX_COMMENT:
  114.               case EX_APPLICATION:
  115.               default:
  116.             while (true) {
  117.                 int n = in.read();
  118.                 if (n == 0) {
  119.                 break;
  120.                 }
  121.                 byte buf[] = new byte[n];
  122.                 if (!readBytes(in, buf, 0, n)) {
  123.                 return;//error("corrupt GIF file");
  124.                 }
  125.             }
  126.             break;
  127.             }
  128.             break;
  129.  
  130.           case IMAGESEP:
  131.             try {
  132.             readImage(in);
  133.             } catch (ArrayIndexOutOfBoundsException e) {
  134.             if (verbose) {
  135.                 e.printStackTrace();
  136.             }
  137.             return;//error("corrupt gif file");
  138.             }
  139.             break;
  140.  
  141.           case TERMINATOR:
  142.             return;
  143.  
  144.           case -1:
  145.             return;
  146.  
  147.           default:
  148.             return;//error("corrupt GIF file (parse) [" + code + "].");
  149.             //break;
  150.         }
  151.         }
  152.     } finally {
  153.         in.close();
  154.     }
  155.     }
  156.  
  157.     /**
  158.      * Read Image header
  159.      */
  160.     private void readHeader(InputStream in) {
  161.     // Create a buffer
  162.     byte buf[] = new byte[256*3];
  163.  
  164.     // Read the header
  165.     if (!readBytes(in, buf, 0, 13)) {
  166.         throw new IOException();
  167.     }
  168.  
  169.     // Check header
  170.     if ((buf[0] != 'G') || (buf[1] != 'I') || (buf[2] != 'F')) {
  171.         error("not a GIF file.");
  172.     }
  173.  
  174.     // colormap info
  175.     int ch = buf[10] & 0xFF;
  176.     if ((ch & COLORMAPMASK) == 0) {
  177.         error("no global colormap.");
  178.     }
  179.     num_colors = 1 << ((ch & 0x7) + 1);
  180.  
  181.     // supposed to be NULL
  182.     if (buf[12] != 0) {
  183.         error("corrupt GIF file (nonull).");
  184.     }
  185.  
  186.     // Read colors
  187.     if (!readBytes(in, buf, 0, num_colors * 3)) {
  188.         throw new IOException();
  189.     }
  190.  
  191.     // Allocate color map
  192.     red = new byte[num_colors];
  193.     green = new byte[num_colors];
  194.     blue = new byte[num_colors];
  195.  
  196.     // Copy colors
  197.     for (int i = 0 ; i < num_colors ; i++) {
  198.         red[i]   = buf[i*3 + 0];
  199.         green[i] = buf[i*3 + 1];
  200.         blue[i]  = buf[i*3 + 2];
  201.     }
  202.  
  203.         // nothing transparent yet.
  204.     trans_index = num_colors + 1;
  205.     }
  206.  
  207.     private void readImage(InputStream in) {
  208.     int tm = 0;
  209.  
  210.     if (verbose) {
  211.         tm = System.nowMillis();
  212.     }
  213.  
  214.     // Allocate the buffer
  215.     byte block[] = new byte[256 + 3];
  216.  
  217.     // Read the image descriptor
  218.     if (!readBytes(in, block, 0, 10)) {
  219.         throw new IOException();
  220.     }
  221.     width = (block[4] & 0xFF) | (block[5] << 8);
  222.     height = (block[6] & 0xFF) | (block[7] << 8);
  223.     boolean interlace = (block[8] & INTERLACEMASK) != 0;
  224.     int initCodeSize = block[9] & 0xFF;
  225.  
  226.     /* observers may want to know the image size */
  227.     /* annoying I have to say setChanged */
  228.     setChanged();
  229.     notifyObservers();
  230.  
  231.     // allocate the raster data
  232.     byte ras[] = raster = new byte[width * height];
  233.  
  234.     if (verbose) {
  235.         System.out.print("Reading a " + width + " by " + height + " " +
  236.               (interlace ? "" : "non-") + "interlaced image...");
  237.     }
  238.  
  239.     // Patrick Naughton:
  240.     // Note that I ignore the possible existence of a local color map.
  241.     // I'm told there aren't many files around that use them, and the
  242.     // spec says it's defined for future use.  This could lead to an
  243.     // error reading some files.
  244.          //
  245.     // Start reading the image data. First we get the intial code size
  246.     // and compute decompressor constant values, based on this code
  247.     // size.
  248.     //
  249.     // The GIF spec has it that the code size is the code size used to
  250.     // compute the above values is the code size given in the file,
  251.     // but the code size used in compression/decompression is the code
  252.     // size given in the file plus one. (thus the ++).
  253.  
  254.     // Arthur van Hoff:
  255.     // The following narly code reads LZW compressed data blocks and
  256.     // dumps it into the image data. The input stream is broken up into
  257.     // blocks of 1-255 characters, each preceded by a length byte.
  258.     // 3-12 bit codes are read from these blocks. The codes correspond to
  259.     // entry is the hashtable (the prefix, suffix stuff), and the appropriate
  260.     // pixels are written to the image.
  261.  
  262.     int clearCode = (1 << initCodeSize);
  263.     int eofCode = clearCode + 1;
  264.     int bitMask = num_colors - 1;
  265.  
  266.     if (verbose) {
  267.         System.out.print("Decompressing...");
  268.         System.out.flush();
  269.     }
  270.  
  271.  
  272.     // Variables used to form reading data
  273.     boolean blockEnd = false;
  274.     int remain = 0;
  275.     int byteoff = 0;
  276.     int accumbits = 0;
  277.     int accumdata = 0;
  278.  
  279.     // Variables used to decompress the data
  280.     int codeSize = initCodeSize + 1;
  281.     int maxCode = 1 << codeSize;
  282.     int codeMask = maxCode - 1;
  283.     int freeCode = clearCode + 2;
  284.     int code = 0;
  285.     int oldCode = 0;;
  286.     byte prevChar = 0;
  287.  
  288.     // Temproray storage for decompression
  289.     short prefix[] = new short[4096];
  290.     byte suffix[] = new byte[4096];
  291.     byte outCode[] = new byte[1025];
  292.  
  293.     // Variables used for writing pixels
  294.     int x = width;
  295.     int y = 0;
  296.     int off = 0;
  297.     int pass = 0;
  298.  
  299.     // Read codes until the eofCode is encountered
  300.     while (true) {
  301.         
  302.         if (accumbits < codeSize) {
  303.         // fill the buffer if needed
  304.         remain -= 2;
  305.         while (remain < 0 && !blockEnd) {
  306.             // move remaining bytes to the beginning of the buffer
  307.             block[0] = block[byteoff];
  308.             byteoff = 0;
  309.  
  310.             // read the next block length
  311.             int blockLength = in.read();
  312.             if (blockLength < 0) {
  313. //            throw new IOException();
  314.             return;        // quietly accept truncated GIF images
  315.             }
  316.             if (blockLength == 0) {
  317.             blockEnd = true;
  318.             }
  319.  
  320.             // fill the block
  321.             if (!readBytes(in, block, remain + 2, blockLength)) {
  322. //            throw new IOException();
  323.             return;        // quietly accept truncated GIF images
  324.             }
  325.             remain += blockLength;
  326.         }
  327.  
  328.         // 2 bytes at a time saves checking for accumbits < codeSize.
  329.         // We know we'll get enough and also that we can't overflow
  330.         // since codeSize <= 12.
  331.         accumdata += (block[byteoff++] & 0xff) << accumbits;
  332.         accumbits += 8;
  333.         accumdata += (block[byteoff++] & 0xff) << accumbits;
  334.         accumbits += 8;
  335.         }
  336.  
  337.         // Compute the code
  338.         code = accumdata & codeMask;
  339.         accumdata >>= codeSize;
  340.         accumbits -= codeSize;
  341.  
  342.         //
  343.         // Interpret the code
  344.         //
  345.         if (code == clearCode) {
  346.         // Clear code sets everything back to its initial value, then
  347.         // reads the immediately subsequent code as uncompressed data.
  348.         if (verbose) {
  349.             System.out.print(".");
  350.             System.out.flush();
  351.         }
  352.  
  353.         // Note that freeCode is one less than it is supposed to be,
  354.         // this is because it will be incremented next time round the loop
  355.         freeCode = clearCode + 1;
  356.         codeSize = initCodeSize + 1;
  357.         maxCode = 1 << codeSize;
  358.         codeMask = maxCode - 1;
  359.  
  360.         // Continue if we've NOT reached the end, some Gif images contain bogus
  361.         // codes after the last clear code.
  362.         if (off < raster.length) {
  363.             continue;
  364.         }
  365.  
  366.         // pretend we've reached the end of the data
  367.         code = eofCode;
  368.         }
  369.  
  370.         if (code == eofCode) {
  371.         // make sure we read the whole block of pixels.
  372.         if (!blockEnd) {
  373.             in.read();
  374.         }
  375.         
  376.         if (verbose) {
  377.             System.out.println("done in " + (System.nowMillis() - tm) + "ms");
  378.         }
  379.         return;
  380.         } 
  381.  
  382.         // It must be data: save code in CurCode
  383.         int curCode = code;
  384.         int outCount = outCode.length;
  385.  
  386.         // If greater or equal to freeCode, not in the hash table
  387.         // yet; repeat the last character decoded
  388.         if (curCode >= freeCode) {
  389.         curCode = oldCode;
  390.         outCode[--outCount] = prevChar;
  391.         }
  392.  
  393.         // Unless this code is raw data, pursue the chain pointed
  394.         // to by curCode through the hash table to its end; each
  395.         // code in the chain puts its associated output code on
  396.         // the output queue.
  397.         while (curCode > bitMask) {
  398.         outCode[--outCount] = suffix[curCode];
  399.         curCode = prefix[curCode];
  400.         }
  401.  
  402.         // The last code in the chain is treated as raw data.
  403.         prevChar = (byte)curCode;
  404.         outCode[--outCount] = prevChar;
  405.  
  406.         // Now we put the data out to the Output routine. It's
  407.         // been stacked LIFO, so deal with it that way...
  408.         int len = outCode.length - outCount;
  409.         if (len > 2 && len < x) {
  410.         x -= len;
  411.         System.arraycopy(outCode, outCount, ras, off, len);
  412.         off += len;
  413.         } else while (--len >= 0) {
  414.         ras[off++] = outCode[outCount++];
  415.  
  416.         // Update the X-coordinate, and if it overflows, update the
  417.         // Y-coordinate
  418.         if (--x == 0) {
  419.             // If a non-interlaced picture, just increment y to the next
  420.             // scan line.  If it's interlaced, deal with the interlace as
  421.             // described in the GIF spec.  Put the decoded scan line out
  422.             // to the screen if we haven't gone past the bottom of it
  423.             x = width;
  424.             if (interlace) {
  425.             switch (pass) {
  426.               case 0:
  427.                 y += 8;
  428.                 if (y >= height) {
  429.                 pass++;
  430.                 y = 4;
  431.                 }
  432.                 break;
  433.               case 1:
  434.                 y += 8;
  435.                 if (y >= height) {
  436.                 pass++;
  437.                 y = 2;
  438.                 }
  439.                 break;
  440.               case 2:
  441.                 y += 4;
  442.                 if (y >= height) {
  443.                 pass++;
  444.                 y = 1;
  445.                 }
  446.                 break;
  447.               case 3:
  448.                 y += 2;
  449.                 break;
  450.             }
  451.             off = y * width;
  452.             }
  453.  
  454.             // Some files overrun the end
  455.             if (off >= ras.length) {
  456.             break;
  457.             }
  458.         }
  459.         } 
  460.  
  461.         // Build the hash table on-the-fly. No table is stored in
  462.         // the file.
  463.         prefix[freeCode] = (short)oldCode;
  464.         suffix[freeCode] = prevChar;
  465.         oldCode = code;
  466.  
  467.         // Point to the next slot in the table.  If we exceed the
  468.         // maxCode, increment the code size unless
  469.         // it's already 12.  If it is, do nothing: the next code
  470.         // decompressed better be CLEAR
  471.         if (++freeCode >= maxCode) {
  472.         if (codeSize < 12) {
  473.             codeSize++;
  474.             maxCode <<= 1;
  475.             codeMask = maxCode - 1;
  476.         }
  477.         }
  478.     }
  479.     }
  480.  
  481.     /**
  482.      * Testing... 1... 2...
  483.      */
  484.     public static void main(String args[]) {
  485.     GifImage image = new GifImage(args[0]);
  486.     }
  487. }
  488.  
  489.