Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/fmsware/GifDecoder.java


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