1 /*
2 * Copyright (c) 2005, 2017, 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
119 private int srcXSubsampling;
120 private int srcYSubsampling;
121
122 private int dstWidth;
123 private int dstHeight;
124 private int dstMinX;
125 private int dstMinY;
126 private int dstXOffset;
127 private int dstYOffset;
128
129 private int tilesAcross;
130 private int tilesDown;
131
132 private int pixelsRead;
133 private int pixelsToRead;
134
135 public TIFFImageReader(ImageReaderSpi originatingProvider) {
136 super(originatingProvider);
137 }
138
139 public void setInput(Object input,
140 boolean seekForwardOnly,
141 boolean ignoreMetadata) {
142 super.setInput(input, seekForwardOnly, ignoreMetadata);
143
144 // Clear all local values based on the previous stream contents.
145 resetLocal();
146
147 if (input != null) {
148 if (!(input instanceof ImageInputStream)) {
149 throw new IllegalArgumentException("input not an ImageInputStream!");
150 }
151 this.stream = (ImageInputStream) input;
152 } else {
153 this.stream = null;
154 }
155 }
156
157 // Do not seek to the beginning of the stream so as to allow users to
158 // point us at an IFD within some other file format
228 stream.seek(offset);
229 imageStartPosition.add(Long.valueOf(offset));
230 ++index;
231 }
232 } catch (EOFException eofe) {
233 forwardWarningMessage("Ignored " + eofe);
234
235 // Ran off the end of stream: decrement index
236 imageIndex = index > 0 ? index - 1 : 0;
237 } catch (IOException ioe) {
238 throw new IIOException("Couldn't seek!", ioe);
239 }
240
241 if (currIndex != imageIndex) {
242 imageMetadata = null;
243 }
244 currIndex = imageIndex;
245 return imageIndex;
246 }
247
248 public int getNumImages(boolean allowSearch) throws IOException {
249 if (stream == null) {
250 throw new IllegalStateException("Input not set!");
251 }
252 if (seekForwardOnly && allowSearch) {
253 throw new IllegalStateException("seekForwardOnly and allowSearch can't both be true!");
254 }
255
256 if (numImages > 0) {
257 return numImages;
258 }
259 if (allowSearch) {
260 this.numImages = locateImage(Integer.MAX_VALUE) + 1;
261 }
262 return numImages;
263 }
264
265 public IIOMetadata getStreamMetadata() throws IIOException {
266 readHeader();
267 return streamMetadata;
268 }
269
270 // Throw an IndexOutOfBoundsException if index < minIndex,
271 // and bump minIndex if required.
272 private void checkIndex(int imageIndex) {
273 if (imageIndex < minIndex) {
274 throw new IndexOutOfBoundsException("imageIndex < minIndex!");
275 }
276 if (seekForwardOnly) {
277 minIndex = imageIndex;
278 }
279 }
280
281 // Verify that imageIndex is in bounds, find the image IFD, read the
282 // image metadata, initialize instance variables from the metadata.
283 private void seekToImage(int imageIndex) throws IIOException {
284 checkIndex(imageIndex);
470 = Math.min(tileOrStripByteCount,
471 streamLength - getTileOrStripOffset(tileIndex));
472 } else {
473 processWarningOccurred("Stream length is unknown: cannot clamp estimated strip or tile byte count to EOF.");
474 }
475 }
476
477 return tileOrStripByteCount;
478 }
479
480 private int getCompression() {
481 TIFFField f
482 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
483 if (f == null) {
484 return BaselineTIFFTagSet.COMPRESSION_NONE;
485 } else {
486 return f.getAsInt(0);
487 }
488 }
489
490 public int getWidth(int imageIndex) throws IOException {
491 seekToImage(imageIndex);
492 return getWidth();
493 }
494
495 public int getHeight(int imageIndex) throws IOException {
496 seekToImage(imageIndex);
497 return getHeight();
498 }
499
500 /**
501 * Initializes these instance variables from the image metadata:
502 * <pre>
503 * compression
504 * width
505 * height
506 * samplesPerPixel
507 * numBands
508 * colorMap
509 * photometricInterpretation
510 * sampleFormat
511 * bitsPerSample
512 * extraSamples
513 * tileOrStripWidth
514 * tileOrStripHeight
682
683 for (int i = 0; i < samplesPerPixel; i++) {
684 // Replicate initial value if not enough values provided
685 bitsPerSample[i] = replicateFirst ? first : f.getAsInt(i);
686 if (bitsPerSample[i] > BITS_PER_SAMPLE_MAX) {
687 throw new IIOException
688 ("Bits per sample (" + bitsPerSample[i]
689 + ") greater than allowed maximum ("
690 + BITS_PER_SAMPLE_MAX + ")");
691 }
692 }
693
694 // ExtraSamples
695 this.extraSamples = null;
696 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES);
697 if (f != null) {
698 extraSamples = f.getAsInts();
699 }
700 }
701
702 public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IIOException {
703 List<ImageTypeSpecifier> l; // List of ImageTypeSpecifiers
704
705 Integer imageIndexInteger = Integer.valueOf(imageIndex);
706 if (imageTypeMap.containsKey(imageIndexInteger)) {
707 // Return the cached ITS List.
708 l = imageTypeMap.get(imageIndexInteger);
709 } else {
710 // Create a new ITS List.
711 l = new ArrayList<ImageTypeSpecifier>(1);
712
713 // Create the ITS and cache if for later use so that this method
714 // always returns an Iterator containing the same ITS objects.
715 seekToImage(imageIndex);
716 ImageTypeSpecifier itsRaw
717 = TIFFDecompressor.getRawImageTypeSpecifier(photometricInterpretation,
718 compression,
719 samplesPerPixel,
720 bitsPerSample,
721 sampleFormat,
809 && csRaw.getNumComponents()
810 == iccColorSpace.getNumComponents()) {
811 l.add(itsRaw);
812 }
813 } else { // ICCProfile not compatible with SampleModel.
814 // Append the raw ITS to the List.
815 l.add(itsRaw);
816 }
817 } else { // No ICCProfile field or raw ColorModel not component.
818 // Append the raw ITS to the List.
819 l.add(itsRaw);
820 }
821
822 // Cache the ITS List.
823 imageTypeMap.put(imageIndexInteger, l);
824 }
825
826 return l.iterator();
827 }
828
829 public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {
830 seekToImage(imageIndex);
831 TIFFImageMetadata im
832 = new TIFFImageMetadata(imageMetadata.getRootIFD().getTagSetList());
833 Node root
834 = imageMetadata.getAsTree(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME);
835 im.setFromTree(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME, root);
836 return im;
837 }
838
839 public IIOMetadata getStreamMetadata(int imageIndex) throws IIOException {
840 readHeader();
841 TIFFStreamMetadata sm = new TIFFStreamMetadata();
842 Node root = sm.getAsTree(TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME);
843 sm.setFromTree(TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME, root);
844 return sm;
845 }
846
847 public boolean isRandomAccessEasy(int imageIndex) throws IOException {
848 if (currIndex != -1) {
849 seekToImage(currIndex);
850 return getCompression() == BaselineTIFFTagSet.COMPRESSION_NONE;
851 } else {
852 return false;
853 }
854 }
855
856 // Thumbnails
857 public boolean readSupportsThumbnails() {
858 return false;
859 }
860
861 public boolean hasThumbnails(int imageIndex) {
862 return false;
863 }
864
865 public int getNumThumbnails(int imageIndex) throws IOException {
866 return 0;
867 }
868
869 public ImageReadParam getDefaultReadParam() {
870 return new TIFFImageReadParam();
871 }
872
873 public boolean isImageTiled(int imageIndex) throws IOException {
874 seekToImage(imageIndex);
875
876 TIFFField f
877 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
878 return f != null;
879 }
880
881 public int getTileWidth(int imageIndex) throws IOException {
882 seekToImage(imageIndex);
883 return getTileOrStripWidth();
884 }
885
886 public int getTileHeight(int imageIndex) throws IOException {
887 seekToImage(imageIndex);
888 return getTileOrStripHeight();
889 }
890
891 public BufferedImage readTile(int imageIndex, int tileX, int tileY)
892 throws IOException {
893
894 int w = getWidth(imageIndex);
895 int h = getHeight(imageIndex);
896 int tw = getTileWidth(imageIndex);
897 int th = getTileHeight(imageIndex);
898
899 int x = tw * tileX;
900 int y = th * tileY;
901
902 if (tileX < 0 || tileY < 0 || x >= w || y >= h) {
903 throw new IllegalArgumentException("Tile indices are out of bounds!");
904 }
905
906 if (x + tw > w) {
907 tw = w - x;
908 }
909
910 if (y + th > h) {
911 th = h - y;
912 }
913
914 ImageReadParam param = getDefaultReadParam();
915 Rectangle tileRect = new Rectangle(x, y, tw, th);
916 param.setSourceRegion(tileRect);
917
918 return read(imageIndex, param);
919 }
920
921 public boolean canReadRaster() {
922 return false;
923 }
924
925 public Raster readRaster(int imageIndex, ImageReadParam param)
926 throws IOException {
927 throw new UnsupportedOperationException();
928 }
929
930 private int[] sourceBands;
931 private int[] destinationBands;
932
933 private TIFFDecompressor decompressor;
934
935 // floor(num/den)
936 private static int ifloor(int num, int den) {
937 if (num < 0) {
938 num -= den - 1;
939 }
940 return num / den;
941 }
942
943 // ceil(num/den)
944 private static int iceil(int num, int den) {
992
993 if (sourceBands.length != destinationBands.length) {
994 throw new IllegalArgumentException(
995 "sourceBands.length != destinationBands.length");
996 }
997
998 for (int i = 0; i < sourceBands.length; i++) {
999 int sb = sourceBands[i];
1000 if (sb < 0 || sb >= numBands) {
1001 throw new IllegalArgumentException(
1002 "Source band out of range!");
1003 }
1004 int db = destinationBands[i];
1005 if (db < 0 || db >= destNumBands) {
1006 throw new IllegalArgumentException(
1007 "Destination band out of range!");
1008 }
1009 }
1010 }
1011
1012 public RenderedImage readAsRenderedImage(int imageIndex,
1013 ImageReadParam param)
1014 throws IOException {
1015 prepareRead(imageIndex, param);
1016 return new TIFFRenderedImage(this, imageIndex, imageReadParam,
1017 width, height);
1018 }
1019
1020 private void decodeTile(int ti, int tj, int band) throws IOException {
1021 // Compute the region covered by the strip or tile
1022 Rectangle tileRect = new Rectangle(ti * tileOrStripWidth,
1023 tj * tileOrStripHeight,
1024 tileOrStripWidth,
1025 tileOrStripHeight);
1026
1027 // Clip against the image bounds if the image is not tiled. If it
1028 // is tiled, the tile may legally extend beyond the image bounds.
1029 if (!isImageTiled(currIndex)) {
1030 tileRect
1031 = tileRect.intersection(new Rectangle(0, 0, width, height));
1118 decompressor.setOffset(offset);
1119 decompressor.setByteCount((int) byteCount);
1120
1121 decompressor.beginDecoding();
1122
1123 stream.mark();
1124 decompressor.decode();
1125 stream.reset();
1126 }
1127
1128 private void reportProgress() {
1129 // Report image progress/update to listeners after each tile
1130 pixelsRead += dstWidth * dstHeight;
1131 processImageProgress(100.0f * pixelsRead / pixelsToRead);
1132 processImageUpdate(theImage,
1133 dstMinX, dstMinY, dstWidth, dstHeight,
1134 1, 1,
1135 destinationBands);
1136 }
1137
1138 public BufferedImage read(int imageIndex, ImageReadParam param)
1139 throws IOException {
1140 prepareRead(imageIndex, param);
1141 this.theImage = getDestination(param,
1142 getImageTypes(imageIndex),
1143 width, height);
1144
1145 srcXSubsampling = imageReadParam.getSourceXSubsampling();
1146 srcYSubsampling = imageReadParam.getSourceYSubsampling();
1147
1148 Point p = imageReadParam.getDestinationOffset();
1149 dstXOffset = p.x;
1150 dstYOffset = p.y;
1151
1152 // This could probably be made more efficient...
1153 Rectangle srcRegion = new Rectangle(0, 0, 0, 0);
1154 Rectangle destRegion = new Rectangle(0, 0, 0, 0);
1155
1156 computeRegions(imageReadParam, width, height, theImage,
1157 srcRegion, destRegion);
1336 }
1337 }
1338 }
1339 } else {
1340 for (int tj = minTileY; tj <= maxTileY; tj++) {
1341 for (int ti = minTileX; ti <= maxTileX; ti++) {
1342 decodeTile(ti, tj, -1);
1343
1344 reportProgress();
1345 if (abortRequested()) {
1346 processReadAborted();
1347 return theImage;
1348 }
1349 }
1350 }
1351 }
1352 processImageComplete();
1353 return theImage;
1354 }
1355
1356 public void reset() {
1357 super.reset();
1358 resetLocal();
1359 }
1360
1361 protected void resetLocal() {
1362 stream = null;
1363 gotHeader = false;
1364 imageReadParam = getDefaultReadParam();
1365 streamMetadata = null;
1366 currIndex = -1;
1367 imageMetadata = null;
1368 imageStartPosition = new ArrayList<Long>();
1369 numImages = -1;
1370 imageTypeMap = new HashMap<Integer, List<ImageTypeSpecifier>>();
1371 width = -1;
1372 height = -1;
1373 numBands = -1;
1374 tileOrStripWidth = -1;
1375 tileOrStripHeight = -1;
|
1 /*
2 * Copyright (c) 2005, 2020, 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
119 private int srcXSubsampling;
120 private int srcYSubsampling;
121
122 private int dstWidth;
123 private int dstHeight;
124 private int dstMinX;
125 private int dstMinY;
126 private int dstXOffset;
127 private int dstYOffset;
128
129 private int tilesAcross;
130 private int tilesDown;
131
132 private int pixelsRead;
133 private int pixelsToRead;
134
135 public TIFFImageReader(ImageReaderSpi originatingProvider) {
136 super(originatingProvider);
137 }
138
139 @Override
140 public void setInput(Object input,
141 boolean seekForwardOnly,
142 boolean ignoreMetadata) {
143 super.setInput(input, seekForwardOnly, ignoreMetadata);
144
145 // Clear all local values based on the previous stream contents.
146 resetLocal();
147
148 if (input != null) {
149 if (!(input instanceof ImageInputStream)) {
150 throw new IllegalArgumentException("input not an ImageInputStream!");
151 }
152 this.stream = (ImageInputStream) input;
153 } else {
154 this.stream = null;
155 }
156 }
157
158 // Do not seek to the beginning of the stream so as to allow users to
159 // point us at an IFD within some other file format
229 stream.seek(offset);
230 imageStartPosition.add(Long.valueOf(offset));
231 ++index;
232 }
233 } catch (EOFException eofe) {
234 forwardWarningMessage("Ignored " + eofe);
235
236 // Ran off the end of stream: decrement index
237 imageIndex = index > 0 ? index - 1 : 0;
238 } catch (IOException ioe) {
239 throw new IIOException("Couldn't seek!", ioe);
240 }
241
242 if (currIndex != imageIndex) {
243 imageMetadata = null;
244 }
245 currIndex = imageIndex;
246 return imageIndex;
247 }
248
249 @Override
250 public int getNumImages(boolean allowSearch) throws IOException {
251 if (stream == null) {
252 throw new IllegalStateException("Input not set!");
253 }
254 if (seekForwardOnly && allowSearch) {
255 throw new IllegalStateException("seekForwardOnly and allowSearch can't both be true!");
256 }
257
258 if (numImages > 0) {
259 return numImages;
260 }
261 if (allowSearch) {
262 this.numImages = locateImage(Integer.MAX_VALUE) + 1;
263 }
264 return numImages;
265 }
266
267 @Override
268 public IIOMetadata getStreamMetadata() throws IIOException {
269 readHeader();
270 return streamMetadata;
271 }
272
273 // Throw an IndexOutOfBoundsException if index < minIndex,
274 // and bump minIndex if required.
275 private void checkIndex(int imageIndex) {
276 if (imageIndex < minIndex) {
277 throw new IndexOutOfBoundsException("imageIndex < minIndex!");
278 }
279 if (seekForwardOnly) {
280 minIndex = imageIndex;
281 }
282 }
283
284 // Verify that imageIndex is in bounds, find the image IFD, read the
285 // image metadata, initialize instance variables from the metadata.
286 private void seekToImage(int imageIndex) throws IIOException {
287 checkIndex(imageIndex);
473 = Math.min(tileOrStripByteCount,
474 streamLength - getTileOrStripOffset(tileIndex));
475 } else {
476 processWarningOccurred("Stream length is unknown: cannot clamp estimated strip or tile byte count to EOF.");
477 }
478 }
479
480 return tileOrStripByteCount;
481 }
482
483 private int getCompression() {
484 TIFFField f
485 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
486 if (f == null) {
487 return BaselineTIFFTagSet.COMPRESSION_NONE;
488 } else {
489 return f.getAsInt(0);
490 }
491 }
492
493 @Override
494 public int getWidth(int imageIndex) throws IOException {
495 seekToImage(imageIndex);
496 return getWidth();
497 }
498
499 @Override
500 public int getHeight(int imageIndex) throws IOException {
501 seekToImage(imageIndex);
502 return getHeight();
503 }
504
505 /**
506 * Initializes these instance variables from the image metadata:
507 * <pre>
508 * compression
509 * width
510 * height
511 * samplesPerPixel
512 * numBands
513 * colorMap
514 * photometricInterpretation
515 * sampleFormat
516 * bitsPerSample
517 * extraSamples
518 * tileOrStripWidth
519 * tileOrStripHeight
687
688 for (int i = 0; i < samplesPerPixel; i++) {
689 // Replicate initial value if not enough values provided
690 bitsPerSample[i] = replicateFirst ? first : f.getAsInt(i);
691 if (bitsPerSample[i] > BITS_PER_SAMPLE_MAX) {
692 throw new IIOException
693 ("Bits per sample (" + bitsPerSample[i]
694 + ") greater than allowed maximum ("
695 + BITS_PER_SAMPLE_MAX + ")");
696 }
697 }
698
699 // ExtraSamples
700 this.extraSamples = null;
701 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES);
702 if (f != null) {
703 extraSamples = f.getAsInts();
704 }
705 }
706
707 @Override
708 public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IIOException {
709 List<ImageTypeSpecifier> l; // List of ImageTypeSpecifiers
710
711 Integer imageIndexInteger = Integer.valueOf(imageIndex);
712 if (imageTypeMap.containsKey(imageIndexInteger)) {
713 // Return the cached ITS List.
714 l = imageTypeMap.get(imageIndexInteger);
715 } else {
716 // Create a new ITS List.
717 l = new ArrayList<ImageTypeSpecifier>(1);
718
719 // Create the ITS and cache if for later use so that this method
720 // always returns an Iterator containing the same ITS objects.
721 seekToImage(imageIndex);
722 ImageTypeSpecifier itsRaw
723 = TIFFDecompressor.getRawImageTypeSpecifier(photometricInterpretation,
724 compression,
725 samplesPerPixel,
726 bitsPerSample,
727 sampleFormat,
815 && csRaw.getNumComponents()
816 == iccColorSpace.getNumComponents()) {
817 l.add(itsRaw);
818 }
819 } else { // ICCProfile not compatible with SampleModel.
820 // Append the raw ITS to the List.
821 l.add(itsRaw);
822 }
823 } else { // No ICCProfile field or raw ColorModel not component.
824 // Append the raw ITS to the List.
825 l.add(itsRaw);
826 }
827
828 // Cache the ITS List.
829 imageTypeMap.put(imageIndexInteger, l);
830 }
831
832 return l.iterator();
833 }
834
835 @Override
836 public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {
837 seekToImage(imageIndex);
838 TIFFImageMetadata im
839 = new TIFFImageMetadata(imageMetadata.getRootIFD().getTagSetList());
840 Node root
841 = imageMetadata.getAsTree(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME);
842 im.setFromTree(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME, root);
843 return im;
844 }
845
846 public IIOMetadata getStreamMetadata(int imageIndex) throws IIOException {
847 readHeader();
848 TIFFStreamMetadata sm = new TIFFStreamMetadata();
849 Node root = sm.getAsTree(TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME);
850 sm.setFromTree(TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME, root);
851 return sm;
852 }
853
854 @Override
855 public boolean isRandomAccessEasy(int imageIndex) throws IOException {
856 if (currIndex != -1) {
857 seekToImage(currIndex);
858 return getCompression() == BaselineTIFFTagSet.COMPRESSION_NONE;
859 } else {
860 return false;
861 }
862 }
863
864 // Thumbnails
865 public boolean readSupportsThumbnails() {
866 return false;
867 }
868
869 @Override
870 public boolean hasThumbnails(int imageIndex) {
871 return false;
872 }
873
874 @Override
875 public int getNumThumbnails(int imageIndex) throws IOException {
876 return 0;
877 }
878
879 @Override
880 public ImageReadParam getDefaultReadParam() {
881 return new TIFFImageReadParam();
882 }
883
884 @Override
885 public boolean isImageTiled(int imageIndex) throws IOException {
886 seekToImage(imageIndex);
887
888 TIFFField f
889 = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
890 return f != null;
891 }
892
893 @Override
894 public int getTileWidth(int imageIndex) throws IOException {
895 seekToImage(imageIndex);
896 return getTileOrStripWidth();
897 }
898
899 @Override
900 public int getTileHeight(int imageIndex) throws IOException {
901 seekToImage(imageIndex);
902 return getTileOrStripHeight();
903 }
904
905 @Override
906 public BufferedImage readTile(int imageIndex, int tileX, int tileY)
907 throws IOException {
908
909 int w = getWidth(imageIndex);
910 int h = getHeight(imageIndex);
911 int tw = getTileWidth(imageIndex);
912 int th = getTileHeight(imageIndex);
913
914 int x = tw * tileX;
915 int y = th * tileY;
916
917 if (tileX < 0 || tileY < 0 || x >= w || y >= h) {
918 throw new IllegalArgumentException("Tile indices are out of bounds!");
919 }
920
921 if (x + tw > w) {
922 tw = w - x;
923 }
924
925 if (y + th > h) {
926 th = h - y;
927 }
928
929 ImageReadParam param = getDefaultReadParam();
930 Rectangle tileRect = new Rectangle(x, y, tw, th);
931 param.setSourceRegion(tileRect);
932
933 return read(imageIndex, param);
934 }
935
936 @Override
937 public boolean canReadRaster() {
938 return false;
939 }
940
941 @Override
942 public Raster readRaster(int imageIndex, ImageReadParam param)
943 throws IOException {
944 throw new UnsupportedOperationException();
945 }
946
947 private int[] sourceBands;
948 private int[] destinationBands;
949
950 private TIFFDecompressor decompressor;
951
952 // floor(num/den)
953 private static int ifloor(int num, int den) {
954 if (num < 0) {
955 num -= den - 1;
956 }
957 return num / den;
958 }
959
960 // ceil(num/den)
961 private static int iceil(int num, int den) {
1009
1010 if (sourceBands.length != destinationBands.length) {
1011 throw new IllegalArgumentException(
1012 "sourceBands.length != destinationBands.length");
1013 }
1014
1015 for (int i = 0; i < sourceBands.length; i++) {
1016 int sb = sourceBands[i];
1017 if (sb < 0 || sb >= numBands) {
1018 throw new IllegalArgumentException(
1019 "Source band out of range!");
1020 }
1021 int db = destinationBands[i];
1022 if (db < 0 || db >= destNumBands) {
1023 throw new IllegalArgumentException(
1024 "Destination band out of range!");
1025 }
1026 }
1027 }
1028
1029 @Override
1030 public RenderedImage readAsRenderedImage(int imageIndex,
1031 ImageReadParam param)
1032 throws IOException {
1033 prepareRead(imageIndex, param);
1034 return new TIFFRenderedImage(this, imageIndex, imageReadParam,
1035 width, height);
1036 }
1037
1038 private void decodeTile(int ti, int tj, int band) throws IOException {
1039 // Compute the region covered by the strip or tile
1040 Rectangle tileRect = new Rectangle(ti * tileOrStripWidth,
1041 tj * tileOrStripHeight,
1042 tileOrStripWidth,
1043 tileOrStripHeight);
1044
1045 // Clip against the image bounds if the image is not tiled. If it
1046 // is tiled, the tile may legally extend beyond the image bounds.
1047 if (!isImageTiled(currIndex)) {
1048 tileRect
1049 = tileRect.intersection(new Rectangle(0, 0, width, height));
1136 decompressor.setOffset(offset);
1137 decompressor.setByteCount((int) byteCount);
1138
1139 decompressor.beginDecoding();
1140
1141 stream.mark();
1142 decompressor.decode();
1143 stream.reset();
1144 }
1145
1146 private void reportProgress() {
1147 // Report image progress/update to listeners after each tile
1148 pixelsRead += dstWidth * dstHeight;
1149 processImageProgress(100.0f * pixelsRead / pixelsToRead);
1150 processImageUpdate(theImage,
1151 dstMinX, dstMinY, dstWidth, dstHeight,
1152 1, 1,
1153 destinationBands);
1154 }
1155
1156 @Override
1157 public BufferedImage read(int imageIndex, ImageReadParam param)
1158 throws IOException {
1159 prepareRead(imageIndex, param);
1160 this.theImage = getDestination(param,
1161 getImageTypes(imageIndex),
1162 width, height);
1163
1164 srcXSubsampling = imageReadParam.getSourceXSubsampling();
1165 srcYSubsampling = imageReadParam.getSourceYSubsampling();
1166
1167 Point p = imageReadParam.getDestinationOffset();
1168 dstXOffset = p.x;
1169 dstYOffset = p.y;
1170
1171 // This could probably be made more efficient...
1172 Rectangle srcRegion = new Rectangle(0, 0, 0, 0);
1173 Rectangle destRegion = new Rectangle(0, 0, 0, 0);
1174
1175 computeRegions(imageReadParam, width, height, theImage,
1176 srcRegion, destRegion);
1355 }
1356 }
1357 }
1358 } else {
1359 for (int tj = minTileY; tj <= maxTileY; tj++) {
1360 for (int ti = minTileX; ti <= maxTileX; ti++) {
1361 decodeTile(ti, tj, -1);
1362
1363 reportProgress();
1364 if (abortRequested()) {
1365 processReadAborted();
1366 return theImage;
1367 }
1368 }
1369 }
1370 }
1371 processImageComplete();
1372 return theImage;
1373 }
1374
1375 @Override
1376 public void reset() {
1377 super.reset();
1378 resetLocal();
1379 }
1380
1381 protected void resetLocal() {
1382 stream = null;
1383 gotHeader = false;
1384 imageReadParam = getDefaultReadParam();
1385 streamMetadata = null;
1386 currIndex = -1;
1387 imageMetadata = null;
1388 imageStartPosition = new ArrayList<Long>();
1389 numImages = -1;
1390 imageTypeMap = new HashMap<Integer, List<ImageTypeSpecifier>>();
1391 width = -1;
1392 height = -1;
1393 numBands = -1;
1394 tileOrStripWidth = -1;
1395 tileOrStripHeight = -1;
|