/Users/lyon/j4p/src/ip/vs/WriteGIF.java

1    /** 
2     * WriteGIF is a class that takes an Image and saves it to 
3     * a GIF format file. 
4     * 
5     * Based upon gifsave.c, which was written and released by 
6     * Sverre H. Huseby. Ported to Java by Adam Doppelt of Brown 
7     * University. 
8     * Modified and integrated with the ImageProc program for 
9     * CS411X, advanced Java Programming at the University of 
10    * Bridgeport. 
11    * Color quantization will be added by 
12    * Victor Silva (victor@cse.bridgeport.edu). 
13    * 
14    */ 
15   package ip.vs; 
16    
17   import java.awt.*; 
18   import java.awt.image.PixelGrabber; 
19   import java.io.BufferedOutputStream; 
20   import java.io.FileOutputStream; 
21   import java.io.IOException; 
22   import java.io.OutputStream; 
23    
24   public class WriteGIF { 
25       short width_, height_; 
26       int numColors_; 
27       byte pixels_[], colors_[]; 
28    
29       ScreenDescriptor sd_; 
30       ImageDescriptor id_; 
31    
32       public static void DoIt(Image img, String fname) 
33               throws IOException, java.awt.AWTException { 
34           // encode the image as a GIF 
35           WriteGIF encode = new WriteGIF(img); 
36    
37           OutputStream output = new BufferedOutputStream( 
38                   new FileOutputStream(fname)); 
39           encode.Write(output); 
40    
41       } 
42    
43       public WriteGIF(Image image) throws AWTException { 
44           width_ = (short) image.getWidth(null); 
45           height_ = (short) image.getHeight(null); 
46    
47           int values[] = new int[width_ * height_]; 
48           PixelGrabber grabber = new PixelGrabber( 
49                   image, 0, 0, width_, height_, values, 0, width_); 
50    
51           try { 
52               if (grabber.grabPixels() != true) 
53                   throw new AWTException("Grabber returned false: " + 
54                           grabber.status()); 
55           } catch (InterruptedException e) { 
56           } 
57           ; 
58    
59           //-------------------------------------------------------------- 
60           // Possible Speed up - do it a row at a time, a la ACME 
61           //-------------------------------------------------------------- 
62           byte r[][] = new byte[width_][height_]; 
63           byte g[][] = new byte[width_][height_]; 
64           byte b[][] = new byte[width_][height_]; 
65           int index = 0; 
66    
67           for (int y = 0; y < height_; ++y) { 
68               for (int x = 0; x < width_; ++x) { 
69                   r[x][y] = (byte) ((values[index] >> 16) & 0xFF); 
70                   g[x][y] = (byte) ((values[index] >> 8) & 0xFF); 
71                   b[x][y] = (byte) ((values[index]) & 0xFF); 
72                   ++index; 
73               } 
74           } 
75           ToIndexedColor(r, g, b); 
76       } 
77    
78    
79       //-------------------------------------------------------------- 
80       // CONSTRUCTOR 
81       //-------------------------------------------------------------- 
82       public WriteGIF(byte r[][], byte g[][], byte b[][]) 
83               throws AWTException { 
84           width_ = (short) (r.length); 
85           height_ = (short) (r[0].length); 
86    
87           ToIndexedColor(r, g, b); 
88       } 
89    
90       public void Write(OutputStream output) throws IOException { 
91           BitUtils.WriteString(output, "GIF87a"); 
92           ScreenDescriptor sd = new ScreenDescriptor(width_, height_, 
93                   numColors_); 
94           sd.Write(output); 
95           output.write(colors_, 0, colors_.length); 
96           ImageDescriptor id = new ImageDescriptor(width_, height_, ','); 
97           id.Write(output); 
98    
99           byte codesize = BitUtils.BitsNeeded(numColors_); 
100          if (codesize == 1) { 
101              ++codesize; 
102          } 
103          output.write(codesize); 
104   
105          LZWCompressor.LZWCompress(output, codesize, pixels_); 
106          output.write(0); 
107   
108          id = new ImageDescriptor((byte) 0, (byte) 0, ';'); 
109          id.Write(output); 
110          output.flush(); 
111      } 
112   
113      void ToIndexedColor(byte r[][], byte g[][], byte b[][]) 
114              throws AWTException { 
115          pixels_ = new byte[width_ * height_]; 
116          colors_ = new byte[256 * 3]; 
117          int colornum = 0; 
118          for (int x = 0; x < width_; ++x) { 
119              for (int y = 0; y < height_; ++y) { 
120                  int search; 
121                  for (search = 0; search < colornum; ++search) { 
122                      if (colors_[search * 3] == r[x][y] && 
123                              colors_[search * 3 + 1] == g[x][y] && 
124                              colors_[search * 3 + 2] == b[x][y]) { 
125                          break; 
126                      } 
127                  } 
128   
129                  // If there are more than 256 colors invoke 
130                  // the color quantization procedure. 
131                  //quantization(); 
132                  if (search > 255) { 
133                      throw new AWTException("Too many colors."); 
134                  } 
135   
136                  pixels_[y * width_ + x] = (byte) search; 
137   
138                  if (search == colornum) { 
139                      colors_[search * 3] = r[x][y]; 
140                      colors_[search * 3 + 1] = g[x][y]; 
141                      colors_[search * 3 + 2] = b[x][y]; 
142                      ++colornum; 
143                  } 
144              } 
145          } 
146          numColors_ = 1 << BitUtils.BitsNeeded(colornum); 
147          byte copy[] = new byte[numColors_ * 3]; 
148          System.arraycopy(colors_, 0, copy, 0, numColors_ * 3); 
149          colors_ = copy; 
150      } 
151  } 
152   
153  class BitFile { 
154      OutputStream output_; 
155      byte buffer_[]; 
156      int index_, bitsLeft_; 
157   
158      public BitFile(OutputStream output) { 
159          output_ = output; 
160          buffer_ = new byte[256]; 
161          index_ = 0; 
162          bitsLeft_ = 8; 
163      } 
164   
165      public void Flush() throws IOException { 
166          int numBytes = index_ + (bitsLeft_ == 8 ? 0 : 1); 
167          if (numBytes > 0) { 
168              output_.write(numBytes); 
169              output_.write(buffer_, 0, numBytes); 
170              buffer_[0] = 0; 
171              index_ = 0; 
172              bitsLeft_ = 8; 
173          } 
174      } 
175   
176      public void WriteBits(int bits, int numbits) throws IOException { 
177          int bitsWritten = 0; 
178          int numBytes = 255; 
179          do { 
180              if ((index_ == 254 && bitsLeft_ == 0) || index_ > 254) { 
181                  output_.write(numBytes); 
182                  output_.write(buffer_, 0, numBytes); 
183   
184                  buffer_[0] = 0; 
185                  index_ = 0; 
186                  bitsLeft_ = 8; 
187              } 
188   
189              if (numbits <= bitsLeft_) { 
190                  buffer_[index_] |= (bits & ((1 << numbits) - 1)) << 
191                          (8 - bitsLeft_); 
192                  bitsWritten += numbits; 
193                  bitsLeft_ -= numbits; 
194                  numbits = 0; 
195              } else { 
196                  buffer_[index_] |= (bits & ((1 << bitsLeft_) - 1)) << 
197                          (8 - bitsLeft_); 
198                  bitsWritten += bitsLeft_; 
199                  bits >>= bitsLeft_; 
200                  numbits -= bitsLeft_; 
201                  buffer_[++index_] = 0; 
202                  bitsLeft_ = 8; 
203              } 
204          } while (numbits != 0); 
205      } 
206  } 
207   
208  class LZWStringTable { 
209      private final static int RES_CODES = 2; 
210      private final static short HASH_FREE = (short) 0xFFFF; 
211      private final static short NEXT_FIRST = (short) 0xFFFF; 
212      private final static int MAXBITS = 12; 
213      private final static int MAXSTR = (1 << MAXBITS); 
214      private final static short HASHSIZE = 9973; 
215      private final static short HASHSTEP = 2039; 
216   
217      byte strChr_[]; 
218      short strNxt_[]; 
219      short strHsh_[]; 
220      short numStrings_; 
221   
222      public LZWStringTable() { 
223          strChr_ = new byte[MAXSTR]; 
224          strNxt_ = new short[MAXSTR]; 
225          strHsh_ = new short[HASHSIZE]; 
226      } 
227   
228      public int AddCharString(short index, byte b) { 
229          int hshidx; 
230   
231          if (numStrings_ >= MAXSTR) { 
232              return 0xFFFF; 
233          } 
234   
235          hshidx = Hash(index, b); 
236          while (strHsh_[hshidx] != HASH_FREE) { 
237              hshidx = (hshidx + HASHSTEP) % HASHSIZE; 
238          } 
239   
240          strHsh_[hshidx] = numStrings_; 
241          strChr_[numStrings_] = b; 
242          strNxt_[numStrings_] = (index != HASH_FREE) ? index : NEXT_FIRST; 
243   
244          return numStrings_++; 
245      } 
246   
247      public short FindCharString(short index, byte b) { 
248          int hshidx, nxtidx; 
249   
250          if (index == HASH_FREE) { 
251              return b; 
252          } 
253   
254          hshidx = Hash(index, b); 
255          while ((nxtidx = strHsh_[hshidx]) != HASH_FREE) { 
256              if (strNxt_[nxtidx] == index && strChr_[nxtidx] == b) { 
257                  return (short) nxtidx; 
258              } 
259              hshidx = (hshidx + HASHSTEP) % HASHSIZE; 
260          } 
261   
262          return (short) 0xFFFF; 
263      } 
264   
265      public void ClearTable(int codesize) { 
266          numStrings_ = 0; 
267   
268          for (int q = 0; q < HASHSIZE; q++) { 
269              strHsh_[q] = HASH_FREE; 
270          } 
271   
272          int w = (1 << codesize) + RES_CODES; 
273          for (int q = 0; q < w; q++) { 
274              AddCharString((short) 0xFFFF, (byte) q); 
275          } 
276      } 
277   
278      static public int Hash(short index, byte lastbyte) { 
279          return ((int) ((short) (lastbyte << 8) ^ index) & 0xFFFF) % HASHSIZE; 
280      } 
281  } 
282   
283  class LZWCompressor { 
284      public static void LZWCompress(OutputStream output, int codesize, 
285                                     byte toCompress[]) throws IOException { 
286          byte c; 
287          short index; 
288          int clearcode, endofinfo, numbits, limit, errcode; 
289          short prefix = (short) 0xFFFF; 
290   
291          BitFile bitFile = new BitFile(output); 
292          LZWStringTable strings = new LZWStringTable(); 
293   
294          clearcode = 1 << codesize; 
295          endofinfo = clearcode + 1; 
296   
297          numbits = codesize + 1; 
298          limit = (1 << numbits) - 1; 
299   
300          strings.ClearTable(codesize); 
301          bitFile.WriteBits(clearcode, numbits); 
302   
303          for (int loop = 0; loop < toCompress.length; ++loop) { 
304              c = toCompress[loop]; 
305              if ((index = strings.FindCharString(prefix, c)) != -1) { 
306                  prefix = index; 
307              } else { 
308                  bitFile.WriteBits(prefix, numbits); 
309                  if (strings.AddCharString(prefix, c) > limit) { 
310                      if (++numbits > 12) { 
311                          bitFile.WriteBits(clearcode, numbits - 1); 
312                          strings.ClearTable(codesize); 
313                          numbits = codesize + 1; 
314                      } 
315                      limit = (1 << numbits) - 1; 
316                  } 
317   
318                  prefix = (short) ((short) c & 0xFF); 
319              } 
320          } 
321   
322          if (prefix != -1) { 
323              bitFile.WriteBits(prefix, numbits); 
324          } 
325   
326          bitFile.WriteBits(endofinfo, numbits); 
327          bitFile.Flush(); 
328      } 
329  } 
330   
331  class ScreenDescriptor { 
332      public short localScreenWidth_, localScreenHeight_; 
333      private byte byte_; 
334      public byte backgroundColorIndex_, pixelAspectRatio_; 
335   
336      public ScreenDescriptor(short width, short height, int numColors) { 
337          localScreenWidth_ = width; 
338          localScreenHeight_ = height; 
339          SetGlobalColorTableSize((byte) (BitUtils.BitsNeeded(numColors) - 1)); 
340          SetGlobalColorTableFlag((byte) 1); 
341          SetSortFlag((byte) 0); 
342          SetColorResolution((byte) 7); 
343          backgroundColorIndex_ = 0; 
344          pixelAspectRatio_ = 0; 
345      } 
346   
347      public void Write(OutputStream output) throws IOException { 
348          BitUtils.WriteWord(output, localScreenWidth_); 
349          BitUtils.WriteWord(output, localScreenHeight_); 
350          output.write(byte_); 
351          output.write(backgroundColorIndex_); 
352          output.write(pixelAspectRatio_); 
353      } 
354   
355      public void SetGlobalColorTableSize(byte num) { 
356          byte_ |= (num & 7); 
357      } 
358   
359      public void SetSortFlag(byte num) { 
360          byte_ |= (num & 1) << 3; 
361      } 
362   
363      public void SetColorResolution(byte num) { 
364          byte_ |= (num & 7) << 4; 
365      } 
366   
367      public void SetGlobalColorTableFlag(byte num) { 
368          byte_ |= (num & 1) << 7; 
369      } 
370  } 
371   
372  class ImageDescriptor { 
373      public byte separator_; 
374      public short leftPosition_, topPosition_, width_, height_; 
375      private byte byte_; 
376   
377      public ImageDescriptor(short width, short height, char separator) { 
378          separator_ = (byte) separator; 
379          leftPosition_ = 0; 
380          topPosition_ = 0; 
381          width_ = width; 
382          height_ = height; 
383          SetLocalColorTableSize((byte) 0); 
384          SetReserved((byte) 0); 
385          SetSortFlag((byte) 0); 
386          SetInterlaceFlag((byte) 0); 
387          SetLocalColorTableFlag((byte) 0); 
388      } 
389   
390      public void Write(OutputStream output) throws IOException { 
391          output.write(separator_); 
392          BitUtils.WriteWord(output, leftPosition_); 
393          BitUtils.WriteWord(output, topPosition_); 
394          BitUtils.WriteWord(output, width_); 
395          BitUtils.WriteWord(output, height_); 
396          output.write(byte_); 
397      } 
398   
399      public void SetLocalColorTableSize(byte num) { 
400          byte_ |= (num & 7); 
401      } 
402   
403      public void SetReserved(byte num) { 
404          byte_ |= (num & 3) << 3; 
405      } 
406   
407      public void SetSortFlag(byte num) { 
408          byte_ |= (num & 1) << 5; 
409      } 
410   
411      public void SetInterlaceFlag(byte num) { 
412          byte_ |= (num & 1) << 6; 
413      } 
414   
415      public void SetLocalColorTableFlag(byte num) { 
416          byte_ |= (num & 1) << 7; 
417      } 
418  } 
419   
420  class BitUtils { 
421      public static byte BitsNeeded(int n) { 
422          byte ret = 1; 
423   
424          if (n-- == 0) { 
425              return 0; 
426          } 
427   
428          while ((n >>= 1) != 0) { 
429              ++ret; 
430          } 
431   
432          return ret; 
433      } 
434   
435      public static void WriteWord(OutputStream output, short w) 
436              throws IOException { 
437          output.write(w & 0xFF); 
438          output.write((w >> 8) & 0xFF); 
439      } 
440   
441      static void WriteString(OutputStream output, 
442                              String string) throws IOException { 
443          for (int loop = 0; loop < string.length(); ++loop) { 
444              output.write((byte) (string.charAt(loop))); 
445          } 
446      } 
447  } 
448