Java/2D Graphics GUI/GIF

Материал из Java эксперт
Перейти к: навигация, поиск

Animated Gif Encoder

<source lang="java"> import java.io.*; import java.awt.*; import java.awt.image.*; /**

* Class AnimatedGifEncoder - Encodes a GIF file consisting of one or more
* frames.
* 
*
 *  Example:
 *     AnimatedGifEncoder e = new AnimatedGifEncoder();
 *     e.start(outputFileName);
 *     e.setDelay(1000);   // 1 frame per sec
 *     e.addFrame(image1);
 *     e.addFrame(image2);
 *     e.finish();
 * 
* 
* No copyright asserted on the source code of this class. May be used for any
* purpose, however, refer to the Unisys LZW patent for restrictions on use of
* the associated LZWEncoder class. Please forward any corrections to
* kweiner@fmsware.ru.
* 
* @author Kevin Weiner, FM Software
* @version 1.03 November 2003
* 
*/

public class AnimatedGifEncoder {

 protected int width; // image size
 protected int height;
 protected Color transparent = null; // transparent color if given
 protected int transIndex; // transparent index in color table
 protected int repeat = -1; // no repeat
 protected int delay = 0; // frame delay (hundredths)
 protected boolean started = false; // ready to output frames
 protected OutputStream out;
 protected BufferedImage image; // current frame
 protected byte[] pixels; // BGR byte array from frame
 protected byte[] indexedPixels; // converted frame indexed to palette
 protected int colorDepth; // number of bit planes
 protected byte[] colorTab; // RGB palette
 protected boolean[] usedEntry = new boolean[256]; // active palette entries
 protected int palSize = 7; // color table size (bits-1)
 protected int dispose = -1; // disposal code (-1 = use default)
 protected boolean closeStream = false; // close stream when finished
 protected boolean firstFrame = true;
 protected boolean sizeSet = false; // if false, get size from first frame
 protected int sample = 10; // default sample interval for quantizer
 /**
  * Sets the delay time between each frame, or changes it for subsequent frames
  * (applies to last frame added).
  * 
  * @param ms
  *          int delay time in milliseconds
  */
 public void setDelay(int ms) {
   delay = Math.round(ms / 10.0f);
 }
 /**
  * Sets the GIF frame disposal code for the last added frame and any
  * subsequent frames. Default is 0 if no transparent color has been set,
  * otherwise 2.
  * 
  * @param code
  *          int disposal code.
  */
 public void setDispose(int code) {
   if (code >= 0) {
     dispose = code;
   }
 }
 /**
  * Sets the number of times the set of GIF frames should be played. Default is
  * 1; 0 means play indefinitely. Must be invoked before the first image is
  * added.
  * 
  * @param iter
  *          int number of iterations.
  * @return
  */
 public void setRepeat(int iter) {
   if (iter >= 0) {
     repeat = iter;
   }
 }
 /**
  * Sets the transparent color for the last added frame and any subsequent
  * frames. Since all colors are subject to modification in the quantization
  * process, the color in the final palette for each frame closest to the given
  * color becomes the transparent color for that frame. May be set to null to
  * indicate no transparent color.
  * 
  * @param c
  *          Color to be treated as transparent on display.
  */
 public void setTransparent(Color c) {
   transparent = c;
 }
 /**
  * Adds next GIF frame. The frame is not written immediately, but is actually
  * deferred until the next frame is received so that timing data can be
  * inserted. Invoking finish() flushes all frames. If
  * setSize was not invoked, the size of the first image is used
  * for all subsequent frames.
  * 
  * @param im
  *          BufferedImage containing frame to write.
  * @return true if successful.
  */
 public boolean addFrame(BufferedImage im) {
   if ((im == null) || !started) {
     return false;
   }
   boolean ok = true;
   try {
     if (!sizeSet) {
       // use first frame"s size
       setSize(im.getWidth(), im.getHeight());
     }
     image = im;
     getImagePixels(); // convert to correct format if necessary
     analyzePixels(); // build color table & map pixels
     if (firstFrame) {
       writeLSD(); // logical screen descriptior
       writePalette(); // global color table
       if (repeat >= 0) {
         // use NS app extension to indicate reps
         writeNetscapeExt();
       }
     }
     writeGraphicCtrlExt(); // write graphic control extension
     writeImageDesc(); // image descriptor
     if (!firstFrame) {
       writePalette(); // local color table
     }
     writePixels(); // encode and write pixel data
     firstFrame = false;
   } catch (IOException e) {
     ok = false;
   }
   return ok;
 }
 /**
  * Flushes any pending data and closes output file. If writing to an
  * OutputStream, the stream is not closed.
  */
 public boolean finish() {
   if (!started)
     return false;
   boolean ok = true;
   started = false;
   try {
     out.write(0x3b); // gif trailer
     out.flush();
     if (closeStream) {
       out.close();
     }
   } catch (IOException e) {
     ok = false;
   }
   // reset for subsequent use
   transIndex = 0;
   out = null;
   image = null;
   pixels = null;
   indexedPixels = null;
   colorTab = null;
   closeStream = false;
   firstFrame = true;
   return ok;
 }
 /**
  * Sets frame rate in frames per second. Equivalent to
  * setDelay(1000/fps).
  * 
  * @param fps
  *          float frame rate (frames per second)
  */
 public void setFrameRate(float fps) {
   if (fps != 0f) {
     delay = Math.round(100f / fps);
   }
 }
 /**
  * Sets quality of color quantization (conversion of images to the maximum 256
  * colors allowed by the GIF specification). Lower values (minimum = 1)
  * produce better colors, but slow processing significantly. 10 is the
  * default, and produces good color mapping at reasonable speeds. Values
  * greater than 20 do not yield significant improvements in speed.
  * 
  * @param quality
  *          int greater than 0.
  * @return
  */
 public void setQuality(int quality) {
   if (quality < 1)
     quality = 1;
   sample = quality;
 }
 /**
  * Sets the GIF frame size. The default size is the size of the first frame
  * added if this method is not invoked.
  * 
  * @param w
  *          int frame width.
  * @param h
  *          int frame width.
  */
 public void setSize(int w, int h) {
   if (started && !firstFrame)
     return;
   width = w;
   height = h;
   if (width < 1)
     width = 320;
   if (height < 1)
     height = 240;
   sizeSet = true;
 }
 /**
  * Initiates GIF file creation on the given stream. The stream is not closed
  * automatically.
  * 
  * @param os
  *          OutputStream on which GIF images are written.
  * @return false if initial write failed.
  */
 public boolean start(OutputStream os) {
   if (os == null)
     return false;
   boolean ok = true;
   closeStream = false;
   out = os;
   try {
     writeString("GIF89a"); // header
   } catch (IOException e) {
     ok = false;
   }
   return started = ok;
 }
 /**
  * Initiates writing of a GIF file with the specified name.
  * 
  * @param file
  *          String containing output file name.
  * @return false if open or initial write failed.
  */
 public boolean start(String file) {
   boolean ok = true;
   try {
     out = new BufferedOutputStream(new FileOutputStream(file));
     ok = start(out);
     closeStream = true;
   } catch (IOException e) {
     ok = false;
   }
   return started = ok;
 }
 /**
  * Analyzes image colors and creates color map.
  */
 protected void analyzePixels() {
   int len = pixels.length;
   int nPix = len / 3;
   indexedPixels = new byte[nPix];
   NeuQuant nq = new NeuQuant(pixels, len, sample);
   // initialize quantizer
   colorTab = nq.process(); // create reduced palette
   // convert map from BGR to RGB
   for (int i = 0; i < colorTab.length; i += 3) {
     byte temp = colorTab[i];
     colorTab[i] = colorTab[i + 2];
     colorTab[i + 2] = temp;
     usedEntry[i / 3] = false;
   }
   // map image pixels to new palette
   int k = 0;
   for (int i = 0; i < nPix; i++) {
     int index = nq.map(pixels[k++] & 0xff, pixels[k++] & 0xff, pixels[k++] & 0xff);
     usedEntry[index] = true;
     indexedPixels[i] = (byte) index;
   }
   pixels = null;
   colorDepth = 8;
   palSize = 7;
   // get closest match to transparent color if specified
   if (transparent != null) {
     transIndex = findClosest(transparent);
   }
 }
 /**
  * Returns index of palette color closest to c
  * 
  */
 protected int findClosest(Color c) {
   if (colorTab == null)
     return -1;
   int r = c.getRed();
   int g = c.getGreen();
   int b = c.getBlue();
   int minpos = 0;
   int dmin = 256 * 256 * 256;
   int len = colorTab.length;
   for (int i = 0; i < len;) {
     int dr = r - (colorTab[i++] & 0xff);
     int dg = g - (colorTab[i++] & 0xff);
     int db = b - (colorTab[i] & 0xff);
     int d = dr * dr + dg * dg + db * db;
     int index = i / 3;
     if (usedEntry[index] && (d < dmin)) {
       dmin = d;
       minpos = index;
     }
     i++;
   }
   return minpos;
 }
 /**
  * Extracts image pixels into byte array "pixels"
  */
 protected void getImagePixels() {
   int w = image.getWidth();
   int h = image.getHeight();
   int type = image.getType();
   if ((w != width) || (h != height) || (type != BufferedImage.TYPE_3BYTE_BGR)) {
     // create new image with right size/format
     BufferedImage temp = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
     Graphics2D g = temp.createGraphics();
     g.drawImage(image, 0, 0, null);
     image = temp;
   }
   pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
 }
 /**
  * Writes Graphic Control Extension
  */
 protected void writeGraphicCtrlExt() throws IOException {
   out.write(0x21); // extension introducer
   out.write(0xf9); // GCE label
   out.write(4); // data block size
   int transp, disp;
   if (transparent == null) {
     transp = 0;
     disp = 0; // dispose = no action
   } else {
     transp = 1;
     disp = 2; // force clear if using transparent color
   }
   if (dispose >= 0) {
     disp = dispose & 7; // user override
   }
   disp <<= 2;
   // packed fields
   out.write(0 | // 1:3 reserved
       disp | // 4:6 disposal
       0 | // 7 user input - 0 = none
       transp); // 8 transparency flag
   writeShort(delay); // delay x 1/100 sec
   out.write(transIndex); // transparent color index
   out.write(0); // block terminator
 }
 /**
  * Writes Image Descriptor
  */
 protected void writeImageDesc() throws IOException {
   out.write(0x2c); // image separator
   writeShort(0); // image position x,y = 0,0
   writeShort(0);
   writeShort(width); // image size
   writeShort(height);
   // packed fields
   if (firstFrame) {
     // no LCT - GCT is used for first (or only) frame
     out.write(0);
   } else {
     // specify normal LCT
     out.write(0x80 | // 1 local color table 1=yes
         0 | // 2 interlace - 0=no
         0 | // 3 sorted - 0=no
         0 | // 4-5 reserved
         palSize); // 6-8 size of color table
   }
 }
 /**
  * Writes Logical Screen Descriptor
  */
 protected void writeLSD() throws IOException {
   // logical screen size
   writeShort(width);
   writeShort(height);
   // packed fields
   out.write((0x80 | // 1 : global color table flag = 1 (gct used)
       0x70 | // 2-4 : color resolution = 7
       0x00 | // 5 : gct sort flag = 0
       palSize)); // 6-8 : gct size
   out.write(0); // background color index
   out.write(0); // pixel aspect ratio - assume 1:1
 }
 /**
  * Writes Netscape application extension to define repeat count.
  */
 protected void writeNetscapeExt() throws IOException {
   out.write(0x21); // extension introducer
   out.write(0xff); // app extension label
   out.write(11); // block size
   writeString("NETSCAPE" + "2.0"); // app id + auth code
   out.write(3); // sub-block size
   out.write(1); // loop sub-block id
   writeShort(repeat); // loop count (extra iterations, 0=repeat forever)
   out.write(0); // block terminator
 }
 /**
  * Writes color table
  */
 protected void writePalette() throws IOException {
   out.write(colorTab, 0, colorTab.length);
   int n = (3 * 256) - colorTab.length;
   for (int i = 0; i < n; i++) {
     out.write(0);
   }
 }
 /**
  * Encodes and writes pixel data
  */
 protected void writePixels() throws IOException {
   LZWEncoder encoder = new LZWEncoder(width, height, indexedPixels, colorDepth);
   encoder.encode(out);
 }
 /**
  * Write 16-bit value to output stream, LSB first
  */
 protected void writeShort(int value) throws IOException {
   out.write(value & 0xff);
   out.write((value >> 8) & 0xff);
 }
 /**
  * Writes string to output stream
  */
 protected void writeString(String s) throws IOException {
   for (int i = 0; i < s.length(); i++) {
     out.write((byte) s.charAt(i));
   }
 }

} /*

* NeuQuant Neural-Net Quantization Algorithm
* ------------------------------------------
* 
* Copyright (c) 1994 Anthony Dekker
* 
* NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
* "Kohonen neural networks for optimal colour quantization" in "Network:
* Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
* the algorithm.
* 
* Any party obtaining a copy of these files from the author, directly or
* indirectly, is granted, free of charge, a full and unrestricted irrevocable,
* world-wide, paid up, royalty-free, nonexclusive right and license to deal in
* this software and documentation files (the "Software"), including without
* limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons who
* receive copies from any such party to do so, with the only requirement being
* that this copyright notice remain intact.
*/

// Ported to Java 12/00 K Weiner class NeuQuant {

 protected static final int netsize = 256; /* number of colours used */
 /* four primes near 500 - assume no image has a length so large */
 /* that it is divisible by all four primes */
 protected static final int prime1 = 499;
 protected static final int prime2 = 491;
 protected static final int prime3 = 487;
 protected static final int prime4 = 503;
 protected static final int minpicturebytes = (3 * prime4);
 /* minimum size for input image */
 /*
  * Program Skeleton ---------------- [select samplefac in range 1..30] [read
  * image from input file] pic = (unsigned char*) malloc(3*width*height);
  * initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output
  * image header, using writecolourmap(f)] inxbuild(); write output image using
  * inxsearch(b,g,r)
  */
 /*
  * Network Definitions -------------------
  */
 protected static final int maxnetpos = (netsize - 1);
 protected static final int netbiasshift = 4; /* bias for colour values */
 protected static final int ncycles = 100; /* no. of learning cycles */
 /* defs for freq and bias */
 protected static final int intbiasshift = 16; /* bias for fractions */
 protected static final int intbias = (((int) 1) << intbiasshift);
 protected static final int gammashift = 10; /* gamma = 1024 */
 protected static final int gamma = (((int) 1) << gammashift);
 protected static final int betashift = 10;
 protected static final int beta = (intbias >> betashift); /* beta = 1/1024 */
 protected static final int betagamma = (intbias << (gammashift - betashift));
 /* defs for decreasing radius factor */
 protected static final int initrad = (netsize >> 3); /*
                                                        * for 256 cols, radius
                                                        * starts
                                                        */
 protected static final int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */
 protected static final int radiusbias = (((int) 1) << radiusbiasshift);
 protected static final int initradius = (initrad * radiusbias); /*
                                                                  * and
                                                                  * decreases
                                                                  * by a
                                                                  */
 protected static final int radiusdec = 30; /* factor of 1/30 each cycle */
 /* defs for decreasing alpha factor */
 protected static final int alphabiasshift = 10; /* alpha starts at 1.0 */
 protected static final int initalpha = (((int) 1) << alphabiasshift);
 protected int alphadec; /* biased by 10 bits */
 /* radbias and alpharadbias used for radpower calculation */
 protected static final int radbiasshift = 8;
 protected static final int radbias = (((int) 1) << radbiasshift);
 protected static final int alpharadbshift = (alphabiasshift + radbiasshift);
 protected static final int alpharadbias = (((int) 1) << alpharadbshift);
 /*
  * Types and Global Variables --------------------------
  */
 protected byte[] thepicture; /* the input image itself */
 protected int lengthcount; /* lengthcount = H*W*3 */
 protected int samplefac; /* sampling factor 1..30 */
 // typedef int pixel[4]; /* BGRc */
 protected int[][] network; /* the network itself - [netsize][4] */
 protected int[] netindex = new int[256];
 /* for network lookup - really 256 */
 protected int[] bias = new int[netsize];
 /* bias and freq arrays for learning */
 protected int[] freq = new int[netsize];
 protected int[] radpower = new int[initrad];
 /* radpower for precomputation */
 /*
  * Initialise network in range (0,0,0) to (255,255,255) and set parameters
  * -----------------------------------------------------------------------
  */
 public NeuQuant(byte[] thepic, int len, int sample) {
   int i;
   int[] p;
   thepicture = thepic;
   lengthcount = len;
   samplefac = sample;
   network = new int[netsize][];
   for (i = 0; i < netsize; i++) {
     network[i] = new int[4];
     p = network[i];
     p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize;
     freq[i] = intbias / netsize; /* 1/netsize */
     bias[i] = 0;
   }
 }
 public byte[] colorMap() {
   byte[] map = new byte[3 * netsize];
   int[] index = new int[netsize];
   for (int i = 0; i < netsize; i++)
     index[network[i][3]] = i;
   int k = 0;
   for (int i = 0; i < netsize; i++) {
     int j = index[i];
     map[k++] = (byte) (network[j][0]);
     map[k++] = (byte) (network[j][1]);
     map[k++] = (byte) (network[j][2]);
   }
   return map;
 }
 /*
  * Insertion sort of network and building of netindex[0..255] (to do after
  * unbias)
  * -------------------------------------------------------------------------------
  */
 public void inxbuild() {
   int i, j, smallpos, smallval;
   int[] p;
   int[] q;
   int previouscol, startpos;
   previouscol = 0;
   startpos = 0;
   for (i = 0; i < netsize; i++) {
     p = network[i];
     smallpos = i;
     smallval = p[1]; /* index on g */
     /* find smallest in i..netsize-1 */
     for (j = i + 1; j < netsize; j++) {
       q = network[j];
       if (q[1] < smallval) { /* index on g */
         smallpos = j;
         smallval = q[1]; /* index on g */
       }
     }
     q = network[smallpos];
     /* swap p (i) and q (smallpos) entries */
     if (i != smallpos) {
       j = q[0];
       q[0] = p[0];
       p[0] = j;
       j = q[1];
       q[1] = p[1];
       p[1] = j;
       j = q[2];
       q[2] = p[2];
       p[2] = j;
       j = q[3];
       q[3] = p[3];
       p[3] = j;
     }
     /* smallval entry is now in position i */
     if (smallval != previouscol) {
       netindex[previouscol] = (startpos + i) >> 1;
       for (j = previouscol + 1; j < smallval; j++)
         netindex[j] = i;
       previouscol = smallval;
       startpos = i;
     }
   }
   netindex[previouscol] = (startpos + maxnetpos) >> 1;
   for (j = previouscol + 1; j < 256; j++)
     netindex[j] = maxnetpos; /* really 256 */
 }
 /*
  * Main Learning Loop ------------------
  */
 public void learn() {
   int i, j, b, g, r;
   int radius, rad, alpha, step, delta, samplepixels;
   byte[] p;
   int pix, lim;
   if (lengthcount < minpicturebytes)
     samplefac = 1;
   alphadec = 30 + ((samplefac - 1) / 3);
   p = thepicture;
   pix = 0;
   lim = lengthcount;
   samplepixels = lengthcount / (3 * samplefac);
   delta = samplepixels / ncycles;
   alpha = initalpha;
   radius = initradius;
   rad = radius >> radiusbiasshift;
   if (rad <= 1)
     rad = 0;
   for (i = 0; i < rad; i++)
     radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad));
   // fprintf(stderr,"beginning 1D learning: initial radius=%d\n", rad);
   if (lengthcount < minpicturebytes)
     step = 3;
   else if ((lengthcount % prime1) != 0)
     step = 3 * prime1;
   else {
     if ((lengthcount % prime2) != 0)
       step = 3 * prime2;
     else {
       if ((lengthcount % prime3) != 0)
         step = 3 * prime3;
       else
         step = 3 * prime4;
     }
   }
   i = 0;
   while (i < samplepixels) {
     b = (p[pix + 0] & 0xff) << netbiasshift;
     g = (p[pix + 1] & 0xff) << netbiasshift;
     r = (p[pix + 2] & 0xff) << netbiasshift;
     j = contest(b, g, r);
     altersingle(alpha, j, b, g, r);
     if (rad != 0)
       alterneigh(rad, j, b, g, r); /* alter neighbours */
     pix += step;
     if (pix >= lim)
       pix -= lengthcount;
     i++;
     if (delta == 0)
       delta = 1;
     if (i % delta == 0) {
       alpha -= alpha / alphadec;
       radius -= radius / radiusdec;
       rad = radius >> radiusbiasshift;
       if (rad <= 1)
         rad = 0;
       for (j = 0; j < rad; j++)
         radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad));
     }
   }
   // fprintf(stderr,"finished 1D learning: final alpha=%f
   // !\n",((float)alpha)/initalpha);
 }
 /*
  * Search for BGR values 0..255 (after net is unbiased) and return colour
  * index
  * ----------------------------------------------------------------------------
  */
 public int map(int b, int g, int r) {
   int i, j, dist, a, bestd;
   int[] p;
   int best;
   bestd = 1000; /* biggest possible dist is 256*3 */
   best = -1;
   i = netindex[g]; /* index on g */
   j = i - 1; /* start at netindex[g] and work outwards */
   while ((i < netsize) || (j >= 0)) {
     if (i < netsize) {
       p = network[i];
       dist = p[1] - g; /* inx key */
       if (dist >= bestd)
         i = netsize; /* stop iter */
       else {
         i++;
         if (dist < 0)
           dist = -dist;
         a = p[0] - b;
         if (a < 0)
           a = -a;
         dist += a;
         if (dist < bestd) {
           a = p[2] - r;
           if (a < 0)
             a = -a;
           dist += a;
           if (dist < bestd) {
             bestd = dist;
             best = p[3];
           }
         }
       }
     }
     if (j >= 0) {
       p = network[j];
       dist = g - p[1]; /* inx key - reverse dif */
       if (dist >= bestd)
         j = -1; /* stop iter */
       else {
         j--;
         if (dist < 0)
           dist = -dist;
         a = p[0] - b;
         if (a < 0)
           a = -a;
         dist += a;
         if (dist < bestd) {
           a = p[2] - r;
           if (a < 0)
             a = -a;
           dist += a;
           if (dist < bestd) {
             bestd = dist;
             best = p[3];
           }
         }
       }
     }
   }
   return (best);
 }
 public byte[] process() {
   learn();
   unbiasnet();
   inxbuild();
   return colorMap();
 }
 /*
  * Unbias network to give byte values 0..255 and record position i to prepare
  * for sort
  * -----------------------------------------------------------------------------------
  */
 public void unbiasnet() {
   int i, j;
   for (i = 0; i < netsize; i++) {
     network[i][0] >>= netbiasshift;
     network[i][1] >>= netbiasshift;
     network[i][2] >>= netbiasshift;
     network[i][3] = i; /* record colour no */
   }
 }
 /*
  * Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in
  * radpower[|i-j|]
  * ---------------------------------------------------------------------------------
  */
 protected void alterneigh(int rad, int i, int b, int g, int r) {
   int j, k, lo, hi, a, m;
   int[] p;
   lo = i - rad;
   if (lo < -1)
     lo = -1;
   hi = i + rad;
   if (hi > netsize)
     hi = netsize;
   j = i + 1;
   k = i - 1;
   m = 1;
   while ((j < hi) || (k > lo)) {
     a = radpower[m++];
     if (j < hi) {
       p = network[j++];
       try {
         p[0] -= (a * (p[0] - b)) / alpharadbias;
         p[1] -= (a * (p[1] - g)) / alpharadbias;
         p[2] -= (a * (p[2] - r)) / alpharadbias;
       } catch (Exception e) {
       } // prevents 1.3 miscompilation
     }
     if (k > lo) {
       p = network[k--];
       try {
         p[0] -= (a * (p[0] - b)) / alpharadbias;
         p[1] -= (a * (p[1] - g)) / alpharadbias;
         p[2] -= (a * (p[2] - r)) / alpharadbias;
       } catch (Exception e) {
       }
     }
   }
 }
 /*
  * Move neuron i towards biased (b,g,r) by factor alpha
  * ----------------------------------------------------
  */
 protected void altersingle(int alpha, int i, int b, int g, int r) {
   /* alter hit neuron */
   int[] n = network[i];
   n[0] -= (alpha * (n[0] - b)) / initalpha;
   n[1] -= (alpha * (n[1] - g)) / initalpha;
   n[2] -= (alpha * (n[2] - r)) / initalpha;
 }
 /*
  * Search for biased BGR values ----------------------------
  */
 protected int contest(int b, int g, int r) {
   /* finds closest neuron (min dist) and updates freq */
   /* finds best neuron (min dist-bias) and returns position */
   /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */
   /* bias[i] = gamma*((1/netsize)-freq[i]) */
   int i, dist, a, biasdist, betafreq;
   int bestpos, bestbiaspos, bestd, bestbiasd;
   int[] n;
   bestd = ~(((int) 1) << 31);
   bestbiasd = bestd;
   bestpos = -1;
   bestbiaspos = bestpos;
   for (i = 0; i < netsize; i++) {
     n = network[i];
     dist = n[0] - b;
     if (dist < 0)
       dist = -dist;
     a = n[1] - g;
     if (a < 0)
       a = -a;
     dist += a;
     a = n[2] - r;
     if (a < 0)
       a = -a;
     dist += a;
     if (dist < bestd) {
       bestd = dist;
       bestpos = i;
     }
     biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift));
     if (biasdist < bestbiasd) {
       bestbiasd = biasdist;
       bestbiaspos = i;
     }
     betafreq = (freq[i] >> betashift);
     freq[i] -= betafreq;
     bias[i] += (betafreq << gammashift);
   }
   freq[bestpos] += beta;
   bias[bestpos] -= betagamma;
   return (bestbiaspos);
 }

} // ============================================================================== // Adapted from Jef Poskanzer"s Java port by way of J. M. G. Elliott. // K Weiner 12/00 class LZWEncoder {

 private static final int EOF = -1;
 private int imgW, imgH;
 private byte[] pixAry;
 private int initCodeSize;
 private int remaining;
 private int curPixel;
 // GIFCOMPR.C - GIF Image compression routines
 //
 // Lempel-Ziv compression based on "compress". GIF modifications by
 // David Rowley (mgardi@watdcsu.waterloo.edu)
 // General DEFINEs
 static final int BITS = 12;
 static final int HSIZE = 5003; // 80% occupancy
 // GIF Image compression - modified "compress"
 //
 // Based on: compress.c - File compression ala IEEE Computer, June 1984.
 //
 // By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
 // Jim McKie (decvax!mcvax!jim)
 // Steve Davies (decvax!vax135!petsd!peora!srd)
 // Ken Turkowski (decvax!decwrl!turtlevax!ken)
 // James A. Woods (decvax!ihnp4!ames!jaw)
 // Joe Orost (decvax!vax135!petsd!joe)
 int n_bits; // number of bits/code
 int maxbits = BITS; // user settable max # bits/code
 int maxcode; // maximum code, given n_bits
 int maxmaxcode = 1 << BITS; // should NEVER generate this code
 int[] htab = new int[HSIZE];
 int[] codetab = new int[HSIZE];
 int hsize = HSIZE; // for dynamic table sizing
 int free_ent = 0; // first unused entry
 // block compression parameters -- after all codes are used up,
 // and compression rate changes, start over.
 boolean clear_flg = false;
 // Algorithm: use open addressing double hashing (no chaining) on the
 // prefix code / next character combination. We do a variant of Knuth"s
 // algorithm D (vol. 3, sec. 6.4) along with G. Knott"s relatively-prime
 // secondary probe. Here, the modular division first probe is gives way
 // to a faster exclusive-or manipulation. Also do block compression with
 // an adaptive reset, whereby the code table is cleared when the compression
 // ratio decreases, but after the table fills. The variable-length output
 // codes are re-sized at this point, and a special CLEAR code is generated
 // for the decompressor. Late addition: construct the table according to
 // file size for noticeable speed improvement on small files. Please direct
 // questions about this implementation to ames!jaw.
 int g_init_bits;
 int ClearCode;
 int EOFCode;
 // output
 //
 // Output the given code.
 // Inputs:
 // code: A n_bits-bit integer. If == -1, then EOF. This assumes
 // that n_bits =< wordsize - 1.
 // Outputs:
 // Outputs code to the file.
 // Assumptions:
 // Chars are 8 bits long.
 // Algorithm:
 // Maintain a BITS character long buffer (so that 8 codes will
 // fit in it exactly). Use the VAX insv instruction to insert each
 // code in turn. When the buffer fills up empty it and start over.
 int cur_accum = 0;
 int cur_bits = 0;
 int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF,
     0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
 // Number of characters so far in this "packet"
 int a_count;
 // Define the storage for the packet accumulator
 byte[] accum = new byte[256];
 // ----------------------------------------------------------------------------
 LZWEncoder(int width, int height, byte[] pixels, int color_depth) {
   imgW = width;
   imgH = height;
   pixAry = pixels;
   initCodeSize = Math.max(2, color_depth);
 }
 // Add a character to the end of the current packet, and if it is 254
 // characters, flush the packet to disk.
 void char_out(byte c, OutputStream outs) throws IOException {
   accum[a_count++] = c;
   if (a_count >= 254)
     flush_char(outs);
 }
 // Clear out the hash table
 // table clear for block compress
 void cl_block(OutputStream outs) throws IOException {
   cl_hash(hsize);
   free_ent = ClearCode + 2;
   clear_flg = true;
   output(ClearCode, outs);
 }
 // reset code table
 void cl_hash(int hsize) {
   for (int i = 0; i < hsize; ++i)
     htab[i] = -1;
 }
 void compress(int init_bits, OutputStream outs) throws IOException {
   int fcode;
   int i /* = 0 */;
   int c;
   int ent;
   int disp;
   int hsize_reg;
   int hshift;
   // Set up the globals: g_init_bits - initial number of bits
   g_init_bits = init_bits;
   // Set up the necessary values
   clear_flg = false;
   n_bits = g_init_bits;
   maxcode = MAXCODE(n_bits);
   ClearCode = 1 << (init_bits - 1);
   EOFCode = ClearCode + 1;
   free_ent = ClearCode + 2;
   a_count = 0; // clear packet
   ent = nextPixel();
   hshift = 0;
   for (fcode = hsize; fcode < 65536; fcode *= 2)
     ++hshift;
   hshift = 8 - hshift; // set hash code range bound
   hsize_reg = hsize;
   cl_hash(hsize_reg); // clear hash table
   output(ClearCode, outs);
   outer_loop: while ((c = nextPixel()) != EOF) {
     fcode = (c << maxbits) + ent;
     i = (c << hshift) ^ ent; // xor hashing
     if (htab[i] == fcode) {
       ent = codetab[i];
       continue;
     } else if (htab[i] >= 0) // non-empty slot
     {
       disp = hsize_reg - i; // secondary hash (after G. Knott)
       if (i == 0)
         disp = 1;
       do {
         if ((i -= disp) < 0)
           i += hsize_reg;
         if (htab[i] == fcode) {
           ent = codetab[i];
           continue outer_loop;
         }
       } while (htab[i] >= 0);
     }
     output(ent, outs);
     ent = c;
     if (free_ent < maxmaxcode) {
       codetab[i] = free_ent++; // code -> hashtable
       htab[i] = fcode;
     } else
       cl_block(outs);
   }
   // Put out the final code.
   output(ent, outs);
   output(EOFCode, outs);
 }
 // ----------------------------------------------------------------------------
 void encode(OutputStream os) throws IOException {
   os.write(initCodeSize); // write "initial code size" byte
   remaining = imgW * imgH; // reset navigation variables
   curPixel = 0;
   compress(initCodeSize + 1, os); // compress and write the pixel data
   os.write(0); // write block terminator
 }
 // Flush the packet to disk, and reset the accumulator
 void flush_char(OutputStream outs) throws IOException {
   if (a_count > 0) {
     outs.write(a_count);
     outs.write(accum, 0, a_count);
     a_count = 0;
   }
 }
 final int MAXCODE(int n_bits) {
   return (1 << n_bits) - 1;
 }
 // ----------------------------------------------------------------------------
 // Return the next pixel from the image
 // ----------------------------------------------------------------------------
 private int nextPixel() {
   if (remaining == 0)
     return EOF;
   --remaining;
   byte pix = pixAry[curPixel++];
   return pix & 0xff;
 }
 void output(int code, OutputStream outs) throws IOException {
   cur_accum &= masks[cur_bits];
   if (cur_bits > 0)
     cur_accum |= (code << cur_bits);
   else
     cur_accum = code;
   cur_bits += n_bits;
   while (cur_bits >= 8) {
     char_out((byte) (cur_accum & 0xff), outs);
     cur_accum >>= 8;
     cur_bits -= 8;
   }
   // If the next entry is going to be too big for the code size,
   // then increase it, if possible.
   if (free_ent > maxcode || clear_flg) {
     if (clear_flg) {
       maxcode = MAXCODE(n_bits = g_init_bits);
       clear_flg = false;
     } else {
       ++n_bits;
       if (n_bits == maxbits)
         maxcode = maxmaxcode;
       else
         maxcode = MAXCODE(n_bits);
     }
   }
   if (code == EOFCode) {
     // At EOF, write the rest of the buffer.
     while (cur_bits > 0) {
       char_out((byte) (cur_accum & 0xff), outs);
       cur_accum >>= 8;
       cur_bits -= 8;
     }
     flush_char(outs);
   }
 }

}



 </source>   



AnimatedGifEncoder - Encodes a GIF file consisting of one or more frames

<source lang="java"> import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; /**

* Class AnimatedGifEncoder - Encodes a GIF file consisting of one or more
* frames.
* 
*
 *  Example:
 *     AnimatedGifEncoder e = new AnimatedGifEncoder();
 *     e.start(outputFileName);
 *     e.setDelay(1000);   // 1 frame per sec
 *     e.addFrame(image1);
 *     e.addFrame(image2);
 *     e.finish();
 * 
* 
* No copyright asserted on the source code of this class. May be used for any
* purpose, however, refer to the Unisys LZW patent for restrictions on use of
* the associated LZWEncoder class. Please forward any corrections to
* kweiner@fmsware.ru.
* 
* @author Kevin Weiner, FM Software
* @version 1.03 November 2003
* 
*/

public class AnimatedGifEncoder {

 protected int width; // image size
 protected int height;
 protected Color transparent = null; // transparent color if given
 protected int transIndex; // transparent index in color table
 protected int repeat = -1; // no repeat
 protected int delay = 0; // frame delay (hundredths)
 protected boolean started = false; // ready to output frames
 protected OutputStream out;
 protected BufferedImage image; // current frame
 protected byte[] pixels; // BGR byte array from frame
 protected byte[] indexedPixels; // converted frame indexed to palette
 protected int colorDepth; // number of bit planes
 protected byte[] colorTab; // RGB palette
 protected boolean[] usedEntry = new boolean[256]; // active palette entries
 protected int palSize = 7; // color table size (bits-1)
 protected int dispose = -1; // disposal code (-1 = use default)
 protected boolean closeStream = false; // close stream when finished
 protected boolean firstFrame = true;
 protected boolean sizeSet = false; // if false, get size from first frame
 protected int sample = 10; // default sample interval for quantizer
 /**
  * Sets the delay time between each frame, or changes it for subsequent frames
  * (applies to last frame added).
  * 
  * @param ms
  *          int delay time in milliseconds
  */
 public void setDelay(int ms) {
   delay = Math.round(ms / 10.0f);
 }
 /**
  * Sets the GIF frame disposal code for the last added frame and any
  * subsequent frames. Default is 0 if no transparent color has been set,
  * otherwise 2.
  * 
  * @param code
  *          int disposal code.
  */
 public void setDispose(int code) {
   if (code >= 0) {
     dispose = code;
   }
 }
 /**
  * Sets the number of times the set of GIF frames should be played. Default is
  * 1; 0 means play indefinitely. Must be invoked before the first image is
  * added.
  * 
  * @param iter
  *          int number of iterations.
  * @return
  */
 public void setRepeat(int iter) {
   if (iter >= 0) {
     repeat = iter;
   }
 }
 /**
  * Sets the transparent color for the last added frame and any subsequent
  * frames. Since all colors are subject to modification in the quantization
  * process, the color in the final palette for each frame closest to the given
  * color becomes the transparent color for that frame. May be set to null to
  * indicate no transparent color.
  * 
  * @param c
  *          Color to be treated as transparent on display.
  */
 public void setTransparent(Color c) {
   transparent = c;
 }
 /**
  * Adds next GIF frame. The frame is not written immediately, but is actually
  * deferred until the next frame is received so that timing data can be
  * inserted. Invoking finish() flushes all frames. If
  * setSize was not invoked, the size of the first image is used
  * for all subsequent frames.
  * 
  * @param im
  *          BufferedImage containing frame to write.
  * @return true if successful.
  */
 public boolean addFrame(BufferedImage im) {
   if ((im == null) || !started) {
     return false;
   }
   boolean ok = true;
   try {
     if (!sizeSet) {
       // use first frame"s size
       setSize(im.getWidth(), im.getHeight());
     }
     image = im;
     getImagePixels(); // convert to correct format if necessary
     analyzePixels(); // build color table & map pixels
     if (firstFrame) {
       writeLSD(); // logical screen descriptior
       writePalette(); // global color table
       if (repeat >= 0) {
         // use NS app extension to indicate reps
         writeNetscapeExt();
       }
     }
     writeGraphicCtrlExt(); // write graphic control extension
     writeImageDesc(); // image descriptor
     if (!firstFrame) {
       writePalette(); // local color table
     }
     writePixels(); // encode and write pixel data
     firstFrame = false;
   } catch (IOException e) {
     ok = false;
   }
   return ok;
 }
 /**
  * Flushes any pending data and closes output file. If writing to an
  * OutputStream, the stream is not closed.
  */
 public boolean finish() {
   if (!started)
     return false;
   boolean ok = true;
   started = false;
   try {
     out.write(0x3b); // gif trailer
     out.flush();
     if (closeStream) {
       out.close();
     }
   } catch (IOException e) {
     ok = false;
   }
   // reset for subsequent use
   transIndex = 0;
   out = null;
   image = null;
   pixels = null;
   indexedPixels = null;
   colorTab = null;
   closeStream = false;
   firstFrame = true;
   return ok;
 }
 /**
  * Sets frame rate in frames per second. Equivalent to
  * setDelay(1000/fps).
  * 
  * @param fps
  *          float frame rate (frames per second)
  */
 public void setFrameRate(float fps) {
   if (fps != 0f) {
     delay = Math.round(100f / fps);
   }
 }
 /**
  * Sets quality of color quantization (conversion of images to the maximum 256
  * colors allowed by the GIF specification). Lower values (minimum = 1)
  * produce better colors, but slow processing significantly. 10 is the
  * default, and produces good color mapping at reasonable speeds. Values
  * greater than 20 do not yield significant improvements in speed.
  * 
  * @param quality
  *          int greater than 0.
  * @return
  */
 public void setQuality(int quality) {
   if (quality < 1)
     quality = 1;
   sample = quality;
 }
 /**
  * Sets the GIF frame size. The default size is the size of the first frame
  * added if this method is not invoked.
  * 
  * @param w
  *          int frame width.
  * @param h
  *          int frame width.
  */
 public void setSize(int w, int h) {
   if (started && !firstFrame)
     return;
   width = w;
   height = h;
   if (width < 1)
     width = 320;
   if (height < 1)
     height = 240;
   sizeSet = true;
 }
 /**
  * Initiates GIF file creation on the given stream. The stream is not closed
  * automatically.
  * 
  * @param os
  *          OutputStream on which GIF images are written.
  * @return false if initial write failed.
  */
 public boolean start(OutputStream os) {
   if (os == null)
     return false;
   boolean ok = true;
   closeStream = false;
   out = os;
   try {
     writeString("GIF89a"); // header
   } catch (IOException e) {
     ok = false;
   }
   return started = ok;
 }
 /**
  * Initiates writing of a GIF file with the specified name.
  * 
  * @param file
  *          String containing output file name.
  * @return false if open or initial write failed.
  */
 public boolean start(String file) {
   boolean ok = true;
   try {
     out = new BufferedOutputStream(new FileOutputStream(file));
     ok = start(out);
     closeStream = true;
   } catch (IOException e) {
     ok = false;
   }
   return started = ok;
 }
 /**
  * Analyzes image colors and creates color map.
  */
 protected void analyzePixels() {
   int len = pixels.length;
   int nPix = len / 3;
   indexedPixels = new byte[nPix];
   NeuQuant nq = new NeuQuant(pixels, len, sample);
   // initialize quantizer
   colorTab = nq.process(); // create reduced palette
   // convert map from BGR to RGB
   for (int i = 0; i < colorTab.length; i += 3) {
     byte temp = colorTab[i];
     colorTab[i] = colorTab[i + 2];
     colorTab[i + 2] = temp;
     usedEntry[i / 3] = false;
   }
   // map image pixels to new palette
   int k = 0;
   for (int i = 0; i < nPix; i++) {
     int index = nq.map(pixels[k++] & 0xff, pixels[k++] & 0xff, pixels[k++] & 0xff);
     usedEntry[index] = true;
     indexedPixels[i] = (byte) index;
   }
   pixels = null;
   colorDepth = 8;
   palSize = 7;
   // get closest match to transparent color if specified
   if (transparent != null) {
     transIndex = findClosest(transparent);
   }
 }
 /**
  * Returns index of palette color closest to c
  * 
  */
 protected int findClosest(Color c) {
   if (colorTab == null)
     return -1;
   int r = c.getRed();
   int g = c.getGreen();
   int b = c.getBlue();
   int minpos = 0;
   int dmin = 256 * 256 * 256;
   int len = colorTab.length;
   for (int i = 0; i < len;) {
     int dr = r - (colorTab[i++] & 0xff);
     int dg = g - (colorTab[i++] & 0xff);
     int db = b - (colorTab[i] & 0xff);
     int d = dr * dr + dg * dg + db * db;
     int index = i / 3;
     if (usedEntry[index] && (d < dmin)) {
       dmin = d;
       minpos = index;
     }
     i++;
   }
   return minpos;
 }
 /**
  * Extracts image pixels into byte array "pixels"
  */
 protected void getImagePixels() {
   int w = image.getWidth();
   int h = image.getHeight();
   int type = image.getType();
   if ((w != width) || (h != height) || (type != BufferedImage.TYPE_3BYTE_BGR)) {
     // create new image with right size/format
     BufferedImage temp = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
     Graphics2D g = temp.createGraphics();
     g.drawImage(image, 0, 0, null);
     image = temp;
   }
   pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
 }
 /**
  * Writes Graphic Control Extension
  */
 protected void writeGraphicCtrlExt() throws IOException {
   out.write(0x21); // extension introducer
   out.write(0xf9); // GCE label
   out.write(4); // data block size
   int transp, disp;
   if (transparent == null) {
     transp = 0;
     disp = 0; // dispose = no action
   } else {
     transp = 1;
     disp = 2; // force clear if using transparent color
   }
   if (dispose >= 0) {
     disp = dispose & 7; // user override
   }
   disp <<= 2;
   // packed fields
   out.write(0 | // 1:3 reserved
       disp | // 4:6 disposal
       0 | // 7 user input - 0 = none
       transp); // 8 transparency flag
   writeShort(delay); // delay x 1/100 sec
   out.write(transIndex); // transparent color index
   out.write(0); // block terminator
 }
 /**
  * Writes Image Descriptor
  */
 protected void writeImageDesc() throws IOException {
   out.write(0x2c); // image separator
   writeShort(0); // image position x,y = 0,0
   writeShort(0);
   writeShort(width); // image size
   writeShort(height);
   // packed fields
   if (firstFrame) {
     // no LCT - GCT is used for first (or only) frame
     out.write(0);
   } else {
     // specify normal LCT
     out.write(0x80 | // 1 local color table 1=yes
         0 | // 2 interlace - 0=no
         0 | // 3 sorted - 0=no
         0 | // 4-5 reserved
         palSize); // 6-8 size of color table
   }
 }
 /**
  * Writes Logical Screen Descriptor
  */
 protected void writeLSD() throws IOException {
   // logical screen size
   writeShort(width);
   writeShort(height);
   // packed fields
   out.write((0x80 | // 1 : global color table flag = 1 (gct used)
       0x70 | // 2-4 : color resolution = 7
       0x00 | // 5 : gct sort flag = 0
       palSize)); // 6-8 : gct size
   out.write(0); // background color index
   out.write(0); // pixel aspect ratio - assume 1:1
 }
 /**
  * Writes Netscape application extension to define repeat count.
  */
 protected void writeNetscapeExt() throws IOException {
   out.write(0x21); // extension introducer
   out.write(0xff); // app extension label
   out.write(11); // block size
   writeString("NETSCAPE" + "2.0"); // app id + auth code
   out.write(3); // sub-block size
   out.write(1); // loop sub-block id
   writeShort(repeat); // loop count (extra iterations, 0=repeat forever)
   out.write(0); // block terminator
 }
 /**
  * Writes color table
  */
 protected void writePalette() throws IOException {
   out.write(colorTab, 0, colorTab.length);
   int n = (3 * 256) - colorTab.length;
   for (int i = 0; i < n; i++) {
     out.write(0);
   }
 }
 /**
  * Encodes and writes pixel data
  */
 protected void writePixels() throws IOException {
   LZWEncoder encoder = new LZWEncoder(width, height, indexedPixels, colorDepth);
   encoder.encode(out);
 }
 /**
  * Write 16-bit value to output stream, LSB first
  */
 protected void writeShort(int value) throws IOException {
   out.write(value & 0xff);
   out.write((value >> 8) & 0xff);
 }
 /**
  * Writes string to output stream
  */
 protected void writeString(String s) throws IOException {
   for (int i = 0; i < s.length(); i++) {
     out.write((byte) s.charAt(i));
   }
 }

} // // Adapted from Jef Poskanzer"s Java port by way of J. M. G. Elliott. // K Weiner 12/00 class LZWEncoder {

 private static final int EOF = -1;
 private int imgW, imgH;
 private byte[] pixAry;
 private int initCodeSize;
 private int remaining;
 private int curPixel;
 // GIFCOMPR.C - GIF Image compression routines
 //
 // Lempel-Ziv compression based on "compress". GIF modifications by
 // David Rowley (mgardi@watdcsu.waterloo.edu)
 // General DEFINEs
 static final int BITS = 12;
 static final int HSIZE = 5003; // 80% occupancy
 // GIF Image compression - modified "compress"
 //
 // Based on: compress.c - File compression ala IEEE Computer, June 1984.
 //
 // By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
 // Jim McKie (decvax!mcvax!jim)
 // Steve Davies (decvax!vax135!petsd!peora!srd)
 // Ken Turkowski (decvax!decwrl!turtlevax!ken)
 // James A. Woods (decvax!ihnp4!ames!jaw)
 // Joe Orost (decvax!vax135!petsd!joe)
 int n_bits; // number of bits/code
 int maxbits = BITS; // user settable max # bits/code
 int maxcode; // maximum code, given n_bits
 int maxmaxcode = 1 << BITS; // should NEVER generate this code
 int[] htab = new int[HSIZE];
 int[] codetab = new int[HSIZE];
 int hsize = HSIZE; // for dynamic table sizing
 int free_ent = 0; // first unused entry
 // block compression parameters -- after all codes are used up,
 // and compression rate changes, start over.
 boolean clear_flg = false;
 // Algorithm: use open addressing double hashing (no chaining) on the
 // prefix code / next character combination. We do a variant of Knuth"s
 // algorithm D (vol. 3, sec. 6.4) along with G. Knott"s relatively-prime
 // secondary probe. Here, the modular division first probe is gives way
 // to a faster exclusive-or manipulation. Also do block compression with
 // an adaptive reset, whereby the code table is cleared when the compression
 // ratio decreases, but after the table fills. The variable-length output
 // codes are re-sized at this point, and a special CLEAR code is generated
 // for the decompressor. Late addition: construct the table according to
 // file size for noticeable speed improvement on small files. Please direct
 // questions about this implementation to ames!jaw.
 int g_init_bits;
 int ClearCode;
 int EOFCode;
 // output
 //
 // Output the given code.
 // Inputs:
 // code: A n_bits-bit integer. If == -1, then EOF. This assumes
 // that n_bits =< wordsize - 1.
 // Outputs:
 // Outputs code to the file.
 // Assumptions:
 // Chars are 8 bits long.
 // Algorithm:
 // Maintain a BITS character long buffer (so that 8 codes will
 // fit in it exactly). Use the VAX insv instruction to insert each
 // code in turn. When the buffer fills up empty it and start over.
 int cur_accum = 0;
 int cur_bits = 0;
 int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF,
     0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
 // Number of characters so far in this "packet"
 int a_count;
 // Define the storage for the packet accumulator
 byte[] accum = new byte[256];
 // ----------------------------------------------------------------------------
 LZWEncoder(int width, int height, byte[] pixels, int color_depth) {
   imgW = width;
   imgH = height;
   pixAry = pixels;
   initCodeSize = Math.max(2, color_depth);
 }
 // Add a character to the end of the current packet, and if it is 254
 // characters, flush the packet to disk.
 void char_out(byte c, OutputStream outs) throws IOException {
   accum[a_count++] = c;
   if (a_count >= 254)
     flush_char(outs);
 }
 // Clear out the hash table
 // table clear for block compress
 void cl_block(OutputStream outs) throws IOException {
   cl_hash(hsize);
   free_ent = ClearCode + 2;
   clear_flg = true;
   output(ClearCode, outs);
 }
 // reset code table
 void cl_hash(int hsize) {
   for (int i = 0; i < hsize; ++i)
     htab[i] = -1;
 }
 void compress(int init_bits, OutputStream outs) throws IOException {
   int fcode;
   int i /* = 0 */;
   int c;
   int ent;
   int disp;
   int hsize_reg;
   int hshift;
   // Set up the globals: g_init_bits - initial number of bits
   g_init_bits = init_bits;
   // Set up the necessary values
   clear_flg = false;
   n_bits = g_init_bits;
   maxcode = MAXCODE(n_bits);
   ClearCode = 1 << (init_bits - 1);
   EOFCode = ClearCode + 1;
   free_ent = ClearCode + 2;
   a_count = 0; // clear packet
   ent = nextPixel();
   hshift = 0;
   for (fcode = hsize; fcode < 65536; fcode *= 2)
     ++hshift;
   hshift = 8 - hshift; // set hash code range bound
   hsize_reg = hsize;
   cl_hash(hsize_reg); // clear hash table
   output(ClearCode, outs);
   outer_loop: while ((c = nextPixel()) != EOF) {
     fcode = (c << maxbits) + ent;
     i = (c << hshift) ^ ent; // xor hashing
     if (htab[i] == fcode) {
       ent = codetab[i];
       continue;
     } else if (htab[i] >= 0) // non-empty slot
     {
       disp = hsize_reg - i; // secondary hash (after G. Knott)
       if (i == 0)
         disp = 1;
       do {
         if ((i -= disp) < 0)
           i += hsize_reg;
         if (htab[i] == fcode) {
           ent = codetab[i];
           continue outer_loop;
         }
       } while (htab[i] >= 0);
     }
     output(ent, outs);
     ent = c;
     if (free_ent < maxmaxcode) {
       codetab[i] = free_ent++; // code -> hashtable
       htab[i] = fcode;
     } else
       cl_block(outs);
   }
   // Put out the final code.
   output(ent, outs);
   output(EOFCode, outs);
 }
 // ----------------------------------------------------------------------------
 void encode(OutputStream os) throws IOException {
   os.write(initCodeSize); // write "initial code size" byte
   remaining = imgW * imgH; // reset navigation variables
   curPixel = 0;
   compress(initCodeSize + 1, os); // compress and write the pixel data
   os.write(0); // write block terminator
 }
 // Flush the packet to disk, and reset the accumulator
 void flush_char(OutputStream outs) throws IOException {
   if (a_count > 0) {
     outs.write(a_count);
     outs.write(accum, 0, a_count);
     a_count = 0;
   }
 }
 final int MAXCODE(int n_bits) {
   return (1 << n_bits) - 1;
 }
 // ----------------------------------------------------------------------------
 // Return the next pixel from the image
 // ----------------------------------------------------------------------------
 private int nextPixel() {
   if (remaining == 0)
     return EOF;
   --remaining;
   byte pix = pixAry[curPixel++];
   return pix & 0xff;
 }
 void output(int code, OutputStream outs) throws IOException {
   cur_accum &= masks[cur_bits];
   if (cur_bits > 0)
     cur_accum |= (code << cur_bits);
   else
     cur_accum = code;
   cur_bits += n_bits;
   while (cur_bits >= 8) {
     char_out((byte) (cur_accum & 0xff), outs);
     cur_accum >>= 8;
     cur_bits -= 8;
   }
   // If the next entry is going to be too big for the code size,
   // then increase it, if possible.
   if (free_ent > maxcode || clear_flg) {
     if (clear_flg) {
       maxcode = MAXCODE(n_bits = g_init_bits);
       clear_flg = false;
     } else {
       ++n_bits;
       if (n_bits == maxbits)
         maxcode = maxmaxcode;
       else
         maxcode = MAXCODE(n_bits);
     }
   }
   if (code == EOFCode) {
     // At EOF, write the rest of the buffer.
     while (cur_bits > 0) {
       char_out((byte) (cur_accum & 0xff), outs);
       cur_accum >>= 8;
       cur_bits -= 8;
     }
     flush_char(outs);
   }
 }

} /*

* NeuQuant Neural-Net Quantization Algorithm
* ------------------------------------------
* 
* Copyright (c) 1994 Anthony Dekker
* 
* NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
* "Kohonen neural networks for optimal colour quantization" in "Network:
* Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
* the algorithm.
* 
* Any party obtaining a copy of these files from the author, directly or
* indirectly, is granted, free of charge, a full and unrestricted irrevocable,
* world-wide, paid up, royalty-free, nonexclusive right and license to deal in
* this software and documentation files (the "Software"), including without
* limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons who
* receive copies from any such party to do so, with the only requirement being
* that this copyright notice remain intact.
*/

// Ported to Java 12/00 K Weiner class NeuQuant {

 protected static final int netsize = 256; /* number of colours used */
 /* four primes near 500 - assume no image has a length so large */
 /* that it is divisible by all four primes */
 protected static final int prime1 = 499;
 protected static final int prime2 = 491;
 protected static final int prime3 = 487;
 protected static final int prime4 = 503;
 protected static final int minpicturebytes = (3 * prime4);
 /* minimum size for input image */
 /*
  * Program Skeleton ---------------- [select samplefac in range 1..30] [read
  * image from input file] pic = (unsigned char*) malloc(3*width*height);
  * initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output
  * image header, using writecolourmap(f)] inxbuild(); write output image using
  * inxsearch(b,g,r)
  */
 /*
  * Network Definitions -------------------
  */
 protected static final int maxnetpos = (netsize - 1);
 protected static final int netbiasshift = 4; /* bias for colour values */
 protected static final int ncycles = 100; /* no. of learning cycles */
 /* defs for freq and bias */
 protected static final int intbiasshift = 16; /* bias for fractions */
 protected static final int intbias = (((int) 1) << intbiasshift);
 protected static final int gammashift = 10; /* gamma = 1024 */
 protected static final int gamma = (((int) 1) << gammashift);
 protected static final int betashift = 10;
 protected static final int beta = (intbias >> betashift); /* beta = 1/1024 */
 protected static final int betagamma = (intbias << (gammashift - betashift));
 /* defs for decreasing radius factor */
 protected static final int initrad = (netsize >> 3); /*
                                                        * for 256 cols, radius
                                                        * starts
                                                        */
 protected static final int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */
 protected static final int radiusbias = (((int) 1) << radiusbiasshift);
 protected static final int initradius = (initrad * radiusbias); /*
                                                                  * and
                                                                  * decreases
                                                                  * by a
                                                                  */
 protected static final int radiusdec = 30; /* factor of 1/30 each cycle */
 /* defs for decreasing alpha factor */
 protected static final int alphabiasshift = 10; /* alpha starts at 1.0 */
 protected static final int initalpha = (((int) 1) << alphabiasshift);
 protected int alphadec; /* biased by 10 bits */
 /* radbias and alpharadbias used for radpower calculation */
 protected static final int radbiasshift = 8;
 protected static final int radbias = (((int) 1) << radbiasshift);
 protected static final int alpharadbshift = (alphabiasshift + radbiasshift);
 protected static final int alpharadbias = (((int) 1) << alpharadbshift);
 /*
  * Types and Global Variables --------------------------
  */
 protected byte[] thepicture; /* the input image itself */
 protected int lengthcount; /* lengthcount = H*W*3 */
 protected int samplefac; /* sampling factor 1..30 */
 // typedef int pixel[4]; /* BGRc */
 protected int[][] network; /* the network itself - [netsize][4] */
 protected int[] netindex = new int[256];
 /* for network lookup - really 256 */
 protected int[] bias = new int[netsize];
 /* bias and freq arrays for learning */
 protected int[] freq = new int[netsize];
 protected int[] radpower = new int[initrad];
 /* radpower for precomputation */
 /*
  * Initialise network in range (0,0,0) to (255,255,255) and set parameters
  * -----------------------------------------------------------------------
  */
 public NeuQuant(byte[] thepic, int len, int sample) {
   int i;
   int[] p;
   thepicture = thepic;
   lengthcount = len;
   samplefac = sample;
   network = new int[netsize][];
   for (i = 0; i < netsize; i++) {
     network[i] = new int[4];
     p = network[i];
     p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize;
     freq[i] = intbias / netsize; /* 1/netsize */
     bias[i] = 0;
   }
 }
 public byte[] colorMap() {
   byte[] map = new byte[3 * netsize];
   int[] index = new int[netsize];
   for (int i = 0; i < netsize; i++)
     index[network[i][3]] = i;
   int k = 0;
   for (int i = 0; i < netsize; i++) {
     int j = index[i];
     map[k++] = (byte) (network[j][0]);
     map[k++] = (byte) (network[j][1]);
     map[k++] = (byte) (network[j][2]);
   }
   return map;
 }
 /*
  * Insertion sort of network and building of netindex[0..255] (to do after
  * unbias)
  * -------------------------------------------------------------------------------
  */
 public void inxbuild() {
   int i, j, smallpos, smallval;
   int[] p;
   int[] q;
   int previouscol, startpos;
   previouscol = 0;
   startpos = 0;
   for (i = 0; i < netsize; i++) {
     p = network[i];
     smallpos = i;
     smallval = p[1]; /* index on g */
     /* find smallest in i..netsize-1 */
     for (j = i + 1; j < netsize; j++) {
       q = network[j];
       if (q[1] < smallval) { /* index on g */
         smallpos = j;
         smallval = q[1]; /* index on g */
       }
     }
     q = network[smallpos];
     /* swap p (i) and q (smallpos) entries */
     if (i != smallpos) {
       j = q[0];
       q[0] = p[0];
       p[0] = j;
       j = q[1];
       q[1] = p[1];
       p[1] = j;
       j = q[2];
       q[2] = p[2];
       p[2] = j;
       j = q[3];
       q[3] = p[3];
       p[3] = j;
     }
     /* smallval entry is now in position i */
     if (smallval != previouscol) {
       netindex[previouscol] = (startpos + i) >> 1;
       for (j = previouscol + 1; j < smallval; j++)
         netindex[j] = i;
       previouscol = smallval;
       startpos = i;
     }
   }
   netindex[previouscol] = (startpos + maxnetpos) >> 1;
   for (j = previouscol + 1; j < 256; j++)
     netindex[j] = maxnetpos; /* really 256 */
 }
 /*
  * Main Learning Loop ------------------
  */
 public void learn() {
   int i, j, b, g, r;
   int radius, rad, alpha, step, delta, samplepixels;
   byte[] p;
   int pix, lim;
   if (lengthcount < minpicturebytes)
     samplefac = 1;
   alphadec = 30 + ((samplefac - 1) / 3);
   p = thepicture;
   pix = 0;
   lim = lengthcount;
   samplepixels = lengthcount / (3 * samplefac);
   delta = samplepixels / ncycles;
   alpha = initalpha;
   radius = initradius;
   rad = radius >> radiusbiasshift;
   if (rad <= 1)
     rad = 0;
   for (i = 0; i < rad; i++)
     radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad));
   // fprintf(stderr,"beginning 1D learning: initial radius=%d\n", rad);
   if (lengthcount < minpicturebytes)
     step = 3;
   else if ((lengthcount % prime1) != 0)
     step = 3 * prime1;
   else {
     if ((lengthcount % prime2) != 0)
       step = 3 * prime2;
     else {
       if ((lengthcount % prime3) != 0)
         step = 3 * prime3;
       else
         step = 3 * prime4;
     }
   }
   i = 0;
   while (i < samplepixels) {
     b = (p[pix + 0] & 0xff) << netbiasshift;
     g = (p[pix + 1] & 0xff) << netbiasshift;
     r = (p[pix + 2] & 0xff) << netbiasshift;
     j = contest(b, g, r);
     altersingle(alpha, j, b, g, r);
     if (rad != 0)
       alterneigh(rad, j, b, g, r); /* alter neighbours */
     pix += step;
     if (pix >= lim)
       pix -= lengthcount;
     i++;
     if (delta == 0)
       delta = 1;
     if (i % delta == 0) {
       alpha -= alpha / alphadec;
       radius -= radius / radiusdec;
       rad = radius >> radiusbiasshift;
       if (rad <= 1)
         rad = 0;
       for (j = 0; j < rad; j++)
         radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad));
     }
   }
   // fprintf(stderr,"finished 1D learning: final alpha=%f
   // !\n",((float)alpha)/initalpha);
 }
 /*
  * Search for BGR values 0..255 (after net is unbiased) and return colour
  * index
  * ----------------------------------------------------------------------------
  */
 public int map(int b, int g, int r) {
   int i, j, dist, a, bestd;
   int[] p;
   int best;
   bestd = 1000; /* biggest possible dist is 256*3 */
   best = -1;
   i = netindex[g]; /* index on g */
   j = i - 1; /* start at netindex[g] and work outwards */
   while ((i < netsize) || (j >= 0)) {
     if (i < netsize) {
       p = network[i];
       dist = p[1] - g; /* inx key */
       if (dist >= bestd)
         i = netsize; /* stop iter */
       else {
         i++;
         if (dist < 0)
           dist = -dist;
         a = p[0] - b;
         if (a < 0)
           a = -a;
         dist += a;
         if (dist < bestd) {
           a = p[2] - r;
           if (a < 0)
             a = -a;
           dist += a;
           if (dist < bestd) {
             bestd = dist;
             best = p[3];
           }
         }
       }
     }
     if (j >= 0) {
       p = network[j];
       dist = g - p[1]; /* inx key - reverse dif */
       if (dist >= bestd)
         j = -1; /* stop iter */
       else {
         j--;
         if (dist < 0)
           dist = -dist;
         a = p[0] - b;
         if (a < 0)
           a = -a;
         dist += a;
         if (dist < bestd) {
           a = p[2] - r;
           if (a < 0)
             a = -a;
           dist += a;
           if (dist < bestd) {
             bestd = dist;
             best = p[3];
           }
         }
       }
     }
   }
   return (best);
 }
 public byte[] process() {
   learn();
   unbiasnet();
   inxbuild();
   return colorMap();
 }
 /*
  * Unbias network to give byte values 0..255 and record position i to prepare
  * for sort
  * -----------------------------------------------------------------------------------
  */
 public void unbiasnet() {
   for (int i = 0; i < netsize; i++) {
     network[i][0] >>= netbiasshift;
     network[i][1] >>= netbiasshift;
     network[i][2] >>= netbiasshift;
     network[i][3] = i; /* record colour no */
   }
 }
 /*
  * Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in
  * radpower[|i-j|]
  * ---------------------------------------------------------------------------------
  */
 protected void alterneigh(int rad, int i, int b, int g, int r) {
   int j, k, lo, hi, a, m;
   int[] p;
   lo = i - rad;
   if (lo < -1)
     lo = -1;
   hi = i + rad;
   if (hi > netsize)
     hi = netsize;
   j = i + 1;
   k = i - 1;
   m = 1;
   while ((j < hi) || (k > lo)) {
     a = radpower[m++];
     if (j < hi) {
       p = network[j++];
       try {
         p[0] -= (a * (p[0] - b)) / alpharadbias;
         p[1] -= (a * (p[1] - g)) / alpharadbias;
         p[2] -= (a * (p[2] - r)) / alpharadbias;
       } catch (Exception e) {
       } // prevents 1.3 miscompilation
     }
     if (k > lo) {
       p = network[k--];
       try {
         p[0] -= (a * (p[0] - b)) / alpharadbias;
         p[1] -= (a * (p[1] - g)) / alpharadbias;
         p[2] -= (a * (p[2] - r)) / alpharadbias;
       } catch (Exception e) {
       }
     }
   }
 }
 /*
  * Move neuron i towards biased (b,g,r) by factor alpha
  * ----------------------------------------------------
  */
 protected void altersingle(int alpha, int i, int b, int g, int r) {
   /* alter hit neuron */
   int[] n = network[i];
   n[0] -= (alpha * (n[0] - b)) / initalpha;
   n[1] -= (alpha * (n[1] - g)) / initalpha;
   n[2] -= (alpha * (n[2] - r)) / initalpha;
 }
 /*
  * Search for biased BGR values ----------------------------
  */
 protected int contest(int b, int g, int r) {
   /* finds closest neuron (min dist) and updates freq */
   /* finds best neuron (min dist-bias) and returns position */
   /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */
   /* bias[i] = gamma*((1/netsize)-freq[i]) */
   int i, dist, a, biasdist, betafreq;
   int bestpos, bestbiaspos, bestd, bestbiasd;
   int[] n;
   bestd = ~(((int) 1) << 31);
   bestbiasd = bestd;
   bestpos = -1;
   bestbiaspos = bestpos;
   for (i = 0; i < netsize; i++) {
     n = network[i];
     dist = n[0] - b;
     if (dist < 0)
       dist = -dist;
     a = n[1] - g;
     if (a < 0)
       a = -a;
     dist += a;
     a = n[2] - r;
     if (a < 0)
       a = -a;
     dist += a;
     if (dist < bestd) {
       bestd = dist;
       bestpos = i;
     }
     biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift));
     if (biasdist < bestbiasd) {
       bestbiasd = biasdist;
       bestbiaspos = i;
     }
     betafreq = (freq[i] >> betashift);
     freq[i] -= betafreq;
     bias[i] += (betafreq << gammashift);
   }
   freq[bestpos] += beta;
   bias[bestpos] -= betagamma;
   return (bestbiaspos);
 }

}


 </source>   



Class for converting images to GIF files

<source lang="java"> /*

* (C) 2004 - Geotechnical Software Services
* 
* This code is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public 
* License as published by the Free Software Foundation; either 
* version 2.1 of the License, or (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public 
* License along with this program; if not, write to the Free 
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
* MA  02111-1307, USA.
*/

package no.geosoft.cc.io;

import java.io.*; import java.awt.*; import java.awt.image.*;

/**

* Class for converting images to GIF files.
*
*

* Contribution: *

    *
  • Sverre H. Huseby (gifsave.c on which this is based)
  • *
  • Adam Doppelt (Initial Java port)
  • *
  • Greg Faron (Initial java port)
  • *
* 
* @author 
*/   

public class GifEncoder {

 private short   imageWidth_, imageHeight_;
 private int     nColors_;
 private byte[]  pixels_ = null;
 private byte[]  colors_ = null;
 /**
  * Constructing a GIF encoder.
  *
  * @param image  The image to encode. The image must be
  *               completely loaded.
  * @throws AWTException  If memory is exhausted or image contains
  *                       more than 256 colors.
  */
 public GifEncoder (Image image)
   throws AWTException
 {
   imageWidth_  = (short) image.getWidth (null);
   imageHeight_ = (short) image.getHeight (null);
   int values[] = new int[imageWidth_ * imageHeight_];
   PixelGrabber grabber = new PixelGrabber (image, 0, 0,
                                            imageWidth_, imageHeight_,
                                            values, 0, imageWidth_);
   try {
     if (grabber.grabPixels() != true)
       throw new AWTException("Grabber returned false: " + grabber.status());
   }
   catch (InterruptedException exception) {
   }
   byte[][] r = new byte[imageWidth_][imageHeight_];
   byte[][] g = new byte[imageWidth_][imageHeight_];
   byte[][] b = new byte[imageWidth_][imageHeight_];
   int index = 0;
   for (int y = 0; y < imageHeight_; y ++) {
     for (int x = 0; x < imageWidth_; x ++, index ++) {
       r[x][y] = (byte) ((values[index] >> 16) & 0xFF);
       g[x][y] = (byte) ((values[index] >> 8)  & 0xFF);
       b[x][y] = (byte) ((values[index] >> 0)  & 0xFF);
     }
   }
   toIndexColor (r, g, b);
 }


 /**
  * Create a GIF encoder. r[i][j] refers to the pixel at
  * column i, row j.
  *
  * @param r  Red intensity values.
  * @param g  Green intensity values.
  * @param b  Blue intensity values.
  * @throws AWTException  If memory is exhausted or image contains
  *                       more than 256 colors.
  */
 public GifEncoder (byte[][] r, byte[][] g, byte[][] b)
   throws AWTException
 {
   imageWidth_  = (short) (r.length);
   imageHeight_ = (short) (r[0].length);
   toIndexColor(r, g, b);
 }


 /**
  * Write image to GIF file.
  * 
  * @param image  Image to write.
  * @param file   File to erite to.
  */
 public static void writeFile (Image image, File file)
   throws AWTException, IOException
 {
   GifEncoder gifEncoder = new GifEncoder (image);
   gifEncoder.write (new FileOutputStream (file));
 }
 
 
 /**
  * Write AWT/Swing component to GIF file.
  * 
  * @param image  Image to write.
  * @param file   File to erite to.
  */
 public static void writeFile (Component component, File file)
   throws AWTException, IOException
 {
   Image image  = component.createImage (component.getWidth(),
                                         component.getHeight());
   Graphics graphics = image.getGraphics();
   component.printAll (graphics);
   GifEncoder.writeFile (image, file);
 }
 
 
 
 /**
  * Writes the image out to a stream in GIF format.
  * This will be a single GIF87a image, non-interlaced, with no
  * background color.
  *
  * @param  stream       The stream to which to output.
  * @throws IOException  Thrown if a write operation fails.
  */
 public void write (OutputStream stream)
   throws IOException
 {
   writeString (stream, "GIF87a");
   writeScreenDescriptor (stream);
   stream.write (colors_, 0, colors_.length);
   writeImageDescriptor (stream, imageWidth_, imageHeight_, ",");
   byte codeSize = bitsNeeded (nColors_);
   if (codeSize == 1) codeSize++;
   stream.write (codeSize);
   writeLzwCompressed (stream, codeSize, pixels_);
   stream.write(0);
   writeImageDescriptor (stream, (short) 0, (short) 0, ";");
   stream.flush();
   stream.close();
 }


 /**
  * Converts rgb desrcription of image to colour
  * number description used by GIF.
  *
  * @param r  Red array of pixels.
  * @param g  Green array of pixels.
  * @param b  Blue array of pixels.
  * @throws   AWTException
  *           Thrown if too many different colours in image.
  */
 private void toIndexColor (byte[][] r, byte[][] g, byte[][] b)
   throws AWTException
 {
   pixels_ = new byte[imageWidth_ * imageHeight_];
   colors_ = new byte[256 * 3];
   int colornum = 0;
   for (int x = 0; x < imageWidth_; x++) {
     for (int y = 0; y < imageHeight_; y++ ) {
       int search;
       for (search = 0; search < colornum; search ++ ) {
         if (colors_[search * 3 + 0] == r[x][y] &&
             colors_[search * 3 + 1] == g[x][y] &&
             colors_[search * 3 + 2] == b[x][y]) {
           break;
         }
       }
       
       if (search > 255)
         throw new AWTException("Too many colors.");
       // Row major order y=row x=col
       pixels_[y * imageWidth_ + x] = (byte) search;
       
       if (search == colornum) {
         colors_[search * 3 + 0] = r[x][y]; // [col][row]
         colors_[search * 3 + 1] = g[x][y];
         colors_[search * 3 + 2] = b[x][y];
         colornum++;
       }
     }
   }
   
   nColors_ = 1 << bitsNeeded (colornum);
   byte copy[] = new byte[nColors_ * 3];
   System.arraycopy (colors_, 0, copy, 0, nColors_ * 3);
   colors_ = copy;
 }
 private byte bitsNeeded (int n)
 {
   if (n-- == 0) return 0;
   byte nBitsNeeded = 1;
   while ((n >>= 1) != 0)
     nBitsNeeded++;
   return nBitsNeeded;
 }
 
 private void writeWord (OutputStream stream, short w)
   throws IOException
 {
   stream.write (w & 0xFF);
   stream.write ((w >> 8) & 0xFF);
 }


 private void writeString (OutputStream stream, String string)
   throws IOException
 {
   for (int i = 0; i < string.length(); i ++ )
     stream.write ((byte) (string.charAt (i)));
 }
 private void writeScreenDescriptor (OutputStream stream)
   throws IOException
 {
   writeWord (stream, imageWidth_);
   writeWord (stream, imageHeight_);
   byte flag = 0;
   // Global color table size
   byte globalColorTableSize = (byte) (bitsNeeded (nColors_) - 1);
   flag |= globalColorTableSize & 7;
   // Global color table flag
   byte globalColorTableFlag = 1;
   flag |= (globalColorTableFlag & 1) << 7;
   // Sort flag
   byte sortFlag = 0;
   flag |= (sortFlag & 1) << 3;
   // Color resolution
   byte colorResolution = 7;
   flag |= (colorResolution & 7) << 4;    
   
   byte backgroundColorIndex = 0;
   byte pixelAspectRatio     = 0;
   stream.write (flag);
   stream.write (backgroundColorIndex);
   stream.write (pixelAspectRatio);
 }
 
 private void writeImageDescriptor (OutputStream stream,
                                    short width, short height, char separator)
   throws IOException
 {
   stream.write (separator);
   short leftPosition = 0;
   short topPosition  = 0;
   
   writeWord (stream, leftPosition);
   writeWord (stream, topPosition);
   writeWord (stream, width);
   writeWord (stream, height);
   byte flag = 0;
   
   // Local color table size
   byte localColorTableSize = 0;
   flag |= (localColorTableSize & 7);    
   // Reserved
   byte reserved = 0;
   flag |= (reserved & 3) << 3;    
   // Sort flag
   byte sortFlag = 0;
   flag |= (sortFlag & 1) << 5;    
   // Interlace flag
   byte interlaceFlag = 0;
   flag |= (interlaceFlag & 1) << 6;    
   // Local color table flag
   byte localColorTableFlag = 0;
   flag |= (localColorTableFlag & 1) << 7;    
   stream.write (flag);
 }


 private void writeLzwCompressed (OutputStream stream, int codeSize,
                                  byte toCompress[])
   throws IOException
 {
   byte c;
   short index;
   int clearcode, endofinfo, numbits, limit, errcode;
   short prefix = (short) 0xFFFF;
   BitFile bitFile = new BitFile (stream);
   LzwStringTable strings = new LzwStringTable();
   clearcode = 1 << codeSize;
   endofinfo = clearcode + 1;
   numbits = codeSize + 1;
   limit = (1 << numbits) - 1;
   strings.clearTable (codeSize);
   bitFile.writeBits(clearcode, numbits);
   for ( int loop = 0; loop < toCompress.length; loop ++ ) {
     c = toCompress[loop];
     if ( (index = strings.findCharString(prefix, c)) != -1 )
       prefix = index;
     else {
       bitFile.writeBits(prefix, numbits);
       if ( strings.addCharString(prefix, c) > limit ) {
         if ( ++ numbits > 12 ) {
           bitFile.writeBits(clearcode, numbits - 1);
           strings.clearTable (codeSize);
           numbits = codeSize + 1;
         }
         
         limit = (1 << numbits) - 1;
       }
       
       prefix = (short) ((short) c & 0xFF);
     }
   }
   
   if ( prefix != -1 )
     bitFile.writeBits(prefix, numbits);
   
   bitFile.writeBits(endofinfo, numbits);
   bitFile.flush();
 }
 
 /**
  * Used to compress the image by looking for repeating
  * elements.
  */
 private 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;
   private byte   strChr_[];
   private short  strNxt_[];
   private short  strHsh_[];
   private short  nStrings_; 


   LzwStringTable()
   {
     strChr_ = new byte[MAXSTR];
     strNxt_ = new short[MAXSTR];
     strHsh_ = new short[HASHSIZE];
   }
 
   int addCharString (short index, byte b)
   {
     int hshidx;
     if ( nStrings_ >= MAXSTR )
       return 0xFFFF;
     hshidx = hash (index, b);
     while ( strHsh_[hshidx] != HASH_FREE )
       hshidx = (hshidx + HASHSTEP) % HASHSIZE;
     strHsh_[hshidx] = nStrings_;
     strChr_[nStrings_] = b;
     strNxt_[nStrings_] = (index != HASH_FREE)?index:NEXT_FIRST;
     return nStrings_++;
   }
   short findCharString(short index, byte b)
   {
     int hshidx, nxtidx;
     if ( index == HASH_FREE )
       return b;
     hshidx = hash (index, b);
     while ( (nxtidx = strHsh_[hshidx]) != HASH_FREE ) {
       if ( strNxt_[nxtidx] == index && strChr_[nxtidx] == b )
         return(short) nxtidx;
       hshidx = (hshidx + HASHSTEP) % HASHSIZE;
     }
     return(short) 0xFFFF;
   }


   void clearTable (int codesize)
   {
     nStrings_ = 0;
     for ( int q = 0; q < HASHSIZE; q ++ )
       strHsh_[q] = HASH_FREE;
     int w = (1 << codesize) + RES_CODES;
     for ( int q = 0; q < w; q ++ )
       this.addCharString((short) 0xFFFF, (byte) q);
   }


   int hash (short index, byte lastbyte)
   {
     return ((int)((short) (lastbyte << 8) ^ index) & 0xFFFF) % HASHSIZE;
   }
 }
 private class BitFile
 {
   private OutputStream stream_ = null;
   private byte[]       buffer_;
   private int          streamIndex_, bitsLeft_;


   BitFile(OutputStream stream)
   {
     stream_      = stream;
     buffer_      = new byte[256];
     streamIndex_ = 0;
     bitsLeft_    = 8;
   }


   void flush()
     throws IOException
   {
     int nBytes = streamIndex_ + ((bitsLeft_ == 8) ? 0 : 1);
                                                 
     if (nBytes > 0) {
       stream_.write (nBytes);
       stream_.write (buffer_, 0, nBytes);
       buffer_[0]   = 0;
       streamIndex_ = 0;
       bitsLeft_    = 8;
     }
   }


   void writeBits (int bits, int nBits)
     throws IOException
   {
     int nBitsWritten = 0;
     int nBytes       = 255;
     do {
       if ((streamIndex_ == 254 && bitsLeft_ == 0) || streamIndex_ > 254) {
         stream_.write (nBytes);
         stream_.write (buffer_, 0, nBytes);
       
         buffer_[0]   = 0;
         streamIndex_ = 0;
         bitsLeft_    = 8;
       }
     
       if (nBits <= bitsLeft_) {
         buffer_[streamIndex_] |= (bits & ((1 << nBits) - 1)) << (8 - bitsLeft_);
                                                            
         nBitsWritten += nBits;
         bitsLeft_    -= nBits;
         nBits         = 0;
       }
       else {
         buffer_[streamIndex_] |= (bits & ((1 << bitsLeft_) - 1)) <<
                                  (8 - bitsLeft_);
                                                            
         nBitsWritten += bitsLeft_;
         bits >>= bitsLeft_;
         nBits -= bitsLeft_;
         buffer_[++streamIndex_] = 0;
         bitsLeft_ = 8;
       }
     
     } while (nBits != 0);
   }
 }

}



 </source>   



Converting GIF to PostScript

<source lang="java"> import java.io.FileInputStream; import java.io.FileOutputStream; import javax.print.Doc; import javax.print.DocFlavor; import javax.print.DocPrintJob; import javax.print.SimpleDoc; import javax.print.StreamPrintService; import javax.print.StreamPrintServiceFactory; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; public class StreamOneFour {

 public static void main(String args[]) throws Exception {
   String infile = "StreamOneFour.java";
   DocFlavor flavor = DocFlavor.INPUT_STREAM.GIF;
   String mimeType = DocFlavor.INPUT_STREAM.POSTSCRIPT.getMimeType();
   StreamPrintServiceFactory[] factories = StreamPrintServiceFactory
       .lookupStreamPrintServiceFactories(flavor, mimeType);
   String filename = "out.ps";
   FileOutputStream fos = new FileOutputStream(filename);
   StreamPrintService sps = factories[0].getPrintService(fos);
   FileInputStream fis = new FileInputStream(infile);
   DocPrintJob dpj = sps.createPrintJob();
   PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
   Doc doc = new SimpleDoc(fis, flavor, null);
   dpj.print(doc, pras);
   fos.close();
 }

}



 </source>   



Decodes a GIF file into one or more frames

<source lang="java">

import java.net.*; import java.io.*; import java.util.*; import java.awt.*; import java.awt.image.*; /**

* Class GifDecoder - Decodes a GIF file into one or more frames.
*
 * Example:
 *    GifDecoder d = new GifDecoder();
 *    d.read("sample.gif");
 *    int n = d.getFrameCount();
 *    for (int i = 0; i < n; i++) {
 *       BufferedImage frame = d.getFrame(i);  // frame i
 *       int t = d.getDelay(i);  // display duration of frame in milliseconds
 *       // do something with frame
 *    }
 * 
* No copyright asserted on the source code of this class.  May be used for
* any purpose, however, refer to the Unisys LZW patent for any additional
* restrictions.  Please forward any corrections to kweiner@fmsware.ru.
*
* @author Kevin Weiner, FM Software; LZW decoder adapted from John Cristy"s ImageMagick.
* @version 1.03 November 2003
*
*/

public class GifDecoder {

 /**
  * File read status: No errors.
  */
 public static final int STATUS_OK = 0;
 /**
  * File read status: Error decoding file (may be partially decoded)
  */
 public static final int STATUS_FORMAT_ERROR = 1;
 /**
  * File read status: Unable to open source.
  */
 public static final int STATUS_OPEN_ERROR = 2;
 protected BufferedInputStream in;
 protected int status;
 protected int width; // full image width
 protected int height; // full image height
 protected boolean gctFlag; // global color table used
 protected int gctSize; // size of global color table
 protected int loopCount = 1; // iterations; 0 = repeat forever
 protected int[] gct; // global color table
 protected int[] lct; // local color table
 protected int[] act; // active color table
 protected int bgIndex; // background color index
 protected int bgColor; // background color
 protected int lastBgColor; // previous bg color
 protected int pixelAspect; // pixel aspect ratio
 protected boolean lctFlag; // local color table flag
 protected boolean interlace; // interlace flag
 protected int lctSize; // local color table size
 protected int ix, iy, iw, ih; // current image rectangle
 protected Rectangle lastRect; // last image rect
 protected BufferedImage image; // current frame
 protected BufferedImage lastImage; // previous frame
 protected byte[] block = new byte[256]; // current data block
 protected int blockSize = 0; // block size
 // last graphic control extension info
 protected int dispose = 0;
 // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
 protected int lastDispose = 0;
 protected boolean transparency = false; // use transparent color
 protected int delay = 0; // delay in milliseconds
 protected int transIndex; // transparent color index
 protected static final int MaxStackSize = 4096;
 // max decoder pixel stack size
 // LZW decoder working arrays
 protected short[] prefix;
 protected byte[] suffix;
 protected byte[] pixelStack;
 protected byte[] pixels;
 protected ArrayList frames; // frames read from current file
 protected int frameCount;
 static class GifFrame {
   public GifFrame(BufferedImage im, int del) {
     image = im;
     delay = del;
   }
   public BufferedImage image;
   public int delay;
 }
 /**
  * Gets display duration for specified frame.
  *
  * @param n int index of frame
  * @return delay in milliseconds
  */
 public int getDelay(int n) {
   //
   delay = -1;
   if ((n >= 0) && (n < frameCount)) {
     delay = ((GifFrame) frames.get(n)).delay;
   }
   return delay;
 }
 /**
  * Gets the number of frames read from file.
  * @return frame count
  */
 public int getFrameCount() {
   return frameCount;
 }
 /**
  * Gets the first (or only) image read.
  *
  * @return BufferedImage containing first frame, or null if none.
  */
 public BufferedImage getImage() {
   return getFrame(0);
 }
 /**
  * Gets the "Netscape" iteration count, if any.
  * A count of 0 means repeat indefinitiely.
  *
  * @return iteration count if one was specified, else 1.
  */
 public int getLoopCount() {
   return loopCount;
 }
 /**
  * Creates new frame image from current data (and previous
  * frames as specified by their disposition codes).
  */
 protected void setPixels() {
   // expose destination image"s pixels as int array
   int[] dest =
     ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
   // fill in starting image contents based on last image"s dispose code
   if (lastDispose > 0) {
     if (lastDispose == 3) {
       // use image before last
       int n = frameCount - 2;
       if (n > 0) {
         lastImage = getFrame(n - 1);
       } else {
         lastImage = null;
       }
     }
     if (lastImage != null) {
       int[] prev =
         ((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData();
       System.arraycopy(prev, 0, dest, 0, width * height);
       // copy pixels
       if (lastDispose == 2) {
         // fill last image rect area with background color
         Graphics2D g = image.createGraphics();
         Color c = null;
         if (transparency) {
           c = new Color(0, 0, 0, 0);  // assume background is transparent
         } else {
           c = new Color(lastBgColor); // use given background color
         }
         g.setColor(c);
         g.setComposite(AlphaComposite.Src); // replace area
         g.fill(lastRect);
         g.dispose();
       }
     }
   }
   // copy each source line to the appropriate place in the destination
   int pass = 1;
   int inc = 8;
   int iline = 0;
   for (int i = 0; i < ih; i++) {
     int line = i;
     if (interlace) {
       if (iline >= ih) {
         pass++;
         switch (pass) {
           case 2 :
             iline = 4;
             break;
           case 3 :
             iline = 2;
             inc = 4;
             break;
           case 4 :
             iline = 1;
             inc = 2;
         }
       }
       line = iline;
       iline += inc;
     }
     line += iy;
     if (line < height) {
       int k = line * width;
       int dx = k + ix; // start of line in dest
       int dlim = dx + iw; // end of dest line
       if ((k + width) < dlim) {
         dlim = k + width; // past dest edge
       }
       int sx = i * iw; // start of line in source
       while (dx < dlim) {
         // map color and insert in destination
         int index = ((int) pixels[sx++]) & 0xff;
         int c = act[index];
         if (c != 0) {
           dest[dx] = c;
         }
         dx++;
       }
     }
   }
 }
 /**
  * Gets the image contents of frame n.
  *
  * @return BufferedImage representation of frame, or null if n is invalid.
  */
 public BufferedImage getFrame(int n) {
   BufferedImage im = null;
   if ((n >= 0) && (n < frameCount)) {
     im = ((GifFrame) frames.get(n)).image;
   }
   return im;
 }
 /**
  * Gets image size.
  *
  * @return GIF image dimensions
  */
 public Dimension getFrameSize() {
   return new Dimension(width, height);
 }
 /**
  * Reads GIF image from stream
  *
  * @param BufferedInputStream containing GIF file.
  * @return read status code (0 = no errors)
  */
 public int read(BufferedInputStream is) {
   init();
   if (is != null) {
     in = is;
     readHeader();
     if (!err()) {
       readContents();
       if (frameCount < 0) {
         status = STATUS_FORMAT_ERROR;
       }
     }
   } else {
     status = STATUS_OPEN_ERROR;
   }
   try {
     is.close();
   } catch (IOException e) {
   }
   return status;
 }
 /**
  * Reads GIF image from stream
  *
  * @param InputStream containing GIF file.
  * @return read status code (0 = no errors)
  */
 public int read(InputStream is) {
   init();
   if (is != null) {
     if (!(is instanceof BufferedInputStream))
       is = new BufferedInputStream(is);
     in = (BufferedInputStream) is;
     readHeader();
     if (!err()) {
       readContents();
       if (frameCount < 0) {
         status = STATUS_FORMAT_ERROR;
       }
     }
   } else {
     status = STATUS_OPEN_ERROR;
   }
   try {
     is.close();
   } catch (IOException e) {
   }
   return status;
 }
 /**
  * Reads GIF file from specified file/URL source  
  * (URL assumed if name contains ":/" or "file:")
  *
  * @param name String containing source
  * @return read status code (0 = no errors)
  */
 public int read(String name) {
   status = STATUS_OK;
   try {
     name = name.trim().toLowerCase();
     if ((name.indexOf("file:") >= 0) ||
       (name.indexOf(":/") > 0)) {
       URL url = new URL(name);
       in = new BufferedInputStream(url.openStream());
     } else {
       in = new BufferedInputStream(new FileInputStream(name));
     }
     status = read(in);
   } catch (IOException e) {
     status = STATUS_OPEN_ERROR;
   }
   return status;
 }
 /**
  * Decodes LZW image data into pixel array.
  * Adapted from John Cristy"s ImageMagick.
  */
 protected void decodeImageData() {
   int NullCode = -1;
   int npix = iw * ih;
   int available, 
     clear,
     code_mask,
     code_size,
     end_of_information,
     in_code,
     old_code,
     bits,
     code,
     count,
     i,
     datum,
     data_size,
     first,
     top,
     bi,
     pi;
   if ((pixels == null) || (pixels.length < npix)) {
     pixels = new byte[npix]; // allocate new pixel array
   }
   if (prefix == null) prefix = new short[MaxStackSize];
   if (suffix == null) suffix = new byte[MaxStackSize];
   if (pixelStack == null) pixelStack = new byte[MaxStackSize + 1];
   //  Initialize GIF data stream decoder.
   data_size = read();
   clear = 1 << data_size;
   end_of_information = clear + 1;
   available = clear + 2;
   old_code = NullCode;
   code_size = data_size + 1;
   code_mask = (1 << code_size) - 1;
   for (code = 0; code < clear; code++) {
     prefix[code] = 0;
     suffix[code] = (byte) code;
   }
   //  Decode GIF pixel stream.
   datum = bits = count = first = top = pi = bi = 0;
   for (i = 0; i < npix;) {
     if (top == 0) {
       if (bits < code_size) {
         //  Load bytes until there are enough bits for a code.
         if (count == 0) {
           // Read a new data block.
           count = readBlock();
           if (count <= 0)
             break;
           bi = 0;
         }
         datum += (((int) block[bi]) & 0xff) << bits;
         bits += 8;
         bi++;
         count--;
         continue;
       }
       //  Get the next code.
       code = datum & code_mask;
       datum >>= code_size;
       bits -= code_size;
       //  Interpret the code
       if ((code > available) || (code == end_of_information))
         break;
       if (code == clear) {
         //  Reset decoder.
         code_size = data_size + 1;
         code_mask = (1 << code_size) - 1;
         available = clear + 2;
         old_code = NullCode;
         continue;
       }
       if (old_code == NullCode) {
         pixelStack[top++] = suffix[code];
         old_code = code;
         first = code;
         continue;
       }
       in_code = code;
       if (code == available) {
         pixelStack[top++] = (byte) first;
         code = old_code;
       }
       while (code > clear) {
         pixelStack[top++] = suffix[code];
         code = prefix[code];
       }
       first = ((int) suffix[code]) & 0xff;
       //  Add a new string to the string table,
       if (available >= MaxStackSize)
         break;
       pixelStack[top++] = (byte) first;
       prefix[available] = (short) old_code;
       suffix[available] = (byte) first;
       available++;
       if (((available & code_mask) == 0)
         && (available < MaxStackSize)) {
         code_size++;
         code_mask += available;
       }
       old_code = in_code;
     }
     //  Pop a pixel off the pixel stack.
     top--;
     pixels[pi++] = pixelStack[top];
     i++;
   }
   for (i = pi; i < npix; i++) {
     pixels[i] = 0; // clear missing pixels
   }
 }
 /**
  * Returns true if an error was encountered during reading/decoding
  */
 protected boolean err() {
   return status != STATUS_OK;
 }
 /**
  * Initializes or re-initializes reader
  */
 protected void init() {
   status = STATUS_OK;
   frameCount = 0;
   frames = new ArrayList();
   gct = null;
   lct = null;
 }
 /**
  * Reads a single byte from the input stream.
  */
 protected int read() {
   int curByte = 0;
   try {
     curByte = in.read();
   } catch (IOException e) {
     status = STATUS_FORMAT_ERROR;
   }
   return curByte;
 }
 /**
  * Reads next variable length block from input.
  *
  * @return number of bytes stored in "buffer"
  */
 protected int readBlock() {
   blockSize = read();
   int n = 0;
   if (blockSize > 0) {
     try {
       int count = 0;
       while (n < blockSize) {
         count = in.read(block, n, blockSize - n);
         if (count == -1) 
           break;
         n += count;
       }
     } catch (IOException e) {
     }
     if (n < blockSize) {
       status = STATUS_FORMAT_ERROR;
     }
   }
   return n;
 }
 /**
  * Reads color table as 256 RGB integer values
  *
  * @param ncolors int number of colors to read
  * @return int array containing 256 colors (packed ARGB with full alpha)
  */
 protected int[] readColorTable(int ncolors) {
   int nbytes = 3 * ncolors;
   int[] tab = null;
   byte[] c = new byte[nbytes];
   int n = 0;
   try {
     n = in.read(c);
   } catch (IOException e) {
   }
   if (n < nbytes) {
     status = STATUS_FORMAT_ERROR;
   } else {
     tab = new int[256]; // max size to avoid bounds checks
     int i = 0;
     int j = 0;
     while (i < ncolors) {
       int r = ((int) c[j++]) & 0xff;
       int g = ((int) c[j++]) & 0xff;
       int b = ((int) c[j++]) & 0xff;
       tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
     }
   }
   return tab;
 }
 /**
  * Main file parser.  Reads GIF content blocks.
  */
 protected void readContents() {
   // read GIF file content blocks
   boolean done = false;
   while (!(done || err())) {
     int code = read();
     switch (code) {
       case 0x2C : // image separator
         readImage();
         break;
       case 0x21 : // extension
         code = read();
         switch (code) {
           case 0xf9 : // graphics control extension
             readGraphicControlExt();
             break;
           case 0xff : // application extension
             readBlock();
             String app = "";
             for (int i = 0; i < 11; i++) {
               app += (char) block[i];
             }
             if (app.equals("NETSCAPE2.0")) {
               readNetscapeExt();
             }
             else
               skip(); // don"t care
             break;
           default : // uninteresting extension
             skip();
         }
         break;
       case 0x3b : // terminator
         done = true;
         break;
       case 0x00 : // bad byte, but keep going and see what happens
         break;
       default :
         status = STATUS_FORMAT_ERROR;
     }
   }
 }
 /**
  * Reads Graphics Control Extension values
  */
 protected void readGraphicControlExt() {
   read(); // block size
   int packed = read(); // packed fields
   dispose = (packed & 0x1c) >> 2; // disposal method
   if (dispose == 0) {
     dispose = 1; // elect to keep old image if discretionary
   }
   transparency = (packed & 1) != 0;
   delay = readShort() * 10; // delay in milliseconds
   transIndex = read(); // transparent color index
   read(); // block terminator
 }
 /**
  * Reads GIF file header information.
  */
 protected void readHeader() {
   String id = "";
   for (int i = 0; i < 6; i++) {
     id += (char) read();
   }
   if (!id.startsWith("GIF")) {
     status = STATUS_FORMAT_ERROR;
     return;
   }
   readLSD();
   if (gctFlag && !err()) {
     gct = readColorTable(gctSize);
     bgColor = gct[bgIndex];
   }
 }
 /**
  * Reads next frame image
  */
 protected void readImage() {
   ix = readShort(); // (sub)image position & size
   iy = readShort();
   iw = readShort();
   ih = readShort();
   int packed = read();
   lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
   interlace = (packed & 0x40) != 0; // 2 - interlace flag
   // 3 - sort flag
   // 4-5 - reserved
   lctSize = 2 << (packed & 7); // 6-8 - local color table size
   if (lctFlag) {
     lct = readColorTable(lctSize); // read table
     act = lct; // make local table active
   } else {
     act = gct; // make global table active
     if (bgIndex == transIndex)
       bgColor = 0;
   }
   int save = 0;
   if (transparency) {
     save = act[transIndex];
     act[transIndex] = 0; // set transparent color if specified
   }
   if (act == null) {
     status = STATUS_FORMAT_ERROR; // no color table defined
   }
   if (err()) return;
   decodeImageData(); // decode pixel data
   skip();
   if (err()) return;
   frameCount++;
   // create new image to receive frame data
   image =
     new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
   setPixels(); // transfer pixel data to image
   frames.add(new GifFrame(image, delay)); // add image to frame list
   if (transparency) {
     act[transIndex] = save;
   }
   resetFrame();
 }
 /**
  * Reads Logical Screen Descriptor
  */
 protected void readLSD() {
   // logical screen size
   width = readShort();
   height = readShort();
   // packed fields
   int packed = read();
   gctFlag = (packed & 0x80) != 0; // 1   : global color table flag
   // 2-4 : color resolution
   // 5   : gct sort flag
   gctSize = 2 << (packed & 7); // 6-8 : gct size
   bgIndex = read(); // background color index
   pixelAspect = read(); // pixel aspect ratio
 }
 /**
  * Reads Netscape extenstion to obtain iteration count
  */
 protected void readNetscapeExt() {
   do {
     readBlock();
     if (block[0] == 1) {
       // loop count sub-block
       int b1 = ((int) block[1]) & 0xff;
       int b2 = ((int) block[2]) & 0xff;
       loopCount = (b2 << 8) | b1;
     }
   } while ((blockSize > 0) && !err());
 }
 /**
  * Reads next 16-bit value, LSB first
  */
 protected int readShort() {
   // read 16-bit value, LSB first
   return read() | (read() << 8);
 }
 /**
  * Resets frame state for reading next image.
  */
 protected void resetFrame() {
   lastDispose = dispose;
   lastRect = new Rectangle(ix, iy, iw, ih);
   lastImage = image;
   lastBgColor = bgColor;
   dispose = 0;
   transparency = false;
   delay = 0;
   lct = null;
 }
 /**
  * Skips variable length blocks up to and including
  * next zero length block.
  */
 protected void skip() {
   do {
     readBlock();
   } while ((blockSize > 0) && !err());
 }

}


 </source>   



Get Gif Properties

<source lang="java"> /*

* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
* 
*      http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// Revised from apache cocoon import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.BufferedInputStream; import java.io.FileInputStream; /**

* @version $Id: ImageUtils.java 587751 2007-10-24 02:41:36Z vgritsenko $
*/

final public class ImageUtils {

   final public static ImageProperties getJpegProperties(File file) throws FileNotFoundException, IOException{
       BufferedInputStream in = null;
       try {
           in = new BufferedInputStream(new FileInputStream(file));
           // check for "magic" header
           byte[] buf = new byte[2];
           int count = in.read(buf, 0, 2);
           if (count < 2) {
               throw new RuntimeException("Not a valid Jpeg file!");
           }
           if ((buf[0]) != (byte) 0xFF || (buf[1]) != (byte) 0xD8) {
               throw new RuntimeException("Not a valid Jpeg file!");
           }
           int width = 0;
           int height = 0;
           char[] comment = null;
           boolean hasDims = false;
           boolean hasComment = false;
           int ch = 0;
           while (ch != 0xDA && !(hasDims && hasComment)) {
               /* Find next marker (JPEG markers begin with 0xFF) */
               while (ch != 0xFF) {
                   ch = in.read();
               }
               /* JPEG markers can be padded with unlimited 0xFF"s */
               while (ch == 0xFF) {
                   ch = in.read();
               }
               /* Now, ch contains the value of the marker. */
               int length = 256 * in.read();
               length += in.read();
               if (length < 2) {
                   throw new RuntimeException("Not a valid Jpeg file!");
               }
               /* Now, length contains the length of the marker. */
               if (ch >= 0xC0 && ch <= 0xC3) {
                   in.read();
                   height = 256 * in.read();
                   height += in.read();
                   width = 256 * in.read();
                   width += in.read();
                   for (int foo = 0; foo < length - 2 - 5; foo++) {
                       in.read();
                   }
                   hasDims = true;
               }
               else if (ch == 0xFE) {
                   // that"s the comment marker
                   comment = new char[length-2];
                   for (int foo = 0; foo < length - 2; foo++)
                       comment[foo] = (char) in.read();
                   hasComment = true;
               }
               else {
                   // just skip marker
                   for (int foo = 0; foo < length - 2; foo++) {
                       in.read();
                   }
               }
           }
           return (new ImageProperties(width, height, comment, "jpeg"));
       }
       finally {
           if (in != null) {
               try {
                   in.close();
               }
               catch (IOException e) {
               }
           }
       }
   }
   final public static ImageProperties getGifProperties(File file) throws FileNotFoundException, IOException{
       BufferedInputStream in = null;
       try {
           in = new BufferedInputStream(new FileInputStream(file));
           byte[] buf = new byte[10];
           int count = in.read(buf, 0, 10);
           if (count < 10) {
               throw new RuntimeException("Not a valid Gif file!");
           }
           if ((buf[0]) != (byte) "G" || (buf[1]) != (byte) "I" || (buf[2]) != (byte) "F") {
               throw new RuntimeException("Not a valid Gif file!");
           }
           int w1 = (buf[6] & 0xff) | (buf[6] & 0x80);
           int w2 = (buf[7] & 0xff) | (buf[7] & 0x80);
           int h1 = (buf[8] & 0xff) | (buf[8] & 0x80);
           int h2 = (buf[9] & 0xff) | (buf[9] & 0x80);
           int width = w1 + (w2 << 8);
           int height = h1 + (h2 << 8);
           return (new ImageProperties(width, height, null,"gif"));
       }
       finally {
           if (in != null) {
               try {
                   in.close();
               }
               catch (IOException e) {
               }
           }
       }
   }

} /*

* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
* 
*      http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**

* @version $Id: ImageProperties.java 587751 2007-10-24 02:41:36Z vgritsenko $
*/

final class ImageProperties {

   final public int width;
   final public int height;
   final public char[] comment;
   final public String type;
   public ImageProperties(int width, int height, char[] comment, String type) {
       this.width = width;
       this.height = height;
       this.rument = comment;
       this.type = type;
   }
   public String toString() {
       StringBuffer sb = new StringBuffer();
       sb.append(type).append(" ").append(width).append("x").append(height);
       if (comment != null) {
           sb.append(" (").append(comment).append(")");
       }
       return (sb.toString());
   }

}

 </source>   



Gif Encoder

<source lang="java"> /*

*
* Part of the InfoGlue Content Management Platform (www.infoglue.org)
*
* 
*
*  Copyright (C)
* 
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2, as published by the
* Free Software Foundation. See the file LICENSE.html for more information.
* 
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc. / 59 Temple
* Place, Suite 330 / Boston, MA 02111-1307 / USA.
*
* 
*/

import java.awt.Color; import java.awt.image.BufferedImage; import java.io.*; import java.util.*; public class GifEncoder {

   String b;
   int g;
   int a;
   int h;
   int f;
   int c[];
   int i[];
   int e[];
   TreeSet d;
   private void a(int ai[], int j, DataOutputStream dataoutputstream) throws Exception
   {
       try
       {
           boolean flag2 = false;
           int l;
           int j3 = (1 << (l = j + 1)) - 1;
           int i2 = (1 << j) + 2;
           byte abyte0[] = new byte[255];
           int ai1[] = new int[4096];
           int ai2[] = new int[4096];
           int ai3[] = new int[4096];
           int ai4[] = new int[i2];
           int k;
           for(k = 0; k < i2; k++)
           {
               ai4[k] = 0xffffffff | k;
               ai3[k] = -1;
           }
           for(; k < 4096; k++)
           {
               ai3[k] = -1;
           }
           System.arraycopy(ai3, 0, ai1, 0, 4096);
           System.arraycopy(ai3, 0, ai2, 0, 4096);
           System.arraycopy(ai4, 0, ai1, 0, i2);
           int j1 = ai[0];
           k = 1;
           boolean flag1 = false;
           int j2 = 0;
           int k2 = (1 << l) - 1;
           boolean flag = true;
           int i3 = 0;
           int i1 = 0;
           j2 |= 1 << j + i3;
           for(i3 += l; i3 >= 8;)
           {
               try
               {
                   abyte0[i1++] = (byte)j2;
               }
               catch(ArrayIndexOutOfBoundsException arrayindexoutofboundsexception)
               {
                   dataoutputstream.writeByte(255);
                   dataoutputstream.write(abyte0);
                   abyte0[i1 = 0] = (byte)j2;
                   i1++;
               }
               i3 -= 8;
               j2 >>= 8;
           }
           try
           {
               do
               {
                   int k1;
                   int l1 = j1 << 16 | (k1 = ai[k++]);
                   int k3;
                   for(k3 = j1; ai1[k3] != l1 && ai2[k3] >= 0; k3 = ai2[k3]) { }
                   if(ai1[k3] != l1)
                   {
                       j2 |= j1 << i3;
                       for(i3 += l; i3 >= 8;)
                       {
                           try
                           {
                               abyte0[i1++] = (byte)j2;
                           }
                           catch(ArrayIndexOutOfBoundsException arrayindexoutofboundsexception1)
                           {
                               dataoutputstream.writeByte(255);
                               dataoutputstream.write(abyte0);
                               abyte0[i1 = 0] = (byte)j2;
                               i1++;
                           }
                           i3 -= 8;
                           j2 >>= 8;
                       }
                       if(i2 > j3)
                       {
                           l++;
                           j3 = (j3 << 1) + 1;
                       }
                       try
                       {
                           ai2[k3] = i2;
                           ai1[i2++] = j1 << 16 | k1;
                           j1 = k1;
                       }
                       catch(ArrayIndexOutOfBoundsException arrayindexoutofboundsexception2)
                       {
                           j1 = k1;
                           l--;
                           j2 |= 1 << j + i3;
                           for(i3 += l; i3 >= 8;)
                           {
                               try
                               {
                                   abyte0[i1++] = (byte)j2;
                               }
                               catch(ArrayIndexOutOfBoundsException arrayindexoutofboundsexception5)
                               {
                                   dataoutputstream.writeByte(255);
                                   dataoutputstream.write(abyte0);
                                   abyte0[i1 = 0] = (byte)j2;
                                   i1++;
                               }
                               i3 -= 8;
                               j2 >>= 8;
                           }
                           j3 = (1 << (l = j + 1)) - 1;
                           i2 = (1 << j) + 2;
                           int l2 = (1 << l) - 1;
                           System.arraycopy(ai3, 0, ai1, 0, 4096);
                           System.arraycopy(ai3, 0, ai2, 0, 4096);
                           System.arraycopy(ai4, 0, ai1, 0, i2);
                       }
                   }
                   else
                   {
                       j1 = k3;
                   }
               }
               while(true);
           }
           catch(Exception exception)
           {
               j2 |= j1 << i3;
           }
           for(i3 += l; i3 >= 8;)
           {
               try
               {
                   abyte0[i1++] = (byte)j2;
               }
               catch(ArrayIndexOutOfBoundsException arrayindexoutofboundsexception3)
               {
                   dataoutputstream.writeByte(255);
                   dataoutputstream.write(abyte0);
                   abyte0[i1 = 0] = (byte)j2;
                   i1++;
               }
               i3 -= 8;
               j2 >>= 8;
           }
           j2 |= (1 << j) + 1 << i3;
           for(i3 += l; i3 > 0;)
           {
               try
               {
                   abyte0[i1++] = (byte)j2;
               }
               catch(ArrayIndexOutOfBoundsException arrayindexoutofboundsexception4)
               {
                   dataoutputstream.writeByte(255);
                   dataoutputstream.write(abyte0);
                   abyte0[i1 = 0] = (byte)j2;
                   i1++;
               }
               i3 -= 8;
               j2 >>= 8;
           }
           dataoutputstream.writeByte(i1);
           dataoutputstream.write(abyte0, 0, i1);
           dataoutputstream.writeByte(0);
           return;
       }
       catch(Exception e) { }
   }
   public void addTransparentColor(Color color)
   {
       try
       {
           if(f < 256)
           {
               c[f++] = color.getRGB();
           }
           return;
       }
       catch(Exception e) { }
   }
   public void setTransparentColors(Vector vector)
   {
       try
       {
           Iterator iterator = vector.iterator();
           while(iterator.hasNext()) 
           {
               Color color = (Color)iterator.next();
               addTransparentColor(color);
           }
           return;
       }
       catch(Exception e) { }
   }
   public void encode(BufferedImage bufferedimage, DataOutputStream dataoutputstream, Hashtable hashtable) throws Exception
   {
       try
       {
           a = bufferedimage.getWidth();
           g = bufferedimage.getHeight();
           e = bufferedimage.getRGB(0, 0, a, g, null, 0, a);
           int i4 = 0;
           b = hashtable.get("encoding").toString();
           if(b.equals("websafe"))
           {
               int ai[] = new int[256];
               i = new int[256];
               h = 8;
               int k1 = 0;
               int j;
               int j1 = j = 0;
               for(; j <= 255; j += 51)
               {
                   for(int l = 0; l <= 255; l += 51)
                   {
                       for(int i1 = 0; i1 <= 255;)
                       {
                           i[j1] = (j << 16) + (l << 8) + i1;
                           ai[k1++] = j1;
                           i1 += 51;
                           j1++;
                       }
                   }
               }
               if(f > 0)
               {
                   int j4 = c[0];
                   int l1 = ((c[0] >> 16 & 0xff) + 25) / 51;
                   int k2 = ((c[0] >> 8 & 0xff) + 25) / 51;
                   int j3 = ((c[0] & 0xff) + 25) / 51;
                   i4 = l1 * 36 + k2 * 6 + j3;
                   for(j = 1; j < f; j++)
                   {
                       int i2 = ((c[j] >> 16 & 0xff) + 25) / 51;
                       int l2 = ((c[j] >> 8 & 0xff) + 25) / 51;
                       int k3 = ((c[j] & 0xff) + 25) / 51;
                       ai[i2 * 36 + l2 * 6 + k3] = i4;
                   }
               }
               j = 0;
               try
               {
                   do
                   {
                       int i5 = e[j];
                       int j2 = ((i5 >> 16 & 0xff) + 25) / 51;
                       int i3 = ((i5 >> 8 & 0xff) + 25) / 51;
                       int l3 = ((i5 & 0xff) + 25) / 51;
                       e[j++] = ai[j2 * 36 + i3 * 6 + l3];
                   }
                   while(true);
               }
               catch(Exception exception1) { }
           }
           /*else
           if(b.equals("optimized"))
           {
               try
               {
                   int k4 = Integer.parseInt(hashtable.get("colors").toString());
                   for(h = 1; k4 - 1 >> h > 0; h++) { }
                   i = new int[1 << h];
                   CSelectiveQuant cselectivequant = new CSelectiveQuant();
                   for(int j5 = 0; j5 < e.length; j5++)
                   {
                       cselectivequant.addPixel(e[j5]);
                   }
                   boolean flag = f > 0;
                   int k5 = flag ? 1 : 0;
                   int ai1[] = cselectivequant.createPalette(k4 - k5);
                   for(int l5 = 0; l5 < i.length; l5++)
                   {
                       try
                       {
                           i[l5] = ai1[l5 - k5];
                       }
                       catch(ArrayIndexOutOfBoundsException arrayindexoutofboundsexception)
                       {
                           i[l5] = 0;
                       }
                   }
                   if(flag)
                   {
                       i4 = 0;
                       for(int i6 = 0; i6 < f; i6++)
                       {
                           cselectivequant.setIndex(c[i6], -1);
                       }
                   }
                   for(int j6 = 0; j6 < e.length; j6++)
                   {
                       e[j6] = cselectivequant.getIndex(e[j6]) + k5;
                   }
               }
               catch(NumberFormatException numberformatexception)
               {
                   CmsLogger.logInfo("Parameter: "colors" is malformated...");
                   return;
               }
           }
           */
           dataoutputstream.write("GIF89a".getBytes());
           dataoutputstream.writeByte(a);
           dataoutputstream.writeByte(a >> 8);
           dataoutputstream.writeByte(g);
           dataoutputstream.writeByte(g >> 8);
           dataoutputstream.writeByte(0xf0 | h - 1);
           dataoutputstream.writeByte(0);
           dataoutputstream.writeByte(0);
           int k = 0;
           try
           {
               do
               {
                   int l4 = i[k++];
                   dataoutputstream.writeByte(l4 >> 16 & 0xff);
                   dataoutputstream.writeByte(l4 >> 8 & 0xff);
                   dataoutputstream.writeByte(l4 & 0xff);
               }
               while(true);
           }
           catch(Exception exception) { }
           if(f > 0)
           {
               dataoutputstream.writeByte(33);
               dataoutputstream.writeByte(249);
               dataoutputstream.writeByte(4);
               dataoutputstream.writeByte(1);
               dataoutputstream.writeByte(0);
               dataoutputstream.writeByte(0);
               dataoutputstream.writeByte(i4);
               dataoutputstream.writeByte(0);
           }
           dataoutputstream.writeByte(44);
           dataoutputstream.writeByte(0);
           dataoutputstream.writeByte(0);
           dataoutputstream.writeByte(0);
           dataoutputstream.writeByte(0);
           dataoutputstream.writeByte(a);
           dataoutputstream.writeByte(a >> 8);
           dataoutputstream.writeByte(g);
           dataoutputstream.writeByte(g >> 8);
           dataoutputstream.writeByte(0);
           dataoutputstream.writeByte(h);
           a(e, h, dataoutputstream);
           dataoutputstream.writeByte(59);
           dataoutputstream.flush();
           return;
       }
       catch(Exception e) { }
   }
   public GifEncoder()
   {
       f = 0;
       c = new int[256];
   }

}


 </source>   



Gif Encoder implements ImageConsumer

<source lang="java"> //** Copyright Statement *************************************************** //The Salmon Open Framework for Internet Applications (SOFIA) // Copyright (C) 1999 - 2002, Salmon LLC // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 2 // as published by the Free Software Foundation; // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // For more information please visit http://www.salmonllc.ru //** End Copyright Statement ***************************************************

// ImageEncoder - abstract class for writing out an image // // Copyright (C) 1996 by Jef Poskanzer <jef@acme.ru>. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS"" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. // // Visit the ACME Labs Java page for up-to-date versions of this and other // fine Java utilities: http://www.acme.ru/java/ import java.util.*; import java.io.*; import java.awt.Image; import java.awt.image.*; import java.awt.*; /**

* Class for writing out an image as a gif.
*/

public class GifEncoder implements ImageConsumer {

   protected OutputStream out;
   private ImageProducer producer;
   private int width = -1;
   private int height = -1;
   private int hintflags = 0;
   private boolean started = false;
   private boolean encoding;
   private IOException iox;
   private static final ColorModel rgbModel = ColorModel.getRGBdefault();
   private Hashtable props = null;
   private boolean accumulate = false;
   private int[] accumulator;
   private boolean interlace = false;
   int[][] rgbPixels;
   IntHashtable colorHash;
   // Adapted from ppmtogif, which is based on GIFENCOD by David
   // Rowley <mgardi@watdscu.waterloo.edu>.  Lempel-Zim compression
   // based on "compress".
   int Width, Height;
   boolean Interlace;
   int curx, cury;
   int CountDown;
   int Pass = 0;
   static final int EOF = -1;
   // GIFCOMPR.C       - GIF Image compression routines
   //
   // Lempel-Ziv compression based on "compress".  GIF modifications by
   // David Rowley (mgardi@watdcsu.waterloo.edu)
   // General DEFINEs
   static final int BITS = 12;
   static final int HSIZE = 5003;    // 80% occupancy
   // GIF Image compression - modified "compress"
   //
   // Based on: compress.c - File compression ala IEEE Computer, June 1984.
   //
   // By Authors:  Spencer W. Thomas      (decvax!harpo!utah-cs!utah-gr!thomas)
   //              Jim McKie              (decvax!mcvax!jim)
   //              Steve Davies           (decvax!vax135!petsd!peora!srd)
   //              Ken Turkowski          (decvax!decwrl!turtlevax!ken)
   //              James A. Woods         (decvax!ihnp4!ames!jaw)
   //              Joe Orost              (decvax!vax135!petsd!joe)
   int n_bits;       // number of bits/code
   int maxbits = BITS;     // user settable max # bits/code
   int maxcode;      // maximum code, given n_bits
   int maxmaxcode = 1 << BITS; // should NEVER generate this code
   int[] htab = new int[HSIZE];
   int[] codetab = new int[HSIZE];
   int hsize = HSIZE;    // for dynamic table sizing
   int free_ent = 0;     // first unused entry
   // block compression parameters -- after all codes are used up,
   // and compression rate changes, start over.
   boolean clear_flg = false;
   // Algorithm:  use open addressing double hashing (no chaining) on the
   // prefix code / next character combination.  We do a variant of Knuth"s
   // algorithm D (vol. 3, sec. 6.4) along with G. Knott"s relatively-prime
   // secondary probe.  Here, the modular division first probe is gives way
   // to a faster exclusive-or manipulation.  Also do block compression with
   // an adaptive reset, whereby the code table is cleared when the compression
   // ratio decreases, but after the table fills.  The variable-length output
   // codes are re-sized at this point, and a special CLEAR code is generated
   // for the decompressor.  Late addition:  construct the table according to
   // file size for noticeable speed improvement on small files.  Please direct
   // questions about this implementation to ames!jaw.
   int g_init_bits;
   int ClearCode;
   int EOFCode;
   // output
   //
   // Output the given code.
   // Inputs:
   //      code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes
   //              that n_bits =< wordsize - 1.
   // Outputs:
   //      Outputs code to the file.
   // Assumptions:
   //      Chars are 8 bits long.
   // Algorithm:
   //      Maintain a BITS character long buffer (so that 8 codes will
   // fit in it exactly).  Use the VAX insv instruction to insert each
   // code in turn.  When the buffer fills up empty it and start over.
   int cur_accum = 0;
   int cur_bits = 0;
   int masks[] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
                  0x001F, 0x003F, 0x007F, 0x00FF,
                  0x01FF, 0x03FF, 0x07FF, 0x0FFF,
                  0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF};
   // GIF Specific routines
   // Number of characters so far in this "packet"
   int a_count;
   // Define the storage for the packet accumulator
   byte[] accum = new byte[256];
   class gifHashitem {
       public int rgb;
       public int count;
       public int index;
       public boolean isTransparent;
       public gifHashitem(int rgb, int count, int index, boolean isTransparent) {
           this.rgb = rgb;
           this.count = count;
           this.index = index;
           this.isTransparent = isTransparent;
       }
   }
   class IntHashtableEntry {
       int hash;
       int key;
       Object value;
       IntHashtableEntry next;
       protected Object clone() {
           IntHashtableEntry entry = new IntHashtableEntry();
           entry.hash = hash;
           entry.key = key;
           entry.value = value;
           entry.next = (next != null) ? (IntHashtableEntry) next.clone() : null;
           return entry;
       }
   }
   class IntHashtable extends Dictionary implements Cloneable {
       private IntHashtableEntry table[];
       private int count;
       private int threshold;
       private float loadFactor;
       public IntHashtable() {
           this(101, 0.75f);
       }
       public IntHashtable(int initialCapacity) {
           this(initialCapacity, 0.75f);
       }
       public IntHashtable(int initialCapacity, float loadFactor) {
           if (initialCapacity <= 0 || loadFactor <= 0.0)
               throw new IllegalArgumentException();
           this.loadFactor = loadFactor;
           table = new IntHashtableEntry[initialCapacity];
           threshold = (int) (initialCapacity * loadFactor);
       }
       public synchronized void clear() {
           IntHashtableEntry tab[] = table;
           for (int index = tab.length; --index >= 0;)
               tab[index] = null;
           count = 0;
       }
       public synchronized Object clone() {
           try {
               IntHashtable t = (IntHashtable) super.clone();
               t.table = new IntHashtableEntry[table.length];
               for (int i = table.length; i-- > 0;)
                   t.table[i] = (table[i] != null) ?  (IntHashtableEntry) table[i].clone() : null;
               return t;
           } catch (CloneNotSupportedException e) {
               throw new InternalError();
           }
       }
       public synchronized boolean contains(Object value) {
           if (value == null)
               throw new NullPointerException();
           IntHashtableEntry tab[] = table;
           for (int i = tab.length; i-- > 0;) {
               for (IntHashtableEntry e = tab[i]; e != null; e = e.next) {
                   if (e.value.equals(value))
                       return true;
               }
           }
           return false;
       }
       public synchronized boolean containsKey(int key) {
           IntHashtableEntry tab[] = table;
           int hash = key;
           int index = (hash & 0x7FFFFFFF) % tab.length;
           for (IntHashtableEntry e = tab[index]; e != null; e = e.next) {
               if (e.hash == hash && e.key == key)
                   return true;
           }
           return false;
       }
       public synchronized Enumeration elements() {
           return new IntHashtableEnumerator(table, false);
       }
       public synchronized Object get(int key) {
           IntHashtableEntry tab[] = table;
           int hash = key;
           int index = (hash & 0x7FFFFFFF) % tab.length;
           for (IntHashtableEntry e = tab[index]; e != null; e = e.next) {
               if (e.hash == hash && e.key == key)
                   return e.value;
           }
           return null;
       }
       public Object get(Object okey) {
           if (!(okey instanceof Integer))
               throw new InternalError("key is not an Integer");
           Integer ikey = (Integer) okey;
           int key = ikey.intValue();
           return get(key);
       }
       public boolean isEmpty() {
           return count == 0;
       }
       public synchronized Enumeration keys() {
           return new IntHashtableEnumerator(table, true);
       }
       public synchronized Object put(int key, Object value) {
           if (value == null)
               throw new NullPointerException();
           IntHashtableEntry tab[] = table;
           int hash = key;
           int index = (hash & 0x7FFFFFFF) % tab.length;
           for (IntHashtableEntry e = tab[index]; e != null; e = e.next) {
               if (e.hash == hash && e.key == key) {
                   Object old = e.value;
                   e.value = value;
                   return old;
               }
           }
           if (count >= threshold) {
               rehash();
               return put(key, value);
           }
           IntHashtableEntry e = new IntHashtableEntry();
           e.hash = hash;
           e.key = key;
           e.value = value;
           e.next = tab[index];
           tab[index] = e;
           ++count;
           return null;
       }
       public Object put(Object okey, Object value) {
           if (!(okey instanceof Integer))
               throw new InternalError("key is not an Integer");
           Integer ikey = (Integer) okey;
           int key = ikey.intValue();
           return put(key, value);
       }
       protected void rehash() {
           int oldCapacity = table.length;
           IntHashtableEntry oldTable[] = table;
           int newCapacity = oldCapacity * 2 + 1;
           IntHashtableEntry newTable[] = new IntHashtableEntry[newCapacity];
           threshold = (int) (newCapacity * loadFactor);
           table = newTable;
           for (int i = oldCapacity; i-- > 0;) {
               for (IntHashtableEntry old = oldTable[i]; old != null;) {
                   IntHashtableEntry e = old;
                   old = old.next;
                   int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                   e.next = newTable[index];
                   newTable[index] = e;
               }
           }
       }
       public synchronized Object remove(int key) {
           IntHashtableEntry tab[] = table;
           int hash = key;
           int index = (hash & 0x7FFFFFFF) % tab.length;
           for (IntHashtableEntry e = tab[index], prev = null; e != null; prev = e, e = e.next) {
               if (e.hash == hash && e.key == key) {
                   if (prev != null)
                       prev.next = e.next;
                   else
                       tab[index] = e.next;
                   --count;
                   return e.value;
               }
           }
           return null;
       }
       public Object remove(Object okey) {
           if (!(okey instanceof Integer))
               throw new InternalError("key is not an Integer");
           Integer ikey = (Integer) okey;
           int key = ikey.intValue();
           return remove(key);
       }
       public int size() {
           return count;
       }
       public synchronized String toString() {
           int max = size() - 1;
           StringBuffer buf = new StringBuffer();
           Enumeration k = keys();
           Enumeration e = elements();
           buf.append("{");
           for (int i = 0; i <= max; ++i) {
               String s1 = k.nextElement().toString();
               String s2 = e.nextElement().toString();
               buf.append(s1 + "=" + s2);
               if (i < max)
                   buf.append(", ");
           }
           buf.append("}");
           return buf.toString();
       }
   }
   class IntHashtableEnumerator implements Enumeration {
       boolean keys;
       int index;
       IntHashtableEntry table[];
       IntHashtableEntry entry;
       IntHashtableEnumerator(IntHashtableEntry table[], boolean keys) {
           this.table = table;
           this.keys = keys;
           this.index = table.length;
       }
       public boolean hasMoreElements() {
           if (entry != null)
               return true;
           while (index-- > 0)
               if ((entry = table[index]) != null)
                   return true;
           return false;
       }
       public Object nextElement() {
           if (entry == null)
               while ((index-- > 0) && ((entry = table[index]) == null)) ;
           if (entry != null) {
               IntHashtableEntry e = entry;
               entry = e.next;
               return keys ? new Integer(e.key) : e.value;
           }
           throw new NoSuchElementException("IntHashtableEnumerator");
       }
   }
   class TransparentFilter extends RGBImageFilter {
       int transparentRGB;
       public TransparentFilter(Color color) {
           transparentRGB = color.getRGB() & 0xFFFFFF;
           canFilterIndexColorModel = true;
       }
       public int filterRGB(int x, int y, int rgb) {
           if ((rgb & 0xFFFFFF) == transparentRGB)
               return 0;
           return rgb;
       }
   }
   /// Constructor.
   // @param producer The ImageProducer to encode.
   // @param out The stream to write the bytes to.
   private GifEncoder(ImageProducer producer, OutputStream out) throws IOException {
       this.producer = producer;
       this.out = out;
   }
   /**
    * Constructor
    *  @param img The image to encode.
    *  @param out The stream to write the bytes to.
    */
   public GifEncoder(Image img, OutputStream out) throws IOException {
       this(img.getSource(), out);
   }
   /**
    * Constructor from Image with interlace setting.
    * @param img The image to encode.
    * @param out The stream to write the GIF to.
    * @param interlace Whether to interlace.
    */
   public GifEncoder(Image img, OutputStream out, boolean interlace) throws IOException {
       this(img, out);
       this.interlace = interlace;
   }
   /** Constructor from Image with interlace setting.
    * @param img The image to encode.
    * @param out The stream to write the GIF to.
    * @param interlace Whether to interlace.
    * @param transparentColor The color to use for transparency
    */
   public GifEncoder(Image img, OutputStream out, boolean interlace, Color transparentColor) throws IOException {
       RGBImageFilter f = new TransparentFilter(transparentColor);
       this.producer = new FilteredImageSource(img.getSource(), f);
       this.out = out;
       this.interlace = interlace;
   }
   // Bump the "curx" and "cury" to point to the next pixel
   void BumpPixel() {
       // Bump the current X position
       ++curx;
       // If we are at the end of a scan line, set curx back to the beginning
       // If we are interlaced, bump the cury to the appropriate spot,
       // otherwise, just increment it.
       if (curx == Width) {
           curx = 0;
           if (!Interlace)
               ++cury;
           else {
               switch (Pass) {
                   case 0:
                       cury += 8;
                       if (cury >= Height) {
                           ++Pass;
                           cury = 4;
                       }
                       break;
                   case 1:
                       cury += 8;
                       if (cury >= Height) {
                           ++Pass;
                           cury = 2;
                       }
                       break;
                   case 2:
                       cury += 4;
                       if (cury >= Height) {
                           ++Pass;
                           cury = 1;
                       }
                       break;
                   case 3:
                       cury += 2;
                       break;
               }
           }
       }
   }
   // Set up the "byte output" routine
   void char_init() {
       a_count = 0;
   }
   // Add a character to the end of the current packet, and if it is 254
   // characters, flush the packet to disk.
   void char_out(byte c, OutputStream outs) throws IOException {
       accum[a_count++] = c;
       if (a_count >= 254)
           flush_char(outs);
   }
   // Clear out the hash table
   // table clear for block compress
   void cl_block(OutputStream outs) throws IOException {
       cl_hash(hsize);
       free_ent = ClearCode + 2;
       clear_flg = true;
       output(ClearCode, outs);
   }
   // reset code table
   void cl_hash(int hsize) {
       for (int i = 0; i < hsize; ++i)
           htab[i] = -1;
   }
   void compress(int init_bits, OutputStream outs) throws IOException {
       int fcode;
       int i /* = 0 */;
       int c;
       int ent;
       int disp;
       int hsize_reg;
       int hshift;
       // Set up the globals:  g_init_bits - initial number of bits
       g_init_bits = init_bits;
       // Set up the necessary values
       clear_flg = false;
       n_bits = g_init_bits;
       maxcode = MAXCODE(n_bits);
       ClearCode = 1 << (init_bits - 1);
       EOFCode = ClearCode + 1;
       free_ent = ClearCode + 2;
       char_init();
       ent = GIFNextPixel();
       hshift = 0;
       for (fcode = hsize; fcode < 65536; fcode *= 2)
           ++hshift;
       hshift = 8 - hshift;      // set hash code range bound
       hsize_reg = hsize;
       cl_hash(hsize_reg); // clear hash table
       output(ClearCode, outs);
       outer_loop:
       while ((c = GIFNextPixel()) != EOF) {
           fcode = (c << maxbits) + ent;
           i = (c << hshift) ^ ent;    // xor hashing
           if (htab[i] == fcode) {
               ent = codetab[i];
               continue;
           } else if (htab[i] >= 0)  // non-empty slot
           {
               disp = hsize_reg - i; // secondary hash (after G. Knott)
               if (i == 0)
                   disp = 1;
               do {
                   if ((i -= disp) < 0)
                       i += hsize_reg;
                   if (htab[i] == fcode) {
                       ent = codetab[i];
                       continue outer_loop;
                   }
               } while (htab[i] >= 0);
           }
           output(ent, outs);
           ent = c;
           if (free_ent < maxmaxcode) {
               codetab[i] = free_ent++;  // code -> hashtable
               htab[i] = fcode;
           } else
               cl_block(outs);
       }
       // Put out the final code.
       output(ent, outs);
       output(EOFCode, outs);
   }
   // Our own methods.
   /**
    *  Call this method after initialization to do the encoding
    */
   public synchronized void encode() throws IOException {
       encoding = true;
       iox = null;
       producer.startProduction(this);
       while (encoding)
           try {
               wait();
           } catch (InterruptedException e) {
           }
       if (iox != null)
           throw iox;
   }
   void encodeDone() throws IOException {
       int transparentIndex = -1;
       int transparentRgb = -1;
       // Put all the pixels into a hash table.
       colorHash = new IntHashtable();
       int index = 0;
       for (int row = 0; row < height; ++row) {
            for (int col = 0; col < width; ++col) {
               int rgb = rgbPixels[row][col];
               boolean isTransparent = ((rgb >>> 24) < 0x80);
               if (isTransparent) {
                   if (transparentIndex < 0) {
                       // First transparent color; remember it.
                       transparentIndex = index;
                       transparentRgb = rgb;
                   } else if (rgb != transparentRgb) {
                       // A second transparent color; replace it with
                       // the first one.
                       rgbPixels[row][col] = rgb = transparentRgb;
                   }
               }
               gifHashitem item =
                       (gifHashitem) colorHash.get(rgb);
               if (item == null) {
                   if (index >= 256)
                       throw new IOException("too many colors for a GIF");
                   item = new gifHashitem(
                           rgb, 1, index, isTransparent);
                   ++index;
                   colorHash.put(rgb, item);
               } else
                   ++item.count;
           }
       }
       // Figure out how many bits to use.
       int logColors;
       if (index <= 2)
           logColors = 1;
       else if (index <= 4)
           logColors = 2;
       else if (index <= 16)
           logColors = 4;
       else
           logColors = 8;
       // Turn colors into colormap entries.
       int mapSize = 1 << logColors;
       byte[] reds = new byte[mapSize];
       byte[] grns = new byte[mapSize];
       byte[] blus = new byte[mapSize];
       for (Enumeration e = colorHash.elements(); e.hasMoreElements();) {
           gifHashitem item = (gifHashitem) e.nextElement();
           reds[item.index] = (byte) ((item.rgb >> 16) & 0xff);
           grns[item.index] = (byte) ((item.rgb >> 8) & 0xff);
           blus[item.index] = (byte) (item.rgb & 0xff);
       }
       GIFEncode(
               out, width, height, interlace, (byte) 0, transparentIndex,
               logColors, reds, grns, blus);
   }
   private void encodeFinish() throws IOException {
       if (accumulate) {
           encodePixels(0, 0, width, height, accumulator, 0, width);
           accumulator = null;
           accumulate = false;
       }
   }
   void encodePixels(
           int x, int y, int w, int h, int[] rgbPixels, int off, int scansize)
           throws IOException {
       // Save the pixels.
       for (int row = 0; row < h; ++row)
           System.arraycopy(
                   rgbPixels, row * scansize + off,
                   this.rgbPixels[y + row], x, w);
   }
   private void encodePixelsWrapper(
           int x, int y, int w, int h, int[] rgbPixels, int off, int scansize)
           throws IOException {
       if (!started) {
           started = true;
           encodeStart(width, height);
           if ((hintflags & TOPDOWNLEFTRIGHT) == 0) {
               accumulate = true;
               accumulator = new int[width * height];
           }
       }
       if (accumulate)
           for (int row = 0; row < h; ++row)
               System.arraycopy(
                       rgbPixels, row * scansize + off,
                       accumulator, (y + row) * width + x,
                       w);
       else
           encodePixels(x, y, w, h, rgbPixels, off, scansize);
   }
   void encodeStart(int width, int height) throws IOException {
       this.width = width;
       this.height = height;
       rgbPixels = new int[height][width];
   }
   // Flush the packet to disk, and reset the accumulator
   void flush_char(OutputStream outs) throws IOException {
       if (a_count > 0) {
           outs.write(a_count);
           outs.write(accum, 0, a_count);
           a_count = 0;
       }
   }
   byte GetPixel(int x, int y) throws IOException {
       gifHashitem item =
               (gifHashitem) colorHash.get(rgbPixels[y][x]);
       if (item == null)
           throw new IOException("color not found");
       return (byte) item.index;
   }
   void GIFEncode(
           OutputStream outs, int Width, int Height, boolean Interlace, byte Background, int Transparent, int BitsPerPixel, byte[] Red, byte[] Green, byte[] Blue)
           throws IOException {
       byte B;
       int LeftOfs, TopOfs;
       int ColorMapSize;
       int InitCodeSize;
       int i;
       this.Width = Width;
       this.Height = Height;
       this.Interlace = Interlace;
       ColorMapSize = 1 << BitsPerPixel;
       LeftOfs = TopOfs = 0;
       // Calculate number of bits we are expecting
       CountDown = Width * Height;
       // Indicate which pass we are on (if interlace)
       Pass = 0;
       // The initial code size
       if (BitsPerPixel <= 1)
           InitCodeSize = 2;
       else
           InitCodeSize = BitsPerPixel;
       // Set up the current x and y position
       curx = 0;
       cury = 0;
       // Write the Magic header
       writeString(outs, "GIF89a");
       // Write out the screen width and height
       Putword(Width, outs);
       Putword(Height, outs);
       // Indicate that there is a global colour map
       B = (byte) 0x80;    // Yes, there is a color map
       // OR in the resolution
       B |= (byte) ((8 - 1) << 4);
       // Not sorted
       // OR in the Bits per Pixel
       B |= (byte) ((BitsPerPixel - 1));
       // Write it out
       Putbyte(B, outs);
       // Write out the Background colour
       Putbyte(Background, outs);
       // Pixel aspect ratio - 1:1.
       //Putbyte( (byte) 49, outs );
       // Java"s GIF reader currently has a bug, if the aspect ratio byte is
       // not zero it throws an ImageFormatException.  It doesn"t know that
       // 49 means a 1:1 aspect ratio.  Well, whatever, zero works with all
       // the other decoders I"ve tried so it probably doesn"t hurt.
       Putbyte((byte) 0, outs);
       // Write out the Global Colour Map
       for (i = 0; i < ColorMapSize; ++i) {
           Putbyte(Red[i], outs);
           Putbyte(Green[i], outs);
           Putbyte(Blue[i], outs);
       }
       // Write out extension for transparent colour index, if necessary.
       if (Transparent != -1) {
           Putbyte((byte) "!", outs);
           Putbyte((byte) 0xf9, outs);
           Putbyte((byte) 4, outs);
           Putbyte((byte) 1, outs);
           Putbyte((byte) 0, outs);
           Putbyte((byte) 0, outs);
           Putbyte((byte) Transparent, outs);
           Putbyte((byte) 0, outs);
       }
       // Write an Image separator
       Putbyte((byte) ",", outs);
       // Write the Image header
       Putword(LeftOfs, outs);
       Putword(TopOfs, outs);
       Putword(Width, outs);
       Putword(Height, outs);
       // Write out whether or not the image is interlaced
       if (Interlace)
           Putbyte((byte) 0x40, outs);
       else
           Putbyte((byte) 0x00, outs);
       // Write out the initial code size
       Putbyte((byte) InitCodeSize, outs);
       // Go and actually compress the data
       compress(InitCodeSize + 1, outs);
       // Write out a Zero-length packet (to end the series)
       Putbyte((byte) 0, outs);
       // Write the GIF file terminator
       Putbyte((byte) ";", outs);
   }
   // Return the next pixel from the image
   int GIFNextPixel() throws IOException {
       byte r;
       if (CountDown == 0)
           return EOF;
       --CountDown;
       r = GetPixel(curx, cury);
       BumpPixel();
       return r & 0xff;
   }
   public void imageComplete(int status) {
       producer.removeConsumer(this);
       if (status == ImageConsumer.IMAGEABORTED)
           iox = new IOException("image aborted");
       else {
           try {
               encodeFinish();
               encodeDone();
           } catch (IOException e) {
               iox = e;
           }
       }
       stop();
   }
   final int MAXCODE(int n_bits) {
       return (1 << n_bits) - 1;
   }
   void output(int code, OutputStream outs) throws IOException {
       cur_accum &= masks[cur_bits];
       if (cur_bits > 0)
           cur_accum |= (code << cur_bits);
       else
           cur_accum = code;
       cur_bits += n_bits;
       while (cur_bits >= 8) {
           char_out((byte) (cur_accum & 0xff), outs);
           cur_accum >>= 8;
           cur_bits -= 8;
       }
       // If the next entry is going to be too big for the code size,
       // then increase it, if possible.
       if (free_ent > maxcode || clear_flg) {
           if (clear_flg) {
               maxcode = MAXCODE(n_bits = g_init_bits);
               clear_flg = false;
           } else {
               ++n_bits;
               if (n_bits == maxbits)
                   maxcode = maxmaxcode;
               else
                   maxcode = MAXCODE(n_bits);
           }
       }
       if (code == EOFCode) {
           // At EOF, write the rest of the buffer.
           while (cur_bits > 0) {
               char_out((byte) (cur_accum & 0xff), outs);
               cur_accum >>= 8;
               cur_bits -= 8;
           }
           flush_char(outs);
       }
   }
   // Write out a byte to the GIF file
   void Putbyte(byte b, OutputStream outs) throws IOException {
       outs.write(b);
   }
   // Write out a word to the GIF file
   void Putword(int w, OutputStream outs) throws IOException {
       Putbyte((byte) (w & 0xff), outs);
       Putbyte((byte) ((w >> 8) & 0xff), outs);
   }
   public void setColorModel(ColorModel model) {
       // Ignore.
   }
   // Methods from ImageConsumer.
   public void setDimensions(int width, int height) {
       this.width = width;
       this.height = height;
   }
   public void setHints(int hintflags) {
       this.hintflags = hintflags;
   }
   public void setPixels(
           int x, int y, int w, int h, ColorModel model, byte[] pixels,
           int off, int scansize) {
       int[] rgbPixels = new int[w];
       for (int row = 0; row < h; ++row) {
           int rowOff = off + row * scansize;
           for (int col = 0; col < w; ++col)
               rgbPixels[col] = model.getRGB(pixels[rowOff + col] & 0xff);
           try {
               encodePixelsWrapper(x, y + row, w, 1, rgbPixels, 0, w);
           } catch (IOException e) {
               iox = e;
               stop();
               return;
           }
       }
   }
   public void setPixels(
           int x, int y, int w, int h, ColorModel model, int[] pixels,
           int off, int scansize) {
       if (model == rgbModel) {
           try {
               encodePixelsWrapper(x, y, w, h, pixels, off, scansize);
           } catch (IOException e) {
               iox = e;
               stop();
               return;
           }
       } else {
           int[] rgbPixels = new int[w];
           for (int row = 0; row < h; ++row) {
               int rowOff = off + row * scansize;
               for (int col = 0; col < w; ++col)
                   rgbPixels[col] = model.getRGB(pixels[rowOff + col]);
               try {
                   encodePixelsWrapper(x, y + row, w, 1, rgbPixels, 0, w);
               } catch (IOException e) {
                   iox = e;
                   stop();
                   return;
               }
           }
       }
   }
   public void setProperties(Hashtable props) {
       this.props = props;
   }
   private synchronized void stop() {
       encoding = false;
       notifyAll();
   }
   static void writeString(OutputStream out, String str) throws IOException {
       byte[] buf = str.getBytes();
       out.write(buf);
   }

}


 </source>   



GIFEncoder is a class which takes an image and saves it to a stream using the GIF file format

<source lang="java"> /*

* @(#)GIFEncoder.java    0.90 4/21/96 Adam Doppelt
*/

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 ( */

public class GIFEncoder {

   short width_, height_;
   int numColors_;
   byte pixels_[], colors_[];
   
   ScreenDescriptor sd_;
   ImageDescriptor id_;
   

/**

* Construct a GIFEncoder. The constructor will convert the image to
* an indexed color array. This may take some time.<P>
* 
* @param image The image to encode. The image must 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 {
 width_ = (short)image.getWidth(null);
 height_ = (short)image.getHeight(null);
 int values[] = new int[width_ * height_];
 PixelGrabber grabber = new PixelGrabber(
     image, 0, 0, width_, height_, values, 0, width_);
 
 try {
     if(grabber.grabPixels() != true)
   throw new AWTException("Grabber returned false: " +
              grabber.status());
 }
 catch (InterruptedException e) { ; }
 
 byte r[][] = new byte[width_][height_];
 byte g[][] = new byte[width_][height_];
 byte b[][] = new byte[width_][height_];
 int index = 0;
 for (int y = 0; y < height_; ++y)
     for (int x = 0; x < 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. This may take some time.<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 {
 width_ = (short)(r.length);
 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.
* This may take some time.<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(width_, height_,
              numColors_);
 sd.Write(output);
 output.write(colors_, 0, colors_.length);
 ImageDescriptor id = new ImageDescriptor(width_, height_, ",");
 id.Write(output);
 byte codesize = BitUtils.BitsNeeded(numColors_);
 if (codesize == 1)
     ++codesize;
 output.write(codesize);
 LZWCompressor.LZWCompress(output, codesize, 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 {
 pixels_ = new byte[width_ * height_];
 colors_ = new byte[256 * 3];
 int colornum = 0;
 for (int x = 0; x < width_; ++x) {
     for (int y = 0; y < height_; ++y) {
   int search;
   for (search = 0; search < colornum; ++search)
       if (colors_[search * 3]     == r[x][y] &&
     colors_[search * 3 + 1] == g[x][y] &&
     colors_[search * 3 + 2] == b[x][y])
     break;
   
   if (search > 255)
       throw new AWTException("Too many colors.");
   pixels_[y * width_ + x] = (byte)search;
   
   if (search == colornum) {
       colors_[search * 3]     = r[x][y];
       colors_[search * 3 + 1] = g[x][y];
       colors_[search * 3 + 2] = b[x][y];
       ++colornum;
   }
     }
 }
 numColors_ = 1 << BitUtils.BitsNeeded(colornum);
 byte copy[] = new byte[numColors_ * 3];
 System.arraycopy(colors_, 0, copy, 0, numColors_ * 3);
 colors_ = copy;
   }
   

} class BitFile {

   OutputStream output_;
   byte buffer_[];
   int index_, bitsLeft_;
   public BitFile(OutputStream output) {
 output_ = output;
 buffer_ = new byte[256];
 index_ = 0;
 bitsLeft_ = 8;
   }
   public void Flush() throws IOException {
 int numBytes = index_ + (bitsLeft_ == 8 ? 0 : 1);
 if (numBytes > 0) {
     output_.write(numBytes);
     output_.write(buffer_, 0, numBytes);
     buffer_[0] = 0;
     index_ = 0;
     bitsLeft_ = 8;
 }
   }
   public void WriteBits(int bits, int numbits) throws IOException {
 int bitsWritten = 0;
 int numBytes = 255;
 do {
     if ((index_ == 254 && bitsLeft_ == 0) || index_ > 254) {
   output_.write(numBytes);
   output_.write(buffer_, 0, numBytes);
   buffer_[0] = 0;
   index_ = 0;
   bitsLeft_ = 8;
     }
     if (numbits <= bitsLeft_) {
   buffer_[index_] |= (bits & ((1 << numbits) - 1)) <<
       (8 - bitsLeft_);
   bitsWritten += numbits;
   bitsLeft_ -= numbits;
   numbits = 0;
     }
     else {
   buffer_[index_] |= (bits & ((1 << bitsLeft_) - 1)) <<
       (8 - bitsLeft_);
   bitsWritten += bitsLeft_;
   bits >>= bitsLeft_;
   numbits -= bitsLeft_;
   buffer_[++index_] = 0;
   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() {
 strChr_ = new byte[MAXSTR];
 strNxt_ = new short[MAXSTR];
 strHsh_ = new short[HASHSIZE];    
   }
   public int AddCharString(short index, byte b) {
 int hshidx;
 if (numStrings_ >= MAXSTR)
     return 0xFFFF;
 
 hshidx = Hash(index, b);
 while (strHsh_[hshidx] != HASH_FREE)
     hshidx = (hshidx + HASHSTEP) % HASHSIZE;
 
 strHsh_[hshidx] = numStrings_;
 strChr_[numStrings_] = b;
 strNxt_[numStrings_] = (index != HASH_FREE) ? index : NEXT_FIRST;
 return numStrings_++;
   }
   
   public short FindCharString(short index, byte b) {
 int hshidx, nxtidx;
 if (index == HASH_FREE)
     return b;
 hshidx = Hash(index, b);
 while ((nxtidx = strHsh_[hshidx]) != HASH_FREE) {
     if (strNxt_[nxtidx] == index && strChr_[nxtidx] == b)
   return (short)nxtidx;
     hshidx = (hshidx + HASHSTEP) % HASHSIZE;
 }
 return (short)0xFFFF;
   }
   public void ClearTable(int codesize) {
 numStrings_ = 0;
 
 for (int q = 0; q < HASHSIZE; q++) {
     strHsh_[q] = HASH_FREE;
 }
 int w = (1 << codesize) + RES_CODES;
 for (int q = 0; q < w; q++)
     AddCharString((short)0xFFFF, (byte)q);
   }
   
   static public int Hash(short index, byte lastbyte) {
 return ((int)((short)(lastbyte << 8) ^ index) & 0xFFFF) % HASHSIZE;
   }

} class LZWCompressor {

   public static void LZWCompress(OutputStream output, int codesize,
          byte toCompress[]) throws IOException {
 byte c;
 short index;
 int clearcode, endofinfo, numbits, limit, errcode;
 short prefix = (short)0xFFFF;
 BitFile bitFile = new BitFile(output);
 LZWStringTable strings = new LZWStringTable();
 clearcode = 1 << codesize;
 endofinfo = clearcode + 1;
   
 numbits = codesize + 1;
 limit = (1 << numbits) - 1;
 
 strings.ClearTable(codesize);
 bitFile.WriteBits(clearcode, numbits);
 for (int loop = 0; loop < toCompress.length; ++loop) {
     c = toCompress[loop];
     if ((index = strings.FindCharString(prefix, c)) != -1)
   prefix = index;
     else {
   bitFile.WriteBits(prefix, numbits);
   if (strings.AddCharString(prefix, c) > limit) {
       if (++numbits > 12) {
     bitFile.WriteBits(clearcode, numbits - 1);
     strings.ClearTable(codesize);
     numbits = codesize + 1;
       }
       limit = (1 << numbits) - 1;
   }
   
   prefix = (short)((short)c & 0xFF);
     }
 }
 
 if (prefix != -1)
     bitFile.WriteBits(prefix, numbits);
 
 bitFile.WriteBits(endofinfo, numbits);
 bitFile.Flush();
   }

} class ScreenDescriptor {

   public short localScreenWidth_, localScreenHeight_;
   private byte byte_;
   public byte backgroundColorIndex_, pixelAspectRatio_;
   public ScreenDescriptor(short width, short height, int numColors) {
 localScreenWidth_ = width;
 localScreenHeight_ = height;
 SetGlobalColorTableSize((byte)(BitUtils.BitsNeeded(numColors) - 1));
 SetGlobalColorTableFlag((byte)1);
 SetSortFlag((byte)0);
 SetColorResolution((byte)7);
 backgroundColorIndex_ = 0;
 pixelAspectRatio_ = 0;
   }
   public void Write(OutputStream output) throws IOException {
 BitUtils.WriteWord(output, localScreenWidth_);
 BitUtils.WriteWord(output, localScreenHeight_);
 output.write(byte_);
 output.write(backgroundColorIndex_);
 output.write(pixelAspectRatio_);
   }
   public void SetGlobalColorTableSize(byte num) {
 byte_ |= (num & 7);
   }
   public void SetSortFlag(byte num) {
 byte_ |= (num & 1) << 3;
   }
   public void SetColorResolution(byte num) {
 byte_ |= (num & 7) << 4;
   }
   
   public void SetGlobalColorTableFlag(byte num) {
 byte_ |= (num & 1) << 7;
   }

} class ImageDescriptor {

   public byte separator_;
   public short leftPosition_, topPosition_, width_, height_;
   private byte byte_;
   public ImageDescriptor(short width, short height, char separator) {
 separator_ = (byte)separator;
 leftPosition_ = 0;
 topPosition_ = 0;
 width_ = width;
 height_ = height;
 SetLocalColorTableSize((byte)0);
 SetReserved((byte)0);
 SetSortFlag((byte)0);
 SetInterlaceFlag((byte)0);
 SetLocalColorTableFlag((byte)0);
   }
   
   public void Write(OutputStream output) throws IOException {
 output.write(separator_);
 BitUtils.WriteWord(output, leftPosition_);
 BitUtils.WriteWord(output, topPosition_);
 BitUtils.WriteWord(output, width_);
 BitUtils.WriteWord(output, height_);    
 output.write(byte_);
   }
   public void SetLocalColorTableSize(byte num) {
 byte_ |= (num & 7);
   }
   public void SetReserved(byte num) {
 byte_ |= (num & 3) << 3;
   }
   public void SetSortFlag(byte num) {
 byte_ |= (num & 1) << 5;
   }
   
   public void SetInterlaceFlag(byte num) {
 byte_ |= (num & 1) << 6;
   }
   public void SetLocalColorTableFlag(byte num) {
 byte_ |= (num & 1) << 7;
   }

} class BitUtils {

   public static byte BitsNeeded(int n) {
 byte ret = 1;
 if (n-- == 0)
     return 0;
 while ((n >>= 1) != 0)
     ++ret;
 
 return ret;
   }    
   public static void WriteWord(OutputStream output,
        short w) throws IOException {
 output.write(w & 0xFF);
 output.write((w >> 8) & 0xFF);
   }
   
   static void WriteString(OutputStream output,
         String string) throws IOException {
 for (int loop = 0; loop < string.length(); ++loop)
     output.write((byte)(string.charAt(loop)));
   }

}


 </source>   



Gif Encoder - writes out an image as a GIF.

<source lang="java">

import java.awt.Image; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.PixelGrabber; import java.io.IOException; import java.io.OutputStream;

/** GifEncoder - writes out an image as a GIF.

*
* Transparency handling and variable bit size courtesy of Jack Palevich.
*
* Copyright (C) 1996 by Jef Poskanzer <jef@acme.ru>.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS"" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Visit the ACME Labs Java page for up-to-date versions of this and other
* fine Java utilities: http://www.acme.ru/java/
*/

public class GifEncoder {

 private boolean interlace = false;
 private int width, height;
 private byte[] pixels;
 private byte[] r, g, b; // the color look-up table
 private int pixelIndex;
 private int numPixels;
 private int transparentPixel = -1; // hpm
 /**
  *  Constructs a new GifEncoder. 
  * @param width The image width.
  * @param height  The image height.
  * @param pixels  The pixel data.
  * @param r   The red look-up table.
  * @param g   The green look-up table.
  * @param b   The blue look-up table.
  */
 public GifEncoder(int width, int height, byte[] pixels, byte[] r, byte[] g, byte[] b) {
   this.width = width;
   this.height = height;
   this.pixels = pixels;
   this.r = r;
   this.g = g;
   this.b = b;
   interlace = false;
   pixelIndex = 0;
   numPixels = width * height;
 }
 /** Constructs a new GifEncoder using an 8-bit AWT Image.
  The image is assumed to be fully loaded. */
 public GifEncoder(Image img) {
   width = img.getWidth(null);
   height = img.getHeight(null);
   pixels = new byte[width * height];
   PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, false);
   try {
     pg.grabPixels();
   } catch (InterruptedException e) {
     System.err.println(e);
   }
   ColorModel cm = pg.getColorModel();
   if (cm instanceof IndexColorModel) {
     pixels = (byte[]) (pg.getPixels());
     // hpm
     IndexColorModel icm = (IndexColorModel) cm;
     setTransparentPixel(icm.getTransparentPixel());
   } else
     throw new IllegalArgumentException("IMAGE_ERROR");
   IndexColorModel m = (IndexColorModel) cm;
   int mapSize = m.getMapSize();
   r = new byte[mapSize];
   g = new byte[mapSize];
   b = new byte[mapSize];
   m.getReds(r);
   m.getGreens(g);
   m.getBlues(b);
   interlace = false;
   pixelIndex = 0;
   numPixels = width * height;
 }
 /** Saves the image as a GIF file. */
 public void write(OutputStream out) throws IOException {
   // Figure out how many bits to use.
   int numColors = r.length;
   int BitsPerPixel;
   if (numColors <= 2)
     BitsPerPixel = 1;
   else if (numColors <= 4)
     BitsPerPixel = 2;
   else if (numColors <= 16)
     BitsPerPixel = 4;
   else
     BitsPerPixel = 8;
   int ColorMapSize = 1 << BitsPerPixel;
   byte[] reds = new byte[ColorMapSize];
   byte[] grns = new byte[ColorMapSize];
   byte[] blus = new byte[ColorMapSize];
   for (int i = 0; i < numColors; i++) {
     reds[i] = r[i];
     grns[i] = g[i];
     blus[i] = b[i];
   }
   // hpm
   GIFEncode(out, width, height, interlace, (byte) 0, getTransparentPixel(), BitsPerPixel, reds, grns, blus);
 }
 // hpm
 public void setTransparentPixel(int pixel) {
   transparentPixel = pixel;
 }
 // hpm
 public int getTransparentPixel() {
   return transparentPixel;
 }
 static void writeString(OutputStream out, String str) throws IOException {
   byte[] buf = str.getBytes();
   out.write(buf);
 }
 // Adapted from ppmtogif, which is based on GIFENCOD by David
 // Rowley <mgardi@watdscu.waterloo.edu>.  Lempel-Zim compression
 // based on "compress".
 int Width, Height;
 boolean Interlace;
 void GIFEncode(OutputStream outs, int Width, int Height, boolean Interlace,
     byte Background, int Transparent, int BitsPerPixel, byte[] Red,
     byte[] Green, byte[] Blue) throws IOException {
   byte B;
   int LeftOfs, TopOfs;
   int ColorMapSize;
   int InitCodeSize;
   int i;
   this.Width = Width;
   this.Height = Height;
   this.Interlace = Interlace;
   ColorMapSize = 1 << BitsPerPixel;
   LeftOfs = TopOfs = 0;
   // The initial code size
   if (BitsPerPixel <= 1)
     InitCodeSize = 2;
   else
     InitCodeSize = BitsPerPixel;
   // Write the Magic header
   writeString(outs, "GIF89a");
   // Write out the screen width and height
   Putword(Width, outs);
   Putword(Height, outs);
   // Indicate that there is a global colour map
   B = (byte) 0x80; // Yes, there is a color map
   // OR in the resolution
   B |= (byte) ((8 - 1) << 4);
   // Not sorted
   // OR in the Bits per Pixel
   B |= (byte) ((BitsPerPixel - 1));
   // Write it out
   Putbyte(B, outs);
   // Write out the Background colour
   Putbyte(Background, outs);
   // Pixel aspect ratio - 1:1.
   //Putbyte( (byte) 49, outs );
   // Java"s GIF reader currently has a bug, if the aspect ratio byte is
   // not zero it throws an ImageFormatException.  It doesn"t know that
   // 49 means a 1:1 aspect ratio.  Well, whatever, zero works with all
   // the other decoders I"ve tried so it probably doesn"t hurt.
   Putbyte((byte) 0, outs);
   // Write out the Global Colour Map
   for (i = 0; i < ColorMapSize; ++i) {
     Putbyte(Red[i], outs);
     Putbyte(Green[i], outs);
     Putbyte(Blue[i], outs);
   }
   // Write out extension for transparent colour index, if necessary.
   if (Transparent != -1) {
     Putbyte((byte) "!", outs);
     Putbyte((byte) 0xf9, outs);
     Putbyte((byte) 4, outs);
     Putbyte((byte) 1, outs);
     Putbyte((byte) 0, outs);
     Putbyte((byte) 0, outs);
     Putbyte((byte) Transparent, outs);
     Putbyte((byte) 0, outs);
   }
   // Write an Image separator
   Putbyte((byte) ",", outs);
   // Write the Image header
   Putword(LeftOfs, outs);
   Putword(TopOfs, outs);
   Putword(Width, outs);
   Putword(Height, outs);
   // Write out whether or not the image is interlaced
   if (Interlace)
     Putbyte((byte) 0x40, outs);
   else
     Putbyte((byte) 0x00, outs);
   // Write out the initial code size
   Putbyte((byte) InitCodeSize, outs);
   // Go and actually compress the data
   compress(InitCodeSize + 1, outs);
   // Write out a Zero-length packet (to end the series)
   Putbyte((byte) 0, outs);
   // Write the GIF file terminator
   Putbyte((byte) ";", outs);
 }
 static final int EOF = -1;
 // Return the next pixel from the image
 int GIFNextPixel() throws IOException {
   if (pixelIndex == numPixels)
     return EOF;
   else
     return ((byte[]) pixels)[pixelIndex++] & 0xff;
 }
 // Write out a word to the GIF file
 void Putword(int w, OutputStream outs) throws IOException {
   Putbyte((byte) (w & 0xff), outs);
   Putbyte((byte) ((w >> 8) & 0xff), outs);
 }
 // Write out a byte to the GIF file
 void Putbyte(byte b, OutputStream outs) throws IOException {
   outs.write(b);
 }
 // GIFCOMPR.C       - GIF Image compression routines
 //
 // Lempel-Ziv compression based on "compress".  GIF modifications by
 // David Rowley (mgardi@watdcsu.waterloo.edu)
 // General DEFINEs
 static final int BITS = 12;
 static final int HSIZE = 5003; // 80% occupancy
 // GIF Image compression - modified "compress"
 //
 // Based on: compress.c - File compression ala IEEE Computer, June 1984.
 //
 // By Authors:  Spencer W. Thomas      (decvax!harpo!utah-cs!utah-gr!thomas)
 //              Jim McKie              (decvax!mcvax!jim)
 //              Steve Davies           (decvax!vax135!petsd!peora!srd)
 //              Ken Turkowski          (decvax!decwrl!turtlevax!ken)
 //              James A. Woods         (decvax!ihnp4!ames!jaw)
 //              Joe Orost              (decvax!vax135!petsd!joe)
 int n_bits; // number of bits/code
 int maxbits = BITS; // user settable max # bits/code
 int maxcode; // maximum code, given n_bits
 int maxmaxcode = 1 << BITS; // should NEVER generate this code
 final int MAXCODE(int n_bits) {
   return (1 << n_bits) - 1;
 }
 int[] htab = new int[HSIZE];
 int[] codetab = new int[HSIZE];
 int hsize = HSIZE; // for dynamic table sizing
 int free_ent = 0; // first unused entry
 // block compression parameters -- after all codes are used up,
 // and compression rate changes, start over.
 boolean clear_flg = false;
 // Algorithm:  use open addressing double hashing (no chaining) on the
 // prefix code / next character combination.  We do a variant of Knuth"s
 // algorithm D (vol. 3, sec. 6.4) along with G. Knott"s relatively-prime
 // secondary probe.  Here, the modular division first probe is gives way
 // to a faster exclusive-or manipulation.  Also do block compression with
 // an adaptive reset, whereby the code table is cleared when the compression
 // ratio decreases, but after the table fills.  The variable-length output
 // codes are re-sized at this point, and a special CLEAR code is generated
 // for the decompressor.  Late addition:  construct the table according to
 // file size for noticeable speed improvement on small files.  Please direct
 // questions about this implementation to ames!jaw.
 int g_init_bits;
 int ClearCode;
 int EOFCode;
 void compress(int init_bits, OutputStream outs) throws IOException {
   int fcode;
   int i /* = 0 */;
   int c;
   int ent;
   int disp;
   int hsize_reg;
   int hshift;
   // Set up the globals:  g_init_bits - initial number of bits
   g_init_bits = init_bits;
   // Set up the necessary values
   clear_flg = false;
   n_bits = g_init_bits;
   maxcode = MAXCODE(n_bits);
   ClearCode = 1 << (init_bits - 1);
   EOFCode = ClearCode + 1;
   free_ent = ClearCode + 2;
   char_init();
   ent = GIFNextPixel();
   hshift = 0;
   for (fcode = hsize; fcode < 65536; fcode *= 2)
     ++hshift;
   hshift = 8 - hshift; // set hash code range bound
   hsize_reg = hsize;
   cl_hash(hsize_reg); // clear hash table
   output(ClearCode, outs);
   outer_loop: while ((c = GIFNextPixel()) != EOF) {
     fcode = (c << maxbits) + ent;
     i = (c << hshift) ^ ent; // xor hashing
     if (htab[i] == fcode) {
       ent = codetab[i];
       continue;
     } else if (htab[i] >= 0) // non-empty slot
     {
       disp = hsize_reg - i; // secondary hash (after G. Knott)
       if (i == 0)
         disp = 1;
       do {
         if ((i -= disp) < 0)
           i += hsize_reg;
         if (htab[i] == fcode) {
           ent = codetab[i];
           continue outer_loop;
         }
       } while (htab[i] >= 0);
     }
     output(ent, outs);
     ent = c;
     if (free_ent < maxmaxcode) {
       codetab[i] = free_ent++; // code -> hashtable
       htab[i] = fcode;
     } else
       cl_block(outs);
   }
   // Put out the final code.
   output(ent, outs);
   output(EOFCode, outs);
 }
 // output
 //
 // Output the given code.
 // Inputs:
 //      code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes
 //              that n_bits =< wordsize - 1.
 // Outputs:
 //      Outputs code to the file.
 // Assumptions:
 //      Chars are 8 bits long.
 // Algorithm:
 //      Maintain a BITS character long buffer (so that 8 codes will
 // fit in it exactly).  Use the VAX insv instruction to insert each
 // code in turn.  When the buffer fills up empty it and start over.
 int cur_accum = 0;
 int cur_bits = 0;
 int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F,
     0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF,
     0x7FFF, 0xFFFF };
 void output(int code, OutputStream outs) throws IOException {
   cur_accum &= masks[cur_bits];
   if (cur_bits > 0)
     cur_accum |= (code << cur_bits);
   else
     cur_accum = code;
   cur_bits += n_bits;
   while (cur_bits >= 8) {
     char_out((byte) (cur_accum & 0xff), outs);
     cur_accum >>= 8;
     cur_bits -= 8;
   }
   // If the next entry is going to be too big for the code size,
   // then increase it, if possible.
   if (free_ent > maxcode || clear_flg) {
     if (clear_flg) {
       maxcode = MAXCODE(n_bits = g_init_bits);
       clear_flg = false;
     } else {
       ++n_bits;
       if (n_bits == maxbits)
         maxcode = maxmaxcode;
       else
         maxcode = MAXCODE(n_bits);
     }
   }
   if (code == EOFCode) {
     // At EOF, write the rest of the buffer.
     while (cur_bits > 0) {
       char_out((byte) (cur_accum & 0xff), outs);
       cur_accum >>= 8;
       cur_bits -= 8;
     }
     flush_char(outs);
   }
 }
 // Clear out the hash table
 // table clear for block compress
 void cl_block(OutputStream outs) throws IOException {
   cl_hash(hsize);
   free_ent = ClearCode + 2;
   clear_flg = true;
   output(ClearCode, outs);
 }
 // reset code table
 void cl_hash(int hsize) {
   for (int i = 0; i < hsize; ++i)
     htab[i] = -1;
 }
 // GIF Specific routines
 // Number of characters so far in this "packet"
 int a_count;
 // Set up the "byte output" routine
 void char_init() {
   a_count = 0;
 }
 // Define the storage for the packet accumulator
 byte[] accum = new byte[256];
 // Add a character to the end of the current packet, and if it is 254
 // characters, flush the packet to disk.
 void char_out(byte c, OutputStream outs) throws IOException {
   accum[a_count++] = c;
   if (a_count >= 254)
     flush_char(outs);
 }
 // Flush the packet to disk, and reset the accumulator
 void flush_char(OutputStream outs) throws IOException {
   if (a_count > 0) {
     outs.write(a_count);
     outs.write(accum, 0, a_count);
     a_count = 0;
   }
 }

} class GifEncoderHashitem {

 public int rgb;
 public int count;
 public int index;
 public boolean isTransparent;
 public GifEncoderHashitem(int rgb, int count, int index,
     boolean isTransparent) {
   this.rgb = rgb;
   this.count = count;
   this.index = index;
   this.isTransparent = isTransparent;
 }

}

 </source>   



Gif file Encoder

<source lang="java"> import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; /**

* Class GifDecoder - Decodes a GIF file into one or more frames. 
*
*
 *  Example:
 *     GifDecoder d = new GifDecoder();
 *     d.read("sample.gif");
 *     int n = d.getFrameCount();
 *     for (int i = 0; i < n; i++) {
 *        BufferedImage frame = d.getFrame(i);  // frame i
 *        int t = d.getDelay(i);  // display duration of frame in milliseconds
 *        // do something with frame
 *     }
 * 
* 
* No copyright asserted on the source code of this class. May be used for any
* purpose, however, refer to the Unisys LZW patent for any additional
* restrictions. Please forward any corrections to kweiner@fmsware.ru.
* 
* @author Kevin Weiner, FM Software; LZW decoder adapted from John Cristy"s
*         ImageMagick.
* @version 1.03 November 2003
* 
*/

public class GifDecoder {

 /**
  * File read status: No errors.
  */
 public static final int STATUS_OK = 0;
 /**
  * File read status: Error decoding file (may be partially decoded)
  */
 public static final int STATUS_FORMAT_ERROR = 1;
 /**
  * File read status: Unable to open source.
  */
 public static final int STATUS_OPEN_ERROR = 2;
 protected BufferedInputStream in;
 protected int status;
 protected int width; // full image width
 protected int height; // full image height
 protected boolean gctFlag; // global color table used
 protected int gctSize; // size of global color table
 protected int loopCount = 1; // iterations; 0 = repeat forever
 protected int[] gct; // global color table
 protected int[] lct; // local color table
 protected int[] act; // active color table
 protected int bgIndex; // background color index
 protected int bgColor; // background color
 protected int lastBgColor; // previous bg color
 protected int pixelAspect; // pixel aspect ratio
 protected boolean lctFlag; // local color table flag
 protected boolean interlace; // interlace flag
 protected int lctSize; // local color table size
 protected int ix, iy, iw, ih; // current image rectangle
 protected Rectangle lastRect; // last image rect
 protected BufferedImage image; // current frame
 protected BufferedImage lastImage; // previous frame
 protected byte[] block = new byte[256]; // current data block
 protected int blockSize = 0; // block size
 // last graphic control extension info
 protected int dispose = 0;
 // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
 protected int lastDispose = 0;
 protected boolean transparency = false; // use transparent color
 protected int delay = 0; // delay in milliseconds
 protected int transIndex; // transparent color index
 protected static final int MaxStackSize = 4096;
 // max decoder pixel stack size
 // LZW decoder working arrays
 protected short[] prefix;
 protected byte[] suffix;
 protected byte[] pixelStack;
 protected byte[] pixels;
 protected ArrayList frames; // frames read from current file
 protected int frameCount;
 static class GifFrame {
   public GifFrame(BufferedImage im, int del) {
     image = im;
     delay = del;
   }
   public BufferedImage image;
   public int delay;
 }
 /**
  * Gets display duration for specified frame.
  * 
  * @param n
  *          int index of frame
  * @return delay in milliseconds
  */
 public int getDelay(int n) {
   //
   delay = -1;
   if ((n >= 0) && (n < frameCount)) {
     delay = ((GifFrame) frames.get(n)).delay;
   }
   return delay;
 }
 /**
  * Gets the number of frames read from file.
  * 
  * @return frame count
  */
 public int getFrameCount() {
   return frameCount;
 }
 /**
  * Gets the first (or only) image read.
  * 
  * @return BufferedImage containing first frame, or null if none.
  */
 public BufferedImage getImage() {
   return getFrame(0);
 }
 /**
  * Gets the "Netscape" iteration count, if any. A count of 0 means repeat
  * indefinitiely.
  * 
  * @return iteration count if one was specified, else 1.
  */
 public int getLoopCount() {
   return loopCount;
 }
 /**
  * Creates new frame image from current data (and previous frames as specified
  * by their disposition codes).
  */
 protected void setPixels() {
   // expose destination image"s pixels as int array
   int[] dest = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
   // fill in starting image contents based on last image"s dispose code
   if (lastDispose > 0) {
     if (lastDispose == 3) {
       // use image before last
       int n = frameCount - 2;
       if (n > 0) {
         lastImage = getFrame(n - 1);
       } else {
         lastImage = null;
       }
     }
     if (lastImage != null) {
       int[] prev = ((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData();
       System.arraycopy(prev, 0, dest, 0, width * height);
       // copy pixels
       if (lastDispose == 2) {
         // fill last image rect area with background color
         Graphics2D g = image.createGraphics();
         Color c = null;
         if (transparency) {
           c = new Color(0, 0, 0, 0); // assume background is transparent
         } else {
           c = new Color(lastBgColor); // use given background color
         }
         g.setColor(c);
         g.setComposite(AlphaComposite.Src); // replace area
         g.fill(lastRect);
         g.dispose();
       }
     }
   }
   // copy each source line to the appropriate place in the destination
   int pass = 1;
   int inc = 8;
   int iline = 0;
   for (int i = 0; i < ih; i++) {
     int line = i;
     if (interlace) {
       if (iline >= ih) {
         pass++;
         switch (pass) {
         case 2:
           iline = 4;
           break;
         case 3:
           iline = 2;
           inc = 4;
           break;
         case 4:
           iline = 1;
           inc = 2;
         }
       }
       line = iline;
       iline += inc;
     }
     line += iy;
     if (line < height) {
       int k = line * width;
       int dx = k + ix; // start of line in dest
       int dlim = dx + iw; // end of dest line
       if ((k + width) < dlim) {
         dlim = k + width; // past dest edge
       }
       int sx = i * iw; // start of line in source
       while (dx < dlim) {
         // map color and insert in destination
         int index = ((int) pixels[sx++]) & 0xff;
         int c = act[index];
         if (c != 0) {
           dest[dx] = c;
         }
         dx++;
       }
     }
   }
 }
 /**
  * Gets the image contents of frame n.
  * 
  * @return BufferedImage representation of frame, or null if n is invalid.
  */
 public BufferedImage getFrame(int n) {
   BufferedImage im = null;
   if ((n >= 0) && (n < frameCount)) {
     im = ((GifFrame) frames.get(n)).image;
   }
   return im;
 }
 /**
  * Gets image size.
  * 
  * @return GIF image dimensions
  */
 public Dimension getFrameSize() {
   return new Dimension(width, height);
 }
 /**
  * Reads GIF image from stream
  * 
  * @param BufferedInputStream
  *          containing GIF file.
  * @return read status code (0 = no errors)
  */
 public int read(BufferedInputStream is) {
   init();
   if (is != null) {
     in = is;
     readHeader();
     if (!err()) {
       readContents();
       if (frameCount < 0) {
         status = STATUS_FORMAT_ERROR;
       }
     }
   } else {
     status = STATUS_OPEN_ERROR;
   }
   try {
     is.close();
   } catch (IOException e) {
   }
   return status;
 }
 /**
  * Reads GIF image from stream
  * 
  * @param InputStream
  *          containing GIF file.
  * @return read status code (0 = no errors)
  */
 public int read(InputStream is) {
   init();
   if (is != null) {
     if (!(is instanceof BufferedInputStream))
       is = new BufferedInputStream(is);
     in = (BufferedInputStream) is;
     readHeader();
     if (!err()) {
       readContents();
       if (frameCount < 0) {
         status = STATUS_FORMAT_ERROR;
       }
     }
   } else {
     status = STATUS_OPEN_ERROR;
   }
   try {
     is.close();
   } catch (IOException e) {
   }
   return status;
 }
 /**
  * Reads GIF file from specified file/URL source (URL assumed if name contains
  * ":/" or "file:")
  * 
  * @param name
  *          String containing source
  * @return read status code (0 = no errors)
  */
 public int read(String name) {
   status = STATUS_OK;
   try {
     name = name.trim().toLowerCase();
     if ((name.indexOf("file:") >= 0) || (name.indexOf(":/") > 0)) {
       URL url = new URL(name);
       in = new BufferedInputStream(url.openStream());
     } else {
       in = new BufferedInputStream(new FileInputStream(name));
     }
     status = read(in);
   } catch (IOException e) {
     status = STATUS_OPEN_ERROR;
   }
   return status;
 }
 /**
  * Decodes LZW image data into pixel array. Adapted from John Cristy"s
  * ImageMagick.
  */
 protected void decodeImageData() {
   int NullCode = -1;
   int npix = iw * ih;
   int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi;
   if ((pixels == null) || (pixels.length < npix)) {
     pixels = new byte[npix]; // allocate new pixel array
   }
   if (prefix == null)
     prefix = new short[MaxStackSize];
   if (suffix == null)
     suffix = new byte[MaxStackSize];
   if (pixelStack == null)
     pixelStack = new byte[MaxStackSize + 1];
   // Initialize GIF data stream decoder.
   data_size = read();
   clear = 1 << data_size;
   end_of_information = clear + 1;
   available = clear + 2;
   old_code = NullCode;
   code_size = data_size + 1;
   code_mask = (1 << code_size) - 1;
   for (code = 0; code < clear; code++) {
     prefix[code] = 0;
     suffix[code] = (byte) code;
   }
   // Decode GIF pixel stream.
   datum = bits = count = first = top = pi = bi = 0;
   for (i = 0; i < npix;) {
     if (top == 0) {
       if (bits < code_size) {
         // Load bytes until there are enough bits for a code.
         if (count == 0) {
           // Read a new data block.
           count = readBlock();
           if (count <= 0)
             break;
           bi = 0;
         }
         datum += (((int) block[bi]) & 0xff) << bits;
         bits += 8;
         bi++;
         count--;
         continue;
       }
       // Get the next code.
       code = datum & code_mask;
       datum >>= code_size;
       bits -= code_size;
       // Interpret the code
       if ((code > available) || (code == end_of_information))
         break;
       if (code == clear) {
         // Reset decoder.
         code_size = data_size + 1;
         code_mask = (1 << code_size) - 1;
         available = clear + 2;
         old_code = NullCode;
         continue;
       }
       if (old_code == NullCode) {
         pixelStack[top++] = suffix[code];
         old_code = code;
         first = code;
         continue;
       }
       in_code = code;
       if (code == available) {
         pixelStack[top++] = (byte) first;
         code = old_code;
       }
       while (code > clear) {
         pixelStack[top++] = suffix[code];
         code = prefix[code];
       }
       first = ((int) suffix[code]) & 0xff;
       // Add a new string to the string table,
       if (available >= MaxStackSize)
         break;
       pixelStack[top++] = (byte) first;
       prefix[available] = (short) old_code;
       suffix[available] = (byte) first;
       available++;
       if (((available & code_mask) == 0) && (available < MaxStackSize)) {
         code_size++;
         code_mask += available;
       }
       old_code = in_code;
     }
     // Pop a pixel off the pixel stack.
     top--;
     pixels[pi++] = pixelStack[top];
     i++;
   }
   for (i = pi; i < npix; i++) {
     pixels[i] = 0; // clear missing pixels
   }
 }
 /**
  * Returns true if an error was encountered during reading/decoding
  */
 protected boolean err() {
   return status != STATUS_OK;
 }
 /**
  * Initializes or re-initializes reader
  */
 protected void init() {
   status = STATUS_OK;
   frameCount = 0;
   frames = new ArrayList();
   gct = null;
   lct = null;
 }
 /**
  * Reads a single byte from the input stream.
  */
 protected int read() {
   int curByte = 0;
   try {
     curByte = in.read();
   } catch (IOException e) {
     status = STATUS_FORMAT_ERROR;
   }
   return curByte;
 }
 /**
  * Reads next variable length block from input.
  * 
  * @return number of bytes stored in "buffer"
  */
 protected int readBlock() {
   blockSize = read();
   int n = 0;
   if (blockSize > 0) {
     try {
       int count = 0;
       while (n < blockSize) {
         count = in.read(block, n, blockSize - n);
         if (count == -1)
           break;
         n += count;
       }
     } catch (IOException e) {
     }
     if (n < blockSize) {
       status = STATUS_FORMAT_ERROR;
     }
   }
   return n;
 }
 /**
  * Reads color table as 256 RGB integer values
  * 
  * @param ncolors
  *          int number of colors to read
  * @return int array containing 256 colors (packed ARGB with full alpha)
  */
 protected int[] readColorTable(int ncolors) {
   int nbytes = 3 * ncolors;
   int[] tab = null;
   byte[] c = new byte[nbytes];
   int n = 0;
   try {
     n = in.read(c);
   } catch (IOException e) {
   }
   if (n < nbytes) {
     status = STATUS_FORMAT_ERROR;
   } else {
     tab = new int[256]; // max size to avoid bounds checks
     int i = 0;
     int j = 0;
     while (i < ncolors) {
       int r = ((int) c[j++]) & 0xff;
       int g = ((int) c[j++]) & 0xff;
       int b = ((int) c[j++]) & 0xff;
       tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
     }
   }
   return tab;
 }
 /**
  * Main file parser. Reads GIF content blocks.
  */
 protected void readContents() {
   // read GIF file content blocks
   boolean done = false;
   while (!(done || err())) {
     int code = read();
     switch (code) {
     case 0x2C: // image separator
       readImage();
       break;
     case 0x21: // extension
       code = read();
       switch (code) {
       case 0xf9: // graphics control extension
         readGraphicControlExt();
         break;
       case 0xff: // application extension
         readBlock();
         String app = "";
         for (int i = 0; i < 11; i++) {
           app += (char) block[i];
         }
         if (app.equals("NETSCAPE2.0")) {
           readNetscapeExt();
         } else
           skip(); // don"t care
         break;
       default: // uninteresting extension
         skip();
       }
       break;
     case 0x3b: // terminator
       done = true;
       break;
     case 0x00: // bad byte, but keep going and see what happens
       break;
     default:
       status = STATUS_FORMAT_ERROR;
     }
   }
 }
 /**
  * Reads Graphics Control Extension values
  */
 protected void readGraphicControlExt() {
   read(); // block size
   int packed = read(); // packed fields
   dispose = (packed & 0x1c) >> 2; // disposal method
   if (dispose == 0) {
     dispose = 1; // elect to keep old image if discretionary
   }
   transparency = (packed & 1) != 0;
   delay = readShort() * 10; // delay in milliseconds
   transIndex = read(); // transparent color index
   read(); // block terminator
 }
 /**
  * Reads GIF file header information.
  */
 protected void readHeader() {
   String id = "";
   for (int i = 0; i < 6; i++) {
     id += (char) read();
   }
   if (!id.startsWith("GIF")) {
     status = STATUS_FORMAT_ERROR;
     return;
   }
   readLSD();
   if (gctFlag && !err()) {
     gct = readColorTable(gctSize);
     bgColor = gct[bgIndex];
   }
 }
 /**
  * Reads next frame image
  */
 protected void readImage() {
   ix = readShort(); // (sub)image position & size
   iy = readShort();
   iw = readShort();
   ih = readShort();
   int packed = read();
   lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
   interlace = (packed & 0x40) != 0; // 2 - interlace flag
   // 3 - sort flag
   // 4-5 - reserved
   lctSize = 2 << (packed & 7); // 6-8 - local color table size
   if (lctFlag) {
     lct = readColorTable(lctSize); // read table
     act = lct; // make local table active
   } else {
     act = gct; // make global table active
     if (bgIndex == transIndex)
       bgColor = 0;
   }
   int save = 0;
   if (transparency) {
     save = act[transIndex];
     act[transIndex] = 0; // set transparent color if specified
   }
   if (act == null) {
     status = STATUS_FORMAT_ERROR; // no color table defined
   }
   if (err())
     return;
   decodeImageData(); // decode pixel data
   skip();
   if (err())
     return;
   frameCount++;
   // create new image to receive frame data
   image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
   setPixels(); // transfer pixel data to image
   frames.add(new GifFrame(image, delay)); // add image to frame list
   if (transparency) {
     act[transIndex] = save;
   }
   resetFrame();
 }
 /**
  * Reads Logical Screen Descriptor
  */
 protected void readLSD() {
   // logical screen size
   width = readShort();
   height = readShort();
   // packed fields
   int packed = read();
   gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
   // 2-4 : color resolution
   // 5 : gct sort flag
   gctSize = 2 << (packed & 7); // 6-8 : gct size
   bgIndex = read(); // background color index
   pixelAspect = read(); // pixel aspect ratio
 }
 /**
  * Reads Netscape extenstion to obtain iteration count
  */
 protected void readNetscapeExt() {
   do {
     readBlock();
     if (block[0] == 1) {
       // loop count sub-block
       int b1 = ((int) block[1]) & 0xff;
       int b2 = ((int) block[2]) & 0xff;
       loopCount = (b2 << 8) | b1;
     }
   } while ((blockSize > 0) && !err());
 }
 /**
  * Reads next 16-bit value, LSB first
  */
 protected int readShort() {
   // read 16-bit value, LSB first
   return read() | (read() << 8);
 }
 /**
  * Resets frame state for reading next image.
  */
 protected void resetFrame() {
   lastDispose = dispose;
   lastRect = new Rectangle(ix, iy, iw, ih);
   lastImage = image;
   lastBgColor = bgColor;
   int dispose = 0;
   boolean transparency = false;
   int delay = 0;
   lct = null;
 }
 /**
  * Skips variable length blocks up to and including next zero length block.
  */
 protected void skip() {
   do {
     readBlock();
   } while ((blockSize > 0) && !err());
 }

}



 </source>   



GIF Writer

<source lang="java"> import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; public class ImageConverterGIF {

 public static void main(String[] args) throws Exception {
   String imageFilePath = "C:/myBmp.bmp";
   String gifFilePath = "C:/myPic.gif";
   File inputFile = new File(imageFilePath);
   BufferedImage image = ImageIO.read(inputFile);
   File outputFile = new File(gifFilePath);
   ImageIO.write(image, "GIF", outputFile);
 }

}


 </source>   



Hide the mouse cursor: use a transparent GIF as the cursor

<source lang="java"> import java.awt.Cursor; import java.awt.Image; import java.awt.Point; import java.awt.Toolkit; import java.awt.image.MemoryImageSource; public class Main {

 public static void main(String[] argv) throws Exception {
   int[] pixels = new int[16 * 16];
   Image image = Toolkit.getDefaultToolkit().createImage(
       new MemoryImageSource(16, 16, pixels, 0, 16));
   Cursor transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(
       image, new Point(0, 0), "invisibleCursor");
 }

}


</source>