Source code: org/apache/batik/ext/awt/image/codec/tiff/TIFFImage.java
1 /*
2
3 Copyright 2001,2003 The Apache Software Foundation
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16
17 */
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 // Compression types
54 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 // Image types
65 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 // Incidental tags
77 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 // Fax compression related variables
95 long tiffT4Options;
96 long tiffT6Options;
97 int fillOrder;
98
99 // LZW compression related variable
100 int predictor;
101
102 // TTN2 JPEG related variables
103 JPEGDecodeParam decodeParam = null;
104 boolean colorConvertJPEG = false;
105
106 // DEFLATE variables
107 Inflater inflater = null;
108
109 // Endian-ness indicator
110 boolean isBigEndian;
111
112 int imageType;
113 boolean isWhiteZero = false;
114 int dataType;
115
116 boolean decodePaletteAsShorts;
117 boolean tiled;
118
119 // Decoders
120 private TIFFFaxDecoder decoder = null;
121 private TIFFLZWDecoder lzwDecoder = null;
122
123 /**
124 * Decode a buffer of data into a Raster with the specified location.
125 *
126 * @param data buffer contain an interchange or abbreviated datastream.
127 * @param decodeParam decoding parameters; may be null unless the
128 * data buffer contains an abbreviated datastream in which case
129 * it may not be null or an error will occur.
130 * @param colorConvert whether to perform color conversion; in this
131 * case that would be limited to YCbCr-to-RGB.
132 * @param minX the X position of the returned Raster.
133 * @param minY the Y position of the returned Raster.
134 */
135 private static final Raster decodeJPEG(byte[] data,
136 JPEGDecodeParam decodeParam,
137 boolean colorConvert,
138 int minX,
139 int minY) {
140 // Create an InputStream from the compressed data array.
141 ByteArrayInputStream jpegStream = new ByteArrayInputStream(data);
142
143 // Create a decoder.
144 JPEGImageDecoder decoder = decodeParam == null ?
145 JPEGCodec.createJPEGDecoder(jpegStream) :
146 JPEGCodec.createJPEGDecoder(jpegStream,
147 decodeParam);
148
149 // Decode the compressed data into a Raster.
150 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 // Translate the decoded Raster to the specified location and return.
160 return jpegRaster.createTranslatedChild(minX, minY);
161 }
162
163 /**
164 * Inflates <code>deflated</code> into <code>inflated</code> using the
165 * <code>Inflater</code> constructed during class instantiation.
166 */
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 /**
189 * Return as a long[] the value of a TIFF_LONG or TIFF_SHORT field.
190 */
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 /**
210 * Constructs a TIFFImage that acquires its data from a given
211 * SeekableStream and reads from a particular IFD of the stream.
212 * The index of the first IFD is 0.
213 *
214 * @param stream the SeekableStream to read from.
215 * @param param an instance of TIFFDecodeParam, or null.
216 * @param directory the index of the IFD to read from.
217 */
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 // Read the specified directory.
231 TIFFDirectory dir = param.getIFDOffset() == null ?
232 new TIFFDirectory(stream, directory) :
233 new TIFFDirectory(stream, param.getIFDOffset().longValue(),
234 directory);
235
236 // Get the number of samples per pixel
237 TIFFField sfield = dir.getField(TIFFImageDecoder.TIFF_SAMPLES_PER_PIXEL);
238 int samplesPerPixel = sfield == null ? 1 : (int)sfield.getAsLong(0);
239
240 // Read the TIFF_PLANAR_CONFIGURATION field
241 TIFFField planarConfigurationField =
242 dir.getField(TIFFImageDecoder.TIFF_PLANAR_CONFIGURATION);
243 char[] planarConfiguration = planarConfigurationField == null ?
244 new char[] {1} :
245 planarConfigurationField.getAsChars();
246
247 // Support planar format (band sequential) only for 1 sample/pixel.
248 if (planarConfiguration[0] != 1 && samplesPerPixel != 1) {
249 throw new RuntimeException("TIFFImage0");
250 }
251
252 // Read the TIFF_BITS_PER_SAMPLE field
253 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 // Ensure that all samples have the same bit depth.
262 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 // Read the TIFF_SAMPLE_FORMAT tag to see whether the data might be
271 // signed or floating point
272 TIFFField sampleFormatField =
273 dir.getField(TIFFImageDecoder.TIFF_SAMPLE_FORMAT);
274
275 char[] sampleFormat = null;
276 if (sampleFormatField != null) {
277 sampleFormat = sampleFormatField.getAsChars();
278
279 // Check that all the samples have the same format
280 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 // Set the data type based on the sample size and format.
291 boolean isValidDataFormat = false;
292 switch(sampleSize) {
293 case 1:
294 case 4:
295 case 8:
296 if(sampleFormat[0] != 3) {
297 // Ignore whether signed or unsigned: treat all as unsigned.
298 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 // Figure out what compression if any, is being used.
324 TIFFField compField = dir.getField(TIFFImageDecoder.TIFF_COMPRESSION);
325 compression = compField == null ? COMP_NONE : compField.getAsInt(0);
326
327 // Get the photometric interpretation.
328 int photometricType = (int)dir.getFieldAsLong(
329 TIFFImageDecoder.TIFF_PHOTOMETRIC_INTERPRETATION);
330
331 // Determine which kind of image we are dealing with.
332 imageType = TYPE_UNSUPPORTED;
333 switch(photometricType) {
334 case 0: // WhiteIsZero
335 isWhiteZero = true;
336 case 1: // BlackIsZero
337 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: // RGB
352 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: // RGB Palette
363 if(samplesPerPixel == 1 &&
364 (sampleSize == 4 || sampleSize == 8 || sampleSize == 16)) {
365 imageType = TYPE_PALETTE;
366 }
367 break;
368 case 4: // Transparency mask
369 if(sampleSize == 1 && samplesPerPixel == 1) {
370 imageType = TYPE_BILEVEL;
371 }
372 break;
373 case 6: // YCbCr
374 if(compression == COMP_JPEG_TTN2 &&
375 sampleSize == 8 && samplesPerPixel == 3) {
376 // Set color conversion flag.
377 colorConvertJPEG = param.getJPEGDecompressYCbCrToRGB();
378
379 // Set type to RGB if color converting.
380 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: // Other including CMYK, CIE L*a*b*, unknown.
398 if(sampleSize % 8 == 0) {
399 imageType = TYPE_GENERIC;
400 }
401 }
402
403 // Bail out if not one of the supported types.
404 if(imageType == TYPE_UNSUPPORTED) {
405 throw new RuntimeException("TIFFImage4");
406 }
407
408 // Set basic image layout
409 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 // Set a preliminary band count. This may be changed later as needed.
415 numBands = samplesPerPixel;
416
417 // Figure out if any extra samples are present.
418 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 // Image is in tiled format
425 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 // Image is in stripped format, looks like tiles to us
438 // Note: Some legacy files may have tile width and height
439 // written but use the strip offsets and byte counts fields
440 // instead of the tile offsets and byte counts. Therefore
441 // we default here to the tile dimensions if they are written.
442 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 // Default is infinity (2^32 -1), basically the entire image
450
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 // 2^32 - 1 (effectively infinity, entire image is 1 strip)
461 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 // Calculate number of tiles and the tileSize in bytes
485 tilesX = (bounds.width + tileWidth - 1)/tileWidth;
486 tilesY = (bounds.height + tileHeight - 1)/tileHeight;
487 tileSize = tileWidth * tileHeight * numBands;
488
489 // Check whether big endian or little endian format is used.
490 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 // Default Fill Order
498 fillOrder = 1;
499 }
500
501 switch(compression) {
502 case COMP_NONE:
503 case COMP_PACKBITS:
504 // Do nothing.
505 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 // Fax T.4 compression options
517 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 // Use default value
524 tiffT4Options = 0;
525 }
526 }
527
528 // Fax T.6 compression options
529 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 // Use default value
536 tiffT6Options = 0;
537 }
538 }
539
540 // Fax encoding, need to create the Fax decoder.
541 decoder = new TIFFFaxDecoder(fillOrder,
542 tileWidth, tileHeight);
543 break;
544
545 case COMP_LZW:
546 // LZW compression used, need to create the LZW decoder.
547 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 // Create decodeParam from JPEGTables field if present.
581 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 // Create a pixel interleaved SampleModel with decreasing
629 // band offsets.
630 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 { // hasAlpha
649 // Transparency.OPAQUE signifies image data that is
650 // completely opaque, meaning that all pixels have an alpha
651 // value of 1.0. So the extra band gets ignored, which is
652 // what we want.
653 int transparency = Transparency.OPAQUE;
654 if(extraSamples == 1) { // associated (premultiplied) alpha
655 transparency = Transparency.TRANSLUCENT;
656 } else if(extraSamples == 2) { // unassociated alpha
657 transparency = Transparency.BITMASK;
658 }
659
660 colorModel =
661 createAlphaComponentColorModel(dataType,
662 numBands,
663 extraSamples == 1,
664 transparency);
665 }
666 break;
667
668 case TYPE_GENERIC:
669 case TYPE_YCBCR_SUB:
670 // For this case we can't display the image, so we create a
671 // SampleModel with increasing bandOffsets, and keep the
672 // ColorModel as null, as there is no appropriate ColorModel.
673
674 int[] bandOffsets = new int[numBands];
675 for (int i=0; i<numBands; i++) {
676 bandOffsets[i] = i;
677 }
678
679 sampleModel = new PixelInterleavedSampleModel
680 (dataType, tileWidth, tileHeight,
681 numBands, numBands * tileWidth, bandOffsets);
682 colorModel = null;
683 break;
684
685 case TYPE_PALETTE:
686 // Get the colormap
687 TIFFField cfield = dir.getField(TIFFImageDecoder.TIFF_COLORMAP);
688 if (cfield == null) {
689 throw new RuntimeException("TIFFImage11");
690 } else {
691 colormap = cfield.getAsChars();
692 }
693
694 // Could be either 1 or 3 bands depending on whether we use
695 // IndexColorModel or not.
696 if (decodePaletteAsShorts) {
697 numBands = 3;
698
699 // If no SampleFormat tag was specified and if the
700 // sampleSize is less than or equal to 8, then the
701 // dataType was initially set to byte, but now we want to
702 // expand the palette as shorts, so the dataType should
703 // be ushort.
704 if (dataType == DataBuffer.TYPE_BYTE) {
705 dataType = DataBuffer.TYPE_USHORT;
706 }
707
708 // Data will have to be unpacked into a 3 band short image
709 // as we do not have a IndexColorModel that can deal with
710 // a colormodel whose entries are of short data type.
711 sampleModel = createPixelInterleavedSampleModel
712 (dataType, tileWidth, tileHeight, numBands);
713
714 colorModel = new ComponentColorModel
715 (ColorSpace.getInstance(ColorSpace.CS_sRGB),
716 new int[] { 16, 16, 16 }, false, false,
717 Transparency.OPAQUE, dataType);
718
719 } else {
720
721 numBands = 1;
722
723 if (sampleSize == 4) {
724 // Pixel data will not be unpacked, will use
725 // MPPSM to store packed data and
726 // IndexColorModel to do the unpacking.
727 sampleModel = new MultiPixelPackedSampleModel
728 (DataBuffer.TYPE_BYTE, tileWidth, tileHeight,
729 sampleSize);
730 } else if (sampleSize == 8) {
731
732 sampleModel = createPixelInterleavedSampleModel
733 (DataBuffer.TYPE_BYTE, tileWidth, tileHeight,
734 numBands);
735 } else if (sampleSize == 16) {
736
737 // Here datatype has to be unsigned since we
738 // are storing indices into the
739 // IndexColorModel palette. Ofcourse the
740 // actual palette entries are allowed to be
741 // negative.
742 dataType = DataBuffer.TYPE_USHORT;
743 sampleModel = createPixelInterleavedSampleModel
744 (DataBuffer.TYPE_USHORT, tileWidth, tileHeight,
745 numBands);
746 }
747
748 int bandLength = colormap.length/3;
749 byte r[] = new byte[bandLength];
750 byte g[] = new byte[bandLength];
751 byte b[] = new byte[bandLength];
752
753 int gIndex = bandLength;
754 int bIndex = bandLength * 2;
755
756 if (dataType == DataBuffer.TYPE_SHORT) {
757
758 for (int i=0; i<bandLength; i++) {
759 r[i] = param.decodeSigned16BitsTo8Bits
760 ((short)colormap[i]);
761 g[i] = param.decodeSigned16BitsTo8Bits
762 ((short)colormap[gIndex+i]);
763 b[i] = param.decodeSigned16BitsTo8Bits
764 ((short)colormap[bIndex+i]);
765 }
766
767 } else {
768
769 for (int i=0; i<bandLength; i++) {
770 r[i] = param.decode16BitsTo8Bits
771 (colormap[i] & 0xffff);
772 g[i] = param.decode16BitsTo8Bits
773 (colormap[gIndex+i] & 0xffff);
774 b[i] = param.decode16BitsTo8Bits
775 (colormap[bIndex+i] & 0xffff);
776 }
777
778 }
779
780 colorModel = new IndexColorModel(sampleSize,
781 bandLength, r, g, b);
782 }
783 break;
784
785 default:
786 throw new RuntimeException("TIFFImage4");
787 }
788
789 Map properties = new HashMap();
790 // Set a property "tiff_directory".
791 properties.put("tiff_directory", dir);
792
793 // System.out.println("Constructed TIFF");
794
795 init((CachableRed)null, bounds, colorModel, sampleModel,
796 0, 0, properties);
797 }
798
799 /**
800 * Reads a private IFD from a given offset in the stream. This
801 * method may be used to obtain IFDs that are referenced
802 * only by private tag values.
803 */
804 public TIFFDirectory getPrivateIFD(long offset) throws IOException {
805 return new TIFFDirectory(stream, offset, 0);
806 }
807
808
809 public WritableRaster copyData(WritableRaster wr) {
810 copyToRaster(wr);
811 return wr;
812 }
813
814
815 /**
816 * Returns tile (tileX, tileY) as a Raster.
817 */
818 public synchronized Raster getTile(int tileX, int tileY) {
819 if ((tileX < 0) || (tileX >= tilesX) ||
820 (tileY < 0) || (tileY >= tilesY)) {
821 throw new IllegalArgumentException("TIFFImage12");
822 }
823
824 // System.out.println("Called TIFF getTile:" + tileX + "," + tileY);
825
826
827 // Get the data array out of the DataBuffer
828 byte bdata[] = null;
829 short sdata[] = null;
830 int idata[] = null;
831
832 SampleModel sampleModel = getSampleModel();
833 WritableRaster tile = makeTile(tileX,tileY);
834
835 DataBuffer buffer = tile.getDataBuffer();
836
837 int dataType = sampleModel.getDataType();
838 if (dataType == DataBuffer.TYPE_BYTE) {
839 bdata = ((DataBufferByte)buffer).getData();
840 } else if (dataType == DataBuffer.TYPE_USHORT) {
841 sdata = ((DataBufferUShort)buffer).getData();
842 } else if (dataType == DataBuffer.TYPE_SHORT) {
843 sdata = ((DataBufferShort)buffer).getData();
844 } else if (dataType == DataBuffer.TYPE_INT) {
845 idata = ((DataBufferInt)buffer).getData();
846 }
847
848 // Variables used for swapping when converting from RGB to BGR
849 byte bswap;
850 short sswap;
851 int iswap;
852
853 // Save original file pointer position and seek to tile data location.
854 long save_offset = 0;
855 try {
856 save_offset = stream.getFilePointer();
857 stream.seek(tileOffsets[tileY*tilesX + tileX]);
858 } catch (IOException ioe) {
859 throw new RuntimeException("TIFFImage13");
860 }
861
862 // Number of bytes in this tile (strip) after compression.
863 int byteCount = (int)tileByteCounts[tileY*tilesX + tileX];
864
865 // Find out the number of bytes in the current tile
866 Rectangle newRect;
867 if (!tiled)
868 newRect = tile.getBounds();
869 else
870 newRect = new Rectangle(tile.getMinX(), tile.getMinY(),
871 tileWidth, tileHeight);
872
873 int unitsInThisTile = newRect.width * newRect.height * numBands;
874
875 // Allocate read buffer if needed.
876 byte data[] = compression != COMP_NONE || imageType == TYPE_PALETTE ?
877 new byte[byteCount] : null;
878
879 // Read the data, uncompressing as needed. There are four cases:
880 // bilevel, palette-RGB, 4-bit grayscale, and everything else.
881 if(imageType == TYPE_BILEVEL) { // bilevel
882 try {
883 if (compression == COMP_PACKBITS) {
884 stream.readFully(data, 0, byteCount);
885
886 // Since the decompressed data will still be packed
887 // 8 pixels into 1 byte, calculate bytesInThisTile
888 int bytesInThisTile;
889 if ((newRect.width % 8) == 0) {
890 bytesInThisTile = (newRect.width/8) * newRect.height;
891 } else {
892 bytesInThisTile =
893 (newRect.width/8 + 1) * newRect.height;
894 }
895 decodePackbits(data, bytesInThisTile, bdata);
896 } else if (compression == COMP_LZW) {
897 stream.readFully(data, 0, byteCount);
898 lzwDecoder.decode(data, bdata, newRect.height);
899 } else if (compression == COMP_FAX_G3_1D) {
900 stream.readFully(data, 0, byteCount);
901 decoder.decode1D(bdata, data, 0, newRect.height);
902 } else if (compression == COMP_FAX_G3_2D) {
903 stream.readFully(data, 0, byteCount);
904 decoder.decode2D(bdata, data, 0, newRect.height,
905 tiffT4Options);
906 } else if (compression == COMP_FAX_G4_2D) {
907 stream.readFully(data, 0, byteCount);
908 decoder.decodeT6(bdata, data, 0, newRect.height,
909 tiffT6Options);
910 } else if (compression == COMP_DEFLATE) {
911 stream.readFully(data, 0, byteCount);
912 inflate(data, bdata);
913 } else if (compression == COMP_NONE) {
914 stream.readFully(bdata, 0, byteCount);
915 }
916
917 stream.seek(save_offset);
918 } catch (IOException ioe) {
919 throw new RuntimeException("TIFFImage13");
920 }
921 } else if(imageType == TYPE_PALETTE) { // palette-RGB
922 if (sampleSize == 16) {
923
924 if (decodePaletteAsShorts) {
925
926 short tempData[]= null;
927
928 // At this point the data is 1 banded and will
929 // become 3 banded only after we've done the palette
930 // lookup, since unitsInThisTile was calculated with
931 // 3 bands, we need to divide this by 3.
932 int unitsBeforeLookup = unitsInThisTile / 3;
933
934 // Since unitsBeforeLookup is the number of shorts,
935 // but we do our decompression in terms of bytes, we
936 // need to multiply it by 2 in order to figure out
937 // how many bytes we'll get after decompression.
938 int entries = unitsBeforeLookup * 2;
939
940 // Read the data, if compressed, decode it, reset the pointer
941 try {
942
943 if (compression == COMP_PACKBITS) {
944
945 stream.readFully(data, 0, byteCount);
946
947 byte byteArray[] = new byte[entries];
948 decodePackbits(data, entries, byteArray);
949 tempData = new short[unitsBeforeLookup];
950 interpretBytesAsShorts(byteArray, tempData,
951 unitsBeforeLookup);
952
953 } else if (compression == COMP_LZW) {
954
955 // Read in all the compressed data for this tile
956 stream.readFully(data, 0, byteCount);
957
958 byte byteArray[] = new byte[entries];
959 lzwDecoder.decode(data, byteArray, newRect.height);
960 tempData = new short[unitsBeforeLookup];
961 interpretBytesAsShorts(byteArray, tempData,
962 unitsBeforeLookup);
963
964 } else if (compression == COMP_DEFLATE) {
965
966 stream.readFully(data, 0, byteCount);
967 byte byteArray[] = new byte[entries];
968 inflate(data, byteArray);
969 tempData = new short[unitsBeforeLookup];
970 interpretBytesAsShorts(byteArray, tempData,
971 unitsBeforeLookup);
972
973 } else if (compression == COMP_NONE) {
974
975 // byteCount tells us how many bytes are there
976 // in this tile, but we need to read in shorts,
977 // which will take half the space, so while
978 // allocating we divide byteCount by 2.
979 tempData = new short[byteCount/2];
980 readShorts(byteCount/2, tempData);
981 }
982
983 stream.seek(save_offset);
984
985 } catch (IOException ioe) {
986 throw new RuntimeException("TIFFImage13");
987 }
988
989 if (dataType == DataBuffer.TYPE_USHORT) {
990
991 // Expand the palette image into an rgb image with ushort
992 // data type.
993 int cmapValue;
994 int count = 0, lookup, len = colormap.length/3;
995 int len2 = len * 2;
996 for (int i=0; i<unitsBeforeLookup; i++) {
997 // Get the index into the colormap
998 lookup = tempData[i] & 0xffff;
999 // Get the blue value
1000 cmapValue = colormap[lookup+len2];
1001 sdata[count++] = (short)(cmapValue & 0xffff);
1002 // Get the green value
1003 cmapValue = colormap[lookup+len];
1004 sdata[count++] = (short)(cmapValue & 0xffff);
1005 // Get the red value
1006 cmapValue = colormap[lookup];
1007 sdata[count++] = (short)(cmapValue & 0xffff);
1008 }
1009
1010 } else if (dataType == DataBuffer.TYPE_SHORT) {
1011
1012 // Expand the palette image into an rgb image with
1013 // short data type.
1014 int cmapValue;
1015 int count = 0, lookup, len = colormap.length/3;
1016 int len2 = len * 2;
1017 for (int i=0; i<unitsBeforeLookup; i++) {
1018 // Get the index into the colormap
1019 lookup = tempData[i] & 0xffff;
1020 // Get the blue value
1021 cmapValue = colormap[lookup+len2];
1022 sdata[count++] = (short)cmapValue;
1023 // Get the green value
1024 cmapValue = colormap[lookup+len];
1025 sdata[count++] = (short)cmapValue;
1026 // Get the red value
1027 cmapValue = colormap[lookup];
1028 sdata[count++] = (short)cmapValue;
1029 }
1030 }
1031
1032 } else {
1033
1034 // No lookup being done here, when RGB values are needed,
1035 // the associated IndexColorModel can be used to get them.
1036
1037 try {
1038
1039 if (compression == COMP_PACKBITS) {
1040
1041 stream.readFully(data, 0, byteCount);
1042
1043 // Since unitsInThisTile is the number of shorts,
1044 // but we do our decompression in terms of bytes, we
1045 // need to multiply unitsInThisTile by 2 in order to
1046 // figure out how many bytes we'll get after
1047 // decompression.
1048 int bytesInThisTile = unitsInThisTile * 2;
1049
1050 byte byteArray[] = new byte[bytesInThisTile];
1051 decodePackbits(data, bytesInThisTile, byteArray);
1052 interpretBytesAsShorts(byteArray, sdata,
1053 unitsInThisTile);
1054
1055 } else if (compression == COMP_LZW) {
1056
1057 stream.readFully(data, 0, byteCount);
1058
1059 // Since unitsInThisTile is the number of shorts,
1060 // but we do our decompression in terms of bytes, we
1061 // need to multiply unitsInThisTile by 2 in order to
1062 // figure out how many bytes we'll get after
1063 // decompression.
1064 byte byteArray[] = new byte[unitsInThisTile * 2];
1065 lzwDecoder.decode(data, byteArray, newRect.height);
1066 interpretBytesAsShorts(byteArray, sdata,
1067 unitsInThisTile);
1068
1069 } else if (compression == COMP_DEFLATE) {
1070
1071 stream.readFully(data, 0, byteCount);
1072 byte byteArray[] = new byte[unitsInThisTile * 2];
1073 inflate(data, byteArray);
1074 interpretBytesAsShorts(byteArray, sdata,
1075 unitsInThisTile);
1076
1077 } else if (compression == COMP_NONE) {
1078
1079 readShorts(byteCount/2, sdata);
1080 }
1081
1082 stream.seek(save_offset);
1083
1084 } catch (IOException ioe) {
1085 throw new RuntimeException("TIFFImage13");
1086 }
1087 }
1088
1089 } else if (sampleSize == 8) {
1090
1091 if (decodePaletteAsShorts) {
1092
1093 byte tempData[]= null;
1094
1095 // At this point the data is 1 banded and will
1096 // become 3 banded only after we've done the palette
1097 // lookup, since unitsInThisTile was calculated with
1098 // 3 bands, we need to divide this by 3.
1099 int unitsBeforeLookup = unitsInThisTile / 3;
1100
1101 // Read the data, if compressed, decode it, reset the pointer
1102 try {
1103
1104 if (compression == COMP_PACKBITS) {
1105
1106 stream.readFully(data, 0, byteCount);
1107 tempData = new byte[unitsBeforeLookup];
1108 decodePackbits(data, unitsBeforeLookup, tempData);
1109
1110 } else if (compression == COMP_LZW) {
1111
1112 stream.readFully(data, 0, byteCount);
1113 tempData = new byte[unitsBeforeLookup];
1114 lzwDecoder.decode(data, tempData, newRect.height);
1115
1116 } else if (compression == COMP_JPEG_TTN2) {
1117
1118 stream.readFully(data, 0, byteCount);
1119 Raster tempTile = decodeJPEG(data,
1120 decodeParam,
1121 colorConvertJPEG,
1122 tile.getMinX(),
1123 tile.getMinY());
1124 int[] tempPixels = new int[unitsBeforeLookup];
1125 tempTile.getPixels(tile.getMinX(),
1126 tile.getMinY(),
1127 tile.getWidth(),
1128 tile.getHeight(),
1129 tempPixels);
1130 tempData = new byte[unitsBeforeLookup];
1131 for(int i = 0; i < unitsBeforeLookup; i++) {
1132 tempData[i] = (byte)tempPixels[i];
1133 }
1134
1135 } else if (compression == COMP_DEFLATE) {
1136
1137 stream.readFully(data, 0, byteCount);
1138 tempData = new byte[unitsBeforeLookup];
1139 inflate(data, tempData);
1140
1141 } else if (compression == COMP_NONE) {
1142
1143 tempData = new byte[byteCount];
1144 stream.readFully(tempData, 0, byteCount);
1145 }
1146
1147 stream.seek(save_offset);
1148
1149 } catch (IOException ioe) {
1150 throw new RuntimeException("TIFFImage13");
1151 }
1152
1153 // Expand the palette image into an rgb image with ushort
1154 // data type.
1155 int cmapValue;
1156 int count = 0, lookup, len = colormap.length/3;
1157 int len2 = len * 2;
1158 for (int i=0; i<unitsBeforeLookup; i++) {
1159 // Get the index into the colormap
1160 lookup = tempData[i] & 0xff;
1161 // Get the blue value
1162 cmapValue = colormap[lookup+len2];
1163 sdata[count++] = (short)(cmapValue & 0xffff);
1164 // Get the green value
1165 cmapValue = colormap[lookup+len];
1166 sdata[count++] = (short)(cmapValue & 0xffff);
1167 // Get the red value
1168 cmapValue = colormap[lookup];
1169 sdata[count++] = (short)(cmapValue & 0xffff);
1170 }
1171 } else {
1172
1173 // No lookup being done here, when RGB values are needed,
1174 // the associated IndexColorModel can be used to get them.
1175
1176 try {
1177
1178 if (compression == COMP_PACKBITS) {
1179
1180 stream.readFully(data, 0, byteCount);
1181 decodePackbits(data, unitsInThisTile, bdata);
1182
1183 } else if (compression == COMP_LZW) {
1184
1185 stream.readFully(data, 0, byteCount);
1186 lzwDecoder.decode(data, bdata, newRect.height);
1187
1188 } else if (compression == COMP_JPEG_TTN2) {
1189
1190 stream.readFully(data, 0, byteCount);
1191 tile.setRect(decodeJPEG(data,
1192 decodeParam,
1193 colorConvertJPEG,
1194 tile.getMinX(),
1195 tile.getMinY()));
1196
1197 } else if (compression == COMP_DEFLATE) {
1198
1199 stream.readFully(data, 0, byteCount);
1200 inflate(data, bdata);
1201
1202 } else if (compression == COMP_NONE) {
1203
1204 stream.readFully(bdata, 0, byteCount);
1205 }
1206
1207 stream.seek(save_offset);
1208
1209 } catch (IOException ioe) {
1210 throw new RuntimeException("TIFFImage13");
1211 }
1212 }
1213
1214 } else if (sampleSize == 4) {
1215
1216 int padding = (newRect.width % 2 == 0) ? 0 : 1;
1217 int bytesPostDecoding = ((newRect.width/2 + padding) *
1218 newRect.height);
1219
1220 // Output short images
1221 if (decodePaletteAsShorts) {
1222
1223 byte tempData[] = null;
1224
1225 try {
1226 stream.readFully(data, 0, byteCount);
1227 stream.seek(save_offset);
1228 } catch (IOException ioe) {
1229 throw new