View Javadoc
1   package de.onyxbits.giftedmotion;
2   
3   import java.awt.Point;
4   import java.awt.Rectangle;
5   import java.awt.image.BufferedImage;
6   import java.awt.image.DataBuffer;
7   import java.awt.image.WritableRaster;
8   import java.io.BufferedInputStream;
9   import java.io.DataInputStream;
10  import java.io.EOFException;
11  import java.io.InputStream;
12  import java.io.IOException;
13  import java.nio.ByteOrder;
14  import java.util.ArrayList;
15  import java.util.Arrays;
16  import java.util.Iterator;
17  import java.util.List;
18  
19  import javax.imageio.IIOException;
20  import javax.imageio.ImageReader;
21  import javax.imageio.ImageReadParam;
22  import javax.imageio.ImageTypeSpecifier;
23  import javax.imageio.metadata.IIOMetadata;
24  import javax.imageio.spi.ImageReaderSpi;
25  import javax.imageio.stream.ImageInputStream;
26  
27  import com.sun.imageio.plugins.common.ReaderUtil;
28  import com.sun.imageio.plugins.gif.GIFImageMetadata;
29  import com.sun.imageio.plugins.gif.GIFImageReader;
30  import com.sun.imageio.plugins.gif.GIFStreamMetadata;
31  
32  public class PatchedGIFImageReader extends ImageReader {
33  
34      public PatchedGIFImageReader(ImageReaderSpi originatingProvider) {super(originatingProvider);}
35  
36  	// The current ImageInputStream source.
37      ImageInputStream stream = null;
38  
39      // Per-stream settings
40  
41      // True if the file header including stream metadata has been read.
42      boolean gotHeader = false;
43  
44      // Global metadata, read once per input setting.
45      GIFStreamMetadata streamMetadata = null;
46  
47      // The current image index
48      int currIndex = -1;
49  
50      // Metadata for image at 'currIndex', or null.
51      GIFImageMetadata imageMetadata = null;
52  
53      // A List of Longs indicating the stream positions of the
54      // start of the metadata for each image.  Entries are added
55      // as needed.
56      List imageStartPosition = new ArrayList();
57  
58      // Length of metadata for image at 'currIndex', valid only if
59      // imageMetadata != null.
60      int imageMetadataLength;
61  
62      // The number of images in the stream, if known, otherwise -1.
63      int numImages = -1;
64  
65      // Variables used by the LZW decoding process
66      byte[] block = new byte[255];
67      int blockLength = 0;
68      int bitPos = 0;
69      int nextByte = 0;
70      int initCodeSize;
71      int clearCode;
72      int eofCode;
73  
74      // 32-bit lookahead buffer
75      int next32Bits = 0;
76  
77      // Try if the end of the data blocks has been found,
78      // and we are simply draining the 32-bit buffer
79      boolean lastBlockFound = false;
80  
81      // The image to be written.
82      BufferedImage theImage = null;
83  
84      // The image's tile.
85      WritableRaster theTile = null;
86  
87      // The image dimensions (from the stream).
88      int width = -1, height = -1;
89  
90      // The pixel currently being decoded (in the stream's coordinates).
91      int streamX = -1, streamY = -1;
92  
93      // The number of rows decoded
94      int rowsDone = 0;
95  
96      // The current interlace pass, starting with 0.
97      int interlacePass = 0;
98  
99      // End per-stream settings
100 
101     // Constants used to control interlacing.
102     static final int[] interlaceIncrement = { 8, 8, 4, 2, -1 };
103     static final int[] interlaceOffset = { 0, 4, 2, 1, -1 };
104 
105     // Take input from an ImageInputStream
106     public void setInput(Object input,
107                          boolean seekForwardOnly,
108                          boolean ignoreMetadata) {
109         super.setInput(input, seekForwardOnly, ignoreMetadata);
110         if (input != null) {
111             if (!(input instanceof ImageInputStream)) {
112                 throw new IllegalArgumentException
113                     ("input not an ImageInputStream!");
114             }
115             this.stream = (ImageInputStream)input;
116         } else {
117             this.stream = null;
118         }
119 
120         // Clear all values based on the previous stream contents
121         resetStreamSettings();
122     }
123 
124     public int getNumImages(boolean allowSearch) throws IIOException {
125         if (stream == null) {
126             throw new IllegalStateException("Input not set!");
127         }
128         if (seekForwardOnly && allowSearch) {
129             throw new IllegalStateException
130                 ("seekForwardOnly and allowSearch can't both be true!");
131         }
132 
133         if (numImages > 0) {
134             return numImages;
135         }
136         if (allowSearch) {
137             this.numImages = locateImage(Integer.MAX_VALUE) + 1;
138         }
139         return numImages;
140     }
141 
142     // Throw an IndexOutOfBoundsException if index < minIndex,
143     // and bump minIndex if required.
144     private void checkIndex(int imageIndex) {
145         if (imageIndex < minIndex) {
146             throw new IndexOutOfBoundsException("imageIndex < minIndex!");
147         }
148         if (seekForwardOnly) {
149             minIndex = imageIndex;
150         }
151     }
152 
153     public int getWidth(int imageIndex) throws IIOException {
154         checkIndex(imageIndex);
155 
156         int index = locateImage(imageIndex);
157         if (index != imageIndex) {
158             throw new IndexOutOfBoundsException();
159         }
160         readMetadata();
161         return imageMetadata.imageWidth;
162     }
163 
164     public int getHeight(int imageIndex) throws IIOException {
165         checkIndex(imageIndex);
166 
167         int index = locateImage(imageIndex);
168         if (index != imageIndex) {
169             throw new IndexOutOfBoundsException();
170         }
171         readMetadata();
172         return imageMetadata.imageHeight;
173     }
174 
175     public Iterator getImageTypes(int imageIndex) throws IIOException {
176         checkIndex(imageIndex);
177 
178         int index = locateImage(imageIndex);
179         if (index != imageIndex) {
180             throw new IndexOutOfBoundsException();
181         }
182         readMetadata();
183 
184         List l = new ArrayList(1);
185 
186         byte[] colorTable;
187         if (imageMetadata.localColorTable != null) {
188             colorTable = imageMetadata.localColorTable;
189         } else {
190             colorTable = streamMetadata.globalColorTable;
191         }
192 
193         // Normalize color table length to 2^1, 2^2, 2^4, or 2^8
194         int length = colorTable.length/3;
195         int bits;
196         if (length == 2) {
197             bits = 1;
198         } else if (length == 4) {
199             bits = 2;
200         } else if (length == 8 || length == 16) {
201             // Bump from 3 to 4 bits
202             bits = 4;
203         } else {
204             // Bump to 8 bits
205             bits = 8;
206         }
207         int lutLength = 1 << bits;
208         byte[] r = new byte[lutLength];
209         byte[] g = new byte[lutLength];
210         byte[] b = new byte[lutLength];
211 
212         // Entries from length + 1 to lutLength - 1 will be 0
213         int rgbIndex = 0;
214         for (int i = 0; i < length; i++) {
215             r[i] = colorTable[rgbIndex++];
216             g[i] = colorTable[rgbIndex++];
217             b[i] = colorTable[rgbIndex++];
218         }
219 
220         byte[] a = null;
221         if (imageMetadata.transparentColorFlag) {
222             a = new byte[lutLength];
223             Arrays.fill(a, (byte)255);
224 
225             // Some files erroneously have a transparent color index
226             // of 255 even though there are fewer than 256 colors.
227             int idx = Math.min(imageMetadata.transparentColorIndex,
228                                lutLength - 1);
229             a[idx] = (byte)0;
230         }
231 
232         int[] bitsPerSample = new int[1];
233         bitsPerSample[0] = bits;
234         l.add(ImageTypeSpecifier.createIndexed(r, g, b, a, bits,
235                                                DataBuffer.TYPE_BYTE));
236         return l.iterator();
237     }
238 
239     public ImageReadParam getDefaultReadParam() {
240         return new ImageReadParam();
241     }
242 
243     public IIOMetadata getStreamMetadata() throws IIOException {
244         readHeader();
245         return streamMetadata;
246     }
247 
248     public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {
249         checkIndex(imageIndex);
250 
251         int index = locateImage(imageIndex);
252         if (index != imageIndex) {
253             throw new IndexOutOfBoundsException("Bad image index!");
254         }
255         readMetadata();
256         return imageMetadata;
257     }
258 
259     // BEGIN LZW STUFF
260 
261     private void initNext32Bits() {
262         next32Bits = block[0] & 0xff;
263         next32Bits |= (block[1] & 0xff) << 8;
264         next32Bits |= (block[2] & 0xff) << 16;
265         next32Bits |= block[3] << 24;
266         nextByte = 4;
267     }
268 
269     // Load a block (1-255 bytes) at a time, and maintain
270     // a 32-bit lookahead buffer that is filled from the left
271     // and extracted from the right.
272     //
273     // When the last block is found, we continue to
274     //
275     private int getCode(int codeSize, int codeMask) throws IOException {
276         if (bitPos + codeSize > 32) {
277             return eofCode; // No more data available
278         }
279 
280         int code = (next32Bits >> bitPos) & codeMask;
281         bitPos += codeSize;
282 
283         // Shift in a byte of new data at a time
284         while (bitPos >= 8 && !lastBlockFound) {
285             next32Bits >>>= 8;
286             bitPos -= 8;
287 
288             // Check if current block is out of bytes
289             if (nextByte >= blockLength) {
290                 // Get next block size
291                 blockLength = stream.readUnsignedByte();
292                 if (blockLength == 0) {
293                     lastBlockFound = true;
294                     return code;
295                 } else {
296                     int left = blockLength;
297                     int off = 0;
298                     while (left > 0) {
299                         int nbytes = stream.read(block, off, left);
300                         off += nbytes;
301                         left -= nbytes;
302                     }
303                     nextByte = 0;
304                 }
305             }
306 
307             next32Bits |= block[nextByte++] << 24;
308         }
309 
310         return code;
311     }
312 
313     public void initializeStringTable(int[] prefix,
314                                       byte[] suffix,
315                                       byte[] initial,
316                                       int[] length) {
317         int numEntries = 1 << initCodeSize;
318         for (int i = 0; i < numEntries; i++) {
319             prefix[i] = -1;
320             suffix[i] = (byte)i;
321             initial[i] = (byte)i;
322             length[i] = 1;
323         }
324 
325         // Fill in the entire table for robustness against
326         // out-of-sequence codes.
327         for (int i = numEntries; i < 4096; i++) {
328             prefix[i] = -1;
329             length[i] = 1;
330         }
331 
332         // tableIndex = numEntries + 2;
333         // codeSize = initCodeSize + 1;
334         // codeMask = (1 << codeSize) - 1;
335     }
336 
337     Rectangle sourceRegion;
338     int sourceXSubsampling;
339     int sourceYSubsampling;
340     int sourceMinProgressivePass;
341     int sourceMaxProgressivePass;
342 
343     Point destinationOffset;
344     Rectangle destinationRegion;
345 
346     // Used only if IIOReadUpdateListeners are present
347     int updateMinY;
348     int updateYStep;
349 
350     boolean decodeThisRow = true;
351     int destY = 0;
352 
353     byte[] rowBuf;
354 
355     private void outputRow() {
356         // Clip against ImageReadParam
357         int width = Math.min(sourceRegion.width,
358                              destinationRegion.width*sourceXSubsampling);
359         int destX = destinationRegion.x;
360 
361         if (sourceXSubsampling == 1) {
362             theTile.setDataElements(destX, destY, width, 1, rowBuf);
363         } else {
364             for (int x = 0; x < width; x += sourceXSubsampling, destX++) {
365                 theTile.setSample(destX, destY, 0, rowBuf[x] & 0xff);
366             }
367         }
368 
369         // Update IIOReadUpdateListeners, if any
370         if (updateListeners != null) {
371             int[] bands = { 0 };
372             // updateYStep will have been initialized if
373             // updateListeners is non-null
374             processImageUpdate(theImage,
375                                destX, destY,
376                                width, 1, 1, updateYStep,
377                                bands);
378         }
379     }
380 
381     private void computeDecodeThisRow() {
382         this.decodeThisRow =
383             (destY < destinationRegion.y + destinationRegion.height) &&
384             (streamY >= sourceRegion.y) &&
385             (streamY < sourceRegion.y + sourceRegion.height) &&
386             (((streamY - sourceRegion.y) % sourceYSubsampling) == 0);
387     }
388 
389     private void outputPixels(byte[] string, int len) {
390         if (interlacePass < sourceMinProgressivePass ||
391             interlacePass > sourceMaxProgressivePass) {
392             return;
393         }
394 
395         for (int i = 0; i < len; i++) {
396             if (streamX >= sourceRegion.x) {
397                 rowBuf[streamX - sourceRegion.x] = string[i];
398             }
399 
400             // Process end-of-row
401             ++streamX;
402             if (streamX == width) {
403                 // Update IIOReadProgressListeners
404                 ++rowsDone;
405                 processImageProgress(100.0F*rowsDone/height);
406 
407                 if (decodeThisRow) {
408                     outputRow();
409                 }
410 
411                 streamX = 0;
412                 if (imageMetadata.interlaceFlag) {
413                     streamY += interlaceIncrement[interlacePass];
414                     if (streamY >= height) {
415                         // Inform IIOReadUpdateListeners of end of pass
416                         if (updateListeners != null) {
417                             processPassComplete(theImage);
418                         }
419 
420                         ++interlacePass;
421                         if (interlacePass > sourceMaxProgressivePass) {
422                             return;
423                         }
424                         streamY = interlaceOffset[interlacePass];
425                         startPass(interlacePass);
426                     }
427                 } else {
428                     ++streamY;
429                 }
430 
431                 // Determine whether pixels from this row will
432                 // be written to the destination
433                 this.destY = destinationRegion.y +
434                     (streamY - sourceRegion.y)/sourceYSubsampling;
435                 computeDecodeThisRow();
436             }
437         }
438     }
439 
440     // END LZW STUFF
441 
442     private void readHeader() throws IIOException {
443         if (gotHeader) {
444             return;
445         }
446         if (stream == null) {
447             throw new IllegalStateException("Input not set!");
448         }
449 
450         // Create an object to store the stream metadata
451         this.streamMetadata = new GIFStreamMetadata();
452 
453         try {
454             stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
455 
456             byte[] signature = new byte[6];
457             stream.readFully(signature);
458 
459             StringBuffer version = new StringBuffer(3);
460             version.append((char)signature[3]);
461             version.append((char)signature[4]);
462             version.append((char)signature[5]);
463             streamMetadata.version = version.toString();
464 
465             streamMetadata.logicalScreenWidth = stream.readUnsignedShort();
466             streamMetadata.logicalScreenHeight = stream.readUnsignedShort();
467 
468             int packedFields = stream.readUnsignedByte();
469             boolean globalColorTableFlag = (packedFields & 0x80) != 0;
470             streamMetadata.colorResolution = ((packedFields >> 4) & 0x7) + 1;
471             streamMetadata.sortFlag = (packedFields & 0x8) != 0;
472             int numGCTEntries = 1 << ((packedFields & 0x7) + 1);
473 
474             streamMetadata.backgroundColorIndex = stream.readUnsignedByte();
475             streamMetadata.pixelAspectRatio = stream.readUnsignedByte();
476 
477             if (globalColorTableFlag) {
478                 streamMetadata.globalColorTable = new byte[3*numGCTEntries];
479                 stream.readFully(streamMetadata.globalColorTable);
480             } else {
481                 streamMetadata.globalColorTable = null;
482             }
483 
484             // Found position of metadata for image 0
485             imageStartPosition.add(Long.valueOf(stream.getStreamPosition()));
486         } catch (IOException e) {
487             throw new IIOException("I/O error reading header!", e);
488         }
489 
490         gotHeader = true;
491     }
492 
493     private boolean skipImage() throws IIOException {
494         // Stream must be at the beginning of an image descriptor
495         // upon exit
496 
497         try {
498             while (true) {
499                 int blockType = stream.readUnsignedByte();
500 
501                 if (blockType == 0x2c) {
502                     stream.skipBytes(8);
503 
504                     int packedFields = stream.readUnsignedByte();
505                     if ((packedFields & 0x80) != 0) {
506                         // Skip color table if any
507                         int bits = (packedFields & 0x7) + 1;
508                         stream.skipBytes(3*(1 << bits));
509                     }
510 
511                     stream.skipBytes(1);
512 
513                     int length = 0;
514                     do {
515                         length = stream.readUnsignedByte();
516                         stream.skipBytes(length);
517                     } while (length > 0);
518 
519                     return true;
520                 } else if (blockType == 0x3b) {
521                     return false;
522                 } else if (blockType == 0x21) {
523                     int label = stream.readUnsignedByte();
524 
525                     int length = 0;
526                     do {
527                         length = stream.readUnsignedByte();
528                         stream.skipBytes(length);
529                     } while (length > 0);
530                 } else if (blockType == 0x0) {
531                     // EOF
532                     return false;
533                 } else {
534                     int length = 0;
535                     do {
536                         length = stream.readUnsignedByte();
537                         stream.skipBytes(length);
538                     } while (length > 0);
539                 }
540             }
541         } catch (EOFException e) {
542             return false;
543         } catch (IOException e) {
544             throw new IIOException("I/O error locating image!", e);
545         }
546     }
547 
548     private int locateImage(int imageIndex) throws IIOException {
549         readHeader();
550 
551         try {
552             // Find closest known index
553             int index = Math.min(imageIndex, imageStartPosition.size() - 1);
554 
555             // Seek to that position
556             Long l = (Long)imageStartPosition.get(index);
557             stream.seek(l.longValue());
558 
559             // Skip images until at desired index or last image found
560             while (index < imageIndex) {
561                 if (!skipImage()) {
562                     --index;
563                     return index;
564                 }
565 
566                 Long l1 = new Long(stream.getStreamPosition());
567                 imageStartPosition.add(l1);
568                 ++index;
569             }
570         } catch (IOException e) {
571             throw new IIOException("Couldn't seek!", e);
572         }
573 
574         if (currIndex != imageIndex) {
575             imageMetadata = null;
576         }
577         currIndex = imageIndex;
578         return imageIndex;
579     }
580 
581     // Read blocks of 1-255 bytes, stop at a 0-length block
582     private byte[] concatenateBlocks() throws IOException {
583         byte[] data = new byte[0];
584         while (true) {
585             int length = stream.readUnsignedByte();
586             if (length == 0) {
587                 break;
588             }
589             byte[] newData = new byte[data.length + length];
590             System.arraycopy(data, 0, newData, 0, data.length);
591             stream.readFully(newData, data.length, length);
592             data = newData;
593         }
594 
595         return data;
596     }
597 
598     // Stream must be positioned at start of metadata for 'currIndex'
599     private void readMetadata() throws IIOException {
600         if (stream == null) {
601             throw new IllegalStateException("Input not set!");
602         }
603 
604         try {
605             // Create an object to store the image metadata
606             this.imageMetadata = new GIFImageMetadata();
607 
608             long startPosition = stream.getStreamPosition();
609             while (true) {
610                 int blockType = stream.readUnsignedByte();
611                 if (blockType == 0x2c) { // Image Descriptor
612                     imageMetadata.imageLeftPosition =
613                         stream.readUnsignedShort();
614                     imageMetadata.imageTopPosition =
615                         stream.readUnsignedShort();
616                     imageMetadata.imageWidth = stream.readUnsignedShort();
617                     imageMetadata.imageHeight = stream.readUnsignedShort();
618 
619                     int idPackedFields = stream.readUnsignedByte();
620                     boolean localColorTableFlag =
621                         (idPackedFields & 0x80) != 0;
622                     imageMetadata.interlaceFlag = (idPackedFields & 0x40) != 0;
623                     imageMetadata.sortFlag = (idPackedFields & 0x20) != 0;
624                     int numLCTEntries = 1 << ((idPackedFields & 0x7) + 1);
625 
626                     if (localColorTableFlag) {
627                         // Read color table if any
628                         imageMetadata.localColorTable =
629                             new byte[3*numLCTEntries];
630                         stream.readFully(imageMetadata.localColorTable);
631                     } else {
632                         imageMetadata.localColorTable = null;
633                     }
634 
635                     // Record length of this metadata block
636                     this.imageMetadataLength =
637                         (int)(stream.getStreamPosition() - startPosition);
638 
639                     // Now positioned at start of LZW-compressed pixels
640                     return;
641                 } else if (blockType == 0x21) { // Extension block
642                     int label = stream.readUnsignedByte();
643 
644                     if (label == 0xf9) { // Graphics Control Extension
645                         int gceLength = stream.readUnsignedByte(); // 4
646                         int gcePackedFields = stream.readUnsignedByte();
647                         imageMetadata.disposalMethod =
648                             (gcePackedFields >> 2) & 0x3;
649                         imageMetadata.userInputFlag =
650                             (gcePackedFields & 0x2) != 0;
651                         imageMetadata.transparentColorFlag =
652                             (gcePackedFields & 0x1) != 0;
653 
654                         imageMetadata.delayTime = stream.readUnsignedShort();
655                         imageMetadata.transparentColorIndex
656                             = stream.readUnsignedByte();
657 
658                         int terminator = stream.readUnsignedByte();
659                     } else if (label == 0x1) { // Plain text extension
660                         int length = stream.readUnsignedByte();
661                         imageMetadata.hasPlainTextExtension = true;
662                         imageMetadata.textGridLeft =
663                             stream.readUnsignedShort();
664                         imageMetadata.textGridTop =
665                             stream.readUnsignedShort();
666                         imageMetadata.textGridWidth =
667                             stream.readUnsignedShort();
668                         imageMetadata.textGridHeight =
669                             stream.readUnsignedShort();
670                         imageMetadata.characterCellWidth =
671                             stream.readUnsignedByte();
672                         imageMetadata.characterCellHeight =
673                             stream.readUnsignedByte();
674                         imageMetadata.textForegroundColor =
675                             stream.readUnsignedByte();
676                         imageMetadata.textBackgroundColor =
677                             stream.readUnsignedByte();
678                         imageMetadata.text = concatenateBlocks();
679                     } else if (label == 0xfe) { // Comment extension
680                         byte[] comment = concatenateBlocks();
681                         if (imageMetadata.comments == null) {
682                             imageMetadata.comments = new ArrayList();
683                         }
684                         imageMetadata.comments.add(comment);
685                     } else if (label == 0xff) { // Application extension
686                         int blockSize = stream.readUnsignedByte();
687                         byte[] applicationID = new byte[8];
688                         byte[] authCode = new byte[3];
689 
690                         // read available data
691                         byte[] blockData = new byte[blockSize];
692                         stream.readFully(blockData);
693 
694                         int offset = copyData(blockData, 0, applicationID);
695                         offset = copyData(blockData, offset, authCode);
696 
697                         byte[] applicationData = concatenateBlocks();
698 
699                         if (offset < blockSize) {
700                             int len = blockSize - offset;
701                             byte[] data =
702                                 new byte[len + applicationData.length];
703 
704                             System.arraycopy(blockData, offset, data, 0, len);
705                             System.arraycopy(applicationData, 0, data, len,
706                                              applicationData.length);
707 
708                             applicationData = data;
709                         }
710 
711                         // Init lists if necessary
712                         if (imageMetadata.applicationIDs == null) {
713                             imageMetadata.applicationIDs = new ArrayList();
714                             imageMetadata.authenticationCodes =
715                                 new ArrayList();
716                             imageMetadata.applicationData = new ArrayList();
717                         }
718                         imageMetadata.applicationIDs.add(applicationID);
719                         imageMetadata.authenticationCodes.add(authCode);
720                         imageMetadata.applicationData.add(applicationData);
721                     } else {
722                         // Skip over unknown extension blocks
723                         int length = 0;
724                         do {
725                             length = stream.readUnsignedByte();
726                             stream.skipBytes(length);
727                         } while (length > 0);
728                     }
729                 } else if (blockType == 0x3b) { // Trailer
730                     throw new IndexOutOfBoundsException
731                         ("Attempt to read past end of image sequence!");
732                 } else {
733                     throw new IIOException("Unexpected block type " +
734                                            blockType + "!");
735                 }
736             }
737         } catch (IIOException iioe) {
738             throw iioe;
739         } catch (IOException ioe) {
740             throw new IIOException("I/O error reading image metadata!", ioe);
741         }
742     }
743 
744     private int copyData(byte[] src, int offset, byte[] dst) {
745         int len = dst.length;
746         int rest = src.length - offset;
747         if (len > rest) {
748             len = rest;
749         }
750         System.arraycopy(src, offset, dst, 0, len);
751         return offset + len;
752     }
753 
754     private void startPass(int pass) {
755         if (updateListeners == null) {
756             return;
757         }
758 
759         int y = 0;
760         int yStep = 1;
761         if (imageMetadata.interlaceFlag) {
762             y = interlaceOffset[interlacePass];
763             yStep = interlaceIncrement[interlacePass];
764         }
765 
766         int[] vals = ReaderUtil.
767             computeUpdatedPixels(sourceRegion,
768                                  destinationOffset,
769                                  destinationRegion.x,
770                                  destinationRegion.y,
771                                  destinationRegion.x +
772                                  destinationRegion.width - 1,
773                                  destinationRegion.y +
774                                  destinationRegion.height - 1,
775                                  sourceXSubsampling,
776                                  sourceYSubsampling,
777                                  0,
778                                  y,
779                                  destinationRegion.width,
780                                  (destinationRegion.height + yStep - 1)/yStep,
781                                  1,
782                                  yStep);
783 
784         // Initialized updateMinY and updateYStep
785         this.updateMinY = vals[1];
786         this.updateYStep = vals[5];
787 
788         // Inform IIOReadUpdateListeners of new pass
789         int[] bands = { 0 };
790 
791         processPassStarted(theImage,
792                            interlacePass,
793                            sourceMinProgressivePass,
794                            sourceMaxProgressivePass,
795                            0,
796                            updateMinY,
797                            1,
798                            updateYStep,
799                            bands);
800     }
801 
802     public BufferedImage read(int imageIndex, ImageReadParam param)
803         throws IIOException {
804         if (stream == null) {
805             throw new IllegalStateException("Input not set!");
806         }
807         checkIndex(imageIndex);
808 
809         int index = locateImage(imageIndex);
810         if (index != imageIndex) {
811             throw new IndexOutOfBoundsException("imageIndex out of bounds!");
812         }
813 
814         clearAbortRequest();
815         readMetadata();
816 
817         // A null ImageReadParam means we use the default
818         if (param == null) {
819             param = getDefaultReadParam();
820         }
821 
822         // Initialize the destination image
823         Iterator imageTypes = getImageTypes(imageIndex);
824         this.theImage = getDestination(param,
825                                        imageTypes,
826                                        imageMetadata.imageWidth,
827                                        imageMetadata.imageHeight);
828         this.theTile = theImage.getWritableTile(0, 0);
829         this.width = imageMetadata.imageWidth;
830         this.height = imageMetadata.imageHeight;
831         this.streamX = 0;
832         this.streamY = 0;
833         this.rowsDone = 0;
834         this.interlacePass = 0;
835 
836         // Get source region, taking subsampling offsets into account,
837         // and clipping against the true source bounds
838 
839         this.sourceRegion = new Rectangle(0, 0, 0, 0);
840         this.destinationRegion = new Rectangle(0, 0, 0, 0);
841         computeRegions(param, width, height, theImage,
842                        sourceRegion, destinationRegion);
843         this.destinationOffset = new Point(destinationRegion.x,
844                                            destinationRegion.y);
845 
846         this.sourceXSubsampling = param.getSourceXSubsampling();
847         this.sourceYSubsampling = param.getSourceYSubsampling();
848         this.sourceMinProgressivePass =
849             Math.max(param.getSourceMinProgressivePass(), 0);
850         this.sourceMaxProgressivePass =
851             Math.min(param.getSourceMaxProgressivePass(), 3);
852 
853         this.destY = destinationRegion.y +
854             (streamY - sourceRegion.y)/sourceYSubsampling;
855         computeDecodeThisRow();
856 
857         // Inform IIOReadProgressListeners of start of image
858         processImageStarted(imageIndex);
859         startPass(0);
860 
861         this.rowBuf = new byte[width];
862 
863         try {
864             // Read and decode the image data, fill in theImage
865             this.initCodeSize = stream.readUnsignedByte();
866 
867             // Read first data block
868             this.blockLength = stream.readUnsignedByte();
869             int left = blockLength;
870             int off = 0;
871             while (left > 0) {
872                 int nbytes = stream.read(block, off, left);
873                 left -= nbytes;
874                 off += nbytes;
875             }
876 
877             this.bitPos = 0;
878             this.nextByte = 0;
879             this.lastBlockFound = false;
880             this.interlacePass = 0;
881 
882             // Init 32-bit buffer
883             initNext32Bits();
884 
885             this.clearCode = 1 << initCodeSize;
886             this.eofCode = clearCode + 1;
887 
888             int code, oldCode = 0;
889 
890             int[] prefix = new int[4096];
891             byte[] suffix = new byte[4096];
892             byte[] initial = new byte[4096];
893             int[] length = new int[4096];
894             byte[] string = new byte[4096];
895 
896             initializeStringTable(prefix, suffix, initial, length);
897             int tableIndex = (1 << initCodeSize) + 2;
898             int codeSize = initCodeSize + 1;
899             int codeMask = (1 << codeSize) - 1;
900 
901             while (!abortRequested()) {
902                 code = getCode(codeSize, codeMask);
903 
904                 if (code == clearCode) {
905                     initializeStringTable(prefix, suffix, initial, length);
906                     tableIndex = (1 << initCodeSize) + 2;
907                     codeSize = initCodeSize + 1;
908                     codeMask = (1 << codeSize) - 1;
909 
910                     code = getCode(codeSize, codeMask);
911                     if (code == eofCode) {
912                         // Inform IIOReadProgressListeners of end of image
913                         processImageComplete();
914                         return theImage;
915                     }
916                 } else if (code == eofCode) {
917                     // Inform IIOReadProgressListeners of end of image
918                     processImageComplete();
919                     return theImage;
920                 } else {
921                     int newSuffixIndex;
922                     if (code < tableIndex) {
923                         newSuffixIndex = code;
924                     } else { // code == tableIndex
925                         newSuffixIndex = oldCode;
926                         if (code != tableIndex) {
927                             // warning - code out of sequence
928                             // possibly data corruption
929                             processWarningOccurred("Out-of-sequence code!");
930                         }
931                     }
932 
933                     try
934                     {
935                     	int ti = tableIndex;
936                     
937                         int oc = oldCode;
938     
939                         prefix[ti] = oc;
940                         suffix[ti] = initial[newSuffixIndex];
941                         initial[ti] = initial[oc];
942                         length[ti] = length[oc] + 1;
943     
944                         ++tableIndex;
945                         if ((tableIndex == (1 << codeSize)) &&
946                             (tableIndex < 4096)) {
947                             ++codeSize;
948                             codeMask = (1 << codeSize) - 1;
949                         }
950                     } catch (ArrayIndexOutOfBoundsException e)
951                     {
952                     	//Die.
953                     	//Pretend that the clearcode was found.
954                     	initializeStringTable(prefix, suffix, initial, length);
955                         tableIndex = (1 << initCodeSize) + 2;
956                         codeSize = initCodeSize + 1;
957                         codeMask = (1 << codeSize) - 1;
958 
959                         code = getCode(codeSize, codeMask);
960                         if (code == eofCode) {
961                             // Inform IIOReadProgressListeners of end of image
962                             processImageComplete();
963                             return theImage;
964                         }
965                     }
966                 }
967 
968                 // Reverse code
969                 int c = code;
970                 int len = length[c];
971                 for (int i = len - 1; i >= 0; i--) {
972                     string[i] = suffix[c];
973                     c = prefix[c];
974                 }
975 
976                 outputPixels(string, len);
977                 oldCode = code;
978             }
979 
980             processReadAborted();
981             return theImage;
982         } catch (IOException e) {
983             e.printStackTrace();
984             throw new IIOException("I/O error reading image!", e);
985         }
986     }
987 
988     /**
989      * Remove all settings including global settings such as
990      * <code>Locale</code>s and listeners, as well as stream settings.
991      */
992     public void reset() {
993         super.reset();
994         resetStreamSettings();
995     }
996 
997     /**
998      * Remove local settings based on parsing of a stream.
999      */
1000     private void resetStreamSettings() {
1001         gotHeader = false;
1002         streamMetadata = null;
1003         currIndex = -1;
1004         imageMetadata = null;
1005         imageStartPosition = new ArrayList();
1006         numImages = -1;
1007 
1008         // No need to reinitialize 'block'
1009         blockLength = 0;
1010         bitPos = 0;
1011         nextByte = 0;
1012 
1013         next32Bits = 0;
1014         lastBlockFound = false;
1015 
1016         theImage = null;
1017         theTile = null;
1018         width = -1;
1019         height = -1;
1020         streamX = -1;
1021         streamY = -1;
1022         rowsDone = 0;
1023         interlacePass = 0;
1024     }
1025 }