001/* 002 * $RCSfile: TIFFImageReader.java,v $ 003 * 004 * 005 * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 011 * - Redistribution of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * 014 * - Redistribution in binary form must reproduce the above copyright 015 * notice, this list of conditions and the following disclaimer in 016 * the documentation and/or other materials provided with the 017 * distribution. 018 * 019 * Neither the name of Sun Microsystems, Inc. or the names of 020 * contributors may be used to endorse or promote products derived 021 * from this software without specific prior written permission. 022 * 023 * This software is provided "AS IS," without a warranty of any 024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, 032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR 034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 035 * POSSIBILITY OF SUCH DAMAGES. 036 * 037 * You acknowledge that this software is not designed or intended for 038 * use in the design, construction, operation or maintenance of any 039 * nuclear facility. 040 * 041 * $Revision: 1.13 $ 042 * $Date: 2007/12/19 20:17:02 $ 043 * $State: Exp $ 044 */ 045package com.github.jaiimageio.impl.plugins.tiff; 046 047import java.awt.Point; 048import java.awt.Rectangle; 049import java.awt.color.ColorSpace; 050import java.awt.color.ICC_ColorSpace; 051import java.awt.color.ICC_Profile; 052import java.awt.image.BufferedImage; 053import java.awt.image.ColorModel; 054import java.awt.image.ComponentColorModel; 055import java.awt.image.Raster; 056import java.awt.image.RenderedImage; 057import java.awt.image.SampleModel; 058import java.io.IOException; 059import java.nio.ByteOrder; 060import java.util.ArrayList; 061import java.util.HashMap; 062import java.util.Iterator; 063import java.util.List; 064 065import javax.imageio.IIOException; 066import javax.imageio.ImageIO; 067import javax.imageio.ImageReadParam; 068import javax.imageio.ImageReader; 069import javax.imageio.ImageTypeSpecifier; 070import javax.imageio.metadata.IIOMetadata; 071import javax.imageio.spi.ImageReaderSpi; 072import javax.imageio.stream.ImageInputStream; 073 074import org.w3c.dom.Node; 075 076import com.github.jaiimageio.impl.common.ImageUtil; 077import com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet; 078import com.github.jaiimageio.plugins.tiff.TIFFColorConverter; 079import com.github.jaiimageio.plugins.tiff.TIFFDecompressor; 080import com.github.jaiimageio.plugins.tiff.TIFFField; 081import com.github.jaiimageio.plugins.tiff.TIFFImageReadParam; 082 083public class TIFFImageReader extends ImageReader { 084 085 private static final boolean DEBUG = false; // XXX 'false' for release!!! 086 087 // The current ImageInputStream source. 088 ImageInputStream stream = null; 089 090 // True if the file header has been read. 091 boolean gotHeader = false; 092 093 ImageReadParam imageReadParam = getDefaultReadParam(); 094 095 // Stream metadata, or null. 096 TIFFStreamMetadata streamMetadata = null; 097 098 // The current image index. 099 int currIndex = -1; 100 101 // Metadata for image at 'currIndex', or null. 102 TIFFImageMetadata imageMetadata = null; 103 104 // A <code>List</code> of <code>Long</code>s indicating the stream 105 // positions of the start of the IFD for each image. Entries 106 // are added as needed. 107 List imageStartPosition = new ArrayList(); 108 109 // The number of images in the stream, if known, otherwise -1. 110 int numImages = -1; 111 112 // The ImageTypeSpecifiers of the images in the stream. 113 // Contains a map of Integers to Lists. 114 HashMap imageTypeMap = new HashMap(); 115 116 BufferedImage theImage = null; 117 118 int width = -1; 119 int height = -1; 120 int numBands = -1; 121 int tileOrStripWidth = -1, tileOrStripHeight = -1; 122 123 int planarConfiguration = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY; 124 125 int rowsDone = 0; 126 127 int compression; 128 int photometricInterpretation; 129 int samplesPerPixel; 130 int[] sampleFormat; 131 int[] bitsPerSample; 132 int[] extraSamples; 133 char[] colorMap; 134 135 int sourceXOffset; 136 int sourceYOffset; 137 int srcXSubsampling; 138 int srcYSubsampling; 139 140 int dstWidth; 141 int dstHeight; 142 int dstMinX; 143 int dstMinY; 144 int dstXOffset; 145 int dstYOffset; 146 147 int tilesAcross; 148 int tilesDown; 149 150 int pixelsRead; 151 int pixelsToRead; 152 153 public TIFFImageReader(ImageReaderSpi originatingProvider) { 154 super(originatingProvider); 155 } 156 157 public void setInput(Object input, 158 boolean seekForwardOnly, 159 boolean ignoreMetadata) { 160 super.setInput(input, seekForwardOnly, ignoreMetadata); 161 162 // Clear all local values based on the previous stream contents. 163 resetLocal(); 164 165 if (input != null) { 166 if (!(input instanceof ImageInputStream)) { 167 throw new IllegalArgumentException 168 ("input not an ImageInputStream!"); 169 } 170 this.stream = (ImageInputStream)input; 171 } else { 172 this.stream = null; 173 } 174 } 175 176 // Do not seek to the beginning of the stream so as to allow users to 177 // point us at an IFD within some other file format 178 private void readHeader() throws IIOException { 179 if (gotHeader) { 180 return; 181 } 182 if (stream == null) { 183 throw new IllegalStateException("Input not set!"); 184 } 185 186 // Create an object to store the stream metadata 187 this.streamMetadata = new TIFFStreamMetadata(); 188 189 try { 190 int byteOrder = stream.readUnsignedShort(); 191 if (byteOrder == 0x4d4d) { 192 streamMetadata.byteOrder = ByteOrder.BIG_ENDIAN; 193 stream.setByteOrder(ByteOrder.BIG_ENDIAN); 194 } else if (byteOrder == 0x4949) { 195 streamMetadata.byteOrder = ByteOrder.LITTLE_ENDIAN; 196 stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); 197 } else { 198 processWarningOccurred( 199 "Bad byte order in header, assuming little-endian"); 200 streamMetadata.byteOrder = ByteOrder.LITTLE_ENDIAN; 201 stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); 202 } 203 204 int magic = stream.readUnsignedShort(); 205 if (magic != 42) { 206 processWarningOccurred( 207 "Bad magic number in header, continuing"); 208 } 209 210 // Seek to start of first IFD 211 long offset = stream.readUnsignedInt(); 212 imageStartPosition.add(new Long(offset)); 213 stream.seek(offset); 214 } catch (IOException e) { 215 throw new IIOException("I/O error reading header!", e); 216 } 217 218 gotHeader = true; 219 } 220 221 private int locateImage(int imageIndex) throws IIOException { 222 readHeader(); 223 224 try { 225 // Find closest known index 226 int index = Math.min(imageIndex, imageStartPosition.size() - 1); 227 228 // Seek to that position 229 Long l = (Long)imageStartPosition.get(index); 230 stream.seek(l.longValue()); 231 232 // Skip IFDs until at desired index or last image found 233 while (index < imageIndex) { 234 int count = stream.readUnsignedShort(); 235 stream.skipBytes(12*count); 236 237 long offset = stream.readUnsignedInt(); 238 if (offset == 0) { 239 return index; 240 } 241 242 imageStartPosition.add(new Long(offset)); 243 stream.seek(offset); 244 ++index; 245 } 246 } catch (IOException e) { 247 throw new IIOException("Couldn't seek!", e); 248 } 249 250 if (currIndex != imageIndex) { 251 imageMetadata = null; 252 } 253 currIndex = imageIndex; 254 return imageIndex; 255 } 256 257 public int getNumImages(boolean allowSearch) throws IOException { 258 if (stream == null) { 259 throw new IllegalStateException("Input not set!"); 260 } 261 if (seekForwardOnly && allowSearch) { 262 throw new IllegalStateException 263 ("seekForwardOnly and allowSearch can't both be true!"); 264 } 265 266 if (numImages > 0) { 267 return numImages; 268 } 269 if (allowSearch) { 270 this.numImages = locateImage(Integer.MAX_VALUE) + 1; 271 } 272 return numImages; 273 } 274 275 public IIOMetadata getStreamMetadata() throws IIOException { 276 readHeader(); 277 return streamMetadata; 278 } 279 280 // Throw an IndexOutOfBoundsException if index < minIndex, 281 // and bump minIndex if required. 282 private void checkIndex(int imageIndex) { 283 if (imageIndex < minIndex) { 284 throw new IndexOutOfBoundsException("imageIndex < minIndex!"); 285 } 286 if (seekForwardOnly) { 287 minIndex = imageIndex; 288 } 289 } 290 291 // Verify that imageIndex is in bounds, find the image IFD, read the 292 // image metadata, initialize instance variables from the metadata. 293 private void seekToImage(int imageIndex) throws IIOException { 294 checkIndex(imageIndex); 295 296 int index = locateImage(imageIndex); 297 if (index != imageIndex) { 298 throw new IndexOutOfBoundsException("imageIndex out of bounds!"); 299 } 300 301 readMetadata(); 302 303 initializeFromMetadata(); 304 } 305 306 // Stream must be positioned at start of IFD for 'currIndex' 307 private void readMetadata() throws IIOException { 308 if (stream == null) { 309 throw new IllegalStateException("Input not set!"); 310 } 311 312 if (imageMetadata != null) { 313 return; 314 } 315 try { 316 // Create an object to store the image metadata 317 List tagSets; 318 if (imageReadParam instanceof TIFFImageReadParam) { 319 tagSets = 320 ((TIFFImageReadParam)imageReadParam).getAllowedTagSets(); 321 } else { 322 tagSets = new ArrayList(1); 323 tagSets.add(BaselineTIFFTagSet.getInstance()); 324 } 325 326 this.imageMetadata = new TIFFImageMetadata(tagSets); 327 imageMetadata.initializeFromStream(stream, ignoreMetadata); 328 } catch (IIOException iioe) { 329 throw iioe; 330 } catch (IOException ioe) { 331 throw new IIOException("I/O error reading image metadata!", ioe); 332 } 333 } 334 335 private int getWidth() { 336 return this.width; 337 } 338 339 private int getHeight() { 340 return this.height; 341 } 342 343 private int getNumBands() { 344 return this.numBands; 345 } 346 347 // Returns tile width if image is tiled, else image width 348 private int getTileOrStripWidth() { 349 TIFFField f = 350 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH); 351 return (f == null) ? getWidth() : f.getAsInt(0); 352 } 353 354 // Returns tile height if image is tiled, else strip height 355 private int getTileOrStripHeight() { 356 TIFFField f = 357 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_LENGTH); 358 if (f != null) { 359 return f.getAsInt(0); 360 } 361 362 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP); 363 // Default for ROWS_PER_STRIP is 2^32 - 1, i.e., infinity 364 int h = (f == null) ? -1 : f.getAsInt(0); 365 return (h == -1) ? getHeight() : h; 366 } 367 368 private int getPlanarConfiguration() { 369 TIFFField f = 370 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION); 371 if (f != null) { 372 int planarConfigurationValue = f.getAsInt(0); 373 if(planarConfigurationValue == 374 BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) { 375 // Some writers (e.g. Kofax standard Multi-Page TIFF 376 // Storage Filter v2.01.000; cf. bug 4929147) do not 377 // correctly set the value of this field. Attempt to 378 // ascertain whether the value is correctly Planar. 379 if(getCompression() == 380 BaselineTIFFTagSet.COMPRESSION_OLD_JPEG && 381 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) != 382 null) { 383 // JPEG interchange format cannot have 384 // PlanarConfiguration value Chunky so reset. 385 processWarningOccurred("PlanarConfiguration \"Planar\" value inconsistent with JPEGInterchangeFormat; resetting to \"Chunky\"."); 386 planarConfigurationValue = 387 BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY; 388 } else { 389 TIFFField offsetField = 390 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS); 391 if (offsetField == null) { 392 // Tiles 393 offsetField = 394 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS); 395 int tw = getTileOrStripWidth(); 396 int th = getTileOrStripHeight(); 397 int tAcross = (getWidth() + tw - 1)/tw; 398 int tDown = (getHeight() + th - 1)/th; 399 int tilesPerImage = tAcross*tDown; 400 long[] offsetArray = offsetField.getAsLongs(); 401 if(offsetArray != null && 402 offsetArray.length == tilesPerImage) { 403 // Length of offsets array is 404 // TilesPerImage for Chunky and 405 // SamplesPerPixel*TilesPerImage for Planar. 406 processWarningOccurred("PlanarConfiguration \"Planar\" value inconsistent with TileOffsets field value count; resetting to \"Chunky\"."); 407 planarConfigurationValue = 408 BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY; 409 } 410 } else { 411 // Strips 412 int rowsPerStrip = getTileOrStripHeight(); 413 int stripsPerImage = 414 (getHeight() + rowsPerStrip - 1)/rowsPerStrip; 415 long[] offsetArray = offsetField.getAsLongs(); 416 if(offsetArray != null && 417 offsetArray.length == stripsPerImage) { 418 // Length of offsets array is 419 // StripsPerImage for Chunky and 420 // SamplesPerPixel*StripsPerImage for Planar. 421 processWarningOccurred("PlanarConfiguration \"Planar\" value inconsistent with StripOffsets field value count; resetting to \"Chunky\"."); 422 planarConfigurationValue = 423 BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY; 424 } 425 } 426 } 427 } 428 return planarConfigurationValue; 429 } 430 431 return BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY; 432 } 433 434 private long getTileOrStripOffset(int tileIndex) throws IIOException { 435 TIFFField f = 436 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS); 437 if (f == null) { 438 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS); 439 } 440 if (f == null) { 441 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT); 442 } 443 444 if(f == null) { 445 throw new IIOException 446 ("Missing required strip or tile offsets field."); 447 } 448 449 return f.getAsLong(tileIndex); 450 } 451 452 private long getTileOrStripByteCount(int tileIndex) throws IOException { 453 TIFFField f = 454 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS); 455 if (f == null) { 456 f = 457 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS); 458 } 459 if (f == null) { 460 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); 461 } 462 463 long tileOrStripByteCount; 464 if(f != null) { 465 tileOrStripByteCount = f.getAsLong(tileIndex); 466 } else { 467 processWarningOccurred("TIFF directory contains neither StripByteCounts nor TileByteCounts field: attempting to calculate from strip or tile width and height."); 468 469 // Initialize to number of bytes per strip or tile assuming 470 // no compression. 471 int bitsPerPixel = bitsPerSample[0]; 472 for(int i = 1; i < samplesPerPixel; i++) { 473 bitsPerPixel += bitsPerSample[i]; 474 } 475 int bytesPerRow = (getTileOrStripWidth()*bitsPerPixel + 7)/8; 476 tileOrStripByteCount = bytesPerRow*getTileOrStripHeight(); 477 478 // Clamp to end of stream if possible. 479 long streamLength = stream.length(); 480 if(streamLength != -1) { 481 tileOrStripByteCount = 482 Math.min(tileOrStripByteCount, 483 streamLength - getTileOrStripOffset(tileIndex)); 484 } else { 485 processWarningOccurred("Stream length is unknown: cannot clamp estimated strip or tile byte count to EOF."); 486 } 487 } 488 489 return tileOrStripByteCount; 490 } 491 492 private int getCompression() { 493 TIFFField f = 494 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION); 495 if (f == null) { 496 return BaselineTIFFTagSet.COMPRESSION_NONE; 497 } else { 498 return f.getAsInt(0); 499 } 500 } 501 502 public int getWidth(int imageIndex) throws IOException { 503 seekToImage(imageIndex); 504 return getWidth(); 505 } 506 507 public int getHeight(int imageIndex) throws IOException { 508 seekToImage(imageIndex); 509 return getHeight(); 510 } 511 512 /** 513 * Initializes these instance variables from the image metadata: 514 * <pre> 515 * compression 516 * width 517 * height 518 * samplesPerPixel 519 * numBands 520 * colorMap 521 * photometricInterpretation 522 * sampleFormat 523 * bitsPerSample 524 * extraSamples 525 * tileOrStripWidth 526 * tileOrStripHeight 527 * </pre> 528 */ 529 private void initializeFromMetadata() { 530 TIFFField f; 531 532 // Compression 533 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION); 534 if (f == null) { 535 processWarningOccurred 536 ("Compression field is missing; assuming no compression"); 537 compression = BaselineTIFFTagSet.COMPRESSION_NONE; 538 } else { 539 compression = f.getAsInt(0); 540 } 541 542 // Whether key dimensional information is absent. 543 boolean isMissingDimension = false; 544 545 // ImageWidth -> width 546 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH); 547 if (f != null) { 548 this.width = f.getAsInt(0); 549 } else { 550 processWarningOccurred("ImageWidth field is missing."); 551 isMissingDimension = true; 552 } 553 554 // ImageLength -> height 555 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH); 556 if (f != null) { 557 this.height = f.getAsInt(0); 558 } else { 559 processWarningOccurred("ImageLength field is missing."); 560 isMissingDimension = true; 561 } 562 563 // SamplesPerPixel 564 f = 565 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL); 566 if (f != null) { 567 samplesPerPixel = f.getAsInt(0); 568 } else { 569 samplesPerPixel = 1; 570 isMissingDimension = true; 571 } 572 573 // If any dimension is missing and there is a JPEG stream available 574 // get the information from it. 575 int defaultBitDepth = 1; 576 if(isMissingDimension && 577 (f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT)) != null) { 578 Iterator iter = ImageIO.getImageReadersByFormatName("JPEG"); 579 if(iter != null && iter.hasNext()) { 580 ImageReader jreader = (ImageReader)iter.next(); 581 try { 582 stream.mark(); 583 stream.seek(f.getAsLong(0)); 584 jreader.setInput(stream); 585 if(imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH) == null) { 586 this.width = jreader.getWidth(0); 587 } 588 if(imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH) == null) { 589 this.height = jreader.getHeight(0); 590 } 591 ImageTypeSpecifier imageType = jreader.getRawImageType(0); 592 if(imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL) == null) { 593 this.samplesPerPixel = 594 imageType.getSampleModel().getNumBands(); 595 } 596 stream.reset(); 597 defaultBitDepth = 598 imageType.getColorModel().getComponentSize(0); 599 } catch(IOException e) { 600 // Ignore it and proceed: an error will occur later. 601 } 602 jreader.dispose(); 603 } 604 } 605 606 if (samplesPerPixel < 1) { 607 processWarningOccurred("Samples per pixel < 1!"); 608 } 609 610 // SamplesPerPixel -> numBands 611 numBands = samplesPerPixel; 612 613 // ColorMap 614 this.colorMap = null; 615 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COLOR_MAP); 616 if (f != null) { 617 // Grab color map 618 colorMap = f.getAsChars(); 619 } 620 621 // PhotometricInterpretation 622 f = 623 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION); 624 if (f == null) { 625 if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_RLE || 626 compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_4 || 627 compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_6) { 628 processWarningOccurred 629 ("PhotometricInterpretation field is missing; "+ 630 "assuming WhiteIsZero"); 631 photometricInterpretation = 632 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO; 633 } else if(this.colorMap != null) { 634 photometricInterpretation = 635 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR; 636 } else if(samplesPerPixel == 3 || samplesPerPixel == 4) { 637 photometricInterpretation = 638 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB; 639 } else { 640 processWarningOccurred 641 ("PhotometricInterpretation field is missing; "+ 642 "assuming BlackIsZero"); 643 photometricInterpretation = 644 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO; 645 } 646 } else { 647 photometricInterpretation = f.getAsInt(0); 648 } 649 650 // SampleFormat 651 boolean replicateFirst = false; 652 int first = -1; 653 654 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT); 655 sampleFormat = new int[samplesPerPixel]; 656 replicateFirst = false; 657 if (f == null) { 658 replicateFirst = true; 659 first = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED; 660 } else if (f.getCount() != samplesPerPixel) { 661 replicateFirst = true; 662 first = f.getAsInt(0); 663 } 664 665 for (int i = 0; i < samplesPerPixel; i++) { 666 sampleFormat[i] = replicateFirst ? first : f.getAsInt(i); 667 if (sampleFormat[i] != 668 BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER && 669 sampleFormat[i] != 670 BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER && 671 sampleFormat[i] != 672 BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT && 673 sampleFormat[i] != 674 BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED) { 675 processWarningOccurred( 676 "Illegal value for SAMPLE_FORMAT, assuming SAMPLE_FORMAT_UNDEFINED"); 677 sampleFormat[i] = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED; 678 } 679 } 680 681 // BitsPerSample 682 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE); 683 this.bitsPerSample = new int[samplesPerPixel]; 684 replicateFirst = false; 685 if (f == null) { 686 replicateFirst = true; 687 first = defaultBitDepth; 688 } else if (f.getCount() != samplesPerPixel) { 689 replicateFirst = true; 690 first = f.getAsInt(0); 691 } 692 693 for (int i = 0; i < samplesPerPixel; i++) { 694 // Replicate initial value if not enough values provided 695 bitsPerSample[i] = replicateFirst ? first : f.getAsInt(i); 696 697 if (DEBUG) { 698 System.out.println("bitsPerSample[" + i + "] = " 699 + bitsPerSample[i]); 700 } 701 } 702 703 // ExtraSamples 704 this.extraSamples = null; 705 f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES); 706 if (f != null) { 707 extraSamples = f.getAsInts(); 708 } 709 710// System.out.println("colorMap = " + colorMap); 711// if (colorMap != null) { 712// for (int i = 0; i < colorMap.length; i++) { 713// System.out.println("colorMap[" + i + "] = " + (int)(colorMap[i])); 714// } 715// } 716 717 } 718 719 public Iterator getImageTypes(int imageIndex) throws IIOException { 720 List l; // List of ImageTypeSpecifiers 721 722 Integer imageIndexInteger = new Integer(imageIndex); 723 if(imageTypeMap.containsKey(imageIndexInteger)) { 724 // Return the cached ITS List. 725 l = (List)imageTypeMap.get(imageIndexInteger); 726 } else { 727 // Create a new ITS List. 728 l = new ArrayList(1); 729 730 // Create the ITS and cache if for later use so that this method 731 // always returns an Iterator containing the same ITS objects. 732 seekToImage(imageIndex); 733 ImageTypeSpecifier itsRaw = 734 TIFFDecompressor.getRawImageTypeSpecifier 735 (photometricInterpretation, 736 compression, 737 samplesPerPixel, 738 bitsPerSample, 739 sampleFormat, 740 extraSamples, 741 colorMap); 742 743 // Check for an ICCProfile field. 744 TIFFField iccProfileField = 745 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_ICC_PROFILE); 746 747 // If an ICCProfile field is present change the ImageTypeSpecifier 748 // to use it if the data layout is component type. 749 if(iccProfileField != null && 750 itsRaw.getColorModel() instanceof ComponentColorModel) { 751 // Create a ColorSpace from the profile. 752 byte[] iccProfileValue = iccProfileField.getAsBytes(); 753 ICC_Profile iccProfile = 754 ICC_Profile.getInstance(iccProfileValue); 755 ICC_ColorSpace iccColorSpace = 756 new ICC_ColorSpace(iccProfile); 757 758 // Get the raw sample and color information. 759 ColorModel cmRaw = itsRaw.getColorModel(); 760 ColorSpace csRaw = cmRaw.getColorSpace(); 761 SampleModel smRaw = itsRaw.getSampleModel(); 762 763 // Get the number of samples per pixel and the number 764 // of color components. 765 int numBands = smRaw.getNumBands(); 766 int numComponents = iccColorSpace.getNumComponents(); 767 768 // Replace the ColorModel with the ICC ColorModel if the 769 // numbers of samples and color components are amenable. 770 if(numBands == numComponents || 771 numBands == numComponents + 1) { 772 // Set alpha flags. 773 boolean hasAlpha = numComponents != numBands; 774 boolean isAlphaPre = 775 hasAlpha && cmRaw.isAlphaPremultiplied(); 776 777 // Create a ColorModel of the same class and with 778 // the same transfer type. 779 ColorModel iccColorModel = 780 new ComponentColorModel(iccColorSpace, 781 cmRaw.getComponentSize(), 782 hasAlpha, 783 isAlphaPre, 784 cmRaw.getTransparency(), 785 cmRaw.getTransferType()); 786 787 // Prepend the ICC profile-based ITS to the List. The 788 // ColorModel and SampleModel are guaranteed to be 789 // compatible as the old and new ColorModels are both 790 // ComponentColorModels with the same transfer type 791 // and the same number of components. 792 l.add(new ImageTypeSpecifier(iccColorModel, smRaw)); 793 794 // Append the raw ITS to the List if and only if its 795 // ColorSpace has the same type and number of components 796 // as the ICC ColorSpace. 797 if(csRaw.getType() == iccColorSpace.getType() && 798 csRaw.getNumComponents() == 799 iccColorSpace.getNumComponents()) { 800 l.add(itsRaw); 801 } 802 } else { // ICCProfile not compatible with SampleModel. 803 // Append the raw ITS to the List. 804 l.add(itsRaw); 805 } 806 } else { // No ICCProfile field or raw ColorModel not component. 807 // Append the raw ITS to the List. 808 l.add(itsRaw); 809 } 810 811 // Cache the ITS List. 812 imageTypeMap.put(imageIndexInteger, l); 813 } 814 815 return l.iterator(); 816 } 817 818 public IIOMetadata getImageMetadata(int imageIndex) throws IIOException { 819 seekToImage(imageIndex); 820 TIFFImageMetadata im = 821 new TIFFImageMetadata(imageMetadata.getRootIFD().getTagSetList()); 822 Node root = 823 imageMetadata.getAsTree(TIFFImageMetadata.nativeMetadataFormatName); 824 im.setFromTree(TIFFImageMetadata.nativeMetadataFormatName, root); 825 return im; 826 } 827 828 public IIOMetadata getStreamMetadata(int imageIndex) throws IIOException { 829 readHeader(); 830 TIFFStreamMetadata sm = new TIFFStreamMetadata(); 831 Node root = sm.getAsTree(TIFFStreamMetadata.nativeMetadataFormatName); 832 sm.setFromTree(TIFFStreamMetadata.nativeMetadataFormatName, root); 833 return sm; 834 } 835 836 public boolean isRandomAccessEasy(int imageIndex) throws IOException { 837 if(currIndex != -1) { 838 seekToImage(currIndex); 839 return getCompression() == BaselineTIFFTagSet.COMPRESSION_NONE; 840 } else { 841 return false; 842 } 843 } 844 845 // Thumbnails 846 847 public boolean readSupportsThumbnails() { 848 return false; 849 } 850 851 public boolean hasThumbnails(int imageIndex) { 852 return false; 853 } 854 855 public int getNumThumbnails(int imageIndex) throws IOException { 856 return 0; 857 } 858 859 public ImageReadParam getDefaultReadParam() { 860 return new TIFFImageReadParam(); 861 } 862 863 public boolean isImageTiled(int imageIndex) throws IOException { 864 seekToImage(imageIndex); 865 866 TIFFField f = 867 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH); 868 return f != null; 869 } 870 871 public int getTileWidth(int imageIndex) throws IOException { 872 seekToImage(imageIndex); 873 return getTileOrStripWidth(); 874 } 875 876 public int getTileHeight(int imageIndex) throws IOException { 877 seekToImage(imageIndex); 878 return getTileOrStripHeight(); 879 } 880 881 public BufferedImage readTile(int imageIndex, int tileX, int tileY) 882 throws IOException { 883 884 int w = getWidth(imageIndex); 885 int h = getHeight(imageIndex); 886 int tw = getTileWidth(imageIndex); 887 int th = getTileHeight(imageIndex); 888 889 int x = tw*tileX; 890 int y = th*tileY; 891 892 if(tileX < 0 || tileY < 0 || x >= w || y >= h) { 893 throw new IllegalArgumentException 894 ("Tile indices are out of bounds!"); 895 } 896 897 if (x + tw > w) { 898 tw = w - x; 899 } 900 901 if (y + th > h) { 902 th = h - y; 903 } 904 905 ImageReadParam param = getDefaultReadParam(); 906 Rectangle tileRect = new Rectangle(x, y, tw, th); 907 param.setSourceRegion(tileRect); 908 909 return read(imageIndex, param); 910 } 911 912 public boolean canReadRaster() { 913 // Enable this? 914 return false; 915 } 916 917 public Raster readRaster(int imageIndex, ImageReadParam param) 918 throws IOException { 919 // Enable this? 920 throw new UnsupportedOperationException(); 921 } 922 923// public BufferedImage readTileRaster(int imageIndex, 924// int tileX, int tileY) 925// throws IOException { 926// } 927 928 private int[] sourceBands; 929 private int[] destinationBands; 930 931 private TIFFDecompressor decompressor; 932 933 // floor(num/den) 934 private static int ifloor(int num, int den) { 935 if (num < 0) { 936 num -= den - 1; 937 } 938 return num/den; 939 } 940 941 // ceil(num/den) 942 private static int iceil(int num, int den) { 943 if (num > 0) { 944 num += den - 1; 945 } 946 return num/den; 947 } 948 949 private void prepareRead(int imageIndex, ImageReadParam param) 950 throws IOException { 951 if (stream == null) { 952 throw new IllegalStateException("Input not set!"); 953 } 954 955 // A null ImageReadParam means we use the default 956 if (param == null) { 957 param = getDefaultReadParam(); 958 } 959 960 this.imageReadParam = param; 961 962 seekToImage(imageIndex); 963 964 this.tileOrStripWidth = getTileOrStripWidth(); 965 this.tileOrStripHeight = getTileOrStripHeight(); 966 this.planarConfiguration = getPlanarConfiguration(); 967 968 this.sourceBands = param.getSourceBands(); 969 if (sourceBands == null) { 970 sourceBands = new int[numBands]; 971 for (int i = 0; i < numBands; i++) { 972 sourceBands[i] = i; 973 } 974 } 975 976 // Initialize the destination image 977 Iterator imageTypes = getImageTypes(imageIndex); 978 ImageTypeSpecifier theImageType = 979 ImageUtil.getDestinationType(param, imageTypes); 980 981 int destNumBands = theImageType.getSampleModel().getNumBands(); 982 983 this.destinationBands = param.getDestinationBands(); 984 if (destinationBands == null) { 985 destinationBands = new int[destNumBands]; 986 for (int i = 0; i < destNumBands; i++) { 987 destinationBands[i] = i; 988 } 989 } 990 991 if (sourceBands.length != destinationBands.length) { 992 throw new IllegalArgumentException( 993 "sourceBands.length != destinationBands.length"); 994 } 995 996 for (int i = 0; i < sourceBands.length; i++) { 997 int sb = sourceBands[i]; 998 if (sb < 0 || sb >= numBands) { 999 throw new IllegalArgumentException( 1000 "Source band out of range!"); 1001 } 1002 int db = destinationBands[i]; 1003 if (db < 0 || db >= destNumBands) { 1004 throw new IllegalArgumentException( 1005 "Destination band out of range!"); 1006 } 1007 } 1008 } 1009 1010 public RenderedImage readAsRenderedImage(int imageIndex, 1011 ImageReadParam param) 1012 throws IOException { 1013 prepareRead(imageIndex, param); 1014 return new TIFFRenderedImage(this, imageIndex, imageReadParam, 1015 width, height); 1016 } 1017 1018 private void decodeTile(int ti, int tj, int band) throws IOException { 1019 if(DEBUG) { 1020 System.out.println("decodeTile("+ti+","+tj+","+band+")"); 1021 } 1022 1023 // Compute the region covered by the strip or tile 1024 Rectangle tileRect = new Rectangle(ti*tileOrStripWidth, 1025 tj*tileOrStripHeight, 1026 tileOrStripWidth, 1027 tileOrStripHeight); 1028 1029 // Clip against the image bounds if the image is not tiled. If it 1030 // is tiled, the tile may legally extend beyond the image bounds. 1031 if(!isImageTiled(currIndex)) { 1032 tileRect = 1033 tileRect.intersection(new Rectangle(0, 0, width, height)); 1034 } 1035 1036 // Return if the intersection is empty. 1037 if(tileRect.width <= 0 || tileRect.height <= 0) { 1038 return; 1039 } 1040 1041 int srcMinX = tileRect.x; 1042 int srcMinY = tileRect.y; 1043 int srcWidth = tileRect.width; 1044 int srcHeight = tileRect.height; 1045 1046 // Determine dest region that can be derived from the 1047 // source region 1048 1049 dstMinX = iceil(srcMinX - sourceXOffset, srcXSubsampling); 1050 int dstMaxX = ifloor(srcMinX + srcWidth - 1 - sourceXOffset, 1051 srcXSubsampling); 1052 1053 dstMinY = iceil(srcMinY - sourceYOffset, srcYSubsampling); 1054 int dstMaxY = ifloor(srcMinY + srcHeight - 1 - sourceYOffset, 1055 srcYSubsampling); 1056 1057 dstWidth = dstMaxX - dstMinX + 1; 1058 dstHeight = dstMaxY - dstMinY + 1; 1059 1060 dstMinX += dstXOffset; 1061 dstMinY += dstYOffset; 1062 1063 // Clip against image bounds 1064 1065 Rectangle dstRect = new Rectangle(dstMinX, dstMinY, 1066 dstWidth, dstHeight); 1067 dstRect = 1068 dstRect.intersection(theImage.getRaster().getBounds()); 1069 1070 dstMinX = dstRect.x; 1071 dstMinY = dstRect.y; 1072 dstWidth = dstRect.width; 1073 dstHeight = dstRect.height; 1074 1075 if (dstWidth <= 0 || dstHeight <= 0) { 1076 return; 1077 } 1078 1079 // Backwards map dest region to source to determine 1080 // active source region 1081 1082 int activeSrcMinX = (dstMinX - dstXOffset)*srcXSubsampling + 1083 sourceXOffset; 1084 int sxmax = 1085 (dstMinX + dstWidth - 1 - dstXOffset)*srcXSubsampling + 1086 sourceXOffset; 1087 int activeSrcWidth = sxmax - activeSrcMinX + 1; 1088 1089 int activeSrcMinY = (dstMinY - dstYOffset)*srcYSubsampling + 1090 sourceYOffset; 1091 int symax = 1092 (dstMinY + dstHeight - 1 - dstYOffset)*srcYSubsampling + 1093 sourceYOffset; 1094 int activeSrcHeight = symax - activeSrcMinY + 1; 1095 1096 decompressor.setSrcMinX(srcMinX); 1097 decompressor.setSrcMinY(srcMinY); 1098 decompressor.setSrcWidth(srcWidth); 1099 decompressor.setSrcHeight(srcHeight); 1100 1101 decompressor.setDstMinX(dstMinX); 1102 decompressor.setDstMinY(dstMinY); 1103 decompressor.setDstWidth(dstWidth); 1104 decompressor.setDstHeight(dstHeight); 1105 1106 decompressor.setActiveSrcMinX(activeSrcMinX); 1107 decompressor.setActiveSrcMinY(activeSrcMinY); 1108 decompressor.setActiveSrcWidth(activeSrcWidth); 1109 decompressor.setActiveSrcHeight(activeSrcHeight); 1110 1111 int tileIndex = tj*tilesAcross + ti; 1112 1113 if (planarConfiguration == 1114 BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) { 1115 tileIndex += band*tilesAcross*tilesDown; 1116 } 1117 1118 long offset = getTileOrStripOffset(tileIndex); 1119 long byteCount = getTileOrStripByteCount(tileIndex); 1120 1121 // 1122 // Attempt to handle truncated streams, i.e., where reading the 1123 // compressed strip or tile would result in an EOFException. The 1124 // number of bytes to read is clamped to the number available 1125 // from the stream starting at the indicated position in the hope 1126 // that the decompressor will handle it. 1127 // 1128 long streamLength = stream.length(); 1129 if(streamLength > 0 && offset + byteCount > streamLength) { 1130 processWarningOccurred("Attempting to process truncated stream."); 1131 if(Math.max(byteCount = streamLength - offset, 0) == 0) { 1132 processWarningOccurred("No bytes in strip/tile: skipping."); 1133 return; 1134 } 1135 } 1136 1137 decompressor.setStream(stream); 1138 decompressor.setOffset(offset); 1139 decompressor.setByteCount((int)byteCount); 1140 1141 decompressor.beginDecoding(); 1142 1143 stream.mark(); 1144 decompressor.decode(); 1145 stream.reset(); 1146 } 1147 1148 private void reportProgress() { 1149 // Report image progress/update to listeners after each tile 1150 pixelsRead += dstWidth*dstHeight; 1151 processImageProgress(100.0f*pixelsRead/pixelsToRead); 1152 processImageUpdate(theImage, 1153 dstMinX, dstMinY, dstWidth, dstHeight, 1154 1, 1, 1155 destinationBands); 1156 } 1157 1158 public BufferedImage read(int imageIndex, ImageReadParam param) 1159 throws IOException { 1160 prepareRead(imageIndex, param); 1161 this.theImage = getDestination(param, 1162 getImageTypes(imageIndex), 1163 width, height); 1164 1165 srcXSubsampling = imageReadParam.getSourceXSubsampling(); 1166 srcYSubsampling = imageReadParam.getSourceYSubsampling(); 1167 1168 Point p = imageReadParam.getDestinationOffset(); 1169 dstXOffset = p.x; 1170 dstYOffset = p.y; 1171 1172 // This could probably be made more efficient... 1173 Rectangle srcRegion = new Rectangle(0, 0, 0, 0); 1174 Rectangle destRegion = new Rectangle(0, 0, 0, 0); 1175 1176 computeRegions(imageReadParam, width, height, theImage, 1177 srcRegion, destRegion); 1178 1179 // Initial source pixel, taking source region and source 1180 // subsamplimg offsets into account 1181 sourceXOffset = srcRegion.x; 1182 sourceYOffset = srcRegion.y; 1183 1184 pixelsToRead = destRegion.width*destRegion.height; 1185 pixelsRead = 0; 1186 1187 processImageStarted(imageIndex); 1188 processImageProgress(0.0f); 1189 1190 tilesAcross = (width + tileOrStripWidth - 1)/tileOrStripWidth; 1191 tilesDown = (height + tileOrStripHeight - 1)/tileOrStripHeight; 1192 1193 int compression = getCompression(); 1194 1195 // Attempt to get decompressor and color converted from the read param 1196 1197 TIFFColorConverter colorConverter = null; 1198 if (imageReadParam instanceof TIFFImageReadParam) { 1199 TIFFImageReadParam tparam = 1200 (TIFFImageReadParam)imageReadParam; 1201 this.decompressor = tparam.getTIFFDecompressor(); 1202 colorConverter = tparam.getColorConverter(); 1203 } 1204 1205 // If we didn't find one, use a standard decompressor 1206 if (this.decompressor == null) { 1207 if (compression == 1208 BaselineTIFFTagSet.COMPRESSION_NONE) { 1209 // Get the fillOrder field. 1210 TIFFField fillOrderField = 1211 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER); 1212 1213 // Set the decompressor based on the fill order. 1214 if(fillOrderField != null && fillOrderField.getAsInt(0) == 2) { 1215 this.decompressor = new TIFFLSBDecompressor(); 1216 } else { 1217 this.decompressor = new TIFFNullDecompressor(); 1218 } 1219 } else if (compression == 1220 BaselineTIFFTagSet.COMPRESSION_CCITT_T_6) { 1221 1222 1223 // Fall back to the Java decompressor. 1224 if (this.decompressor == null) { 1225 if(DEBUG) { 1226 System.out.println("Using Java T.6 decompressor"); 1227 } 1228 this.decompressor = new TIFFFaxDecompressor(); 1229 } 1230 } else if (compression == 1231 BaselineTIFFTagSet.COMPRESSION_CCITT_T_4) { 1232 1233 // Fall back to the Java decompressor. 1234 if (this.decompressor == null) { 1235 if(DEBUG) { 1236 System.out.println("Using Java T.4 decompressor"); 1237 } 1238 this.decompressor = new TIFFFaxDecompressor(); 1239 } 1240 } else if (compression == 1241 BaselineTIFFTagSet.COMPRESSION_CCITT_RLE) { 1242 this.decompressor = new TIFFFaxDecompressor(); 1243 } else if (compression == 1244 BaselineTIFFTagSet.COMPRESSION_PACKBITS) { 1245 if(DEBUG) { 1246 System.out.println("Using TIFFPackBitsDecompressor"); 1247 } 1248 this.decompressor = new TIFFPackBitsDecompressor(); 1249 } else if (compression == 1250 BaselineTIFFTagSet.COMPRESSION_LZW) { 1251 if(DEBUG) { 1252 System.out.println("Using TIFFLZWDecompressor"); 1253 } 1254 TIFFField predictorField = 1255 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PREDICTOR); 1256 int predictor = ((predictorField == null) ? 1257 BaselineTIFFTagSet.PREDICTOR_NONE : 1258 predictorField.getAsInt(0)); 1259 this.decompressor = new TIFFLZWDecompressor(predictor); 1260 } else if (compression == 1261 BaselineTIFFTagSet.COMPRESSION_JPEG) { 1262 this.decompressor = new TIFFJPEGDecompressor(); 1263 } else if (compression == 1264 BaselineTIFFTagSet.COMPRESSION_ZLIB || 1265 compression == 1266 BaselineTIFFTagSet.COMPRESSION_DEFLATE) { 1267 TIFFField predictorField = 1268 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PREDICTOR); 1269 int predictor = ((predictorField == null) ? 1270 BaselineTIFFTagSet.PREDICTOR_NONE : 1271 predictorField.getAsInt(0)); 1272 this.decompressor = new TIFFDeflateDecompressor(predictor); 1273 } else if (compression == 1274 BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) { 1275 TIFFField JPEGProcField = 1276 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_PROC); 1277 if(JPEGProcField == null) { 1278 processWarningOccurred 1279 ("JPEGProc field missing; assuming baseline sequential JPEG process."); 1280 } else if(JPEGProcField.getAsInt(0) != 1281 BaselineTIFFTagSet.JPEG_PROC_BASELINE) { 1282 throw new IIOException 1283 ("Old-style JPEG supported for baseline sequential JPEG process only!"); 1284 } 1285 this.decompressor = new TIFFOldJPEGDecompressor(); 1286 //throw new IIOException("Old-style JPEG not supported!"); 1287 } else { 1288 throw new IIOException 1289 ("Unsupported compression type (tag number = "+ 1290 compression+")!"); 1291 } 1292 1293 if (photometricInterpretation == 1294 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR && 1295 compression != BaselineTIFFTagSet.COMPRESSION_JPEG && 1296 compression != BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) { 1297 boolean convertYCbCrToRGB = 1298 theImage.getColorModel().getColorSpace().getType() == 1299 ColorSpace.TYPE_RGB; 1300 TIFFDecompressor wrappedDecompressor = 1301 this.decompressor instanceof TIFFNullDecompressor ? 1302 null : this.decompressor; 1303 this.decompressor = 1304 new TIFFYCbCrDecompressor(wrappedDecompressor, 1305 convertYCbCrToRGB); 1306 } 1307 } 1308 1309 if(DEBUG) { 1310 System.out.println("\nDecompressor class = "+ 1311 decompressor.getClass().getName()+"\n"); 1312 } 1313 1314 if (colorConverter == null) { 1315 if (photometricInterpretation == 1316 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB && 1317 theImage.getColorModel().getColorSpace().getType() == 1318 ColorSpace.TYPE_RGB) { 1319 colorConverter = new TIFFCIELabColorConverter(); 1320 } else if (photometricInterpretation == 1321 BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR && 1322 !(this.decompressor instanceof TIFFYCbCrDecompressor) && 1323 compression != BaselineTIFFTagSet.COMPRESSION_JPEG && 1324 compression != BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) { 1325 colorConverter = new TIFFYCbCrColorConverter(imageMetadata); 1326 } 1327 } 1328 1329 decompressor.setReader(this); 1330 decompressor.setMetadata(imageMetadata); 1331 decompressor.setImage(theImage); 1332 1333 decompressor.setPhotometricInterpretation(photometricInterpretation); 1334 decompressor.setCompression(compression); 1335 decompressor.setSamplesPerPixel(samplesPerPixel); 1336 decompressor.setBitsPerSample(bitsPerSample); 1337 decompressor.setSampleFormat(sampleFormat); 1338 decompressor.setExtraSamples(extraSamples); 1339 decompressor.setColorMap(colorMap); 1340 1341 decompressor.setColorConverter(colorConverter); 1342 1343 decompressor.setSourceXOffset(sourceXOffset); 1344 decompressor.setSourceYOffset(sourceYOffset); 1345 decompressor.setSubsampleX(srcXSubsampling); 1346 decompressor.setSubsampleY(srcYSubsampling); 1347 1348 decompressor.setDstXOffset(dstXOffset); 1349 decompressor.setDstYOffset(dstYOffset); 1350 1351 decompressor.setSourceBands(sourceBands); 1352 decompressor.setDestinationBands(destinationBands); 1353 1354 // Compute bounds on the tile indices for this source region. 1355 int minTileX = 1356 TIFFImageWriter.XToTileX(srcRegion.x, 0, tileOrStripWidth); 1357 int minTileY = 1358 TIFFImageWriter.YToTileY(srcRegion.y, 0, tileOrStripHeight); 1359 int maxTileX = 1360 TIFFImageWriter.XToTileX(srcRegion.x + srcRegion.width - 1, 1361 0, tileOrStripWidth); 1362 int maxTileY = 1363 TIFFImageWriter.YToTileY(srcRegion.y + srcRegion.height - 1, 1364 0, tileOrStripHeight); 1365 1366 boolean isAbortRequested = false; 1367 if (planarConfiguration == 1368 BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) { 1369 1370 decompressor.setPlanar(true); 1371 1372 int[] sb = new int[1]; 1373 int[] db = new int[1]; 1374 for (int tj = minTileY; tj <= maxTileY; tj++) { 1375 for (int ti = minTileX; ti <= maxTileX; ti++) { 1376 for (int band = 0; band < numBands; band++) { 1377 sb[0] = sourceBands[band]; 1378 decompressor.setSourceBands(sb); 1379 db[0] = destinationBands[band]; 1380 decompressor.setDestinationBands(db); 1381 //XXX decompressor.beginDecoding(); 1382 1383 // The method abortRequested() is synchronized 1384 // so check it only once per loop just before 1385 // doing any actual decoding. 1386 if(abortRequested()) { 1387 isAbortRequested = true; 1388 break; 1389 } 1390 1391 decodeTile(ti, tj, band); 1392 } 1393 1394 if(isAbortRequested) break; 1395 1396 reportProgress(); 1397 } 1398 1399 if(isAbortRequested) break; 1400 } 1401 } else { 1402 //XXX decompressor.beginDecoding(); 1403 1404 for (int tj = minTileY; tj <= maxTileY; tj++) { 1405 for (int ti = minTileX; ti <= maxTileX; ti++) { 1406 // The method abortRequested() is synchronized 1407 // so check it only once per loop just before 1408 // doing any actual decoding. 1409 if(abortRequested()) { 1410 isAbortRequested = true; 1411 break; 1412 } 1413 1414 decodeTile(ti, tj, -1); 1415 1416 reportProgress(); 1417 } 1418 1419 if(isAbortRequested) break; 1420 } 1421 } 1422 1423 if (isAbortRequested) { 1424 processReadAborted(); 1425 } else { 1426 processImageComplete(); 1427 } 1428 1429 return theImage; 1430 } 1431 1432 public void reset() { 1433 super.reset(); 1434 resetLocal(); 1435 } 1436 1437 protected void resetLocal() { 1438 stream = null; 1439 gotHeader = false; 1440 imageReadParam = getDefaultReadParam(); 1441 streamMetadata = null; 1442 currIndex = -1; 1443 imageMetadata = null; 1444 imageStartPosition = new ArrayList(); 1445 numImages = -1; 1446 imageTypeMap = new HashMap(); 1447 width = -1; 1448 height = -1; 1449 numBands = -1; 1450 tileOrStripWidth = -1; 1451 tileOrStripHeight = -1; 1452 planarConfiguration = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY; 1453 rowsDone = 0; 1454 } 1455 1456 /** 1457 * Package scope method to allow decompressors, for example, to 1458 * emit warning messages. 1459 */ 1460 void forwardWarningMessage(String warning) { 1461 processWarningOccurred(warning); 1462 } 1463 1464 protected static BufferedImage getDestination(ImageReadParam param, 1465 Iterator imageTypes, 1466 int width, int height) 1467 throws IIOException { 1468 if (imageTypes == null || !imageTypes.hasNext()) { 1469 throw new IllegalArgumentException("imageTypes null or empty!"); 1470 } 1471 1472 BufferedImage dest = null; 1473 ImageTypeSpecifier imageType = null; 1474 1475 // If param is non-null, use it 1476 if (param != null) { 1477 // Try to get the image itself 1478 dest = param.getDestination(); 1479 if (dest != null) { 1480 return dest; 1481 } 1482 1483 // No image, get the image type 1484 imageType = param.getDestinationType(); 1485 } 1486 1487 // No info from param, use fallback image type 1488 if (imageType == null) { 1489 Object o = imageTypes.next(); 1490 if (!(o instanceof ImageTypeSpecifier)) { 1491 throw new IllegalArgumentException 1492 ("Non-ImageTypeSpecifier retrieved from imageTypes!"); 1493 } 1494 imageType = (ImageTypeSpecifier)o; 1495 } else { 1496 boolean foundIt = false; 1497 while (imageTypes.hasNext()) { 1498 ImageTypeSpecifier type = 1499 (ImageTypeSpecifier)imageTypes.next(); 1500 if (type.equals(imageType)) { 1501 foundIt = true; 1502 break; 1503 } 1504 } 1505 1506 if (!foundIt) { 1507 throw new IIOException 1508 ("Destination type from ImageReadParam does not match!"); 1509 } 1510 } 1511 1512 Rectangle srcRegion = new Rectangle(0,0,0,0); 1513 Rectangle destRegion = new Rectangle(0,0,0,0); 1514 computeRegions(param, 1515 width, 1516 height, 1517 null, 1518 srcRegion, 1519 destRegion); 1520 1521 int destWidth = destRegion.x + destRegion.width; 1522 int destHeight = destRegion.y + destRegion.height; 1523 // Create a new image based on the type specifier 1524 1525 if ((long)destWidth*destHeight > Integer.MAX_VALUE) { 1526 throw new IllegalArgumentException 1527 ("width*height > Integer.MAX_VALUE!"); 1528 } 1529 1530 return imageType.createBufferedImage(destWidth, destHeight); 1531 } 1532 1533 protected boolean isLsb() 1534 { 1535 boolean isLsb = false; 1536 if( null != imageMetadata ) { 1537 // Get the fillOrder field. 1538 TIFFField fillOrderField = 1539 imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER); 1540 1541 if(fillOrderField != null && fillOrderField.getAsInt(0) == 2) { 1542 isLsb = true ; 1543 } 1544 } 1545 return isLsb ; 1546 } 1547}