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("sample.gif");
026 * int n = d.getFrameCount();
027 * for (int i = 0; i < 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(0, 0, 0, 0); // 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 = 1 << 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 = (1 << code_size) - 1;
381 for (code = 0; code < clear; code++) {
382 prefix[code] = 0;
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[bi] & 0xff) << 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 = (1 << code_size) - 1;
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[code] & 0xff;
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[i] = 0; // 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 = 3 * 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 = 2 << (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[transIndex] = 0; // 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 = 2 << (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[1] & 0xff;
711 int b2 = block[2] & 0xff;
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 }
|