gt: 	      hshidx = (hshidx + HASHSTEP) % HASHSIZE;
context: package com.idega.graphics;
  
  import java.io.*;
  import java.awt.*;
  import java.awt.image.*;
  
  /**
   * GIFEncoder is a class which takes an image and saves it to a stream
   * using the GIF file format (<A
   * HREF="http://www.dcs.ed.ac.uk/%7Emxr/gfx/">Graphics Interchange
   * Format</A>). A GIFEncoder
   * is constructed with either an AWT Image (which must be fully
   * loaded) or a set of RGB arrays. The image can be written out with a
   * call to <CODE>Write</CODE>.<P>
   *
   * Three caveats:
   * <UL>
   *   <LI>GIFEncoder will convert the image to indexed color upon
   *   construction. This will take some time, depending on the size of
   *   the image. Also, actually writing the image out (Write) will take
   *   time.<P>
   *
   *   <LI>The image cannot have more than 256 colors, since GIF is an 8
   *   bit format. For a 24 bit to 8 bit quantization algorithm, see
   *   Graphics Gems II III.2 by Xialoin Wu. Or check out his <A
   *   HREF="http://www.csd.uwo.ca/faculty/wu/cq.c">C source</A>.<P>
   *
   *   <LI>Since the image must be completely loaded into memory,
   *   GIFEncoder may have problems with large images. Attempting to
   *   encode an image which will not fit into memory will probably
   *   result in the following exception:<P>
   *   <CODE>java.awt.AWTException: Grabber returned false: 192</CODE><P>
   * </UL><P>
   *
   * GIFEncoder is based upon gifsave.c, which was written and released
   * by:<P>
   * <CENTER>
   *                                  Sverre H. Huseby<BR>
   *                                   Bjoelsengt. 17<BR>
   *                                     N-0468 Oslo<BR>
   *                                       Norway<P>
   *
   *                                 Phone: +47 2 230539<BR>
   *                                 sverrehu@ifi.uio.no<P>
   * </CENTER>
   * @version 0.90 21 Apr 1996
   * @author <A HREF="http://www.cs.brown.edu/people/amd/">Adam Doppelt</A> */
  public class GIFEncoder {
    short width_;
    short height_;
    int numColors_;
    byte pixels_[];
    byte colors_[];
  
  	/**
  	 * 
  	 * @uml.property name="sd_"
  	 * @uml.associationEnd multiplicity="(0 1)"
  	 */
  	ScreenDescriptor sd_;
  
  	/**
  	 * 
  	 * @uml.property name="id_"
  	 * @uml.associationEnd multiplicity="(0 1)"
  	 */
  	ImageDescriptor id_;
  
    /**
     * Construct a GIFEncoder. The constructor will convert the image to
     * an indexed color array. <B>This may take some time.</B><P>
     *
     * @param image The image to encode. The image <B>must</B> be completely loaded.
     * @exception AWTException Will be thrown if the pixel grab fails. This
     * can happen if Java runs out of memory. It may also indicate that the image
     * contains more than 256 colors.
     **/
  
    public GIFEncoder(Image image) throws AWTException {
      this.width_ = (short)image.getWidth(null);
  	  this.height_ = (short)image.getHeight(null);
  
  	  int values[] = new int[this.width_ * this.height_];
  	  PixelGrabber grabber = new PixelGrabber(image,0,0,this.width_,this.height_,values,0,this.width_);
  
  	  try {
        if(grabber.grabPixels() != true) {
  		throw new AWTException("Grabber returned false: " + grabber.status());
  	}
  	  }
  	  catch (InterruptedException e) {
    	}
  
  	  byte r[][] = new byte[this.width_][this.height_];
  	  byte g[][] = new byte[this.width_][this.height_];
  	  byte b[][] = new byte[this.width_][this.height_];
  	  int index = 0;
  	  for (int y = 0; y < this.height_; y++) {
  		for (int x = 0; x < this.width_; x++) {
  		    r[x][y] = (byte)((values[index] >> 16) & 0xFF);
  		    g[x][y] = (byte)((values[index] >> 8) & 0xFF);
  		    b[x][y] = (byte)((values[index]) & 0xFF);
  		    index++;
  	    }
  	}
  	    ToIndexedColor(r, g, b);
      }
  
    /**
     * Construct a GIFEncoder. The constructor will convert the image to
     * an indexed color array. <B>This may take some time.</B><P>
     *
     * Each array stores intensity values for the image. In other words,
     * r[x][y] refers to the red intensity of the pixel at column x, row
     * y.<P>
     *
     * @param r An array containing the red intensity values.
     * @param g An array containing the green intensity values.
     * @param b An array containing the blue intensity values.
     *
     * @exception AWTException Will be thrown if the image contains more than
     * 256 colors.
     **/
    public GIFEncoder(byte r[][], byte g[][], byte b[][]) throws AWTException {
  	  this.width_ = (short)(r.length);
  	  this.height_ = (short)(r[0].length);
  
  	  ToIndexedColor(r, g, b);
    }
  
    /**
     * Writes the image out to a stream in the GIF file format. This will
     * be a single GIF87a image, non-interlaced, with no background color.
     * <B>This may take some time.</B><P>
     *
     * @param output The stream to output to. This should probably be a
     * buffered stream.
     *
     * @exception IOException Will be thrown if a write operation fails.
     **/
    public void Write(OutputStream output) throws IOException {
  	  BitUtils.WriteString(output, "GIF87a");
  
  	  ScreenDescriptor sd = new ScreenDescriptor(this.width_,this.height_,this.numColors_);
  	  sd.Write(output);
  
  	  output.write(this.colors_,0,this.colors_.length);
  
    	ImageDescriptor id = new ImageDescriptor(this.width_,this.height_,',');
  	  id.Write(output);
  
    	byte codesize = BitUtils.BitsNeeded(this.numColors_);
  	  if (codesize == 1) {
  		codesize++;
  	}
  	  output.write(codesize);
  
  	  LZWCompressor.LZWCompress(output,codesize,this.pixels_);
  	  output.write(0);
  
  	  id = new ImageDescriptor((byte)0,(byte)0,';');
  	  id.Write(output);
  	  output.flush();
    }
  
    void ToIndexedColor(byte r[][], byte g[][], byte b[][]) throws AWTException {
  	  this.pixels_ = new byte[this.width_ * this.height_];
  	  this.colors_ = new byte[256 * 3];
  	  int colornum = 0;
  	  for (int x = 0; x < this.width_; x++) {
  	    for (int y = 0; y < this.height_; y++) {
  		    int search;
  		    for (search = 0; search < colornum; search++) {
  				if (this.colors_[search * 3] == r[x][y] &&
  				        this.colors_[search * 3 + 1] == g[x][y] &&
  				  this.colors_[search * 3 + 2] == b[x][y]) {
  					break;
  				}
  			}
  
  		      if (search > 255) {
  				throw new AWTException("Too many colors.");
  			}
  
  		      this.pixels_[y * this.width_ + x] = (byte)search;
  
  		      if (search == colornum) {
  		        this.colors_[search * 3] = r[x][y];
  		        this.colors_[search * 3 + 1] = g[x][y];
  		        this.colors_[search * 3 + 2] = b[x][y];
  		        colornum++;
  		      }
  	      }
  	    }
  	    this.numColors_ = 1 << BitUtils.BitsNeeded(colornum);
  	    byte copy[] = new byte[this.numColors_ * 3];
  	    System.arraycopy(this.colors_,0,copy,0,this.numColors_ * 3);
  	    this.colors_ = copy;
      }
    }
  
    class BitFile {
      OutputStream output_;
      byte buffer_[];
      int index_;
      int bitsLeft_;
  
      public BitFile(OutputStream output) {
  	    this.output_ = output;
  	    this.buffer_ = new byte[256];
  	    this.index_ = 0;
  	    this.bitsLeft_ = 8;
      }
  
      public void Flush() throws IOException {
  	    int numBytes = this.index_ + (this.bitsLeft_ == 8 ? 0 : 1);
  	    if (numBytes > 0) {
  	      this.output_.write(numBytes);
  	      this.output_.write(this.buffer_, 0, numBytes);
  	      this.buffer_[0] = 0;
  	      this.index_ = 0;
  	      this.bitsLeft_ = 8;
  	    }
      }
  
      public void WriteBits(int bits, int numbits) throws IOException {
  	    int bitsWritten = 0;
  	    int numBytes = 255;
  	    do {
  	      if ((this.index_ == 254 && this.bitsLeft_ == 0) || this.index_ > 254) {
  		      this.output_.write(numBytes);
  		      this.output_.write(this.buffer_, 0, numBytes);
  
  		      this.buffer_[0] = 0;
  		      this.index_ = 0;
  		      this.bitsLeft_ = 8;
  	      }
  
  	      if (numbits <= this.bitsLeft_) {
  		      this.buffer_[this.index_] |= (bits & ((1 << numbits) - 1)) << (8 - this.bitsLeft_);
  		      bitsWritten += numbits;
  		      this.bitsLeft_ -= numbits;
  		      numbits = 0;
  	      }
  	      else {
  		      this.buffer_[this.index_] |= (bits & ((1 << this.bitsLeft_) - 1)) << (8 - this.bitsLeft_);
  		      bitsWritten += this.bitsLeft_;
  		      bits >>= this.bitsLeft_;
  		      numbits -= this.bitsLeft_;
  		      this.buffer_[++this.index_] = 0;
  		      this.bitsLeft_ = 8;
          }
  	    }
        while (numbits != 0);
      }
    }
  
    class LZWStringTable {
      private final static int RES_CODES = 2;
      private final static short HASH_FREE = (short)0xFFFF;
      private final static short NEXT_FIRST = (short)0xFFFF;
      private final static int MAXBITS = 12;
      private final static int MAXSTR = (1 << MAXBITS);
      private final static short HASHSIZE = 9973;
      private final static short HASHSTEP = 2039;
  
      byte strChr_[];
      short strNxt_[];
      short strHsh_[];
      short numStrings_;
  
      public LZWStringTable() {
  	    this.strChr_ = new byte[MAXSTR];
  	    this.strNxt_ = new short[MAXSTR];
  	    this.strHsh_ = new short[HASHSIZE];
      }
  
      public int AddCharString(short index, byte b) {
  	    int hshidx;
  
  	    if (this.numStrings_ >= MAXSTR) {
  			return 0xFFFF;
  		}
  
  	    hshidx = Hash(index, b);
  	    while (this.strHsh_[hshidx] != HASH_FREE) {
  			hshidx = (hshidx + HASHSTEP) % HASHSIZE;
  		}
  
  	    this.strHsh_[hshidx] = this.numStrings_;
  	    this.strChr_[this.numStrings_] = b;
  	    this.strNxt_[this.numStrings_] = (index != HASH_FREE) ? index : NEXT_FIRST;
  
  	    return this.numStrings_++;
      }
  
      public short FindCharString(short index, byte b) {
  	    int hshidx, nxtidx;
  
  	    if (index == HASH_FREE) {
  			return b;
  		}
  
  	    hshidx = Hash(index, b);
  	    while ((nxtidx = this.strHsh_[hshidx]) != HASH_FREE) {
  	      if (this.strNxt_[nxtidx] == index && this.strChr_[nxtidx] == b) {
  			return (short)nxtidx;
  		}
  