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
37 ImageInputStream stream = null;
38
39
40
41
42 boolean gotHeader = false;
43
44
45 GIFStreamMetadata streamMetadata = null;
46
47
48 int currIndex = -1;
49
50
51 GIFImageMetadata imageMetadata = null;
52
53
54
55
56 List imageStartPosition = new ArrayList();
57
58
59
60 int imageMetadataLength;
61
62
63 int numImages = -1;
64
65
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
75 int next32Bits = 0;
76
77
78
79 boolean lastBlockFound = false;
80
81
82 BufferedImage theImage = null;
83
84
85 WritableRaster theTile = null;
86
87
88 int width = -1, height = -1;
89
90
91 int streamX = -1, streamY = -1;
92
93
94 int rowsDone = 0;
95
96
97 int interlacePass = 0;
98
99
100
101
102 static final int[] interlaceIncrement = { 8, 8, 4, 2, -1 };
103 static final int[] interlaceOffset = { 0, 4, 2, 1, -1 };
104
105
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
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
143
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
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
202 bits = 4;
203 } else {
204
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
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
226
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
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
270
271
272
273
274
275 private int getCode(int codeSize, int codeMask) throws IOException {
276 if (bitPos + codeSize > 32) {
277 return eofCode;
278 }
279
280 int code = (next32Bits >> bitPos) & codeMask;
281 bitPos += codeSize;
282
283
284 while (bitPos >= 8 && !lastBlockFound) {
285 next32Bits >>>= 8;
286 bitPos -= 8;
287
288
289 if (nextByte >= blockLength) {
290
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
326
327 for (int i = numEntries; i < 4096; i++) {
328 prefix[i] = -1;
329 length[i] = 1;
330 }
331
332
333
334
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
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
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
370 if (updateListeners != null) {
371 int[] bands = { 0 };
372
373
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
401 ++streamX;
402 if (streamX == width) {
403
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
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
432
433 this.destY = destinationRegion.y +
434 (streamY - sourceRegion.y)/sourceYSubsampling;
435 computeDecodeThisRow();
436 }
437 }
438 }
439
440
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
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
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
495
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
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
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
553 int index = Math.min(imageIndex, imageStartPosition.size() - 1);
554
555
556 Long l = (Long)imageStartPosition.get(index);
557 stream.seek(l.longValue());
558
559
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
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
599 private void readMetadata() throws IIOException {
600 if (stream == null) {
601 throw new IllegalStateException("Input not set!");
602 }
603
604 try {
605
606 this.imageMetadata = new GIFImageMetadata();
607
608 long startPosition = stream.getStreamPosition();
609 while (true) {
610 int blockType = stream.readUnsignedByte();
611 if (blockType == 0x2c) {
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
628 imageMetadata.localColorTable =
629 new byte[3*numLCTEntries];
630 stream.readFully(imageMetadata.localColorTable);
631 } else {
632 imageMetadata.localColorTable = null;
633 }
634
635
636 this.imageMetadataLength =
637 (int)(stream.getStreamPosition() - startPosition);
638
639
640 return;
641 } else if (blockType == 0x21) {
642 int label = stream.readUnsignedByte();
643
644 if (label == 0xf9) {
645 int gceLength = stream.readUnsignedByte();
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) {
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) {
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) {
686 int blockSize = stream.readUnsignedByte();
687 byte[] applicationID = new byte[8];
688 byte[] authCode = new byte[3];
689
690
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
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
723 int length = 0;
724 do {
725 length = stream.readUnsignedByte();
726 stream.skipBytes(length);
727 } while (length > 0);
728 }
729 } else if (blockType == 0x3b) {
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
785 this.updateMinY = vals[1];
786 this.updateYStep = vals[5];
787
788
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
818 if (param == null) {
819 param = getDefaultReadParam();
820 }
821
822
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
837
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
858 processImageStarted(imageIndex);
859 startPass(0);
860
861 this.rowBuf = new byte[width];
862
863 try {
864
865 this.initCodeSize = stream.readUnsignedByte();
866
867
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
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
913 processImageComplete();
914 return theImage;
915 }
916 } else if (code == eofCode) {
917
918 processImageComplete();
919 return theImage;
920 } else {
921 int newSuffixIndex;
922 if (code < tableIndex) {
923 newSuffixIndex = code;
924 } else {
925 newSuffixIndex = oldCode;
926 if (code != tableIndex) {
927
928
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
953
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
962 processImageComplete();
963 return theImage;
964 }
965 }
966 }
967
968
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
990
991
992 public void reset() {
993 super.reset();
994 resetStreamSettings();
995 }
996
997
998
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
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 }