code: package UI;
  
  import java.io.*;
  import java.awt.*;
  import java.awt.image.*;
  
  
  public class GifEncoder {
  
  	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 <code>finish()</code> flushes all
  	 * frames.  If <code>setSize</code> 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
  	 * <code>setDelay(1000/fps)</code>.
  	 *
  	 * @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));
  		}
  	}
  }
  