GifDecoder.java
001 /* This library is free software; you can redistribute it and/or
002  * modify it under the terms of the GNU Lesser General Public
003  * License as published by the Free Software Foundation; either
004  * version 2.1 of the License, or (at your option) any later version.
005  * <p/>
006  * This library is distributed in the hope that it will be useful,
007  * but WITHOUT ANY WARRANTY; without even the implied warranty of
008  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
009  * Lesser General Public License for more details.  
010  */
011 package furbelow;
012 
013 import java.net.*;
014 import java.io.*;
015 import java.util.*;
016 import java.awt.*;
017 import java.awt.image.*;
018 
019 /**
020  * Class GifDecoder - Decodes a GIF file into one or more frames. <br>
021  
022  <pre>
023  *   Example:
024  *      GifDecoder d = new GifDecoder();
025  *      d.read(&quot;sample.gif&quot;);
026  *      int n = d.getFrameCount();
027  *      for (int i = 0; i &lt; n; i++) {
028  *         BufferedImage frame = d.getFrame(i);  // frame i
029  *         int t = d.getDelay(i);  // display duration of frame in milliseconds
030  *         // do something with frame
031  *      }
032  </pre>
033  
034  * No copyright asserted on the source code of this class. May be used
035  * for any purpose, however, refer to the Unisys LZW patent for any
036  * additional restrictions. Please forward any corrections to
037  * kweiner@fmsware.com.
038  
039  @author Kevin Weiner, FM Software; LZW decoder adapted from John
040  *         Cristy's ImageMagick.
041  @version 1.03 November 2003
042  */
043 public class GifDecoder {
044     /**
045      * File read status: No errors.
046      */
047     public static final int STATUS_OK = 0;
048     /**
049      * File read status: Error decoding file (may be partially decoded)
050      */
051     public static final int STATUS_FORMAT_ERROR = 1;
052     /**
053      * File read status: Unable to open source.
054      */
055     public static final int STATUS_OPEN_ERROR = 2;
056     protected BufferedInputStream in;
057     protected int status;
058     protected int width; // full image width
059     protected int height; // full image height
060     protected boolean gctFlag; // global color table used
061     protected int gctSize; // size of global color table
062     protected int loopCount = 1// iterations; 0 = repeat forever
063     protected int[] gct; // global color table
064     protected int[] lct; // local color table
065     protected int[] act; // active color table
066     protected int bgIndex; // background color index
067     protected int bgColor; // background color
068     protected int lastBgColor; // previous bg color
069     protected int pixelAspect; // pixel aspect ratio
070     protected boolean lctFlag; // local color table flag
071     protected boolean interlace; // interlace flag
072     protected int lctSize; // local color table size
073     protected int ix, iy, iw, ih; // current image rectangle
074     protected Rectangle lastRect; // last image rect
075     protected BufferedImage image; // current frame
076     protected BufferedImage lastImage; // previous frame
077     protected byte[] block = new byte[256]// current data block
078     protected int blockSize = 0// block size
079     // last graphic control extension info
080     protected int dispose = 0;
081     // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
082     protected int lastDispose = 0;
083     protected boolean transparency = false// use transparent color
084     protected int delay = 0// delay in milliseconds
085     protected int transIndex; // transparent color index
086     protected static final int MaxStackSize = 4096;
087     // max decoder pixel stack size
088     // LZW decoder working arrays
089     protected short[] prefix;
090     protected byte[] suffix;
091     protected byte[] pixelStack;
092     protected byte[] pixels;
093     protected ArrayList frames; // frames read from current file
094     protected int frameCount;
095 
096     static class GifFrame {
097         public GifFrame(BufferedImage im, int del) {
098             image = im;
099             delay = del;
100         }
101 
102         public BufferedImage image;
103         public int delay;
104     }
105 
106     /**
107      * Gets display duration for specified frame.
108      
109      @param n
110      *            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      
125      @return frame count
126      */
127     public int getFrameCount() {
128         return frameCount;
129     }
130 
131     /**
132      * Gets the first (or only) image read.
133      
134      @return BufferedImage containing first frame, or null if none.
135      */
136     public BufferedImage getImage() {
137         return getFrame(0);
138     }
139 
140     /**
141      * Gets the "Netscape" iteration count, if any. A count of 0 means
142      * repeat indefinitiely.
143      
144      @return iteration count if one was specified, else 1.
145      */
146     public int getLoopCount() {
147         return loopCount;
148     }
149 
150     /**
151      * Creates new frame image from current data (and previous frames as
152      * specified by their disposition codes).
153      */
154     protected void setPixels() {
155         // expose destination image's pixels as int array
156         int[] dest = ((DataBufferInt)image.getRaster().getDataBuffer())
157                                                                        .getData();
158         // fill in starting image contents based on last image's dispose
159         // code
160         if (lastDispose > 0) {
161             if (lastDispose == 3) {
162                 // use image before last
163                 int n = frameCount - 2;
164                 if (n > 0) {
165                     lastImage = getFrame(n - 1);
166                 }
167                 else {
168                     lastImage = null;
169                 }
170             }
171             if (lastImage != null) {
172                 int[] prev = ((DataBufferInt)lastImage.getRaster()
173                                                       .getDataBuffer())
174                                                                        .getData();
175                 System.arraycopy(prev, 0, dest, 0, width * height);
176                 // copy pixels
177                 if (lastDispose == 2) {
178                     // fill last image rect area with background color
179                     Graphics2D g = image.createGraphics();
180                     Color c = null;
181                     if (transparency) {
182                         c = new Color(0000)// assume background
183                         // is transparent
184                     }
185                     else {
186                         c = new Color(lastBgColor)// use given
187                         // background color
188                     }
189                     g.setColor(c);
190                     g.setComposite(AlphaComposite.Src)// replace area
191                     g.fill(lastRect);
192                     g.dispose();
193                 }
194             }
195         }
196         // copy each source line to the appropriate place in the
197         // destination
198         int pass = 1;
199         int inc = 8;
200         int iline = 0;
201         for (int i = 0; i < ih; i++) {
202             int line = i;
203             if (interlace) {
204                 if (iline >= ih) {
205                     pass++;
206                     switch (pass) {
207                     case 2:
208                         iline = 4;
209                         break;
210                     case 3:
211                         iline = 2;
212                         inc = 4;
213                         break;
214                     case 4:
215                         iline = 1;
216                         inc = 2;
217                     }
218                 }
219                 line = iline;
220                 iline += inc;
221             }
222             line += iy;
223             if (line < height) {
224                 int k = line * width;
225                 int dx = k + ix; // start of line in dest
226                 int dlim = dx + iw; // end of dest line
227                 if ((k + width< dlim) {
228                     dlim = k + width; // past dest edge
229                 }
230                 int sx = i * iw; // start of line in source
231                 while (dx < dlim) {
232                     // map color and insert in destination
233                     int index = pixels[sx++0xff;
234                     int c = act[index];
235                     if (c != 0) {
236                         dest[dx= c;
237                     }
238                     dx++;
239                 }
240             }
241         }
242     }
243 
244     /**
245      * Gets the image contents of frame n.
246      
247      @return BufferedImage representation of frame, or null if n is
248      *         invalid.
249      */
250     public BufferedImage getFrame(int n) {
251         BufferedImage im = null;
252         if ((n >= 0&& (n < frameCount)) {
253             im = ((GifFrame)frames.get(n)).image;
254         }
255         return im;
256     }
257 
258     /**
259      * Gets image size.
260      
261      @return GIF image dimensions
262      */
263     public Dimension getFrameSize() {
264         return new Dimension(width, height);
265     }
266 
267     /**
268      * Reads GIF image from stream
269      
270      @param is BufferedInputStream
271      *            containing GIF file.
272      @return read status code (0 = no errors)
273      */
274     public int read(BufferedInputStream is) {
275         init();
276         if (is != null) {
277             in = is;
278             readHeader();
279             if (!err()) {
280                 readContents();
281                 if (frameCount < 0) {
282                     status = STATUS_FORMAT_ERROR;
283                 }
284             }
285         }
286         else {
287             status = STATUS_OPEN_ERROR;
288         }
289         try {
290             is.close();
291         }
292         catch (IOException e) {
293         }
294         return status;
295     }
296 
297     /**
298      * Reads GIF image from stream
299      
300      @param is InputStream 
301      *            containing GIF file.
302      @return read status code (0 = no errors)
303      */
304     public int read(InputStream is) {
305         init();
306         if (is != null) {
307             if (!(is instanceof BufferedInputStream))
308                 is = new BufferedInputStream(is);
309             in = (BufferedInputStream)is;
310             readHeader();
311             if (!err()) {
312                 readContents();
313                 if (frameCount < 0) {
314                     status = STATUS_FORMAT_ERROR;
315                 }
316             }
317         }
318         else {
319             status = STATUS_OPEN_ERROR;
320         }
321         try {
322             is.close();
323         }
324         catch (IOException e) {
325         }
326         return status;
327     }
328 
329     /**
330      * Reads GIF file from specified file/URL source (URL assumed if
331      * name contains ":/" or "file:")
332      
333      @param name
334      *            String containing source
335      @return read status code (0 = no errors)
336      */
337     public int read(String name) {
338         status = STATUS_OK;
339         try {
340             name = name.trim().toLowerCase();
341             if ((name.indexOf("file:">= 0|| (name.indexOf(":/"0)) {
342                 URL url = new URL(name);
343                 in = new BufferedInputStream(url.openStream());
344             }
345             else {
346                 in = new BufferedInputStream(new FileInputStream(name));
347             }
348             status = read(in);
349         }
350         catch (IOException e) {
351             status = STATUS_OPEN_ERROR;
352         }
353         return status;
354     }
355 
356     /**
357      * Decodes LZW image data into pixel array. Adapted from John
358      * Cristy's ImageMagick.
359      */
360     protected void decodeImageData() {
361         int NullCode = -1;
362         int npix = iw * ih;
363         int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi;
364         if ((pixels == null|| (pixels.length < npix)) {
365             pixels = new byte[npix]// allocate new pixel array
366         }
367         if (prefix == null)
368             prefix = new short[MaxStackSize];
369         if (suffix == null)
370             suffix = new byte[MaxStackSize];
371         if (pixelStack == null)
372             pixelStack = new byte[MaxStackSize + 1];
373         // Initialize GIF data stream decoder.
374         data_size = read();
375         clear = << data_size;
376         end_of_information = clear + 1;
377         available = clear + 2;
378         old_code = NullCode;
379         code_size = data_size + 1;
380         code_mask = (<< code_size1;
381         for (code = 0; code < clear; code++) {
382             prefix[code0;
383             suffix[code(byte)code;
384         }
385         // Decode GIF pixel stream.
386         datum = bits = count = first = top = pi = bi = 0;
387         for (i = 0; i < npix;) {
388             if (top == 0) {
389                 if (bits < code_size) {
390                     // Load bytes until there are enough bits for a
391                     // code.
392                     if (count == 0) {
393                         // Read a new data block.
394                         count = readBlock();
395                         if (count <= 0)
396                             break;
397                         bi = 0;
398                     }
399                     datum += (block[bi0xff<< bits;
400                     bits += 8;
401                     bi++;
402                     count--;
403                     continue;
404                 }
405                 // Get the next code.
406                 code = datum & code_mask;
407                 datum >>= code_size;
408                 bits -= code_size;
409                 // Interpret the code
410                 if ((code > available|| (code == end_of_information))
411                     break;
412                 if (code == clear) {
413                     // Reset decoder.
414                     code_size = data_size + 1;
415                     code_mask = (<< code_size1;
416                     available = clear + 2;
417                     old_code = NullCode;
418                     continue;
419                 }
420                 if (old_code == NullCode) {
421                     pixelStack[top++= suffix[code];
422                     old_code = code;
423                     first = code;
424                     continue;
425                 }
426                 in_code = code;
427                 if (code == available) {
428                     pixelStack[top++(byte)first;
429                     code = old_code;
430                 }
431                 while (code > clear) {
432                     pixelStack[top++= suffix[code];
433                     code = prefix[code];
434                 }
435                 first = suffix[code0xff;
436                 // Add a new string to the string table,
437                 if (available >= MaxStackSize)
438                     break;
439                 pixelStack[top++(byte)first;
440                 prefix[available(short)old_code;
441                 suffix[available(byte)first;
442                 available++;
443                 if (((available & code_mask== 0)
444                     && (available < MaxStackSize)) {
445                     code_size++;
446                     code_mask += available;
447                 }
448                 old_code = in_code;
449             }
450             // Pop a pixel off the pixel stack.
451             top--;
452             pixels[pi++= pixelStack[top];
453             i++;
454         }
455         for (i = pi; i < npix; i++) {
456             pixels[i0// clear missing pixels
457         }
458     }
459 
460     /**
461      * Returns true if an error was encountered during reading/decoding
462      */
463     protected boolean err() {
464         return status != STATUS_OK;
465     }
466 
467     /**
468      * Initializes or re-initializes reader
469      */
470     protected void init() {
471         status = STATUS_OK;
472         frameCount = 0;
473         frames = new ArrayList();
474         gct = null;
475         lct = null;
476     }
477 
478     /**
479      * Reads a single byte from the input stream.
480      */
481     protected int read() {
482         int curByte = 0;
483         try {
484             curByte = in.read();
485         }
486         catch (IOException e) {
487             status = STATUS_FORMAT_ERROR;
488         }
489         return curByte;
490     }
491 
492     /**
493      * Reads next variable length block from input.
494      
495      @return number of bytes stored in "buffer"
496      */
497     protected int readBlock() {
498         blockSize = read();
499         int n = 0;
500         if (blockSize > 0) {
501             try {
502                 int count = 0;
503                 while (n < blockSize) {
504                     count = in.read(block, n, blockSize - n);
505                     if (count == -1)
506                         break;
507                     n += count;
508                 }
509             }
510             catch (IOException e) {
511             }
512             if (n < blockSize) {
513                 status = STATUS_FORMAT_ERROR;
514             }
515         }
516         return n;
517     }
518 
519     /**
520      * Reads color table as 256 RGB integer values
521      
522      @param ncolors
523      *            int number of colors to read
524      @return int array containing 256 colors (packed ARGB with full
525      *         alpha)
526      */
527     protected int[] readColorTable(int ncolors) {
528         int nbytes = * ncolors;
529         int[] tab = null;
530         byte[] c = new byte[nbytes];
531         int n = 0;
532         try {
533             n = in.read(c);
534         }
535         catch (IOException e) {
536         }
537         if (n < nbytes) {
538             status = STATUS_FORMAT_ERROR;
539         }
540         else {
541             tab = new int[256]// max size to avoid bounds checks
542             int i = 0;
543             int j = 0;
544             while (i < ncolors) {
545                 int r = c[j++0xff;
546                 int g = c[j++0xff;
547                 int b = c[j++0xff;
548                 tab[i++0xff000000 (r << 16(g << 8| b;
549             }
550         }
551         return tab;
552     }
553 
554     /**
555      * Main file parser. Reads GIF content blocks.
556      */
557     protected void readContents() {
558         // read GIF file content blocks
559         boolean done = false;
560         while (!(done || err())) {
561             int code = read();
562             switch (code) {
563             case 0x2C// image separator
564                 readImage();
565                 break;
566             case 0x21// extension
567                 code = read();
568                 switch (code) {
569                 case 0xf9// graphics control extension
570                     readGraphicControlExt();
571                     break;
572                 case 0xff// application extension
573                     readBlock();
574                     String app = "";
575                     for (int i = 0; i < 11; i++) {
576                         app += (char)block[i];
577                     }
578                     if (app.equals("NETSCAPE2.0")) {
579                         readNetscapeExt();
580                     }
581                     else
582                         skip()// don't care
583                     break;
584                 default// uninteresting extension
585                     skip();
586                 }
587                 break;
588             case 0x3b// terminator
589                 done = true;
590                 break;
591             case 0x00// bad byte, but keep going and see what happens
592                 break;
593             default:
594                 status = STATUS_FORMAT_ERROR;
595             }
596         }
597     }
598 
599     /**
600      * Reads Graphics Control Extension values
601      */
602     protected void readGraphicControlExt() {
603         read()// block size
604         int packed = read()// packed fields
605         dispose = (packed & 0x1c>> 2// disposal method
606         if (dispose == 0) {
607             dispose = 1// elect to keep old image if discretionary
608         }
609         transparency = (packed & 1!= 0;
610         delay = readShort() 10// delay in milliseconds
611         transIndex = read()// transparent color index
612         read()// block terminator
613     }
614 
615     /**
616      * Reads GIF file header information.
617      */
618     protected void readHeader() {
619         String id = "";
620         for (int i = 0; i < 6; i++) {
621             id += (char)read();
622         }
623         if (!id.startsWith("GIF")) {
624             status = STATUS_FORMAT_ERROR;
625             return;
626         }
627         readLSD();
628         if (gctFlag && !err()) {
629             gct = readColorTable(gctSize);
630             bgColor = gct[bgIndex];
631         }
632     }
633 
634     /**
635      * Reads next frame image
636      */
637     protected void readImage() {
638         ix = readShort()// (sub)image position & size
639         iy = readShort();
640         iw = readShort();
641         ih = readShort();
642         int packed = read();
643         lctFlag = (packed & 0x80!= 0// 1 - local color table flag
644         interlace = (packed & 0x40!= 0// 2 - interlace flag
645         // 3 - sort flag
646         // 4-5 - reserved
647         lctSize = << (packed & 7)// 6-8 - local color table size
648         if (lctFlag) {
649             lct = readColorTable(lctSize)// read table
650             act = lct; // make local table active
651         }
652         else {
653             act = gct; // make global table active
654             if (bgIndex == transIndex)
655                 bgColor = 0;
656         }
657         int save = 0;
658         if (transparency) {
659             save = act[transIndex];
660             act[transIndex0// set transparent color if
661             // specified
662         }
663         if (act == null) {
664             status = STATUS_FORMAT_ERROR; // no color table defined
665         }
666         if (err())
667             return;
668         decodeImageData()// decode pixel data
669         skip();
670         if (err())
671             return;
672         frameCount++;
673         // create new image to receive frame data
674         image = new BufferedImage(width, height,
675                                   BufferedImage.TYPE_INT_ARGB_PRE);
676         setPixels()// transfer pixel data to image
677         frames.add(new GifFrame(image, delay))// add image to frame
678         // list
679         if (transparency) {
680             act[transIndex= save;
681         }
682         resetFrame();
683     }
684 
685     /**
686      * Reads Logical Screen Descriptor
687      */
688     protected void readLSD() {
689         // logical screen size
690         width = readShort();
691         height = readShort();
692         // packed fields
693         int packed = read();
694         gctFlag = (packed & 0x80!= 0// 1 : global color table flag
695         // 2-4 : color resolution
696         // 5 : gct sort flag
697         gctSize = << (packed & 7)// 6-8 : gct size
698         bgIndex = read()// background color index
699         pixelAspect = read()// pixel aspect ratio
700     }
701 
702     /**
703      * Reads Netscape extenstion to obtain iteration count
704      */
705     protected void readNetscapeExt() {
706         do {
707             readBlock();
708             if (block[0== 1) {
709                 // loop count sub-block
710                 int b1 = block[10xff;
711                 int b2 = block[20xff;
712                 loopCount = (b2 << 8| b1;
713             }
714         while ((blockSize > 0&& !err());
715     }
716 
717     /**
718      * Reads next 16-bit value, LSB first
719      */
720     protected int readShort() {
721         // read 16-bit value, LSB first
722         return read() (read() << 8);
723     }
724 
725     /**
726      * Resets frame state for reading next image.
727      */
728     protected void resetFrame() {
729         lastDispose = dispose;
730         lastRect = new Rectangle(ix, iy, iw, ih);
731         lastImage = image;
732         lastBgColor = bgColor;
733         dispose = 0;//was local
734         transparency = false;//was local
735         delay = 0;//was local
736         lct = null;
737     }
738 
739     /**
740      * Skips variable length blocks up to and including next zero length
741      * block.
742      */
743     protected void skip() {
744         do {
745             readBlock();
746         while ((blockSize > 0&& !err());
747     }
748 }