/Users/lyon/j4p/src/ip/gif/neuquantAnimation/GifDecoder.java

1    package ip.gif.neuquantAnimation; 
2     
3    import java.net.*; 
4    import java.io.*; 
5    import java.util.*; 
6    import java.awt.*; 
7    import java.awt.image.*; 
8     
9    /** 
10    * Class GifDecoder - Decodes a GIF file into one or more frames. 
11    * <br><pre> 
12    * Example: 
13    *    GifDecoder d = new GifDecoder(); 
14    *    d.read("sample.gif"); 
15    *    int n = d.getFrameCount(); 
16    *    for (int i = 0; i < n; i++) { 
17    *       BufferedImage frame = d.getFrame(i);  // frame i 
18    *       int t = d.getDelay(i);  // display duration of frame in milliseconds 
19    *       // do something with frame 
20    *    } 
21    * </pre> 
22    * No copyright asserted on the source code of this class.  May be used for 
23    * any purpose, however, refer to the Unisys LZW patent for any additional 
24    * restrictions.  Please forward any corrections to kweiner@fmsware.com. 
25    * 
26    * @author Kevin Weiner, FM Software; LZW decoder adapted from John Cristy's ImageMagick. 
27    * @version 1.03 November 2003 
28    * 
29    */ 
30    
31   public class GifDecoder { 
32    
33       /** 
34        * File read status: No errors. 
35        */ 
36       public static final int STATUS_OK = 0; 
37    
38       /** 
39        * File read status: Error decoding file (may be partially decoded) 
40        */ 
41       public static final int STATUS_FORMAT_ERROR = 1; 
42    
43       /** 
44        * File read status: Unable to open source. 
45        */ 
46       public static final int STATUS_OPEN_ERROR = 2; 
47    
48       protected BufferedInputStream in; 
49       protected int status; 
50    
51       protected int width; // full image width 
52       protected int height; // full image height 
53       protected boolean gctFlag; // global color table used 
54       protected int gctSize; // size of global color table 
55       protected int loopCount = 1; // iterations; 0 = repeat forever 
56    
57       protected int[] gct; // global color table 
58       protected int[] lct; // local color table 
59       protected int[] act; // active color table 
60    
61       protected int bgIndex; // background color index 
62       protected int bgColor; // background color 
63       protected int lastBgColor; // previous bg color 
64       protected int pixelAspect; // pixel aspect ratio 
65    
66       protected boolean lctFlag; // local color table flag 
67       protected boolean interlace; // interlace flag 
68       protected int lctSize; // local color table size 
69    
70       protected int ix, iy, iw, ih; // current image rectangle 
71       protected Rectangle lastRect; // last image rect 
72       protected BufferedImage image; // current frame 
73       protected BufferedImage lastImage; // previous frame 
74    
75       protected byte[] block = new byte[256]; // current data block 
76       protected int blockSize = 0; // block size 
77    
78       // last graphic control extension info 
79       protected int dispose = 0; 
80       // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev 
81       protected int lastDispose = 0; 
82       protected boolean transparency = false; // use transparent color 
83       protected int delay = 0; // delay in milliseconds 
84       protected int transIndex; // transparent color index 
85    
86       protected static final int MaxStackSize = 4096; 
87       // max decoder pixel stack size 
88    
89       // LZW decoder working arrays 
90       protected short[] prefix; 
91       protected byte[] suffix; 
92       protected byte[] pixelStack; 
93       protected byte[] pixels; 
94    
95       protected ArrayList frames; // frames read from current file 
96       protected int frameCount; 
97    
98       static class GifFrame { 
99           public GifFrame(BufferedImage im, int del) { 
100              image = im; 
101              delay = del; 
102          } 
103          public BufferedImage image; 
104          public int delay; 
105      } 
106   
107      /** 
108       * Gets display duration for specified frame. 
109       * 
110       * @param n int index of frame 
111       * @return delay in milliseconds 
112       */ 
113      public int getDelay(int n) { 
114          // 
115          delay = -1; 
116          if ((n >= 0) && (n < frameCount)) { 
117              delay = ((GifFrame) frames.get(n)).delay; 
118          } 
119          return delay; 
120      } 
121   
122      /** 
123       * Gets the number of frames read from file. 
124       * @return frame count 
125       */ 
126      public int getFrameCount() { 
127          return frameCount; 
128      } 
129   
130      /** 
131       * Gets the first (or only) image read. 
132       * 
133       * @return BufferedImage containing first frame, or null if none. 
134       */ 
135      public BufferedImage getImage() { 
136          return getFrame(0); 
137      } 
138   
139      /** 
140       * Gets the "Netscape" iteration count, if any. 
141       * A count of 0 means repeat indefinitiely. 
142       * 
143       * @return iteration count if one was specified, else 1. 
144       */ 
145      public int getLoopCount() { 
146          return loopCount; 
147      } 
148   
149      /** 
150       * Creates new frame image from current data (and previous 
151       * frames as specified by their disposition codes). 
152       */ 
153      protected void setPixels() { 
154          // expose destination image's pixels as int array 
155          int[] dest = 
156              ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); 
157   
158          // fill in starting image contents based on last image's dispose code 
159          if (lastDispose > 0) { 
160              if (lastDispose == 3) { 
161                  // use image before last 
162                  int n = frameCount - 2; 
163                  if (n > 0) { 
164                      lastImage = getFrame(n - 1); 
165                  } else { 
166                      lastImage = null; 
167                  } 
168              } 
169   
170              if (lastImage != null) { 
171                  int[] prev = 
172                      ((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData(); 
173                  System.arraycopy(prev, 0, dest, 0, width * height); 
174                  // copy pixels 
175   
176                  if (lastDispose == 2) { 
177                      // fill last image rect area with background color 
178                      Graphics2D g = image.createGraphics(); 
179                      Color c = null; 
180                      if (transparency) { 
181                          c = new Color(0, 0, 0, 0);  // assume background is transparent 
182                      } else { 
183                          c = new Color(lastBgColor); // use given background color 
184                      } 
185                      g.setColor(c); 
186                      g.setComposite(AlphaComposite.Src); // replace area 
187                      g.fill(lastRect); 
188                      g.dispose(); 
189                  } 
190              } 
191          } 
192   
193          // copy each source line to the appropriate place in the destination 
194          int pass = 1; 
195          int inc = 8; 
196          int iline = 0; 
197          for (int i = 0; i < ih; i++) { 
198              int line = i; 
199              if (interlace) { 
200                  if (iline >= ih) { 
201                      pass++; 
202                      switch (pass) { 
203                          case 2 : 
204                              iline = 4; 
205                              break; 
206                          case 3 : 
207                              iline = 2; 
208                              inc = 4; 
209                              break; 
210                          case 4 : 
211                              iline = 1; 
212                              inc = 2; 
213                      } 
214                  } 
215                  line = iline; 
216                  iline += inc; 
217              } 
218              line += iy; 
219              if (line < height) { 
220                  int k = line * width; 
221                  int dx = k + ix; // start of line in dest 
222                  int dlim = dx + iw; // end of dest line 
223                  if ((k + width) < dlim) { 
224                      dlim = k + width; // past dest edge 
225                  } 
226                  int sx = i * iw; // start of line in source 
227                  while (dx < dlim) { 
228                      // map color and insert in destination 
229                      int index = ((int) pixels[sx++]) & 0xff; 
230                      int c = act[index]; 
231                      if (c != 0) { 
232                          dest[dx] = c; 
233                      } 
234                      dx++; 
235                  } 
236              } 
237          } 
238      } 
239   
240      /** 
241       * Gets the image contents of frame n. 
242       * 
243       * @return BufferedImage representation of frame, or null if n is invalid. 
244       */ 
245      public BufferedImage getFrame(int n) { 
246          BufferedImage im = null; 
247          if ((n >= 0) && (n < frameCount)) { 
248              im = ((GifFrame) frames.get(n)).image; 
249          } 
250          return im; 
251      } 
252   
253      /** 
254       * Gets image size. 
255       * 
256       * @return GIF image dimensions 
257       */ 
258      public Dimension getFrameSize() { 
259          return new Dimension(width, height); 
260      } 
261   
262      /** 
263       * Reads GIF image from stream 
264       * 
265       * @param BufferedInputStream containing GIF file. 
266       * @return read status code (0 = no errors) 
267       */ 
268      public int read(BufferedInputStream is) { 
269          init(); 
270          if (is != null) { 
271              in = is; 
272              readHeader(); 
273              if (!err()) { 
274                  readContents(); 
275                  if (frameCount < 0) { 
276                      status = STATUS_FORMAT_ERROR; 
277                  } 
278              } 
279          } else { 
280              status = STATUS_OPEN_ERROR; 
281          } 
282          try { 
283              is.close(); 
284          } catch (IOException e) { 
285          } 
286          return status; 
287      } 
288   
289      /** 
290       * Reads GIF image from stream 
291       * 
292       * @param InputStream containing GIF file. 
293       * @return read status code (0 = no errors) 
294       */ 
295      public int read(InputStream is) { 
296          init(); 
297          if (is != null) { 
298              if (!(is instanceof BufferedInputStream)) 
299                  is = new BufferedInputStream(is); 
300              in = (BufferedInputStream) is; 
301              readHeader(); 
302              if (!err()) { 
303                  readContents(); 
304                  if (frameCount < 0) { 
305                      status = STATUS_FORMAT_ERROR; 
306                  } 
307              } 
308          } else { 
309              status = STATUS_OPEN_ERROR; 
310          } 
311          try { 
312              is.close(); 
313          } catch (IOException e) { 
314          } 
315          return status; 
316      } 
317   
318      /** 
319       * Reads GIF file from specified file/URL source   
320       * (URL assumed if name contains ":/" or "file:") 
321       * 
322       * @param name String containing source 
323       * @return read status code (0 = no errors) 
324       */ 
325      public int read(String name) { 
326          status = STATUS_OK; 
327          try { 
328              name = name.trim().toLowerCase(); 
329              if ((name.indexOf("file:") >= 0) || 
330                  (name.indexOf(":/") > 0)) { 
331                  URL url = new URL(name); 
332                  in = new BufferedInputStream(url.openStream()); 
333              } else { 
334                  in = new BufferedInputStream(new FileInputStream(name)); 
335              } 
336              status = read(in); 
337          } catch (IOException e) { 
338              status = STATUS_OPEN_ERROR; 
339          } 
340   
341          return status; 
342      } 
343   
344      /** 
345       * Decodes LZW image data into pixel array. 
346       * Adapted from John Cristy's ImageMagick. 
347       */ 
348      protected void decodeImageData() { 
349          int NullCode = -1; 
350          int npix = iw * ih; 
351          int available,  
352              clear, 
353              code_mask, 
354              code_size, 
355              end_of_information, 
356              in_code, 
357              old_code, 
358              bits, 
359              code, 
360              count, 
361              i, 
362              datum, 
363              data_size, 
364              first, 
365              top, 
366              bi, 
367              pi; 
368   
369          if ((pixels == null) || (pixels.length < npix)) { 
370              pixels = new byte[npix]; // allocate new pixel array 
371          } 
372          if (prefix == null) prefix = new short[MaxStackSize]; 
373          if (suffix == null) suffix = new byte[MaxStackSize]; 
374          if (pixelStack == null) pixelStack = new byte[MaxStackSize + 1]; 
375   
376          //  Initialize GIF data stream decoder. 
377   
378          data_size = read(); 
379          clear = 1 << data_size; 
380          end_of_information = clear + 1; 
381          available = clear + 2; 
382          old_code = NullCode; 
383          code_size = data_size + 1; 
384          code_mask = (1 << code_size) - 1; 
385          for (code = 0; code < clear; code++) { 
386              prefix[code] = 0; 
387              suffix[code] = (byte) code; 
388          } 
389   
390          //  Decode GIF pixel stream. 
391   
392          datum = bits = count = first = top = pi = bi = 0; 
393   
394          for (i = 0; i < npix;) { 
395              if (top == 0) { 
396                  if (bits < code_size) { 
397                      //  Load bytes until there are enough bits for a code. 
398                      if (count == 0) { 
399                          // Read a new data block. 
400                          count = readBlock(); 
401                          if (count <= 0) 
402                              break; 
403                          bi = 0; 
404                      } 
405                      datum += (((int) block[bi]) & 0xff) << bits; 
406                      bits += 8; 
407                      bi++; 
408                      count--; 
409                      continue; 
410                  } 
411   
412                  //  Get the next code. 
413   
414                  code = datum & code_mask; 
415                  datum >>= code_size; 
416                  bits -= code_size; 
417   
418                  //  Interpret the code 
419   
420                  if ((code > available) || (code == end_of_information)) 
421                      break; 
422                  if (code == clear) { 
423                      //  Reset decoder. 
424                      code_size = data_size + 1; 
425                      code_mask = (1 << code_size) - 1; 
426                      available = clear + 2; 
427                      old_code = NullCode; 
428                      continue; 
429                  } 
430                  if (old_code == NullCode) { 
431                      pixelStack[top++] = suffix[code]; 
432                      old_code = code; 
433                      first = code; 
434                      continue; 
435                  } 
436                  in_code = code; 
437                  if (code == available) { 
438                      pixelStack[top++] = (byte) first; 
439                      code = old_code; 
440                  } 
441                  while (code > clear) { 
442                      pixelStack[top++] = suffix[code]; 
443                      code = prefix[code]; 
444                  } 
445                  first = ((int) suffix[code]) & 0xff; 
446   
447                  //  Add a new string to the string table, 
448   
449                  if (available >= MaxStackSize) 
450                      break; 
451                  pixelStack[top++] = (byte) first; 
452                  prefix[available] = (short) old_code; 
453                  suffix[available] = (byte) first; 
454                  available++; 
455                  if (((available & code_mask) == 0) 
456                      && (available < MaxStackSize)) { 
457                      code_size++; 
458                      code_mask += available; 
459                  } 
460                  old_code = in_code; 
461              } 
462   
463              //  Pop a pixel off the pixel stack. 
464   
465              top--; 
466              pixels[pi++] = pixelStack[top]; 
467              i++; 
468          } 
469   
470          for (i = pi; i < npix; i++) { 
471              pixels[i] = 0; // clear missing pixels 
472          } 
473   
474      } 
475   
476      /** 
477       * Returns true if an error was encountered during reading/decoding 
478       */ 
479      protected boolean err() { 
480          return status != STATUS_OK; 
481      } 
482   
483      /** 
484       * Initializes or re-initializes reader 
485       */ 
486      protected void init() { 
487          status = STATUS_OK; 
488          frameCount = 0; 
489          frames = new ArrayList(); 
490          gct = null; 
491          lct = null; 
492      } 
493   
494      /** 
495       * Reads a single byte from the input stream. 
496       */ 
497      protected int read() { 
498          int curByte = 0; 
499          try { 
500              curByte = in.read(); 
501          } catch (IOException e) { 
502              status = STATUS_FORMAT_ERROR; 
503          } 
504          return curByte; 
505      } 
506   
507      /** 
508       * Reads next variable length block from input. 
509       * 
510       * @return number of bytes stored in "buffer" 
511       */ 
512      protected int readBlock() { 
513          blockSize = read(); 
514          int n = 0; 
515          if (blockSize > 0) { 
516              try { 
517                  int count = 0; 
518                  while (n < blockSize) { 
519                      count = in.read(block, n, blockSize - n); 
520                      if (count == -1)  
521                          break; 
522                      n += count; 
523                  } 
524              } catch (IOException e) { 
525              } 
526   
527              if (n < blockSize) { 
528                  status = STATUS_FORMAT_ERROR; 
529              } 
530          } 
531          return n; 
532      } 
533   
534      /** 
535       * Reads color table as 256 RGB integer values 
536       * 
537       * @param ncolors int number of colors to read 
538       * @return int array containing 256 colors (packed ARGB with full alpha) 
539       */ 
540      protected int[] readColorTable(int ncolors) { 
541          int nbytes = 3 * ncolors; 
542          int[] tab = null; 
543          byte[] c = new byte[nbytes]; 
544          int n = 0; 
545          try { 
546              n = in.read(c); 
547          } catch (IOException e) { 
548          } 
549          if (n < nbytes) { 
550              status = STATUS_FORMAT_ERROR; 
551          } else { 
552              tab = new int[256]; // max size to avoid bounds checks 
553              int i = 0; 
554              int j = 0; 
555              while (i < ncolors) { 
556                  int r = ((int) c[j++]) & 0xff; 
557                  int g = ((int) c[j++]) & 0xff; 
558                  int b = ((int) c[j++]) & 0xff; 
559                  tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b; 
560              } 
561          } 
562          return tab; 
563      } 
564   
565      /** 
566       * Main file parser.  Reads GIF content blocks. 
567       */ 
568      protected void readContents() { 
569          // read GIF file content blocks 
570          boolean done = false; 
571          while (!(done || err())) { 
572              int code = read(); 
573              switch (code) { 
574   
575                  case 0x2C : // image separator 
576                      readImage(); 
577                      break; 
578   
579                  case 0x21 : // extension 
580                      code = read(); 
581                      switch (code) { 
582                          case 0xf9 : // graphics control extension 
583                              readGraphicControlExt(); 
584                              break; 
585   
586                          case 0xff : // application extension 
587                              readBlock(); 
588                              String app = ""; 
589                              for (int i = 0; i < 11; i++) { 
590                                  app += (char) block[i]; 
591                              } 
592                              if (app.equals("NETSCAPE2.0")) { 
593                                  readNetscapeExt(); 
594                              } 
595                              else 
596                                  skip(); // don't care 
597                              break; 
598   
599                          default : // uninteresting extension 
600                              skip(); 
601                      } 
602                      break; 
603   
604                  case 0x3b : // terminator 
605                      done = true; 
606                      break; 
607   
608                  case 0x00 : // bad byte, but keep going and see what happens 
609                      break; 
610   
611                  default : 
612                      status = STATUS_FORMAT_ERROR; 
613              } 
614          } 
615      } 
616   
617      /** 
618       * Reads Graphics Control Extension values 
619       */ 
620      protected void readGraphicControlExt() { 
621          read(); // block size 
622          int packed = read(); // packed fields 
623          dispose = (packed & 0x1c) >> 2; // disposal method 
624          if (dispose == 0) { 
625              dispose = 1; // elect to keep old image if discretionary 
626          } 
627          transparency = (packed & 1) != 0; 
628          delay = readShort() * 10; // delay in milliseconds 
629          transIndex = read(); // transparent color index 
630          read(); // block terminator 
631      } 
632   
633      /** 
634       * Reads GIF file header information. 
635       */ 
636      protected void readHeader() { 
637          String id = ""; 
638          for (int i = 0; i < 6; i++) { 
639              id += (char) read(); 
640          } 
641          if (!id.startsWith("GIF")) { 
642              status = STATUS_FORMAT_ERROR; 
643              return; 
644          } 
645   
646          readLSD(); 
647          if (gctFlag && !err()) { 
648              gct = readColorTable(gctSize); 
649              bgColor = gct[bgIndex]; 
650          } 
651      } 
652   
653      /** 
654       * Reads next frame image 
655       */ 
656      protected void readImage() { 
657          ix = readShort(); // (sub)image position & size 
658          iy = readShort(); 
659          iw = readShort(); 
660          ih = readShort(); 
661   
662          int packed = read(); 
663          lctFlag = (packed & 0x80) != 0; // 1 - local color table flag 
664          interlace = (packed & 0x40) != 0; // 2 - interlace flag 
665          // 3 - sort flag 
666          // 4-5 - reserved 
667          lctSize = 2 << (packed & 7); // 6-8 - local color table size 
668   
669          if (lctFlag) { 
670              lct = readColorTable(lctSize); // read table 
671              act = lct; // make local table active 
672          } else { 
673              act = gct; // make global table active 
674              if (bgIndex == transIndex) 
675                  bgColor = 0; 
676          } 
677          int save = 0; 
678          if (transparency) { 
679              save = act[transIndex]; 
680              act[transIndex] = 0; // set transparent color if specified 
681          } 
682   
683          if (act == null) { 
684              status = STATUS_FORMAT_ERROR; // no color table defined 
685          } 
686   
687          if (err()) return; 
688   
689          decodeImageData(); // decode pixel data 
690          skip(); 
691   
692          if (err()) return; 
693   
694          frameCount++; 
695   
696          // create new image to receive frame data 
697          image = 
698              new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE); 
699   
700          setPixels(); // transfer pixel data to image 
701   
702          frames.add(new GifFrame(image, delay)); // add image to frame list 
703   
704          if (transparency) { 
705              act[transIndex] = save; 
706          } 
707          resetFrame(); 
708   
709      } 
710   
711      /** 
712       * Reads Logical Screen Descriptor 
713       */ 
714      protected void readLSD() { 
715   
716          // logical screen size 
717          width = readShort(); 
718          height = readShort(); 
719   
720          // packed fields 
721          int packed = read(); 
722          gctFlag = (packed & 0x80) != 0; // 1   : global color table flag 
723          // 2-4 : color resolution 
724          // 5   : gct sort flag 
725          gctSize = 2 << (packed & 7); // 6-8 : gct size 
726   
727          bgIndex = read(); // background color index 
728          pixelAspect = read(); // pixel aspect ratio 
729      } 
730   
731      /** 
732       * Reads Netscape extenstion to obtain iteration count 
733       */ 
734      protected void readNetscapeExt() { 
735          do { 
736              readBlock(); 
737              if (block[0] == 1) { 
738                  // loop count sub-block 
739                  int b1 = ((int) block[1]) & 0xff; 
740                  int b2 = ((int) block[2]) & 0xff; 
741                  loopCount = (b2 << 8) | b1; 
742              } 
743          } while ((blockSize > 0) && !err()); 
744      } 
745   
746      /** 
747       * Reads next 16-bit value, LSB first 
748       */ 
749      protected int readShort() { 
750          // read 16-bit value, LSB first 
751          return read() | (read() << 8); 
752      } 
753   
754      /** 
755       * Resets frame state for reading next image. 
756       */ 
757      protected void resetFrame() { 
758          lastDispose = dispose; 
759          lastRect = new Rectangle(ix, iy, iw, ih); 
760          lastImage = image; 
761          lastBgColor = bgColor; 
762          int dispose = 0; 
763          boolean transparency = false; 
764          int delay = 0; 
765          lct = null; 
766      } 
767   
768      /** 
769       * Skips variable length blocks up to and including 
770       * next zero length block. 
771       */ 
772      protected void skip() { 
773          do { 
774              readBlock(); 
775          } while ((blockSize > 0) && !err()); 
776      } 
777  } 
778