| 1 18 package org.apache.batik.ext.awt.image.codec.tiff; 19 20 import java.awt.Rectangle ; 21 import java.awt.Transparency ; 22 import java.awt.color.ColorSpace ; 23 import java.awt.image.ColorModel ; 24 import java.awt.image.ComponentColorModel ; 25 import java.awt.image.DataBuffer ; 26 import java.awt.image.DataBufferByte ; 27 import java.awt.image.DataBufferInt ; 28 import java.awt.image.DataBufferShort ; 29 import java.awt.image.DataBufferUShort ; 30 import java.awt.image.IndexColorModel ; 31 import java.awt.image.MultiPixelPackedSampleModel ; 32 import java.awt.image.PixelInterleavedSampleModel ; 33 import java.awt.image.Raster ; 34 import java.awt.image.SampleModel ; 35 import java.awt.image.WritableRaster ; 36 import java.io.ByteArrayInputStream ; 37 import java.io.IOException ; 38 import java.util.HashMap ; 39 import java.util.Map ; 40 import java.util.zip.DataFormatException ; 41 import java.util.zip.Inflater ; 42 43 import org.apache.batik.ext.awt.image.codec.SeekableStream; 44 import org.apache.batik.ext.awt.image.rendered.AbstractRed; 45 import org.apache.batik.ext.awt.image.rendered.CachableRed; 46 47 import com.sun.image.codec.jpeg.JPEGCodec; 48 import com.sun.image.codec.jpeg.JPEGDecodeParam; 49 import com.sun.image.codec.jpeg.JPEGImageDecoder; 50 51 public class TIFFImage extends AbstractRed { 52 53 public static final int COMP_NONE = 1; 55 public static final int COMP_FAX_G3_1D = 2; 56 public static final int COMP_FAX_G3_2D = 3; 57 public static final int COMP_FAX_G4_2D = 4; 58 public static final int COMP_LZW = 5; 59 public static final int COMP_JPEG_OLD = 6; 60 public static final int COMP_JPEG_TTN2 = 7; 61 public static final int COMP_PACKBITS = 32773; 62 public static final int COMP_DEFLATE = 32946; 63 64 private static final int TYPE_UNSUPPORTED = -1; 66 private static final int TYPE_BILEVEL = 0; 67 private static final int TYPE_GRAY_4BIT = 1; 68 private static final int TYPE_GRAY = 2; 69 private static final int TYPE_GRAY_ALPHA = 3; 70 private static final int TYPE_PALETTE = 4; 71 private static final int TYPE_RGB = 5; 72 private static final int TYPE_RGB_ALPHA = 6; 73 private static final int TYPE_YCBCR_SUB = 7; 74 private static final int TYPE_GENERIC = 8; 75 76 private static final int TIFF_JPEG_TABLES = 347; 78 private static final int TIFF_YCBCR_SUBSAMPLING = 530; 79 80 SeekableStream stream; 81 int tileSize; 82 int tilesX, tilesY; 83 long[] tileOffsets; 84 long[] tileByteCounts; 85 char[] colormap; 86 int sampleSize; 87 int compression; 88 byte[] palette; 89 int numBands; 90 91 int chromaSubH; 92 int chromaSubV; 93 94 long tiffT4Options; 96 long tiffT6Options; 97 int fillOrder; 98 99 int predictor; 101 102 JPEGDecodeParam decodeParam = null; 104 boolean colorConvertJPEG = false; 105 106 Inflater inflater = null; 108 109 boolean isBigEndian; 111 112 int imageType; 113 boolean isWhiteZero = false; 114 int dataType; 115 116 boolean decodePaletteAsShorts; 117 boolean tiled; 118 119 private TIFFFaxDecoder decoder = null; 121 private TIFFLZWDecoder lzwDecoder = null; 122 123 135 private static final Raster decodeJPEG(byte[] data, 136 JPEGDecodeParam decodeParam, 137 boolean colorConvert, 138 int minX, 139 int minY) { 140 ByteArrayInputStream jpegStream = new ByteArrayInputStream (data); 142 143 JPEGImageDecoder decoder = decodeParam == null ? 145 JPEGCodec.createJPEGDecoder(jpegStream) : 146 JPEGCodec.createJPEGDecoder(jpegStream, 147 decodeParam); 148 149 Raster jpegRaster; 151 try { 152 jpegRaster = colorConvert ? 153 decoder.decodeAsBufferedImage().getWritableTile(0, 0) : 154 decoder.decodeAsRaster(); 155 } catch (IOException ioe) { 156 throw new RuntimeException ("TIFFImage13"); 157 } 158 159 return jpegRaster.createTranslatedChild(minX, minY); 161 } 162 163 167 private final void inflate(byte[] deflated, byte[] inflated) { 168 inflater.setInput(deflated); 169 try { 170 inflater.inflate(inflated); 171 } catch(DataFormatException dfe) { 172 throw new RuntimeException ("TIFFImage17"+": "+ 173 dfe.getMessage()); 174 } 175 inflater.reset(); 176 } 177 178 private static SampleModel createPixelInterleavedSampleModel 179 (int dataType, int tileWidth, int tileHeight, int bands) { 180 int [] bandOffsets = new int[bands]; 181 for (int i=0; i<bands; i++) 182 bandOffsets[i] = i; 183 return new PixelInterleavedSampleModel  184 (dataType, tileWidth, tileHeight, bands, 185 tileWidth*bands, bandOffsets); 186 } 187 188 191 private final long[] getFieldAsLongs(TIFFField field) { 192 long[] value = null; 193 194 if(field.getType() == TIFFField.TIFF_SHORT) { 195 char[] charValue = field.getAsChars(); 196 value = new long[charValue.length]; 197 for(int i = 0; i < charValue.length; i++) { 198 value[i] = charValue[i] & 0xffff; 199 } 200 } else if(field.getType() == TIFFField.TIFF_LONG) { 201 value = field.getAsLongs(); 202 } else { 203 throw new RuntimeException (); 204 } 205 206 return value; 207 } 208 209 218 public TIFFImage(SeekableStream stream, 219 TIFFDecodeParam param, 220 int directory) 221 throws IOException { 222 223 this.stream = stream; 224 if (param == null) { 225 param = new TIFFDecodeParam(); 226 } 227 228 decodePaletteAsShorts = param.getDecodePaletteAsShorts(); 229 230 TIFFDirectory dir = param.getIFDOffset() == null ? 232 new TIFFDirectory(stream, directory) : 233 new TIFFDirectory(stream, param.getIFDOffset().longValue(), 234 directory); 235 236 TIFFField sfield = dir.getField(TIFFImageDecoder.TIFF_SAMPLES_PER_PIXEL); 238 int samplesPerPixel = sfield == null ? 1 : (int)sfield.getAsLong(0); 239 240 TIFFField planarConfigurationField = 242 dir.getField(TIFFImageDecoder.TIFF_PLANAR_CONFIGURATION); 243 char[] planarConfiguration = planarConfigurationField == null ? 244 new char[] {1} : 245 planarConfigurationField.getAsChars(); 246 247 if (planarConfiguration[0] != 1 && samplesPerPixel != 1) { 249 throw new RuntimeException ("TIFFImage0"); 250 } 251 252 TIFFField bitsField = 254 dir.getField(TIFFImageDecoder.TIFF_BITS_PER_SAMPLE); 255 char[] bitsPerSample = null; 256 if(bitsField != null) { 257 bitsPerSample = bitsField.getAsChars(); 258 } else { 259 bitsPerSample = new char[] {1}; 260 261 for (int i = 1; i < bitsPerSample.length; i++) { 263 if (bitsPerSample[i] != bitsPerSample[0]) { 264 throw new RuntimeException ("TIFFImage1"); 265 } 266 } 267 } 268 sampleSize = bitsPerSample[0]; 269 270 TIFFField sampleFormatField = 273 dir.getField(TIFFImageDecoder.TIFF_SAMPLE_FORMAT); 274 275 char[] sampleFormat = null; 276 if (sampleFormatField != null) { 277 sampleFormat = sampleFormatField.getAsChars(); 278 279 for (int l=1; l<sampleFormat.length; l++) { 281 if (sampleFormat[l] != sampleFormat[0]) { 282 throw new RuntimeException ("TIFFImage2"); 283 } 284 } 285 286 } else { 287 sampleFormat = new char[] {1}; 288 } 289 290 boolean isValidDataFormat = false; 292 switch(sampleSize) { 293 case 1: 294 case 4: 295 case 8: 296 if(sampleFormat[0] != 3) { 297 dataType = DataBuffer.TYPE_BYTE; 299 isValidDataFormat = true; 300 } 301 break; 302 case 16: 303 if(sampleFormat[0] != 3) { 304 dataType = sampleFormat[0] == 2 ? 305 DataBuffer.TYPE_SHORT : DataBuffer.TYPE_USHORT; 306 isValidDataFormat = true; 307 } 308 break; 309 case 32: 310 if (sampleFormat[0] == 3) 311 isValidDataFormat = false; 312 else { 313 dataType = DataBuffer.TYPE_INT; 314 isValidDataFormat = true; 315 } 316 break; 317 } 318 319 if(!isValidDataFormat) { 320 throw new RuntimeException ("TIFFImage3"); 321 } 322 323 TIFFField compField = dir.getField(TIFFImageDecoder.TIFF_COMPRESSION); 325 compression = compField == null ? COMP_NONE : compField.getAsInt(0); 326 327 int photometricType = (int)dir.getFieldAsLong( 329 TIFFImageDecoder.TIFF_PHOTOMETRIC_INTERPRETATION); 330 331 imageType = TYPE_UNSUPPORTED; 333 switch(photometricType) { 334 case 0: isWhiteZero = true; 336 case 1: if(sampleSize == 1 && samplesPerPixel == 1) { 338 imageType = TYPE_BILEVEL; 339 } else if(sampleSize == 4 && samplesPerPixel == 1) { 340 imageType = TYPE_GRAY_4BIT; 341 } else if(sampleSize % 8 == 0) { 342 if(samplesPerPixel == 1) { 343 imageType = TYPE_GRAY; 344 } else if(samplesPerPixel == 2) { 345 imageType = TYPE_GRAY_ALPHA; 346 } else { 347 imageType = TYPE_GENERIC; 348 } 349 } 350 break; 351 case 2: if(sampleSize % 8 == 0) { 353 if(samplesPerPixel == 3) { 354 imageType = TYPE_RGB; 355 } else if(samplesPerPixel == 4) { 356 imageType = TYPE_RGB_ALPHA; 357 } else { 358 imageType = TYPE_GENERIC; 359 } 360 } 361 break; 362 case 3: if(samplesPerPixel == 1 && 364 (sampleSize == 4 || sampleSize == 8 || sampleSize == 16)) { 365 imageType = TYPE_PALETTE; 366 } 367 break; 368 case 4: if(sampleSize == 1 && samplesPerPixel == 1) { 370 imageType = TYPE_BILEVEL; 371 } 372 break; 373 case 6: if(compression == COMP_JPEG_TTN2 && 375 sampleSize == 8 && samplesPerPixel == 3) { 376 colorConvertJPEG = param.getJPEGDecompressYCbCrToRGB(); 378 379 imageType = colorConvertJPEG ? TYPE_RGB : TYPE_GENERIC; 381 } else { 382 TIFFField chromaField = dir.getField(TIFF_YCBCR_SUBSAMPLING); 383 if(chromaField != null) { 384 chromaSubH = chromaField.getAsInt(0); 385 chromaSubV = chromaField.getAsInt(1); 386 } else { 387 chromaSubH = chromaSubV = 2; 388 } 389 390 if(chromaSubH*chromaSubV == 1) { 391 imageType = TYPE_GENERIC; 392 } else if(sampleSize == 8 && samplesPerPixel == 3) { 393 imageType = TYPE_YCBCR_SUB; 394 } 395 } 396 break; 397 default: if(sampleSize % 8 == 0) { 399 imageType = TYPE_GENERIC; 400 } 401 } 402 403 if(imageType == TYPE_UNSUPPORTED) { 405 throw new RuntimeException ("TIFFImage4"); 406 } 407 408 Rectangle bounds = new Rectangle  410 (0, 0, 411 (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_WIDTH), 412 (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_LENGTH)); 413 414 numBands = samplesPerPixel; 416 417 TIFFField efield = dir.getField(TIFFImageDecoder.TIFF_EXTRA_SAMPLES); 419 int extraSamples = efield == null ? 0 : (int)efield.getAsLong(0); 420 421 int tileWidth, tileHeight; 422 if (dir.getField(TIFFImageDecoder.TIFF_TILE_OFFSETS) != null) { 423 tiled = true; 424 tileWidth = 426 (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_WIDTH); 427 tileHeight = 428 (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_LENGTH); 429 tileOffsets = 430 (dir.getField(TIFFImageDecoder.TIFF_TILE_OFFSETS)).getAsLongs(); 431 tileByteCounts = 432 getFieldAsLongs(dir.getField(TIFFImageDecoder.TIFF_TILE_BYTE_COUNTS)); 433 434 } else { 435 tiled = false; 436 437 tileWidth = 443 dir.getField(TIFFImageDecoder.TIFF_TILE_WIDTH) != null ? 444 (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_WIDTH) : 445 bounds.width; 446 TIFFField field = 447 dir.getField(TIFFImageDecoder.TIFF_ROWS_PER_STRIP); 448 if (field == null) { 449 451 tileHeight = 452 dir.getField(TIFFImageDecoder.TIFF_TILE_LENGTH) != null ? 453 (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_LENGTH): 454 bounds.height; 455 } else { 456 long l = field.getAsLong(0); 457 long infinity = 1; 458 infinity = (infinity << 32) - 1; 459 if (l == infinity) { 460 tileHeight = bounds.height; 462 } else { 463 tileHeight = (int)l; 464 } 465 } 466 467 TIFFField tileOffsetsField = 468 dir.getField(TIFFImageDecoder.TIFF_STRIP_OFFSETS); 469 if (tileOffsetsField == null) { 470 throw new RuntimeException ("TIFFImage5"); 471 } else { 472 tileOffsets = getFieldAsLongs(tileOffsetsField); 473 } 474 475 TIFFField tileByteCountsField = 476 dir.getField(TIFFImageDecoder.TIFF_STRIP_BYTE_COUNTS); 477 if (tileByteCountsField == null) { 478 throw new RuntimeException ("TIFFImage6"); 479 } else { 480 tileByteCounts = getFieldAsLongs(tileByteCountsField); 481 } 482 } 483 484 tilesX = (bounds.width + tileWidth - 1)/tileWidth; 486 tilesY = (bounds.height + tileHeight - 1)/tileHeight; 487 tileSize = tileWidth * tileHeight * numBands; 488 489 isBigEndian = dir.isBigEndian(); 491 492 TIFFField fillOrderField = 493 dir.getField(TIFFImageDecoder.TIFF_FILL_ORDER); 494 if (fillOrderField != null) { 495 fillOrder = fillOrderField.getAsInt(0); 496 } else { 497 fillOrder = 1; 499 } 500 501 switch(compression) { 502 case COMP_NONE: 503 case COMP_PACKBITS: 504 break; 506 case COMP_DEFLATE: 507 inflater = new Inflater (); 508 break; 509 case COMP_FAX_G3_1D: 510 case COMP_FAX_G3_2D: 511 case COMP_FAX_G4_2D: 512 if(sampleSize != 1) { 513 throw new RuntimeException ("TIFFImage7"); 514 } 515 516 if (compression == 3) { 518 TIFFField t4OptionsField = 519 dir.getField(TIFFImageDecoder.TIFF_T4_OPTIONS); 520 if (t4OptionsField != null) { 521 tiffT4Options = t4OptionsField.getAsLong(0); 522 } else { 523 tiffT4Options = 0; 525 } 526 } 527 528 if (compression == 4) { 530 TIFFField t6OptionsField = 531 dir.getField(TIFFImageDecoder.TIFF_T6_OPTIONS); 532 if (t6OptionsField != null) { 533 tiffT6Options = t6OptionsField.getAsLong(0); 534 } else { 535 tiffT6Options = 0; 537 } 538 } 539 540 decoder = new TIFFFaxDecoder(fillOrder, 542 tileWidth, tileHeight); 543 break; 544 545 case COMP_LZW: 546 TIFFField predictorField = 548 dir.getField(TIFFImageDecoder.TIFF_PREDICTOR); 549 550 if (predictorField == null) { 551 predictor = 1; 552 } else { 553 predictor = predictorField.getAsInt(0); 554 555 if (predictor != 1 && predictor != 2) { 556 throw new RuntimeException ("TIFFImage8"); 557 } 558 559 if (predictor == 2 && sampleSize != 8) { 560 throw new RuntimeException (sampleSize + 561 "TIFFImage9"); 562 } 563 } 564 565 lzwDecoder = new TIFFLZWDecoder(tileWidth, predictor, 566 samplesPerPixel); 567 break; 568 569 case COMP_JPEG_OLD: 570 throw new RuntimeException ("TIFFImage15"); 571 572 case COMP_JPEG_TTN2: 573 if(!(sampleSize == 8 && 574 ((imageType == TYPE_GRAY && samplesPerPixel == 1) || 575 (imageType == TYPE_PALETTE && samplesPerPixel == 1) || 576 (imageType == TYPE_RGB && samplesPerPixel == 3)))) { 577 throw new RuntimeException ("TIFFImage16"); 578 } 579 580 if(dir.isTagPresent(TIFF_JPEG_TABLES)) { 582 TIFFField jpegTableField = dir.getField(TIFF_JPEG_TABLES); 583 byte[] jpegTable = jpegTableField.getAsBytes(); 584 ByteArrayInputStream tableStream = 585 new ByteArrayInputStream (jpegTable); 586 JPEGImageDecoder decoder = 587 JPEGCodec.createJPEGDecoder(tableStream); 588 decoder.decodeAsRaster(); 589 decodeParam = decoder.getJPEGDecodeParam(); 590 } 591 592 break; 593 default: 594 throw new RuntimeException ("TIFFImage10"); 595 } 596 597 ColorModel colorModel = null; 598 SampleModel sampleModel = null; 599 switch(imageType) { 600 case TYPE_BILEVEL: 601 case TYPE_GRAY_4BIT: 602 sampleModel = 603 new MultiPixelPackedSampleModel (dataType, 604 tileWidth, 605 tileHeight, 606 sampleSize); 607 if(imageType == TYPE_BILEVEL) { 608 byte[] map = new byte[] {(byte)(isWhiteZero ? 255 : 0), 609 (byte)(isWhiteZero ? 0 : 255)}; 610 colorModel = new IndexColorModel (1, 2, map, map, map); 611 } else { 612 byte [] map = new byte[16]; 613 if (isWhiteZero) { 614 for (int i=0; i<map.length; i++) 615 map[i] = (byte)(255-(16*i)); 616 } else { 617 for (int i=0; i<map.length; i++) 618 map[i] = (byte)(16*i); 619 } 620 colorModel = new IndexColorModel (4, 16, map, map, map); 621 } 622 break; 623 624 case TYPE_GRAY: 625 case TYPE_GRAY_ALPHA: 626 case TYPE_RGB: 627 case TYPE_RGB_ALPHA: 628 int[] reverseOffsets = new int[numBands]; 631 for (int i=0; i<numBands; i++) { 632 reverseOffsets[i] = numBands - 1 - i; 633 } 634 sampleModel = new PixelInterleavedSampleModel  635 (dataType, tileWidth, tileHeight, 636 numBands, numBands*tileWidth, reverseOffsets); 637 638 if(imageType == TYPE_GRAY) { 639 colorModel = new ComponentColorModel  640 (ColorSpace.getInstance(ColorSpace.CS_GRAY), 641 new int[] { sampleSize }, false, false, 642 Transparency.OPAQUE, dataType); 643 } else if (imageType == TYPE_RGB) { 644 colorModel = new ComponentColorModel  645 (ColorSpace.getInstance(ColorSpace.CS_sRGB), 646 new int[] { sampleSize, sampleSize, sampleSize }, 647 false, false, Transparency.OPAQUE, dataType); 648 } else { int transparency = Transparency.OPAQUE; 654 if(extraSamples == 1) { transparency = Transparency.TRANSLUCENT; 656 } else if(extraSamples == 2) { transparency = Transparency.BITMASK; 658 } 659 660 colorModel = 661 createAlphaComponentColorModel(dataType, 662 numBands, 663
|