// // Media Representation Theory // // // // Giffy // // Written by // Pradubkiat Bouklee & Ming-shu Chung // // // Language: Java 1.1 import java.awt.image.*; import java.awt.*; import java.io.*; import java.lang.Integer; public class Giffy { Giffy() {} public static void main(String args[]) throws Exception { Giffy gif_combine =new Giffy(); int imagenumber=0; String str=null; // "grpahic control extesion" object PicData Control = new PicData(); DataInputStream Keyin = new DataInputStream(System.in); // need a component in order to use MediaTracker Frame f = new Frame("GIFTest"); System.out.println("How many image do you want to combine?"); imagenumber = Integer.parseInt(Keyin.readLine()); System.out.println(" "); System.out.flush(); String picname[] = new String[(imagenumber+1)]; //key in the name of image you want to combine for(int j=0; j < imagenumber; j++) { System.out.println("Please input the name of No."+(j+1)+" image....."); picname[j] = Keyin.readLine(); } //key in the output file System.out.println("Please input the name of the output file...."); picname[imagenumber] = Keyin.readLine(); // load the image Image[] image = new Image[imagenumber]; for (int i=0;i < imagenumber;i++) { image[i] = f.getToolkit().getImage(picname[i]); } // wait for the image to entirely load MediaTracker tracker = new MediaTracker(f); for (int i=0;i < imagenumber;i++) { tracker.addImage(image[i], 0); } try { tracker.waitForAll(); } catch (InterruptedException e) { ; } if (tracker.statusID(0, true) != MediaTracker.COMPLETE) throw new AWTException("Could not load: " + picname[0] + " " + tracker.statusID(0, true)); OutputStream output = new BufferedOutputStream( new FileOutputStream(picname[imagenumber])); System.out.println(" "); // encode the image as a GIF for (int i=0;i> 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. * 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. * r: An array containing the red intensity values. * g: An array containing the green intensity values. * b: An array containing the blue intensity values. * Exception AWTException Will be thrown if the image contains more than * 256 colors. */ public GIF89aEncoderBlock(byte r[][], byte g[][], byte b[][], int imagenumber) throws AWTException { width_ = (short)(r.length); height_ = (short)(r[0].length); imagenumber_ = imagenumber; ToIndexedColor(r, g, b); } /* * Writes the image out to a stream in the GIF file format. This will * be a single GIF89a image, non-interlaced, with no background color. * 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 { if (imagenumber_ == 1) // First image uses global color map { //Write header(Signature) BitUtils.WriteString(output, "GIF89a"); ScreenDescriptor sd = new ScreenDescriptor(width_, height_,numColors_); sd.Write(output); //Write global color map output.write(colors_, 0, colors_.length); //Write Netscape Extension Block NetscapeAppExt ns = new NetscapeAppExt(Contr.loop); ns.Write(output); //Write Graphic Control Extension Block GraphicConExt gc = new GraphicConExt(Contr); gc.Write(output); //Write ImageDescriptor Block ImageDescriptor id = new ImageDescriptor(width_, height_, ','); id.SetLocalColorTableSize((byte) (BitUtils.BitsNeeded(numColors_) -1));////////TESTESTESTEST id.Write(output); //Write Code size to image data block byte codesize = BitUtils.BitsNeeded(numColors_); if (codesize == 1) ++codesize; output.write(codesize); //Write image data block LZWCompressor.LZWCompress(output, codesize, pixels_); output.write(0); } else { //Second image and so on use local color map //Write Graphic Control Extension Block GraphicConExt gc = new GraphicConExt(Contr); gc.Write(output); //Write ImageDescriptor Block ImageDescriptor id = new ImageDescriptor(width_, height_, ','); id.SetLocalColorTableSize((byte) (BitUtils.BitsNeeded(numColors_) -1)); //local color map size id.SetLocalColorTableFlag((byte) 1); //M =1 because of using Local color map id.Write(output); //Write local color map output.write(colors_, 0, colors_.length); //If M =0, we have to skip this line //Write Code size to image data block byte codesize = BitUtils.BitsNeeded(numColors_); if (codesize == 1) ++codesize; output.write(codesize); //Write image data block LZWCompressor.LZWCompress(output, codesize, pixels_); output.write(0); } //End else } public static void WriteCommentEnd(OutputStream output,String comment) throws IOException { //write comment to the file CommentExt cx = new CommentExt(comment); cx.Write(output); ImageDescriptor id = new ImageDescriptor((byte)0, (byte)0, ';'); //Write Gif Trialer 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; } } //Netscape Application Extension Block class NetscapeAppExt { private final static byte gifExtCode_ = 33; private final static byte appExtLabel_ = -1; private final static byte lengthAppBlock_ = 11; private final static byte lengthDataSubB_ = 3; private final static byte byte16_ = 1; public short loop_; private final static byte dataSubBTer_ = 0; public NetscapeAppExt(short nloop) { loop_ = nloop; } public void Write(OutputStream output) throws IOException { output.write(gifExtCode_); output.write(appExtLabel_); output.write(lengthAppBlock_); BitUtils.WriteString(output, "NETSCAPE"); BitUtils.WriteString(output, "2.0"); output.write(lengthDataSubB_); output.write(byte16_); BitUtils.WriteWord(output, loop_); output.write(dataSubBTer_); } } //Comment Extension Block class CommentExt { private final static byte gifExtCode_ = 33; private final static byte commentLabel_ = -2; public String comment_; private byte blocksize_; private final static byte dataSubBTer_ = 0; public CommentExt(String comment) { comment_ = new String(comment); blocksize_ = (byte) comment_.length(); } public void Write(OutputStream output) throws IOException { output.write(gifExtCode_); output.write(commentLabel_); output.write(blocksize_); BitUtils.WriteString(output, comment_); output.write(dataSubBTer_); } } //Graphic Control Extension Block class GraphicConExt { PicData conExt; private final static byte gifExtCode_ = 33; private final static byte graphicConLabel_ = -7; private final static byte blockSize_ = 4; public byte byte_; public short delay_; public byte tColorIndex_; private final static byte dataSubBTer_ = 0; public GraphicConExt(PicData conExt) { byte_ = conExt.flag; delay_= conExt.delay; tColorIndex_=conExt.index; } public void Write(OutputStream output) throws IOException { output.write(gifExtCode_); //Extension Introducer output.write(graphicConLabel_); //Graphic Control Lable output.write(blockSize_); //Block Size output.write(byte_); // BitUtils.WriteWord(output, delay_); //Delay time output.write(tColorIndex_); //Transparent Color Index (0-255) output.write(dataSubBTer_); } } 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))); } }