KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > imageio > plugins > gif > GIFImageReader


1 /*
2  * @(#)GIFImageReader.java 1.44 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package com.sun.imageio.plugins.gif;
9
10 import java.awt.Point JavaDoc;
11 import java.awt.Rectangle JavaDoc;
12 import java.awt.image.BufferedImage JavaDoc;
13 import java.awt.image.DataBuffer JavaDoc;
14 import java.awt.image.WritableRaster JavaDoc;
15 import java.io.BufferedInputStream JavaDoc;
16 import java.io.DataInputStream JavaDoc;
17 import java.io.EOFException JavaDoc;
18 import java.io.InputStream JavaDoc;
19 import java.io.IOException JavaDoc;
20 import java.nio.ByteOrder JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.Arrays JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25 import javax.imageio.IIOException JavaDoc;
26 import javax.imageio.ImageReader JavaDoc;
27 import javax.imageio.ImageReadParam JavaDoc;
28 import javax.imageio.ImageTypeSpecifier JavaDoc;
29 import javax.imageio.metadata.IIOMetadata JavaDoc;
30 import javax.imageio.spi.ImageReaderSpi JavaDoc;
31 import javax.imageio.stream.ImageInputStream JavaDoc;
32
33 /**
34  * @version 0.5
35  */

36 public class GIFImageReader extends ImageReader JavaDoc {
37
38     // The current ImageInputStream source.
39
ImageInputStream JavaDoc stream = null;
40
41     // Per-stream settings
42

43     // True if the file header including stream metadata has been read.
44
boolean gotHeader = false;
45     
46     // Global metadata, read once per input setting.
47
GIFStreamMetadata streamMetadata = null;
48
49     // The current image index
50
int currIndex = -1;
51
52     // Metadata for image at 'currIndex', or null.
53
GIFImageMetadata imageMetadata = null;
54
55     // A List of Longs indicating the stream positions of the
56
// start of the metadata for each image. Entries are added
57
// as needed.
58
List JavaDoc imageStartPosition = new ArrayList JavaDoc();
59
60     // Length of metadata for image at 'currIndex', valid only if
61
// imageMetadata != null.
62
int imageMetadataLength;
63
64     // The number of images in the stream, if known, otherwise -1.
65
int numImages = -1;
66
67     // Variables used by the LZW decoding process
68
byte[] block = new byte[255];
69     int blockLength = 0;
70     int bitPos = 0;
71     int nextByte = 0;
72     int initCodeSize;
73     int clearCode;
74     int eofCode;
75
76     // 32-bit lookahead buffer
77
int next32Bits = 0;
78
79     // Try if the end of the data blocks has been found,
80
// and we are simply draining the 32-bit buffer
81
boolean lastBlockFound = false;
82
83     // The image to be written.
84
BufferedImage JavaDoc theImage = null;
85
86     // The image's tile.
87
WritableRaster JavaDoc theTile = null;
88     
89     // The image dimensions (from the stream).
90
int width = -1, height = -1;
91
92     // The pixel currently being decoded (in the stream's coordinates).
93
int streamX = -1, streamY = -1;
94
95     // The number of rows decoded
96
int rowsDone = 0;
97
98     // The current interlace pass, starting with 0.
99
int interlacePass = 0;
100
101     // End per-stream settings
102

103     // Constants used to control interlacing.
104
static final int[] interlaceIncrement = { 8, 8, 4, 2, -1 };
105     static final int[] interlaceOffset = { 0, 4, 2, 1, -1 };
106
107     public GIFImageReader(ImageReaderSpi JavaDoc originatingProvider) {
108         super(originatingProvider);
109     }
110
111     // Take input from an ImageInputStream
112
public void setInput(Object JavaDoc input,
113                          boolean seekForwardOnly,
114                          boolean ignoreMetadata) {
115         super.setInput(input, seekForwardOnly, ignoreMetadata);
116         if (input != null) {
117             if (!(input instanceof ImageInputStream JavaDoc)) {
118                 throw new IllegalArgumentException JavaDoc
119                     ("input not an ImageInputStream!");
120             }
121             this.stream = (ImageInputStream JavaDoc)input;
122         } else {
123             this.stream = null;
124         }
125         
126         // Clear all values based on the previous stream contents
127
resetStreamSettings();
128     }
129
130     public int getNumImages(boolean allowSearch) throws IIOException JavaDoc {
131         if (stream == null) {
132             throw new IllegalStateException JavaDoc("Input not set!");
133         }
134         if (seekForwardOnly && allowSearch) {
135             throw new IllegalStateException JavaDoc
136                 ("seekForwardOnly and allowSearch can't both be true!");
137         }
138
139         if (numImages > 0) {
140             return numImages;
141         }
142         if (allowSearch) {
143             this.numImages = locateImage(Integer.MAX_VALUE) + 1;
144         }
145         return numImages;
146     }
147
148     // Throw an IndexOutOfBoundsException if index < minIndex,
149
// and bump minIndex if required.
150
private void checkIndex(int imageIndex) {
151         if (imageIndex < minIndex) {
152             throw new IndexOutOfBoundsException JavaDoc("imageIndex < minIndex!");
153         }
154         if (seekForwardOnly) {
155             minIndex = imageIndex;
156         }
157     }
158
159     public int getWidth(int imageIndex) throws IIOException JavaDoc {
160         checkIndex(imageIndex);
161
162         int index = locateImage(imageIndex);
163         if (index != imageIndex) {
164             throw new IndexOutOfBoundsException JavaDoc();
165         }
166         readMetadata();
167         return imageMetadata.imageWidth;
168     }
169
170     public int getHeight(int imageIndex) throws IIOException JavaDoc {
171         checkIndex(imageIndex);
172
173         int index = locateImage(imageIndex);
174         if (index != imageIndex) {
175             throw new IndexOutOfBoundsException JavaDoc();
176         }
177         readMetadata();
178         return imageMetadata.imageHeight;
179     }
180
181     public Iterator JavaDoc getImageTypes(int imageIndex) throws IIOException JavaDoc {
182         checkIndex(imageIndex);
183
184         int index = locateImage(imageIndex);
185         if (index != imageIndex) {
186             throw new IndexOutOfBoundsException JavaDoc();
187         }
188         readMetadata();
189
190         List JavaDoc l = new ArrayList JavaDoc(1);
191
192         byte[] colorTable;
193         if (imageMetadata.localColorTable != null) {
194             colorTable = imageMetadata.localColorTable;
195         } else {
196             colorTable = streamMetadata.globalColorTable;
197         }
198
199         // Normalize color table length to 2^1, 2^2, 2^4, or 2^8
200
int length = colorTable.length/3;
201         int bits;
202         if (length == 2) {
203             bits = 1;
204         } else if (length == 4) {
205             bits = 2;
206         } else if (length == 8 || length == 16) {
207             // Bump from 3 to 4 bits
208
bits = 4;
209         } else {
210             // Bump to 8 bits
211
bits = 8;
212         }
213         int lutLength = 1 << bits;
214         byte[] r = new byte[lutLength];
215         byte[] g = new byte[lutLength];
216         byte[] b = new byte[lutLength];
217
218         // Entries from length + 1 to lutLength - 1 will be 0
219
int rgbIndex = 0;
220         for (int i = 0; i < length; i++) {
221             r[i] = colorTable[rgbIndex++];
222             g[i] = colorTable[rgbIndex++];
223             b[i] = colorTable[rgbIndex++];
224         }
225
226         byte[] a = null;
227         if (imageMetadata.transparentColorFlag) {
228             a = new byte[lutLength];
229             Arrays.fill(a, (byte)255);
230             
231             // Some files erroneously have a transparent color index
232
// of 255 even though there are fewer than 256 colors.
233
int idx = Math.min(imageMetadata.transparentColorIndex,
234                                lutLength - 1);
235             a[idx] = (byte)0;
236         }
237
238         int[] bitsPerSample = new int[1];
239         bitsPerSample[0] = bits;
240         l.add(ImageTypeSpecifier.createIndexed(r, g, b, a, bits,
241                                                DataBuffer.TYPE_BYTE));
242         return l.iterator();
243     }
244
245     public ImageReadParam JavaDoc getDefaultReadParam() {
246         return new ImageReadParam JavaDoc();
247     }
248
249     public IIOMetadata JavaDoc getStreamMetadata() throws IIOException JavaDoc {
250         readHeader();
251         return streamMetadata;
252     }
253
254     public IIOMetadata JavaDoc getImageMetadata(int imageIndex) throws IIOException JavaDoc {
255         checkIndex(imageIndex);
256
257         int index = locateImage(imageIndex);
258         if (index != imageIndex) {
259             throw new IndexOutOfBoundsException JavaDoc("Bad image index!");
260         }
261         readMetadata();
262         return imageMetadata;
263     }
264
265     // BEGIN LZW STUFF
266

267     private void initNext32Bits() {
268         next32Bits = block[0] & 0xff;
269         next32Bits |= (block[1] & 0xff) << 8;
270         next32Bits |= (block[2] & 0xff) << 16;
271         next32Bits |= block[3] << 24;
272         nextByte = 4;
273     }
274
275     // Load a block (1-255 bytes) at a time, and maintain
276
// a 32-bit lookahead buffer that is filled from the left
277
// and extracted from the right.
278
//
279
// When the last block is found, we continue to
280
//
281
private int getCode(int codeSize, int codeMask) throws IOException JavaDoc {
282         if (bitPos + codeSize > 32) {
283             return eofCode; // No more data available
284
}
285
286         int code = (next32Bits >> bitPos) & codeMask;
287         bitPos += codeSize;
288
289         // Shift in a byte of new data at a time
290
while (bitPos >= 8 && !lastBlockFound) {
291             next32Bits >>>= 8;
292             bitPos -= 8;
293             
294             // Check if current block is out of bytes
295
if (nextByte >= blockLength) {
296                 // Get next block size
297
blockLength = stream.readUnsignedByte();
298                 if (blockLength == 0) {
299                     lastBlockFound = true;
300                     return code;
301                 } else {
302                     int left = blockLength;
303                     int off = 0;
304                     while (left > 0) {
305                         int nbytes = stream.read(block, off, left);
306                         off += nbytes;
307                         left -= nbytes;
308                     }
309                     nextByte = 0;
310                 }
311             }
312
313             next32Bits |= block[nextByte++] << 24;
314         }
315
316         return code;
317     }
318
319     public void initializeStringTable(int[] prefix,
320                                       byte[] suffix,
321                                       byte[] initial,
322                                       int[] length) {
323         int numEntries = 1 << initCodeSize;
324     for (int i = 0; i < numEntries; i++) {
325             prefix[i] = -1;
326             suffix[i] = (byte)i;
327             initial[i] = (byte)i;
328             length[i] = 1;
329         }
330         
331         // Fill in the entire table for robustness against
332
// out-of-sequence codes.
333
for (int i = numEntries; i < 4096; i++) {
334             prefix[i] = -1;
335             length[i] = 1;
336         }
337         
338     // tableIndex = numEntries + 2;
339
// codeSize = initCodeSize + 1;
340
// codeMask = (1 << codeSize) - 1;
341
}
342
343     Rectangle JavaDoc sourceRegion;
344     int sourceXSubsampling;
345     int sourceYSubsampling;
346     int sourceMinProgressivePass;
347     int sourceMaxProgressivePass;
348
349     Point JavaDoc destinationOffset;
350     Rectangle JavaDoc destinationRegion;
351
352     // Used only if IIOReadUpdateListeners are present
353
int updateMinY;
354     int updateYStep;
355
356     boolean decodeThisRow = true;
357     int destY = 0;
358
359     byte[] rowBuf;
360
361     private void outputRow() {
362         // Clip against ImageReadParam
363
int width = Math.min(sourceRegion.width,
364                              destinationRegion.width*sourceXSubsampling);
365         int destX = destinationRegion.x;
366
367         if (sourceXSubsampling == 1) {
368             theTile.setDataElements(destX, destY, width, 1, rowBuf);
369         } else {
370             for (int x = 0; x < width; x += sourceXSubsampling, destX++) {
371                 theTile.setSample(destX, destY, 0, rowBuf[x] & 0xff);
372             }
373         }
374
375         // Update IIOReadUpdateListeners, if any
376
if (updateListeners != null) {
377             int[] bands = { 0 };
378             // updateYStep will have been initialized if
379
// updateListeners is non-null
380
processImageUpdate(theImage,
381                                destX, destY,
382                                width, 1, 1, updateYStep,
383                                bands);
384         }
385     }
386
387     private void computeDecodeThisRow() {
388         this.decodeThisRow =
389             (destY < destinationRegion.y + destinationRegion.height) &&
390             (streamY >= sourceRegion.y) &&
391             (streamY < sourceRegion.y + sourceRegion.height) &&
392             (((streamY - sourceRegion.y) % sourceYSubsampling) == 0);
393     }
394
395     private void outputPixels(byte[] string, int len) {
396         if (interlacePass < sourceMinProgressivePass ||
397             interlacePass > sourceMaxProgressivePass) {
398             return;
399         }
400
401         for (int i = 0; i < len; i++) {
402             if (streamX >= sourceRegion.x) {
403                 rowBuf[streamX - sourceRegion.x] = string[i];
404             }
405
406             // Process end-of-row
407
++streamX;
408             if (streamX == width) {
409                 // Update IIOReadProgressListeners
410
++rowsDone;
411                 processImageProgress(100.0F*rowsDone/height);
412
413                 if (decodeThisRow) {
414                     outputRow();
415                 }
416                 
417                 streamX = 0;
418                 if (imageMetadata.interlaceFlag) {
419                     streamY += interlaceIncrement[interlacePass];
420                     if (streamY >= height) {
421                         // Inform IIOReadUpdateListeners of end of pass
422
if (updateListeners != null) {
423                             processPassComplete(theImage);
424                         }
425
426                         ++interlacePass;
427                         if (interlacePass > sourceMaxProgressivePass) {
428                             return;
429                         }
430                         streamY = interlaceOffset[interlacePass];
431                         startPass(interlacePass);
432                     }
433                 } else {
434                     ++streamY;
435                 }
436                 
437                 // Determine whether pixels from this row will
438
// be written to the destination
439
this.destY = destinationRegion.y +
440                     (streamY - sourceRegion.y)/sourceYSubsampling;
441                 computeDecodeThisRow();
442             }
443         }
444     }
445
446     // END LZW STUFF
447

448     private void readHeader() throws IIOException JavaDoc {
449         if (gotHeader) {
450             return;
451         }
452         if (stream == null) {
453             throw new IllegalStateException JavaDoc("Input not set!");
454         }
455
456         // Create an object to store the stream metadata
457
this.streamMetadata = new GIFStreamMetadata();
458
459         try {
460             stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
461             
462             byte[] signature = new byte[6];
463             stream.readFully(signature);
464
465             StringBuffer JavaDoc version = new StringBuffer JavaDoc(3);
466             version.append((char)signature[3]);
467             version.append((char)signature[4]);
468             version.append((char)signature[5]);
469             streamMetadata.version = version.toString();
470
471             streamMetadata.logicalScreenWidth = stream.readUnsignedShort();
472             streamMetadata.logicalScreenHeight = stream.readUnsignedShort();
473
474             int packedFields = stream.readUnsignedByte();
475             boolean globalColorTableFlag = (packedFields & 0x80) != 0;
476             streamMetadata.colorResolution = ((packedFields >> 4) & 0x7) + 1;
477             streamMetadata.sortFlag = (packedFields & 0x8) != 0;
478             int numGCTEntries = 1 << ((packedFields & 0x7) + 1);
479
480             streamMetadata.backgroundColorIndex = stream.readUnsignedByte();
481             streamMetadata.pixelAspectRatio = stream.readUnsignedByte();
482
483             if (globalColorTableFlag) {
484                 streamMetadata.globalColorTable = new byte[3*numGCTEntries];
485                 stream.readFully(streamMetadata.globalColorTable);
486             } else {
487                 streamMetadata.globalColorTable = null;
488             }
489
490             // Found position of metadata for image 0
491
imageStartPosition.add(new Long JavaDoc(stream.getStreamPosition()));
492         } catch (IOException JavaDoc e) {
493             throw new IIOException JavaDoc("I/O error reading header!", e);
494         }
495
496         gotHeader = true;
497     }
498
499     private boolean skipImage() throws IIOException JavaDoc {
500         // Stream must be at the beginning of an image descriptor
501
// upon exit
502

503         try {
504             while (true) {
505                 int blockType = stream.readUnsignedByte();
506
507                 if (blockType == 0x2c) {
508                     stream.skipBytes(8);
509
510                     int packedFields = stream.readUnsignedByte();
511                     if ((packedFields & 0x80) != 0) {
512                         // Skip color table if any
513
int bits = (packedFields & 0x7) + 1;
514                         stream.skipBytes(3*(1 << bits));
515                     }
516
517                     stream.skipBytes(1);
518                     
519                     int length = 0;
520                     do {
521                         length = stream.readUnsignedByte();
522                         stream.skipBytes(length);
523                     } while (length > 0);
524
525                     return true;
526                 } else if (blockType == 0x3b) {
527                     return false;
528                 } else if (blockType == 0x21) {
529                     int label = stream.readUnsignedByte();
530
531                     int length = 0;
532                     do {
533                         length = stream.readUnsignedByte();
534                         stream.skipBytes(length);
535                     } while (length > 0);
536                 } else if (blockType == 0x0) {
537                     // EOF
538
return false;
539                 } else {
540                     int length = 0;
541                     do {
542                         length = stream.readUnsignedByte();
543                         stream.skipBytes(length);
544                     } while (length > 0);
545                 }
546             }
547         } catch (EOFException JavaDoc e) {
548             return false;
549         } catch (IOException JavaDoc e) {
550             throw new IIOException JavaDoc("I/O error locating image!", e);
551         }
552     }
553
554     private int locateImage(int imageIndex) throws IIOException JavaDoc {
555         readHeader();
556
557         try {
558             // Find closest known index
559
int index = Math.min(imageIndex, imageStartPosition.size() - 1);
560             
561             // Seek to that position
562
Long JavaDoc l = (Long JavaDoc)imageStartPosition.get(index);
563             stream.seek(l.longValue());
564
565             // Skip images until at desired index or last image found
566
while (index < imageIndex) {
567                 if (!skipImage()) {
568                     --index;
569                     return index;
570                 }
571                 
572                 Long JavaDoc l1 = new Long JavaDoc(stream.getStreamPosition());
573                 imageStartPosition.add(l1);
574                 ++index;
575             }
576         } catch (IOException JavaDoc e) {
577             throw new IIOException JavaDoc("Couldn't seek!", e);
578         }
579
580         if (currIndex != imageIndex) {
581             imageMetadata = null;
582         }
583         currIndex = imageIndex;
584         return imageIndex;
585     }
586
587     // Read blocks of 1-255 bytes, stop at a 0-length block
588
private byte[] concatenateBlocks() throws IOException JavaDoc {
589         byte[] data = new byte[0];
590         while (true) {
591             int length = stream.readUnsignedByte();
592             if (length == 0) {
593                 break;
594             }
595             byte[] newData = new byte[data.length + length];
596             System.arraycopy(data, 0, newData, 0, data.length);
597             stream.readFully(newData, data.length, length);
598             data = newData;
599         }
600
601         return data;
602     }
603
604     // Stream must be positioned at start of metadata for 'currIndex'
605
private void readMetadata() throws IIOException JavaDoc {
606         if (stream == null) {
607             throw new IllegalStateException JavaDoc("Input not set!");
608         }
609
610         try {
611             // Create an object to store the image metadata
612
this.imageMetadata = new GIFImageMetadata();
613             
614             long startPosition = stream.getStreamPosition();
615             while (true) {
616                 int blockType = stream.readUnsignedByte();
617                 if (blockType == 0x2c) { // Image Descriptor
618
imageMetadata.imageLeftPosition =
619                         stream.readUnsignedShort();
620                     imageMetadata.imageTopPosition =
621                         stream.readUnsignedShort();
622                     imageMetadata.imageWidth = stream.readUnsignedShort();
623                     imageMetadata.imageHeight = stream.readUnsignedShort();
624
625                     int idPackedFields = stream.readUnsignedByte();
626                     boolean localColorTableFlag =
627                         (idPackedFields & 0x80) != 0;
628                     imageMetadata.interlaceFlag = (idPackedFields & 0x40) != 0;
629                     imageMetadata.sortFlag = (idPackedFields & 0x20) != 0;
630                     int numLCTEntries = 1 << ((idPackedFields & 0x7) + 1);
631
632                     if (localColorTableFlag) {
633                         // Read color table if any
634
imageMetadata.localColorTable =
635                             new byte[3*numLCTEntries];
636                         stream.readFully(imageMetadata.localColorTable);
637                     } else {
638                         imageMetadata.localColorTable = null;
639                     }
640
641                     // Record length of this metadata block
642
this.imageMetadataLength =
643                         (int)(stream.getStreamPosition() - startPosition);
644
645                     // Now positioned at start of LZW-compressed pixels
646
return;
647                 } else if (blockType == 0x21) { // Extension block
648
int label = stream.readUnsignedByte();
649
650                     if (label == 0xf9) { // Graphics Control Extension
651
int gceLength = stream.readUnsignedByte(); // 4
652
int gcePackedFields = stream.readUnsignedByte();
653                         imageMetadata.disposalMethod =
654                             (gcePackedFields >> 2) & 0x3;
655                         imageMetadata.userInputFlag =
656                             (gcePackedFields & 0x2) != 0;
657                         imageMetadata.transparentColorFlag =
658                             (gcePackedFields & 0x1) != 0;
659
660                         imageMetadata.delayTime = stream.readUnsignedShort();
661                         imageMetadata.transparentColorIndex
662                             = stream.readUnsignedByte();
663                         
664                         int terminator = stream.readUnsignedByte();
665                     } else if (label == 0x1) { // Plain text extension
666
int length = stream.readUnsignedByte();
667                         imageMetadata.hasPlainTextExtension = true;
668                         imageMetadata.textGridLeft =
669                             stream.readUnsignedShort();
670                         imageMetadata.textGridTop =
671                             stream.readUnsignedShort();
672                         imageMetadata.textGridWidth =
673                             stream.readUnsignedShort();
674                         imageMetadata.textGridHeight =
675                             stream.readUnsignedShort();
676                         imageMetadata.characterCellWidth =
677                             stream.readUnsignedByte();
678                         imageMetadata.characterCellHeight =
679                             stream.readUnsignedByte();
680                         imageMetadata.textForegroundColor =
681                             stream.readUnsignedByte();
682                         imageMetadata.textBackgroundColor =
683                             stream.readUnsignedByte();
684                         imageMetadata.text = concatenateBlocks();
685                     } else if (label == 0xfe) { // Comment extension
686
byte[] comment = concatenateBlocks();
687                         if (imageMetadata.comments == null) {
688                             imageMetadata.comments = new ArrayList JavaDoc();
689                         }
690                         imageMetadata.comments.add(comment);
691                     } else if (label == 0xff) { // Application extension
692
int blockSize = stream.readUnsignedByte();
693                         byte[] applicationID = new byte[8];
694                         stream.readFully(applicationID);
695                         byte[] authCode = new byte[3];
696                         stream.readFully(authCode);
697                         byte[] applicationData = concatenateBlocks();
698
699                         // Init lists if necessary
700
if (imageMetadata.applicationIDs == null) {
701                             imageMetadata.applicationIDs = new ArrayList JavaDoc();
702                             imageMetadata.authenticationCodes =
703                                 new ArrayList JavaDoc();
704                             imageMetadata.applicationData = new ArrayList JavaDoc();
705                         }
706                         imageMetadata.applicationIDs.add(applicationID);
707                         imageMetadata.authenticationCodes.add(authCode);
708                         imageMetadata.applicationData.add(applicationData);
709                     } else {
710                         // Skip over unknown extension blocks
711
int length = 0;
712                         do {
713                             length = stream.readUnsignedByte();
714                             stream.skipBytes(length);
715                         } while (length > 0);
716                     }
717                 } else if (blockType == 0x3b) { // Trailer
718
throw new IndexOutOfBoundsException JavaDoc
719                         ("Attempt to read past end of image sequence!");
720                 } else {
721                     throw new IIOException JavaDoc("Unexpected block type " +
722                                            blockType + "!");
723                 }
724             }
725         } catch (IIOException JavaDoc iioe) {
726             throw iioe;
727         } catch (IOException JavaDoc ioe) {
728             throw new IIOException JavaDoc("I/O error reading image metadata!", ioe);
729         }
730     }
731
732     // Helper for protected computeUpdatedPixels method
733
private static void computeUpdatedPixels(int sourceOffset,
734                                              int sourceExtent,
735                                              int destinationOffset,
736                                              int dstMin,
737                                              int dstMax,
738                                              int sourceSubsampling,
739                                              int passStart,
740                                              int passExtent,
741                                              int passPeriod,
742                                              int[] vals,
743                                              int offset) {
744
745         // We need to satisfy the congruences:
746
// dst = destinationOffset + (src - sourceOffset)/sourceSubsampling
747
//
748
// src - passStart == 0 (mod passPeriod)
749
// src - sourceOffset == 0 (mod sourceSubsampling)
750
//
751
// subject to the inequalities:
752
//
753
// src >= passStart
754
// src < passStart + passExtent
755
// src >= sourceOffset
756
// src < sourceOffset + sourceExtent
757
// dst >= dstMin
758
// dst <= dstmax
759
//
760
// where
761
//
762
// dst = destinationOffset + (src - sourceOffset)/sourceSubsampling
763
//
764
// For now we use a brute-force approach although we could
765
// attempt to analyze the congruences. If passPeriod and
766
// sourceSubsamling are relatively prime, the period will be
767
// their product. If they share a common factor, either the
768
// period will be equal to the larger value, or the sequences
769
// will be completely disjoint, depending on the relationship
770
// between passStart and sourceOffset. Since we only have to do this
771
// twice per image (once each for X and Y), it seems cheap enough
772
// to do it the straightforward way.
773

774         boolean gotPixel = false;
775         int firstDst = -1;
776         int secondDst = -1;
777         int lastDst = -1;
778
779         for (int i = 0; i < passExtent; i++) {
780             int src = passStart + i*passPeriod;
781             if (src < sourceOffset) {
782                 continue;
783             }
784             if ((src - sourceOffset) % sourceSubsampling != 0) {
785                 continue;
786             }
787             if (src >= sourceOffset + sourceExtent) {
788                 break;
789             }
790
791             int dst = destinationOffset +
792                 (src - sourceOffset)/sourceSubsampling;
793             if (dst < dstMin) {
794                 continue;
795             }
796             if (dst > dstMax) {
797                 break;
798             }
799
800             if (!gotPixel) {
801                 firstDst = dst; // Record smallest valid pixel
802
gotPixel = true;
803             } else if (secondDst == -1) {
804                 secondDst = dst; // Record second smallest valid pixel
805
}
806             lastDst = dst; // Record largest valid pixel
807
}
808
809         vals[offset] = firstDst;
810
811         // If we never saw a valid pixel, set width to 0
812
if (!gotPixel) {
813             vals[offset + 2] = 0;
814         } else {
815             vals[offset + 2] = lastDst - firstDst + 1;
816         }
817
818         // The period is given by the difference of any two adjacent pixels
819
vals[offset + 4] = Math.max(secondDst - firstDst, 1);
820     }
821
822     /**
823      * A utility method that computes the exact set of destination
824      * pixels that will be written during a particular decoding pass.
825      * The intent is to simplify the work done by readers in combining
826      * the source region, source subsampling, and destination offset
827      * information obtained from the <code>ImageReadParam</code> with
828      * the offsets and periods of a progressive or interlaced decoding
829      * pass.
830      *
831      * @param sourceRegion a <code>Rectangle</code> containing the
832      * source region being read, offset by the source subsampling
833      * offsets, and clipped against the source bounds, as returned by
834      * the <code>getSourceRegion</code> method.
835      * @param destinationOffset a <code>Point</code> containing the
836      * coordinates of the upper-left pixel to be written in the
837      * destination.
838      * @param dstMinX the smallest X coordinate (inclusive) of the
839      * destination <code>Raster</code>.
840      * @param dstMinY the smallest Y coordinate (inclusive) of the
841      * destination <code>Raster</code>.
842      * @param dstMaxX the largest X coordinate (inclusive) of the destination
843      * <code>Raster</code>.
844      * @param dstMaxY the largest Y coordinate (inclusive) of the destination
845      * <code>Raster</code>.
846      * @param sourceXSubsampling the X subsampling factor.
847      * @param sourceYSubsampling the Y subsampling factor.
848      * @param passXStart the smallest source X coordinate (inclusive)
849      * of the current progressive pass.
850      * @param passYStart the smallest source Y coordinate (inclusive)
851      * of the current progressive pass.
852      * @param passWidth the width in pixels of the current progressive
853      * pass.
854      * @param passHeight the height in pixels of the current progressive
855      * pass.
856      * @param passPeriodX the X period (horizontal spacing between
857      * pixels) of the current progressive pass.
858      * @param passPeriodY the Y period (vertical spacing between
859      * pixels) of the current progressive pass.
860      *
861      * @return an array of 6 <code>int</code>s containing the
862      * destination min X, min Y, width, height, X period and Y period
863      * of the region that will be updated.
864      */

865     private static int[] computeUpdatedPixels(Rectangle JavaDoc sourceRegion,
866                                               Point JavaDoc destinationOffset,
867                                               int dstMinX,
868                                               int dstMinY,
869                                               int dstMaxX,
870                                               int dstMaxY,
871                                               int sourceXSubsampling,
872                                               int sourceYSubsampling,
873                                               int passXStart,
874                                               int passYStart,
875                                               int passWidth,
876                                               int passHeight,
877                                               int passPeriodX,
878                                               int passPeriodY) {
879         int[] vals = new int[6];
880         computeUpdatedPixels(sourceRegion.x, sourceRegion.width,
881                              destinationOffset.x,
882                              dstMinX, dstMaxX, sourceXSubsampling,
883                              passXStart, passWidth, passPeriodX,
884                              vals, 0);
885         computeUpdatedPixels(sourceRegion.y, sourceRegion.height,
886                              destinationOffset.y,
887                              dstMinY, dstMaxY, sourceYSubsampling,
888                              passYStart, passHeight, passPeriodY,
889                              vals, 1);
890         return vals;
891     }
892
893     private void startPass(int pass) {
894         if (updateListeners == null) {
895             return;
896         }
897
898         int y = 0;
899         int yStep = 1;
900         if (imageMetadata.interlaceFlag) {
901             y = interlaceOffset[interlacePass];
902             yStep = interlaceIncrement[interlacePass];
903         }
904
905         int[] vals =
906             computeUpdatedPixels(sourceRegion,
907                                  destinationOffset,
908                                  destinationRegion.x,
909                                  destinationRegion.y,
910                                  destinationRegion.x +
911                                  destinationRegion.width - 1,
912                                  destinationRegion.y +
913                                  destinationRegion.height - 1,
914                                  sourceXSubsampling,
915                                  sourceYSubsampling,
916                                  0,
917                                  y,
918                                  destinationRegion.width,
919                                  (destinationRegion.height + yStep - 1)/yStep,
920                                  1,
921                                  yStep);
922
923         // Initialized updateMinY and updateYStep
924
this.updateMinY = vals[1];
925         this.updateYStep = vals[5];
926
927         // Inform IIOReadUpdateListeners of new pass
928
int[] bands = { 0 };
929
930         processPassStarted(theImage,
931                            interlacePass,
932                            sourceMinProgressivePass,
933                            sourceMaxProgressivePass,
934                            0,
935                            updateMinY,
936                            1,
937                            updateYStep,
938                            bands);
939     }
940
941     public BufferedImage JavaDoc read(int imageIndex, ImageReadParam JavaDoc param)
942         throws IIOException JavaDoc {
943         if (stream == null) {
944             throw new IllegalStateException JavaDoc("Input not set!");
945         }
946         checkIndex(imageIndex);
947
948         int index = locateImage(imageIndex);
949         if (index != imageIndex) {
950             throw new IndexOutOfBoundsException JavaDoc("imageIndex out of bounds!");
951         }
952
953         clearAbortRequest();
954         readMetadata();
955
956         // A null ImageReadParam means we use the default
957
if (param == null) {
958             param = getDefaultReadParam();
959         }
960
961         // Initialize the destination image
962
Iterator JavaDoc imageTypes = getImageTypes(imageIndex);
963         this.theImage = getDestination(param,
964                                        imageTypes,
965                                        imageMetadata.imageWidth,
966                                        imageMetadata.imageHeight);
967         this.theTile = theImage.getWritableTile(0, 0);
968         this.width = imageMetadata.imageWidth;
969         this.height = imageMetadata.imageHeight;
970         this.streamX = 0;
971         this.streamY = 0;
972         this.rowsDone = 0;
973         this.interlacePass = 0;
974
975         // Get source region, taking subsampling offsets into account,
976
// and clipping against the true source bounds
977

978         this.sourceRegion = new Rectangle JavaDoc(0, 0, 0, 0);
979         this.destinationRegion = new Rectangle JavaDoc(0, 0, 0, 0);
980         computeRegions(param, width, height, theImage,
981                        sourceRegion, destinationRegion);
982         this.destinationOffset = new Point JavaDoc(destinationRegion.x,
983                                            destinationRegion.y);
984
985         this.sourceXSubsampling = param.getSourceXSubsampling();
986         this.sourceYSubsampling = param.getSourceYSubsampling();
987         this.sourceMinProgressivePass =
988             Math.max(param.getSourceMinProgressivePass(), 0);
989         this.sourceMaxProgressivePass =
990             Math.min(param.getSourceMaxProgressivePass(), 3);
991
992         this.destY = destinationRegion.y +
993             (streamY - sourceRegion.y)/sourceYSubsampling;
994         computeDecodeThisRow();
995
996         // Inform IIOReadProgressListeners of start of image
997
processImageStarted(imageIndex);
998         startPass(0);
999
1000        this.rowBuf = new byte[width];
1001
1002        try {
1003            // Read and decode the image data, fill in theImage
1004
this.initCodeSize = stream.readUnsignedByte();
1005
1006            // Read first data block
1007
this.blockLength = stream.readUnsignedByte();
1008            int left = blockLength;
1009            int off = 0;
1010            while (left > 0) {
1011                int nbytes = stream.read(block, off, left);
1012                left -= nbytes;
1013                off += nbytes;
1014            }
1015
1016            this.bitPos = 0;
1017            this.nextByte = 0;
1018            this.lastBlockFound = false;
1019            this.interlacePass = 0;
1020
1021            // Init 32-bit buffer
1022
initNext32Bits();
1023
1024            this.clearCode = 1 << initCodeSize;
1025            this.eofCode = clearCode + 1;
1026
1027            int code, oldCode = 0;
1028
1029            int[] prefix = new int[4096];
1030            byte[] suffix = new byte[4096];
1031            byte[] initial = new byte[4096];
1032            int[] length = new int[4096];
1033            byte[] string = new byte[4096];
1034
1035            initializeStringTable(prefix, suffix, initial, length);
1036            int tableIndex = (1 << initCodeSize) + 2;
1037            int codeSize = initCodeSize + 1;
1038            int codeMask = (1 << codeSize) - 1;
1039
1040            while (!abortRequested()) {
1041                code = getCode(codeSize, codeMask);
1042
1043                if (code == clearCode) {
1044                    initializeStringTable(prefix, suffix, initial, length);
1045                    tableIndex = (1 << initCodeSize) + 2;
1046                    codeSize = initCodeSize + 1;
1047                    codeMask = (1 << codeSize) - 1;
1048
1049                    code = getCode(codeSize, codeMask);
1050                    if (code == eofCode) {
1051                        // Inform IIOReadProgressListeners of end of image
1052
processImageComplete();
1053                        return theImage;
1054                    }
1055                } else if (code == eofCode) {
1056                    // Inform IIOReadProgressListeners of end of image
1057
processImageComplete();
1058                    return theImage;
1059                } else {
1060                    int newSuffixIndex;
1061                    if (code < tableIndex) {
1062                        newSuffixIndex = code;
1063                    } else { // code == tableIndex
1064
newSuffixIndex = oldCode;
1065                        if (code != tableIndex) {
1066                            // warning - code out of sequence
1067
// possibly data corruption
1068
processWarningOccurred("Out-of-sequence code!");
1069                        }
1070                    }
1071
1072                    int ti = tableIndex;
1073                    int oc = oldCode;
1074
1075                    prefix[ti] = oc;
1076                    suffix[ti] = initial[newSuffixIndex];
1077                    initial[ti] = initial[oc];
1078                    length[ti] = length[oc] + 1;
1079                    
1080                    ++tableIndex;
1081                    if ((tableIndex == (1 << codeSize)) &&
1082                        (tableIndex < 4096)) {
1083                        ++codeSize;
1084                        codeMask = (1 << codeSize) - 1;
1085                    }
1086                }
1087
1088                // Reverse code
1089
int c = code;
1090                int len = length[c];
1091                for (int i = len - 1; i >= 0; i--) {
1092                    string[i] = suffix[c];
1093                    c = prefix[c];
1094                }
1095                
1096                outputPixels(string, len);
1097                oldCode = code;
1098            }
1099
1100            processReadAborted();
1101            return theImage;
1102        } catch (IOException JavaDoc e) {
1103            e.printStackTrace();
1104            throw new IIOException JavaDoc("I/O error reading image!", e);
1105        }
1106    }
1107    
1108    /**
1109     * Remove all settings including global settings such as
1110     * <code>Locale</code>s and listeners, as well as stream settings.
1111     */

1112    public void reset() {
1113        super.reset();
1114        resetStreamSettings();
1115    }
1116
1117    /**
1118     * Remove local settings based on parsing of a stream.
1119     */

1120    private void resetStreamSettings() {
1121        gotHeader = false;
1122        streamMetadata = null;
1123        currIndex = -1;
1124        imageMetadata = null;
1125        imageStartPosition = new ArrayList JavaDoc();
1126        numImages = -1;
1127
1128        // No need to reinitialize 'block'
1129
blockLength = 0;
1130        bitPos = 0;
1131        nextByte = 0;
1132
1133        next32Bits = 0;
1134        lastBlockFound = false;
1135
1136        theImage = null;
1137        theTile = null;
1138        width = -1;
1139        height = -1;
1140        streamX = -1;
1141        streamY = -1;
1142        rowsDone = 0;
1143        interlacePass = 0;
1144    }
1145}
1146
Popular Tags