jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageReader.java
changeset 34416 68c0d866db5d
child 36448 a07e108d5722
equal deleted inserted replaced
34415:098d54b4051d 34416:68c0d866db5d
       
     1 /*
       
     2  * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package com.sun.imageio.plugins.tiff;
       
    26 
       
    27 import java.awt.Point;
       
    28 import java.awt.Rectangle;
       
    29 import java.awt.color.ColorSpace;
       
    30 import java.awt.color.ICC_ColorSpace;
       
    31 import java.awt.color.ICC_Profile;
       
    32 import java.awt.image.BufferedImage;
       
    33 import java.awt.image.ColorModel;
       
    34 import java.awt.image.ComponentColorModel;
       
    35 import java.awt.image.Raster;
       
    36 import java.awt.image.RenderedImage;
       
    37 import java.awt.image.SampleModel;
       
    38 import java.io.IOException;
       
    39 import java.nio.ByteOrder;
       
    40 import java.util.ArrayList;
       
    41 import java.util.HashMap;
       
    42 import java.util.Iterator;
       
    43 import java.util.List;
       
    44 import javax.imageio.IIOException;
       
    45 import javax.imageio.ImageIO;
       
    46 import javax.imageio.ImageReader;
       
    47 import javax.imageio.ImageReadParam;
       
    48 import javax.imageio.ImageTypeSpecifier;
       
    49 import javax.imageio.metadata.IIOMetadata;
       
    50 import javax.imageio.spi.ImageReaderSpi;
       
    51 import javax.imageio.stream.ImageInputStream;
       
    52 import org.w3c.dom.Node;
       
    53 import com.sun.imageio.plugins.common.ImageUtil;
       
    54 import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
       
    55 import javax.imageio.plugins.tiff.TIFFField;
       
    56 import javax.imageio.plugins.tiff.TIFFImageReadParam;
       
    57 import javax.imageio.plugins.tiff.TIFFTagSet;
       
    58 
       
    59 public class TIFFImageReader extends ImageReader {
       
    60 
       
    61     // A somewhat arbitrary upper bound on SamplesPerPixel. Hyperspectral
       
    62     // images as of this writing appear to be under 300 bands so this should
       
    63     // account for those cases should they arise.
       
    64     private static final int SAMPLES_PER_PIXEL_MAX = 1024;
       
    65 
       
    66     // In baseline TIFF the largest data types are 64-bit long and double.
       
    67     private static final int BITS_PER_SAMPLE_MAX = 64;
       
    68 
       
    69     // The current ImageInputStream source.
       
    70     private ImageInputStream stream = null;
       
    71 
       
    72     // True if the file header has been read.
       
    73     private boolean gotHeader = false;
       
    74 
       
    75     private ImageReadParam imageReadParam = getDefaultReadParam();
       
    76 
       
    77     // Stream metadata, or null.
       
    78     private TIFFStreamMetadata streamMetadata = null;
       
    79 
       
    80     // The current image index.
       
    81     private int currIndex = -1;
       
    82 
       
    83     // Metadata for image at 'currIndex', or null.
       
    84     private TIFFImageMetadata imageMetadata = null;
       
    85 
       
    86     // A <code>List</code> of <code>Long</code>s indicating the stream
       
    87     // positions of the start of the IFD for each image.  Entries
       
    88     // are added as needed.
       
    89     private List<Long> imageStartPosition = new ArrayList<Long>();
       
    90 
       
    91     // The number of images in the stream, if known, otherwise -1.
       
    92     private int numImages = -1;
       
    93 
       
    94     // The ImageTypeSpecifiers of the images in the stream.
       
    95     // Contains a map of Integers to Lists.
       
    96     private HashMap<Integer, List<ImageTypeSpecifier>> imageTypeMap
       
    97             = new HashMap<Integer, List<ImageTypeSpecifier>>();
       
    98 
       
    99     private BufferedImage theImage = null;
       
   100 
       
   101     private int width = -1;
       
   102     private int height = -1;
       
   103     private int numBands = -1;
       
   104     private int tileOrStripWidth = -1, tileOrStripHeight = -1;
       
   105 
       
   106     private int planarConfiguration = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
       
   107 
       
   108     private int compression;
       
   109     private int photometricInterpretation;
       
   110     private int samplesPerPixel;
       
   111     private int[] sampleFormat;
       
   112     private int[] bitsPerSample;
       
   113     private int[] extraSamples;
       
   114     private char[] colorMap;
       
   115 
       
   116     private int sourceXOffset;
       
   117     private int sourceYOffset;
       
   118     private int srcXSubsampling;
       
   119     private int srcYSubsampling;
       
   120 
       
   121     private int dstWidth;
       
   122     private int dstHeight;
       
   123     private int dstMinX;
       
   124     private int dstMinY;
       
   125     private int dstXOffset;
       
   126     private int dstYOffset;
       
   127 
       
   128     private int tilesAcross;
       
   129     private int tilesDown;
       
   130 
       
   131     private int pixelsRead;
       
   132     private int pixelsToRead;
       
   133 
       
   134     public TIFFImageReader(ImageReaderSpi originatingProvider) {
       
   135         super(originatingProvider);
       
   136     }
       
   137 
       
   138     public void setInput(Object input,
       
   139             boolean seekForwardOnly,
       
   140             boolean ignoreMetadata) {
       
   141         super.setInput(input, seekForwardOnly, ignoreMetadata);
       
   142 
       
   143         // Clear all local values based on the previous stream contents.
       
   144         resetLocal();
       
   145 
       
   146         if (input != null) {
       
   147             if (!(input instanceof ImageInputStream)) {
       
   148                 throw new IllegalArgumentException("input not an ImageInputStream!");
       
   149             }
       
   150             this.stream = (ImageInputStream) input;
       
   151         } else {
       
   152             this.stream = null;
       
   153         }
       
   154     }
       
   155 
       
   156     // Do not seek to the beginning of the stream so as to allow users to
       
   157     // point us at an IFD within some other file format
       
   158     private void readHeader() throws IIOException {
       
   159         if (gotHeader) {
       
   160             return;
       
   161         }
       
   162         if (stream == null) {
       
   163             throw new IllegalStateException("Input not set!");
       
   164         }
       
   165 
       
   166         // Create an object to store the stream metadata
       
   167         this.streamMetadata = new TIFFStreamMetadata();
       
   168 
       
   169         try {
       
   170             int byteOrder = stream.readUnsignedShort();
       
   171             if (byteOrder == 0x4d4d) {
       
   172                 streamMetadata.byteOrder = ByteOrder.BIG_ENDIAN;
       
   173                 stream.setByteOrder(ByteOrder.BIG_ENDIAN);
       
   174             } else if (byteOrder == 0x4949) {
       
   175                 streamMetadata.byteOrder = ByteOrder.LITTLE_ENDIAN;
       
   176                 stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
       
   177             } else {
       
   178                 processWarningOccurred(
       
   179                         "Bad byte order in header, assuming little-endian");
       
   180                 streamMetadata.byteOrder = ByteOrder.LITTLE_ENDIAN;
       
   181                 stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
       
   182             }
       
   183 
       
   184             int magic = stream.readUnsignedShort();
       
   185             if (magic != 42) {
       
   186                 processWarningOccurred(
       
   187                         "Bad magic number in header, continuing");
       
   188             }
       
   189 
       
   190             // Seek to start of first IFD
       
   191             long offset = stream.readUnsignedInt();
       
   192             imageStartPosition.add(Long.valueOf(offset));
       
   193             stream.seek(offset);
       
   194         } catch (IOException e) {
       
   195             throw new IIOException("I/O error reading header!", e);
       
   196         }
       
   197 
       
   198         gotHeader = true;
       
   199     }
       
   200 
       
   201     private int locateImage(int imageIndex) throws IIOException {
       
   202         readHeader();
       
   203 
       
   204         try {
       
   205             // Find closest known index
       
   206             int index = Math.min(imageIndex, imageStartPosition.size() - 1);
       
   207 
       
   208             // Seek to that position
       
   209             Long l = imageStartPosition.get(index);
       
   210             stream.seek(l.longValue());
       
   211 
       
   212             // Skip IFDs until at desired index or last image found
       
   213             while (index < imageIndex) {
       
   214                 int count = stream.readUnsignedShort();
       
   215                 stream.skipBytes(12 * count);
       
   216 
       
   217                 long offset = stream.readUnsignedInt();
       
   218                 if (offset == 0) {
       
   219                     return index;
       
   220                 }
       
   221 
       
   222                 imageStartPosition.add(Long.valueOf(offset));
       
   223                 stream.seek(offset);
       
   224                 ++index;
       
   225             }
       
   226         } catch (IOException e) {
       
   227             throw new IIOException("Couldn't seek!", e);
       
   228         }
       
   229 
       
   230         if (currIndex != imageIndex) {
       
   231             imageMetadata = null;
       
   232         }
       
   233         currIndex = imageIndex;
       
   234         return imageIndex;
       
   235     }
       
   236 
       
   237     public int getNumImages(boolean allowSearch) throws IOException {
       
   238         if (stream == null) {
       
   239             throw new IllegalStateException("Input not set!");
       
   240         }
       
   241         if (seekForwardOnly && allowSearch) {
       
   242             throw new IllegalStateException("seekForwardOnly and allowSearch can't both be true!");
       
   243         }
       
   244 
       
   245         if (numImages > 0) {
       
   246             return numImages;
       
   247         }
       
   248         if (allowSearch) {
       
   249             this.numImages = locateImage(Integer.MAX_VALUE) + 1;
       
   250         }
       
   251         return numImages;
       
   252     }
       
   253 
       
   254     public IIOMetadata getStreamMetadata() throws IIOException {
       
   255         readHeader();
       
   256         return streamMetadata;
       
   257     }
       
   258 
       
   259     // Throw an IndexOutOfBoundsException if index < minIndex,
       
   260     // and bump minIndex if required.
       
   261     private void checkIndex(int imageIndex) {
       
   262         if (imageIndex < minIndex) {
       
   263             throw new IndexOutOfBoundsException("imageIndex < minIndex!");
       
   264         }
       
   265         if (seekForwardOnly) {
       
   266             minIndex = imageIndex;
       
   267         }
       
   268     }
       
   269 
       
   270     // Verify that imageIndex is in bounds, find the image IFD, read the
       
   271     // image metadata, initialize instance variables from the metadata.
       
   272     private void seekToImage(int imageIndex) throws IIOException {
       
   273         checkIndex(imageIndex);
       
   274 
       
   275         int index = locateImage(imageIndex);
       
   276         if (index != imageIndex) {
       
   277             throw new IndexOutOfBoundsException("imageIndex out of bounds!");
       
   278         }
       
   279 
       
   280         readMetadata();
       
   281 
       
   282         initializeFromMetadata();
       
   283     }
       
   284 
       
   285     // Stream must be positioned at start of IFD for 'currIndex'
       
   286     private void readMetadata() throws IIOException {
       
   287         if (stream == null) {
       
   288             throw new IllegalStateException("Input not set!");
       
   289         }
       
   290 
       
   291         if (imageMetadata != null) {
       
   292             return;
       
   293         }
       
   294         try {
       
   295             // Create an object to store the image metadata
       
   296             List<TIFFTagSet> tagSets;
       
   297             if (imageReadParam instanceof TIFFImageReadParam) {
       
   298                 tagSets
       
   299                         = ((TIFFImageReadParam) imageReadParam).getAllowedTagSets();
       
   300             } else {
       
   301                 tagSets = new ArrayList<TIFFTagSet>(1);
       
   302                 tagSets.add(BaselineTIFFTagSet.getInstance());
       
   303             }
       
   304 
       
   305             this.imageMetadata = new TIFFImageMetadata(tagSets);
       
   306             imageMetadata.initializeFromStream(stream, ignoreMetadata);
       
   307         } catch (IIOException iioe) {
       
   308             throw iioe;
       
   309         } catch (IOException ioe) {
       
   310             throw new IIOException("I/O error reading image metadata!", ioe);
       
   311         }
       
   312     }
       
   313 
       
   314     private int getWidth() {
       
   315         return this.width;
       
   316     }
       
   317 
       
   318     private int getHeight() {
       
   319         return this.height;
       
   320     }
       
   321 
       
   322     // Returns tile width if image is tiled, else image width
       
   323     private int getTileOrStripWidth() {
       
   324         TIFFField f
       
   325                 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
       
   326         return (f == null) ? getWidth() : f.getAsInt(0);
       
   327     }
       
   328 
       
   329     // Returns tile height if image is tiled, else strip height
       
   330     private int getTileOrStripHeight() {
       
   331         TIFFField f
       
   332                 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_LENGTH);
       
   333         if (f != null) {
       
   334             return f.getAsInt(0);
       
   335         }
       
   336 
       
   337         f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
       
   338         // Default for ROWS_PER_STRIP is 2^32 - 1, i.e., infinity
       
   339         int h = (f == null) ? -1 : f.getAsInt(0);
       
   340         return (h == -1) ? getHeight() : h;
       
   341     }
       
   342 
       
   343     private int getPlanarConfiguration() {
       
   344         TIFFField f
       
   345                 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION);
       
   346         if (f != null) {
       
   347             int planarConfigurationValue = f.getAsInt(0);
       
   348             if (planarConfigurationValue
       
   349                     == BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) {
       
   350                 // Some writers (e.g. Kofax standard Multi-Page TIFF
       
   351                 // Storage Filter v2.01.000; cf. bug 4929147) do not
       
   352                 // correctly set the value of this field. Attempt to
       
   353                 // ascertain whether the value is correctly Planar.
       
   354                 if (getCompression()
       
   355                         == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG
       
   356                         && imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT)
       
   357                         != null) {
       
   358                     // JPEG interchange format cannot have
       
   359                     // PlanarConfiguration value Chunky so reset.
       
   360                     processWarningOccurred("PlanarConfiguration \"Planar\" value inconsistent with JPEGInterchangeFormat; resetting to \"Chunky\".");
       
   361                     planarConfigurationValue
       
   362                             = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
       
   363                 } else {
       
   364                     TIFFField offsetField
       
   365                             = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
       
   366                     if (offsetField == null) {
       
   367                         // Tiles
       
   368                         offsetField
       
   369                                 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
       
   370                         int tw = getTileOrStripWidth();
       
   371                         int th = getTileOrStripHeight();
       
   372                         int tAcross = (getWidth() + tw - 1) / tw;
       
   373                         int tDown = (getHeight() + th - 1) / th;
       
   374                         int tilesPerImage = tAcross * tDown;
       
   375                         long[] offsetArray = offsetField.getAsLongs();
       
   376                         if (offsetArray != null
       
   377                                 && offsetArray.length == tilesPerImage) {
       
   378                             // Length of offsets array is
       
   379                             // TilesPerImage for Chunky and
       
   380                             // SamplesPerPixel*TilesPerImage for Planar.
       
   381                             processWarningOccurred("PlanarConfiguration \"Planar\" value inconsistent with TileOffsets field value count; resetting to \"Chunky\".");
       
   382                             planarConfigurationValue
       
   383                                     = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
       
   384                         }
       
   385                     } else {
       
   386                         // Strips
       
   387                         int rowsPerStrip = getTileOrStripHeight();
       
   388                         int stripsPerImage
       
   389                                 = (getHeight() + rowsPerStrip - 1) / rowsPerStrip;
       
   390                         long[] offsetArray = offsetField.getAsLongs();
       
   391                         if (offsetArray != null
       
   392                                 && offsetArray.length == stripsPerImage) {
       
   393                             // Length of offsets array is
       
   394                             // StripsPerImage for Chunky and
       
   395                             // SamplesPerPixel*StripsPerImage for Planar.
       
   396                             processWarningOccurred("PlanarConfiguration \"Planar\" value inconsistent with StripOffsets field value count; resetting to \"Chunky\".");
       
   397                             planarConfigurationValue
       
   398                                     = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
       
   399                         }
       
   400                     }
       
   401                 }
       
   402             }
       
   403             return planarConfigurationValue;
       
   404         }
       
   405 
       
   406         return BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
       
   407     }
       
   408 
       
   409     private long getTileOrStripOffset(int tileIndex) throws IIOException {
       
   410         TIFFField f
       
   411                 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
       
   412         if (f == null) {
       
   413             f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
       
   414         }
       
   415         if (f == null) {
       
   416             f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
       
   417         }
       
   418 
       
   419         if (f == null) {
       
   420             throw new IIOException("Missing required strip or tile offsets field.");
       
   421         }
       
   422 
       
   423         return f.getAsLong(tileIndex);
       
   424     }
       
   425 
       
   426     private long getTileOrStripByteCount(int tileIndex) throws IOException {
       
   427         TIFFField f
       
   428                 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS);
       
   429         if (f == null) {
       
   430             f
       
   431                     = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
       
   432         }
       
   433         if (f == null) {
       
   434             f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
       
   435         }
       
   436 
       
   437         long tileOrStripByteCount;
       
   438         if (f != null) {
       
   439             tileOrStripByteCount = f.getAsLong(tileIndex);
       
   440         } else {
       
   441             processWarningOccurred("TIFF directory contains neither StripByteCounts nor TileByteCounts field: attempting to calculate from strip or tile width and height.");
       
   442 
       
   443             // Initialize to number of bytes per strip or tile assuming
       
   444             // no compression.
       
   445             int bitsPerPixel = bitsPerSample[0];
       
   446             for (int i = 1; i < samplesPerPixel; i++) {
       
   447                 bitsPerPixel += bitsPerSample[i];
       
   448             }
       
   449             int bytesPerRow = (getTileOrStripWidth() * bitsPerPixel + 7) / 8;
       
   450             tileOrStripByteCount = bytesPerRow * getTileOrStripHeight();
       
   451 
       
   452             // Clamp to end of stream if possible.
       
   453             long streamLength = stream.length();
       
   454             if (streamLength != -1) {
       
   455                 tileOrStripByteCount
       
   456                         = Math.min(tileOrStripByteCount,
       
   457                                 streamLength - getTileOrStripOffset(tileIndex));
       
   458             } else {
       
   459                 processWarningOccurred("Stream length is unknown: cannot clamp estimated strip or tile byte count to EOF.");
       
   460             }
       
   461         }
       
   462 
       
   463         return tileOrStripByteCount;
       
   464     }
       
   465 
       
   466     private int getCompression() {
       
   467         TIFFField f
       
   468                 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
       
   469         if (f == null) {
       
   470             return BaselineTIFFTagSet.COMPRESSION_NONE;
       
   471         } else {
       
   472             return f.getAsInt(0);
       
   473         }
       
   474     }
       
   475 
       
   476     public int getWidth(int imageIndex) throws IOException {
       
   477         seekToImage(imageIndex);
       
   478         return getWidth();
       
   479     }
       
   480 
       
   481     public int getHeight(int imageIndex) throws IOException {
       
   482         seekToImage(imageIndex);
       
   483         return getHeight();
       
   484     }
       
   485 
       
   486     /**
       
   487      * Initializes these instance variables from the image metadata:
       
   488      * <pre>
       
   489      * compression
       
   490      * width
       
   491      * height
       
   492      * samplesPerPixel
       
   493      * numBands
       
   494      * colorMap
       
   495      * photometricInterpretation
       
   496      * sampleFormat
       
   497      * bitsPerSample
       
   498      * extraSamples
       
   499      * tileOrStripWidth
       
   500      * tileOrStripHeight
       
   501      * </pre>
       
   502      */
       
   503     private void initializeFromMetadata() throws IIOException {
       
   504         TIFFField f;
       
   505 
       
   506         // Compression
       
   507         f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
       
   508         if (f == null) {
       
   509             processWarningOccurred("Compression field is missing; assuming no compression");
       
   510             compression = BaselineTIFFTagSet.COMPRESSION_NONE;
       
   511         } else {
       
   512             compression = f.getAsInt(0);
       
   513         }
       
   514 
       
   515         // Whether key dimensional information is absent.
       
   516         boolean isMissingDimension = false;
       
   517 
       
   518         // ImageWidth -> width
       
   519         f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
       
   520         if (f != null) {
       
   521             this.width = f.getAsInt(0);
       
   522         } else {
       
   523             processWarningOccurred("ImageWidth field is missing.");
       
   524             isMissingDimension = true;
       
   525         }
       
   526 
       
   527         // ImageLength -> height
       
   528         f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH);
       
   529         if (f != null) {
       
   530             this.height = f.getAsInt(0);
       
   531         } else {
       
   532             processWarningOccurred("ImageLength field is missing.");
       
   533             isMissingDimension = true;
       
   534         }
       
   535 
       
   536         // SamplesPerPixel
       
   537         f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
       
   538         if (f != null) {
       
   539             samplesPerPixel = f.getAsInt(0);
       
   540         } else {
       
   541             samplesPerPixel = 1;
       
   542             isMissingDimension = true;
       
   543         }
       
   544 
       
   545         // If any dimension is missing and there is a JPEG stream available
       
   546         // get the information from it.
       
   547         int defaultBitDepth = 1;
       
   548         if (isMissingDimension
       
   549                 && (f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT)) != null) {
       
   550             Iterator<ImageReader> iter = ImageIO.getImageReadersByFormatName("JPEG");
       
   551             if (iter != null && iter.hasNext()) {
       
   552                 ImageReader jreader = iter.next();
       
   553                 try {
       
   554                     stream.mark();
       
   555                     stream.seek(f.getAsLong(0));
       
   556                     jreader.setInput(stream);
       
   557                     if (imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH) == null) {
       
   558                         this.width = jreader.getWidth(0);
       
   559                     }
       
   560                     if (imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH) == null) {
       
   561                         this.height = jreader.getHeight(0);
       
   562                     }
       
   563                     ImageTypeSpecifier imageType = jreader.getRawImageType(0);
       
   564                     if (imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL) == null) {
       
   565                         this.samplesPerPixel =
       
   566                             imageType != null ?
       
   567                                 imageType.getSampleModel().getNumBands() : 3;
       
   568                     }
       
   569                     stream.reset();
       
   570                     defaultBitDepth =
       
   571                         imageType != null ?
       
   572                         imageType.getColorModel().getComponentSize(0) : 8;
       
   573                 } catch (IOException e) {
       
   574                     // Ignore it and proceed: an error will occur later.
       
   575                 }
       
   576                 jreader.dispose();
       
   577             }
       
   578         }
       
   579 
       
   580         if (samplesPerPixel < 1) {
       
   581             throw new IIOException("Samples per pixel < 1!");
       
   582         } else if (samplesPerPixel > SAMPLES_PER_PIXEL_MAX) {
       
   583             throw new IIOException
       
   584                 ("Samples per pixel (" + samplesPerPixel
       
   585                 + ") greater than allowed maximum ("
       
   586                 + SAMPLES_PER_PIXEL_MAX + ")");
       
   587         }
       
   588 
       
   589         // SamplesPerPixel -> numBands
       
   590         numBands = samplesPerPixel;
       
   591 
       
   592         // ColorMap
       
   593         this.colorMap = null;
       
   594         f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COLOR_MAP);
       
   595         if (f != null) {
       
   596             // Grab color map
       
   597             colorMap = f.getAsChars();
       
   598         }
       
   599 
       
   600         // PhotometricInterpretation
       
   601         f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
       
   602         if (f == null) {
       
   603             if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_RLE
       
   604                     || compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_4
       
   605                     || compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_6) {
       
   606                 processWarningOccurred("PhotometricInterpretation field is missing; "
       
   607                         + "assuming WhiteIsZero");
       
   608                 photometricInterpretation
       
   609                         = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
       
   610             } else if (this.colorMap != null) {
       
   611                 photometricInterpretation
       
   612                         = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR;
       
   613             } else if (samplesPerPixel == 3 || samplesPerPixel == 4) {
       
   614                 photometricInterpretation
       
   615                         = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB;
       
   616             } else {
       
   617                 processWarningOccurred("PhotometricInterpretation field is missing; "
       
   618                         + "assuming BlackIsZero");
       
   619                 photometricInterpretation
       
   620                         = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
       
   621             }
       
   622         } else {
       
   623             photometricInterpretation = f.getAsInt(0);
       
   624         }
       
   625 
       
   626         // SampleFormat
       
   627         boolean replicateFirst = false;
       
   628         int first = -1;
       
   629 
       
   630         f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
       
   631         sampleFormat = new int[samplesPerPixel];
       
   632         replicateFirst = false;
       
   633         if (f == null) {
       
   634             replicateFirst = true;
       
   635             first = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED;
       
   636         } else if (f.getCount() != samplesPerPixel) {
       
   637             replicateFirst = true;
       
   638             first = f.getAsInt(0);
       
   639         }
       
   640 
       
   641         for (int i = 0; i < samplesPerPixel; i++) {
       
   642             sampleFormat[i] = replicateFirst ? first : f.getAsInt(i);
       
   643             if (sampleFormat[i]
       
   644                     != BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER
       
   645                     && sampleFormat[i]
       
   646                     != BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER
       
   647                     && sampleFormat[i]
       
   648                     != BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT
       
   649                     && sampleFormat[i]
       
   650                     != BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED) {
       
   651                 processWarningOccurred(
       
   652                         "Illegal value for SAMPLE_FORMAT, assuming SAMPLE_FORMAT_UNDEFINED");
       
   653                 sampleFormat[i] = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED;
       
   654             }
       
   655         }
       
   656 
       
   657         // BitsPerSample
       
   658         f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
       
   659         this.bitsPerSample = new int[samplesPerPixel];
       
   660         replicateFirst = false;
       
   661         if (f == null) {
       
   662             replicateFirst = true;
       
   663             first = defaultBitDepth;
       
   664         } else if (f.getCount() != samplesPerPixel) {
       
   665             replicateFirst = true;
       
   666             first = f.getAsInt(0);
       
   667         }
       
   668 
       
   669         for (int i = 0; i < samplesPerPixel; i++) {
       
   670             // Replicate initial value if not enough values provided
       
   671             bitsPerSample[i] = replicateFirst ? first : f.getAsInt(i);
       
   672             if (bitsPerSample[i] > BITS_PER_SAMPLE_MAX) {
       
   673                 throw new IIOException
       
   674                     ("Bits per sample (" + bitsPerSample[i]
       
   675                     + ") greater than allowed maximum ("
       
   676                     + BITS_PER_SAMPLE_MAX + ")");
       
   677             }
       
   678         }
       
   679 
       
   680         // ExtraSamples
       
   681         this.extraSamples = null;
       
   682         f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES);
       
   683         if (f != null) {
       
   684             extraSamples = f.getAsInts();
       
   685         }
       
   686     }
       
   687 
       
   688     public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IIOException {
       
   689         List<ImageTypeSpecifier> l; // List of ImageTypeSpecifiers
       
   690 
       
   691         Integer imageIndexInteger = Integer.valueOf(imageIndex);
       
   692         if (imageTypeMap.containsKey(imageIndexInteger)) {
       
   693             // Return the cached ITS List.
       
   694             l = imageTypeMap.get(imageIndexInteger);
       
   695         } else {
       
   696             // Create a new ITS List.
       
   697             l = new ArrayList<ImageTypeSpecifier>(1);
       
   698 
       
   699             // Create the ITS and cache if for later use so that this method
       
   700             // always returns an Iterator containing the same ITS objects.
       
   701             seekToImage(imageIndex);
       
   702             ImageTypeSpecifier itsRaw
       
   703                     = TIFFDecompressor.getRawImageTypeSpecifier(photometricInterpretation,
       
   704                             compression,
       
   705                             samplesPerPixel,
       
   706                             bitsPerSample,
       
   707                             sampleFormat,
       
   708                             extraSamples,
       
   709                             colorMap);
       
   710 
       
   711             // Check for an ICCProfile field.
       
   712             TIFFField iccProfileField
       
   713                     = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_ICC_PROFILE);
       
   714 
       
   715             // If an ICCProfile field is present change the ImageTypeSpecifier
       
   716             // to use it if the data layout is component type.
       
   717             if (iccProfileField != null
       
   718                     && itsRaw.getColorModel() instanceof ComponentColorModel) {
       
   719                 // Create a ColorSpace from the profile.
       
   720                 byte[] iccProfileValue = iccProfileField.getAsBytes();
       
   721                 ICC_Profile iccProfile
       
   722                         = ICC_Profile.getInstance(iccProfileValue);
       
   723                 ICC_ColorSpace iccColorSpace
       
   724                         = new ICC_ColorSpace(iccProfile);
       
   725 
       
   726                 // Get the raw sample and color information.
       
   727                 ColorModel cmRaw = itsRaw.getColorModel();
       
   728                 ColorSpace csRaw = cmRaw.getColorSpace();
       
   729                 SampleModel smRaw = itsRaw.getSampleModel();
       
   730 
       
   731                 // Get the number of samples per pixel and the number
       
   732                 // of color components.
       
   733                 int numBands = smRaw.getNumBands();
       
   734                 int numComponents = iccColorSpace.getNumComponents();
       
   735 
       
   736                 // Replace the ColorModel with the ICC ColorModel if the
       
   737                 // numbers of samples and color components are amenable.
       
   738                 if (numBands == numComponents
       
   739                         || numBands == numComponents + 1) {
       
   740                     // Set alpha flags.
       
   741                     boolean hasAlpha = numComponents != numBands;
       
   742                     boolean isAlphaPre
       
   743                             = hasAlpha && cmRaw.isAlphaPremultiplied();
       
   744 
       
   745                     // Create a ColorModel of the same class and with
       
   746                     // the same transfer type.
       
   747                     ColorModel iccColorModel
       
   748                             = new ComponentColorModel(iccColorSpace,
       
   749                                     cmRaw.getComponentSize(),
       
   750                                     hasAlpha,
       
   751                                     isAlphaPre,
       
   752                                     cmRaw.getTransparency(),
       
   753                                     cmRaw.getTransferType());
       
   754 
       
   755                     // Prepend the ICC profile-based ITS to the List. The
       
   756                     // ColorModel and SampleModel are guaranteed to be
       
   757                     // compatible as the old and new ColorModels are both
       
   758                     // ComponentColorModels with the same transfer type
       
   759                     // and the same number of components.
       
   760                     l.add(new ImageTypeSpecifier(iccColorModel, smRaw));
       
   761 
       
   762                     // Append the raw ITS to the List if and only if its
       
   763                     // ColorSpace has the same type and number of components
       
   764                     // as the ICC ColorSpace.
       
   765                     if (csRaw.getType() == iccColorSpace.getType()
       
   766                             && csRaw.getNumComponents()
       
   767                             == iccColorSpace.getNumComponents()) {
       
   768                         l.add(itsRaw);
       
   769                     }
       
   770                 } else { // ICCProfile not compatible with SampleModel.
       
   771                     // Append the raw ITS to the List.
       
   772                     l.add(itsRaw);
       
   773                 }
       
   774             } else { // No ICCProfile field or raw ColorModel not component.
       
   775                 // Append the raw ITS to the List.
       
   776                 l.add(itsRaw);
       
   777             }
       
   778 
       
   779             // Cache the ITS List.
       
   780             imageTypeMap.put(imageIndexInteger, l);
       
   781         }
       
   782 
       
   783         return l.iterator();
       
   784     }
       
   785 
       
   786     public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {
       
   787         seekToImage(imageIndex);
       
   788         TIFFImageMetadata im
       
   789                 = new TIFFImageMetadata(imageMetadata.getRootIFD().getTagSetList());
       
   790         Node root
       
   791                 = imageMetadata.getAsTree(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME);
       
   792         im.setFromTree(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME, root);
       
   793         return im;
       
   794     }
       
   795 
       
   796     public IIOMetadata getStreamMetadata(int imageIndex) throws IIOException {
       
   797         readHeader();
       
   798         TIFFStreamMetadata sm = new TIFFStreamMetadata();
       
   799         Node root = sm.getAsTree(TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME);
       
   800         sm.setFromTree(TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME, root);
       
   801         return sm;
       
   802     }
       
   803 
       
   804     public boolean isRandomAccessEasy(int imageIndex) throws IOException {
       
   805         if (currIndex != -1) {
       
   806             seekToImage(currIndex);
       
   807             return getCompression() == BaselineTIFFTagSet.COMPRESSION_NONE;
       
   808         } else {
       
   809             return false;
       
   810         }
       
   811     }
       
   812 
       
   813     // Thumbnails
       
   814     public boolean readSupportsThumbnails() {
       
   815         return false;
       
   816     }
       
   817 
       
   818     public boolean hasThumbnails(int imageIndex) {
       
   819         return false;
       
   820     }
       
   821 
       
   822     public int getNumThumbnails(int imageIndex) throws IOException {
       
   823         return 0;
       
   824     }
       
   825 
       
   826     public ImageReadParam getDefaultReadParam() {
       
   827         return new TIFFImageReadParam();
       
   828     }
       
   829 
       
   830     public boolean isImageTiled(int imageIndex) throws IOException {
       
   831         seekToImage(imageIndex);
       
   832 
       
   833         TIFFField f
       
   834                 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
       
   835         return f != null;
       
   836     }
       
   837 
       
   838     public int getTileWidth(int imageIndex) throws IOException {
       
   839         seekToImage(imageIndex);
       
   840         return getTileOrStripWidth();
       
   841     }
       
   842 
       
   843     public int getTileHeight(int imageIndex) throws IOException {
       
   844         seekToImage(imageIndex);
       
   845         return getTileOrStripHeight();
       
   846     }
       
   847 
       
   848     public BufferedImage readTile(int imageIndex, int tileX, int tileY)
       
   849             throws IOException {
       
   850 
       
   851         int w = getWidth(imageIndex);
       
   852         int h = getHeight(imageIndex);
       
   853         int tw = getTileWidth(imageIndex);
       
   854         int th = getTileHeight(imageIndex);
       
   855 
       
   856         int x = tw * tileX;
       
   857         int y = th * tileY;
       
   858 
       
   859         if (tileX < 0 || tileY < 0 || x >= w || y >= h) {
       
   860             throw new IllegalArgumentException("Tile indices are out of bounds!");
       
   861         }
       
   862 
       
   863         if (x + tw > w) {
       
   864             tw = w - x;
       
   865         }
       
   866 
       
   867         if (y + th > h) {
       
   868             th = h - y;
       
   869         }
       
   870 
       
   871         ImageReadParam param = getDefaultReadParam();
       
   872         Rectangle tileRect = new Rectangle(x, y, tw, th);
       
   873         param.setSourceRegion(tileRect);
       
   874 
       
   875         return read(imageIndex, param);
       
   876     }
       
   877 
       
   878     public boolean canReadRaster() {
       
   879         return false;
       
   880     }
       
   881 
       
   882     public Raster readRaster(int imageIndex, ImageReadParam param)
       
   883             throws IOException {
       
   884         throw new UnsupportedOperationException();
       
   885     }
       
   886 
       
   887     private int[] sourceBands;
       
   888     private int[] destinationBands;
       
   889 
       
   890     private TIFFDecompressor decompressor;
       
   891 
       
   892     // floor(num/den)
       
   893     private static int ifloor(int num, int den) {
       
   894         if (num < 0) {
       
   895             num -= den - 1;
       
   896         }
       
   897         return num / den;
       
   898     }
       
   899 
       
   900     // ceil(num/den)
       
   901     private static int iceil(int num, int den) {
       
   902         if (num > 0) {
       
   903             num += den - 1;
       
   904         }
       
   905         return num / den;
       
   906     }
       
   907 
       
   908     private void prepareRead(int imageIndex, ImageReadParam param)
       
   909             throws IOException {
       
   910         if (stream == null) {
       
   911             throw new IllegalStateException("Input not set!");
       
   912         }
       
   913 
       
   914         // A null ImageReadParam means we use the default
       
   915         if (param == null) {
       
   916             param = getDefaultReadParam();
       
   917         }
       
   918 
       
   919         this.imageReadParam = param;
       
   920 
       
   921         seekToImage(imageIndex);
       
   922 
       
   923         this.tileOrStripWidth = getTileOrStripWidth();
       
   924         this.tileOrStripHeight = getTileOrStripHeight();
       
   925         this.planarConfiguration = getPlanarConfiguration();
       
   926 
       
   927         this.sourceBands = param.getSourceBands();
       
   928         if (sourceBands == null) {
       
   929             sourceBands = new int[numBands];
       
   930             for (int i = 0; i < numBands; i++) {
       
   931                 sourceBands[i] = i;
       
   932             }
       
   933         }
       
   934 
       
   935         // Initialize the destination image
       
   936         Iterator<ImageTypeSpecifier> imageTypes = getImageTypes(imageIndex);
       
   937         ImageTypeSpecifier theImageType
       
   938                 = ImageUtil.getDestinationType(param, imageTypes);
       
   939 
       
   940         int destNumBands = theImageType.getSampleModel().getNumBands();
       
   941 
       
   942         this.destinationBands = param.getDestinationBands();
       
   943         if (destinationBands == null) {
       
   944             destinationBands = new int[destNumBands];
       
   945             for (int i = 0; i < destNumBands; i++) {
       
   946                 destinationBands[i] = i;
       
   947             }
       
   948         }
       
   949 
       
   950         if (sourceBands.length != destinationBands.length) {
       
   951             throw new IllegalArgumentException(
       
   952                     "sourceBands.length != destinationBands.length");
       
   953         }
       
   954 
       
   955         for (int i = 0; i < sourceBands.length; i++) {
       
   956             int sb = sourceBands[i];
       
   957             if (sb < 0 || sb >= numBands) {
       
   958                 throw new IllegalArgumentException(
       
   959                         "Source band out of range!");
       
   960             }
       
   961             int db = destinationBands[i];
       
   962             if (db < 0 || db >= destNumBands) {
       
   963                 throw new IllegalArgumentException(
       
   964                         "Destination band out of range!");
       
   965             }
       
   966         }
       
   967     }
       
   968 
       
   969     public RenderedImage readAsRenderedImage(int imageIndex,
       
   970             ImageReadParam param)
       
   971             throws IOException {
       
   972         prepareRead(imageIndex, param);
       
   973         return new TIFFRenderedImage(this, imageIndex, imageReadParam,
       
   974                 width, height);
       
   975     }
       
   976 
       
   977     private void decodeTile(int ti, int tj, int band) throws IOException {
       
   978         // Compute the region covered by the strip or tile
       
   979         Rectangle tileRect = new Rectangle(ti * tileOrStripWidth,
       
   980                 tj * tileOrStripHeight,
       
   981                 tileOrStripWidth,
       
   982                 tileOrStripHeight);
       
   983 
       
   984         // Clip against the image bounds if the image is not tiled. If it
       
   985         // is tiled, the tile may legally extend beyond the image bounds.
       
   986         if (!isImageTiled(currIndex)) {
       
   987             tileRect
       
   988                     = tileRect.intersection(new Rectangle(0, 0, width, height));
       
   989         }
       
   990 
       
   991         // Return if the intersection is empty.
       
   992         if (tileRect.width <= 0 || tileRect.height <= 0) {
       
   993             return;
       
   994         }
       
   995 
       
   996         int srcMinX = tileRect.x;
       
   997         int srcMinY = tileRect.y;
       
   998         int srcWidth = tileRect.width;
       
   999         int srcHeight = tileRect.height;
       
  1000 
       
  1001         // Determine dest region that can be derived from the
       
  1002         // source region
       
  1003         dstMinX = iceil(srcMinX - sourceXOffset, srcXSubsampling);
       
  1004         int dstMaxX = ifloor(srcMinX + srcWidth - 1 - sourceXOffset,
       
  1005                 srcXSubsampling);
       
  1006 
       
  1007         dstMinY = iceil(srcMinY - sourceYOffset, srcYSubsampling);
       
  1008         int dstMaxY = ifloor(srcMinY + srcHeight - 1 - sourceYOffset,
       
  1009                 srcYSubsampling);
       
  1010 
       
  1011         dstWidth = dstMaxX - dstMinX + 1;
       
  1012         dstHeight = dstMaxY - dstMinY + 1;
       
  1013 
       
  1014         dstMinX += dstXOffset;
       
  1015         dstMinY += dstYOffset;
       
  1016 
       
  1017         // Clip against image bounds
       
  1018         Rectangle dstRect = new Rectangle(dstMinX, dstMinY,
       
  1019                 dstWidth, dstHeight);
       
  1020         dstRect
       
  1021                 = dstRect.intersection(theImage.getRaster().getBounds());
       
  1022 
       
  1023         dstMinX = dstRect.x;
       
  1024         dstMinY = dstRect.y;
       
  1025         dstWidth = dstRect.width;
       
  1026         dstHeight = dstRect.height;
       
  1027 
       
  1028         if (dstWidth <= 0 || dstHeight <= 0) {
       
  1029             return;
       
  1030         }
       
  1031 
       
  1032         // Backwards map dest region to source to determine
       
  1033         // active source region
       
  1034         int activeSrcMinX = (dstMinX - dstXOffset) * srcXSubsampling
       
  1035                 + sourceXOffset;
       
  1036         int sxmax
       
  1037                 = (dstMinX + dstWidth - 1 - dstXOffset) * srcXSubsampling
       
  1038                 + sourceXOffset;
       
  1039         int activeSrcWidth = sxmax - activeSrcMinX + 1;
       
  1040 
       
  1041         int activeSrcMinY = (dstMinY - dstYOffset) * srcYSubsampling
       
  1042                 + sourceYOffset;
       
  1043         int symax
       
  1044                 = (dstMinY + dstHeight - 1 - dstYOffset) * srcYSubsampling
       
  1045                 + sourceYOffset;
       
  1046         int activeSrcHeight = symax - activeSrcMinY + 1;
       
  1047 
       
  1048         decompressor.setSrcMinX(srcMinX);
       
  1049         decompressor.setSrcMinY(srcMinY);
       
  1050         decompressor.setSrcWidth(srcWidth);
       
  1051         decompressor.setSrcHeight(srcHeight);
       
  1052 
       
  1053         decompressor.setDstMinX(dstMinX);
       
  1054         decompressor.setDstMinY(dstMinY);
       
  1055         decompressor.setDstWidth(dstWidth);
       
  1056         decompressor.setDstHeight(dstHeight);
       
  1057 
       
  1058         decompressor.setActiveSrcMinX(activeSrcMinX);
       
  1059         decompressor.setActiveSrcMinY(activeSrcMinY);
       
  1060         decompressor.setActiveSrcWidth(activeSrcWidth);
       
  1061         decompressor.setActiveSrcHeight(activeSrcHeight);
       
  1062 
       
  1063         int tileIndex = tj * tilesAcross + ti;
       
  1064 
       
  1065         if (planarConfiguration
       
  1066                 == BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) {
       
  1067             tileIndex += band * tilesAcross * tilesDown;
       
  1068         }
       
  1069 
       
  1070         long offset = getTileOrStripOffset(tileIndex);
       
  1071         long byteCount = getTileOrStripByteCount(tileIndex);
       
  1072 
       
  1073         decompressor.setStream(stream);
       
  1074         decompressor.setOffset(offset);
       
  1075         decompressor.setByteCount((int) byteCount);
       
  1076 
       
  1077         decompressor.beginDecoding();
       
  1078 
       
  1079         stream.mark();
       
  1080         decompressor.decode();
       
  1081         stream.reset();
       
  1082     }
       
  1083 
       
  1084     private void reportProgress() {
       
  1085         // Report image progress/update to listeners after each tile
       
  1086         pixelsRead += dstWidth * dstHeight;
       
  1087         processImageProgress(100.0f * pixelsRead / pixelsToRead);
       
  1088         processImageUpdate(theImage,
       
  1089                 dstMinX, dstMinY, dstWidth, dstHeight,
       
  1090                 1, 1,
       
  1091                 destinationBands);
       
  1092     }
       
  1093 
       
  1094     public BufferedImage read(int imageIndex, ImageReadParam param)
       
  1095             throws IOException {
       
  1096         prepareRead(imageIndex, param);
       
  1097         this.theImage = getDestination(param,
       
  1098                 getImageTypes(imageIndex),
       
  1099                 width, height);
       
  1100 
       
  1101         srcXSubsampling = imageReadParam.getSourceXSubsampling();
       
  1102         srcYSubsampling = imageReadParam.getSourceYSubsampling();
       
  1103 
       
  1104         Point p = imageReadParam.getDestinationOffset();
       
  1105         dstXOffset = p.x;
       
  1106         dstYOffset = p.y;
       
  1107 
       
  1108         // This could probably be made more efficient...
       
  1109         Rectangle srcRegion = new Rectangle(0, 0, 0, 0);
       
  1110         Rectangle destRegion = new Rectangle(0, 0, 0, 0);
       
  1111 
       
  1112         computeRegions(imageReadParam, width, height, theImage,
       
  1113                 srcRegion, destRegion);
       
  1114 
       
  1115         // Initial source pixel, taking source region and source
       
  1116         // subsamplimg offsets into account
       
  1117         sourceXOffset = srcRegion.x;
       
  1118         sourceYOffset = srcRegion.y;
       
  1119 
       
  1120         pixelsToRead = destRegion.width * destRegion.height;
       
  1121         pixelsRead = 0;
       
  1122 
       
  1123         processImageStarted(imageIndex);
       
  1124         processImageProgress(0.0f);
       
  1125 
       
  1126         tilesAcross = (width + tileOrStripWidth - 1) / tileOrStripWidth;
       
  1127         tilesDown = (height + tileOrStripHeight - 1) / tileOrStripHeight;
       
  1128 
       
  1129         int compression = getCompression();
       
  1130 
       
  1131         // Set the decompressor
       
  1132         if (compression == BaselineTIFFTagSet.COMPRESSION_NONE) {
       
  1133             // Get the fillOrder field.
       
  1134             TIFFField fillOrderField
       
  1135                     = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER);
       
  1136 
       
  1137             // Set the decompressor based on the fill order.
       
  1138             if (fillOrderField != null && fillOrderField.getAsInt(0) == 2) {
       
  1139                 this.decompressor = new TIFFLSBDecompressor();
       
  1140             } else {
       
  1141                 this.decompressor = new TIFFNullDecompressor();
       
  1142             }
       
  1143         } else if (compression
       
  1144                 == BaselineTIFFTagSet.COMPRESSION_CCITT_T_6) {
       
  1145             this.decompressor = new TIFFFaxDecompressor();
       
  1146         } else if (compression
       
  1147                 == BaselineTIFFTagSet.COMPRESSION_CCITT_T_4) {
       
  1148             this.decompressor = new TIFFFaxDecompressor();
       
  1149         } else if (compression
       
  1150                 == BaselineTIFFTagSet.COMPRESSION_CCITT_RLE) {
       
  1151             this.decompressor = new TIFFFaxDecompressor();
       
  1152         } else if (compression
       
  1153                 == BaselineTIFFTagSet.COMPRESSION_PACKBITS) {
       
  1154             this.decompressor = new TIFFPackBitsDecompressor();
       
  1155         } else if (compression
       
  1156                 == BaselineTIFFTagSet.COMPRESSION_LZW) {
       
  1157             TIFFField predictorField
       
  1158                     = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PREDICTOR);
       
  1159             int predictor = ((predictorField == null)
       
  1160                     ? BaselineTIFFTagSet.PREDICTOR_NONE
       
  1161                     : predictorField.getAsInt(0));
       
  1162             this.decompressor = new TIFFLZWDecompressor(predictor);
       
  1163         } else if (compression
       
  1164                 == BaselineTIFFTagSet.COMPRESSION_JPEG) {
       
  1165             this.decompressor = new TIFFJPEGDecompressor();
       
  1166         } else if (compression
       
  1167                 == BaselineTIFFTagSet.COMPRESSION_ZLIB
       
  1168                 || compression
       
  1169                 == BaselineTIFFTagSet.COMPRESSION_DEFLATE) {
       
  1170             TIFFField predictorField
       
  1171                     = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PREDICTOR);
       
  1172             int predictor = ((predictorField == null)
       
  1173                     ? BaselineTIFFTagSet.PREDICTOR_NONE
       
  1174                     : predictorField.getAsInt(0));
       
  1175             this.decompressor = new TIFFDeflateDecompressor(predictor);
       
  1176         } else if (compression
       
  1177                 == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
       
  1178             TIFFField JPEGProcField
       
  1179                     = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_PROC);
       
  1180             if (JPEGProcField == null) {
       
  1181                 processWarningOccurred("JPEGProc field missing; assuming baseline sequential JPEG process.");
       
  1182             } else if (JPEGProcField.getAsInt(0)
       
  1183                     != BaselineTIFFTagSet.JPEG_PROC_BASELINE) {
       
  1184                 throw new IIOException("Old-style JPEG supported for baseline sequential JPEG process only!");
       
  1185             }
       
  1186             this.decompressor = new TIFFOldJPEGDecompressor();
       
  1187             //throw new IIOException("Old-style JPEG not supported!");
       
  1188         } else {
       
  1189             throw new IIOException("Unsupported compression type (tag value = "
       
  1190                     + compression + ")!");
       
  1191         }
       
  1192 
       
  1193         if (photometricInterpretation
       
  1194                 == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR
       
  1195                 && compression != BaselineTIFFTagSet.COMPRESSION_JPEG
       
  1196                 && compression != BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
       
  1197             boolean convertYCbCrToRGB
       
  1198                     = theImage.getColorModel().getColorSpace().getType()
       
  1199                     == ColorSpace.TYPE_RGB;
       
  1200             TIFFDecompressor wrappedDecompressor
       
  1201                     = this.decompressor instanceof TIFFNullDecompressor
       
  1202                             ? null : this.decompressor;
       
  1203             this.decompressor
       
  1204                     = new TIFFYCbCrDecompressor(wrappedDecompressor,
       
  1205                             convertYCbCrToRGB);
       
  1206         }
       
  1207 
       
  1208         TIFFColorConverter colorConverter = null;
       
  1209         if (photometricInterpretation
       
  1210                 == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB
       
  1211                 && theImage.getColorModel().getColorSpace().getType()
       
  1212                 == ColorSpace.TYPE_RGB) {
       
  1213             colorConverter = new TIFFCIELabColorConverter();
       
  1214         } else if (photometricInterpretation
       
  1215                 == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR
       
  1216                 && !(this.decompressor instanceof TIFFYCbCrDecompressor)
       
  1217                 && compression != BaselineTIFFTagSet.COMPRESSION_JPEG
       
  1218                 && compression != BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
       
  1219             colorConverter = new TIFFYCbCrColorConverter(imageMetadata);
       
  1220         }
       
  1221 
       
  1222         decompressor.setReader(this);
       
  1223         decompressor.setMetadata(imageMetadata);
       
  1224         decompressor.setImage(theImage);
       
  1225 
       
  1226         decompressor.setPhotometricInterpretation(photometricInterpretation);
       
  1227         decompressor.setCompression(compression);
       
  1228         decompressor.setSamplesPerPixel(samplesPerPixel);
       
  1229         decompressor.setBitsPerSample(bitsPerSample);
       
  1230         decompressor.setSampleFormat(sampleFormat);
       
  1231         decompressor.setExtraSamples(extraSamples);
       
  1232         decompressor.setColorMap(colorMap);
       
  1233 
       
  1234         decompressor.setColorConverter(colorConverter);
       
  1235 
       
  1236         decompressor.setSourceXOffset(sourceXOffset);
       
  1237         decompressor.setSourceYOffset(sourceYOffset);
       
  1238         decompressor.setSubsampleX(srcXSubsampling);
       
  1239         decompressor.setSubsampleY(srcYSubsampling);
       
  1240 
       
  1241         decompressor.setDstXOffset(dstXOffset);
       
  1242         decompressor.setDstYOffset(dstYOffset);
       
  1243 
       
  1244         decompressor.setSourceBands(sourceBands);
       
  1245         decompressor.setDestinationBands(destinationBands);
       
  1246 
       
  1247         // Compute bounds on the tile indices for this source region.
       
  1248         int minTileX
       
  1249                 = TIFFImageWriter.XToTileX(srcRegion.x, 0, tileOrStripWidth);
       
  1250         int minTileY
       
  1251                 = TIFFImageWriter.YToTileY(srcRegion.y, 0, tileOrStripHeight);
       
  1252         int maxTileX
       
  1253                 = TIFFImageWriter.XToTileX(srcRegion.x + srcRegion.width - 1,
       
  1254                         0, tileOrStripWidth);
       
  1255         int maxTileY
       
  1256                 = TIFFImageWriter.YToTileY(srcRegion.y + srcRegion.height - 1,
       
  1257                         0, tileOrStripHeight);
       
  1258 
       
  1259         if (planarConfiguration
       
  1260                 == BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) {
       
  1261 
       
  1262             decompressor.setPlanar(true);
       
  1263 
       
  1264             int[] sb = new int[1];
       
  1265             int[] db = new int[1];
       
  1266             for (int tj = minTileY; tj <= maxTileY; tj++) {
       
  1267                 for (int ti = minTileX; ti <= maxTileX; ti++) {
       
  1268                     for (int band = 0; band < numBands; band++) {
       
  1269                         sb[0] = sourceBands[band];
       
  1270                         decompressor.setSourceBands(sb);
       
  1271                         db[0] = destinationBands[band];
       
  1272                         decompressor.setDestinationBands(db);
       
  1273 
       
  1274                         decodeTile(ti, tj, band);
       
  1275                     }
       
  1276 
       
  1277                     reportProgress();
       
  1278                 }
       
  1279             }
       
  1280         } else {
       
  1281             for (int tj = minTileY; tj <= maxTileY; tj++) {
       
  1282                 for (int ti = minTileX; ti <= maxTileX; ti++) {
       
  1283                     decodeTile(ti, tj, -1);
       
  1284 
       
  1285                     reportProgress();
       
  1286                 }
       
  1287             }
       
  1288         }
       
  1289 
       
  1290         if (abortRequested()) {
       
  1291             processReadAborted();
       
  1292         } else {
       
  1293             processImageComplete();
       
  1294         }
       
  1295 
       
  1296         return theImage;
       
  1297     }
       
  1298 
       
  1299     public void reset() {
       
  1300         super.reset();
       
  1301         resetLocal();
       
  1302     }
       
  1303 
       
  1304     protected void resetLocal() {
       
  1305         stream = null;
       
  1306         gotHeader = false;
       
  1307         imageReadParam = getDefaultReadParam();
       
  1308         streamMetadata = null;
       
  1309         currIndex = -1;
       
  1310         imageMetadata = null;
       
  1311         imageStartPosition = new ArrayList<Long>();
       
  1312         numImages = -1;
       
  1313         imageTypeMap = new HashMap<Integer, List<ImageTypeSpecifier>>();
       
  1314         width = -1;
       
  1315         height = -1;
       
  1316         numBands = -1;
       
  1317         tileOrStripWidth = -1;
       
  1318         tileOrStripHeight = -1;
       
  1319         planarConfiguration = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
       
  1320     }
       
  1321 
       
  1322     /**
       
  1323      * Package scope method to allow decompressors, for example, to emit warning
       
  1324      * messages.
       
  1325      */
       
  1326     void forwardWarningMessage(String warning) {
       
  1327         processWarningOccurred(warning);
       
  1328     }
       
  1329 }