/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