TIFFImage.java
上传用户:btjssb159
上传日期:2018-01-04
资源大小:241k
文件大小:68k
- /*
- * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * -Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * -Redistribution in binary form must reproduct the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * Neither the name of Sun Microsystems, Inc. or the names of contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * This software is provided "AS IS," without a warranty of any kind. ALL
- * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
- * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
- * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
- * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
- * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
- * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
- * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
- * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- *
- * You acknowledge that Software is not designed,licensed or intended for use in
- * the design, construction, operation or maintenance of any nuclear facility.
- */
- import java.awt.Point;
- import java.awt.Rectangle;
- import java.awt.Transparency;
- import java.awt.color.ColorSpace;
- import java.awt.image.ColorModel;
- import java.awt.image.DataBuffer;
- import java.awt.image.DataBufferByte;
- import java.awt.image.DataBufferShort;
- import java.awt.image.DataBufferUShort;
- import java.awt.image.DataBufferInt;
- import java.awt.image.Raster;
- import java.awt.image.WritableRaster;
- import java.awt.image.RenderedImage;
- import java.awt.image.SampleModel;
- import java.awt.image.IndexColorModel;
- import java.awt.image.MultiPixelPackedSampleModel;
- import java.awt.image.PixelInterleavedSampleModel;
- import java.awt.image.ComponentColorModel;
- import java.io.ByteArrayInputStream;
- import java.io.File;
- import java.io.InputStream;
- import java.io.IOException;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Method;
- import java.util.zip.DataFormatException;
- import java.util.zip.Inflater;
- import com.sun.image.codec.jpeg.JPEGCodec;
- import com.sun.image.codec.jpeg.JPEGDecodeParam;
- import com.sun.image.codec.jpeg.JPEGImageDecoder;
- public class TIFFImage extends SimpleRenderedImage {
- // Compression types
- public static final int COMP_NONE = 1;
- public static final int COMP_FAX_G3_1D = 2;
- public static final int COMP_FAX_G3_2D = 3;
- public static final int COMP_FAX_G4_2D = 4;
- public static final int COMP_LZW = 5;
- public static final int COMP_JPEG_OLD = 6;
- public static final int COMP_JPEG_TTN2 = 7;
- public static final int COMP_PACKBITS = 32773;
- public static final int COMP_DEFLATE = 32946;
- // Image types
- private static final int TYPE_UNSUPPORTED = -1;
- private static final int TYPE_BILEVEL = 0;
- private static final int TYPE_GRAY_4BIT = 1;
- private static final int TYPE_GRAY = 2;
- private static final int TYPE_GRAY_ALPHA = 3;
- private static final int TYPE_PALETTE = 4;
- private static final int TYPE_RGB = 5;
- private static final int TYPE_RGB_ALPHA = 6;
- private static final int TYPE_YCBCR_SUB = 7;
- private static final int TYPE_GENERIC = 8;
- // Incidental tags
- private static final int TIFF_JPEG_TABLES = 347;
- private static final int TIFF_YCBCR_SUBSAMPLING = 530;
- SeekableStream stream;
- int tileSize;
- int tilesX, tilesY;
- long[] tileOffsets;
- long[] tileByteCounts;
- char[] colormap;
- int sampleSize;
- int compression;
- byte[] palette;
- int numBands;
- int chromaSubH;
- int chromaSubV;
- // Fax compression related variables
- long tiffT4Options;
- long tiffT6Options;
- int fillOrder;
- // LZW compression related variable
- int predictor;
- // TTN2 JPEG related variables
- JPEGDecodeParam decodeParam = null;
- boolean colorConvertJPEG = false;
- // DEFLATE variables
- Inflater inflater = null;
- // Endian-ness indicator
- boolean isBigEndian;
-
- int imageType;
- boolean isWhiteZero = false;
- int dataType;
- boolean decodePaletteAsShorts;
- // Decoders
- private TIFFFaxDecoder decoder = null;
- private TIFFLZWDecoder lzwDecoder = null;
- /**
- * Decode a buffer of data into a Raster with the specified location.
- *
- * @param data buffer contain an interchange or abbreviated datastream.
- * @param decodeParam decoding parameters; may be null unless the
- * data buffer contains an abbreviated datastream in which case
- * it may not be null or an error will occur.
- * @param colorConvert whether to perform color conversion; in this
- * case that would be limited to YCbCr-to-RGB.
- * @param minX the X position of the returned Raster.
- * @param minY the Y position of the returned Raster.
- */
- private static final Raster decodeJPEG(byte[] data,
- JPEGDecodeParam decodeParam,
- boolean colorConvert,
- int minX,
- int minY) {
- // Create an InputStream from the compressed data array.
- ByteArrayInputStream jpegStream = new ByteArrayInputStream(data);
- // Create a decoder.
- JPEGImageDecoder decoder = decodeParam == null ?
- JPEGCodec.createJPEGDecoder(jpegStream) :
- JPEGCodec.createJPEGDecoder(jpegStream,
- decodeParam);
- // Decode the compressed data into a Raster.
- Raster jpegRaster;
- try {
- jpegRaster = colorConvert ?
- decoder.decodeAsBufferedImage().getWritableTile(0, 0) :
- decoder.decodeAsRaster();
- } catch (IOException ioe) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage13"));
- }
- // Translate the decoded Raster to the specified location and return.
- return jpegRaster.createTranslatedChild(minX, minY);
- }
- /**
- * Inflates <code>deflated</code> into <code>inflated</code> using the
- * <code>Inflater</code> constructed during class instantiation.
- */
- private final void inflate(byte[] deflated, byte[] inflated) {
- inflater.setInput(deflated);
- try {
- inflater.inflate(inflated);
- } catch(DataFormatException dfe) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage17")+": "+
- dfe.getMessage());
- }
- inflater.reset();
- }
- /**
- * Creates a pixel-interleaved <code>SampleModel</code>. This is a hack
- * to work around a cast exception when using JAI with float data.
- */
- private final static SampleModel
- createPixelInterleavedSampleModel(int dataType,
- int tileWidth,
- int tileHeight,
- int pixelStride,
- int scanlineStride,
- int bandOffsets[]) {
- SampleModel sampleModel = null;
- if(dataType == DataBuffer.TYPE_FLOAT) {
- // This is a hack to make this work with JAI which in some
- // cases downcasts the DataBuffer to a type-specific class.
- // In the case of float data this current means the JAI class
- // javax.media.jai.DataBufferFloat.
- try {
- Class rfClass =
- Class.forName("javax.media.jai.RasterFactory");
- Class[] paramTypes = new Class[] {int.class, int.class,
- int.class, int.class,
- int.class, int[].class};
- Method rfMthd =
- rfClass.getMethod("createPixelInterleavedSampleModel",
- paramTypes);
- Object[] params =
- new Object[] {new Integer(dataType),
- new Integer(tileWidth),
- new Integer(tileHeight),
- new Integer(pixelStride),
- new Integer(scanlineStride),
- bandOffsets};
- sampleModel = (SampleModel)rfMthd.invoke(null, params);
- } catch(Exception e) {
- // Deliberately ignore the Exception.
- }
- }
- // Create a SampleModel for non-float data or, in the case of
- // float data, if it is still null. This latter case should occur
- // if and only if the decoder is being used without JAI.
- if(dataType != DataBuffer.TYPE_FLOAT || sampleModel == null) {
- sampleModel =
- RasterFactory.createPixelInterleavedSampleModel(dataType,
- tileWidth,
- tileHeight,
- pixelStride,
- scanlineStride,
- bandOffsets);
- }
- return sampleModel;
- }
- /**
- * Return as a long[] the value of a TIFF_LONG or TIFF_SHORT field.
- */
- private final long[] getFieldAsLongs(TIFFField field) {
- long[] value = null;
- if(field.getType() == TIFFField.TIFF_SHORT) {
- char[] charValue = field.getAsChars();
- value = new long[charValue.length];
- for(int i = 0; i < charValue.length; i++) {
- value[i] = charValue[i] & 0xffff;
- }
- } else if(field.getType() == TIFFField.TIFF_LONG) {
- value = field.getAsLongs();
- } else {
- throw new RuntimeException();
- }
- return value;
- }
- /**
- * Constructs a TIFFImage that acquires its data from a given
- * SeekableStream and reads from a particular IFD of the stream.
- * The index of the first IFD is 0.
- *
- * @param stream the SeekableStream to read from.
- * @param param an instance of TIFFDecodeParam, or null.
- * @param directory the index of the IFD to read from.
- */
- public TIFFImage(SeekableStream stream,
- TIFFDecodeParam param,
- int directory)
- throws IOException {
- this.stream = stream;
- if (param == null) {
- param = new TIFFDecodeParam();
- }
- decodePaletteAsShorts = param.getDecodePaletteAsShorts();
- // Read the specified directory.
- TIFFDirectory dir = param.getIFDOffset() == null ?
- new TIFFDirectory(stream, directory) :
- new TIFFDirectory(stream, param.getIFDOffset().longValue(),
- directory);
- // Set a property "tiff_directory".
- properties.put("tiff_directory", dir);
- // Get the number of samples per pixel
- TIFFField sfield = dir.getField(TIFFImageDecoder.TIFF_SAMPLES_PER_PIXEL);
- int samplesPerPixel = sfield == null ? 1 : (int)sfield.getAsLong(0);
- // Read the TIFF_PLANAR_CONFIGURATION field
- TIFFField planarConfigurationField =
- dir.getField(TIFFImageDecoder.TIFF_PLANAR_CONFIGURATION);
- char[] planarConfiguration = planarConfigurationField == null ?
- new char[] {1} :
- planarConfigurationField.getAsChars();
- // Support planar format (band sequential) only for 1 sample/pixel.
- if (planarConfiguration[0] != 1 && samplesPerPixel != 1) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage0"));
- }
- // Read the TIFF_BITS_PER_SAMPLE field
- TIFFField bitsField =
- dir.getField(TIFFImageDecoder.TIFF_BITS_PER_SAMPLE);
- char[] bitsPerSample = null;
- if(bitsField != null) {
- bitsPerSample = bitsField.getAsChars();
- } else {
- bitsPerSample = new char[] {1};
- // Ensure that all samples have the same bit depth.
- for (int i = 1; i < bitsPerSample.length; i++) {
- if (bitsPerSample[i] != bitsPerSample[0]) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage1"));
- }
- }
- }
- sampleSize = (int)bitsPerSample[0];
- // Read the TIFF_SAMPLE_FORMAT tag to see whether the data might be
- // signed or floating point
- TIFFField sampleFormatField =
- dir.getField(TIFFImageDecoder.TIFF_SAMPLE_FORMAT);
- char[] sampleFormat = null;
- if (sampleFormatField != null) {
- sampleFormat = sampleFormatField.getAsChars();
- // Check that all the samples have the same format
- for (int l=1; l<sampleFormat.length; l++) {
- if (sampleFormat[l] != sampleFormat[0]) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage2"));
- }
- }
- } else {
- sampleFormat = new char[] {1};
- }
- // Set the data type based on the sample size and format.
- boolean isValidDataFormat = false;
- switch(sampleSize) {
- case 1:
- case 4:
- case 8:
- if(sampleFormat[0] != 3) {
- // Ignore whether signed or unsigned: treat all as unsigned.
- dataType = DataBuffer.TYPE_BYTE;
- isValidDataFormat = true;
- }
- break;
- case 16:
- if(sampleFormat[0] != 3) {
- dataType = sampleFormat[0] == 2 ?
- DataBuffer.TYPE_SHORT : DataBuffer.TYPE_USHORT;
- isValidDataFormat = true;
- }
- break;
- case 32:
- dataType = sampleFormat[0] == 3 ?
- DataBuffer.TYPE_FLOAT : DataBuffer.TYPE_INT;
- isValidDataFormat = true;
- break;
- }
- if(!isValidDataFormat) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage3"));
- }
- // Figure out what compression if any, is being used.
- TIFFField compField = dir.getField(TIFFImageDecoder.TIFF_COMPRESSION);
- compression = compField == null ? COMP_NONE : compField.getAsInt(0);
- // Get the photometric interpretation.
- int photometricType = (int)dir.getFieldAsLong(
- TIFFImageDecoder.TIFF_PHOTOMETRIC_INTERPRETATION);
- // Determine which kind of image we are dealing with.
- imageType = TYPE_UNSUPPORTED;
- switch(photometricType) {
- case 0: // WhiteIsZero
- isWhiteZero = true;
- case 1: // BlackIsZero
- if(sampleSize == 1 && samplesPerPixel == 1) {
- imageType = TYPE_BILEVEL;
- } else if(sampleSize == 4 && samplesPerPixel == 1) {
- imageType = TYPE_GRAY_4BIT;
- } else if(sampleSize % 8 == 0) {
- if(samplesPerPixel == 1) {
- imageType = TYPE_GRAY;
- } else if(samplesPerPixel == 2) {
- imageType = TYPE_GRAY_ALPHA;
- } else {
- imageType = TYPE_GENERIC;
- }
- }
- break;
- case 2: // RGB
- if(sampleSize % 8 == 0) {
- if(samplesPerPixel == 3) {
- imageType = TYPE_RGB;
- } else if(samplesPerPixel == 4) {
- imageType = TYPE_RGB_ALPHA;
- } else {
- imageType = TYPE_GENERIC;
- }
- }
- break;
- case 3: // RGB Palette
- if(samplesPerPixel == 1 &&
- (sampleSize == 4 || sampleSize == 8 || sampleSize == 16)) {
- imageType = TYPE_PALETTE;
- }
- break;
- case 4: // Transparency mask
- if(sampleSize == 1 && samplesPerPixel == 1) {
- imageType = TYPE_BILEVEL;
- }
- break;
- case 6: // YCbCr
- if(compression == COMP_JPEG_TTN2 &&
- sampleSize == 8 && samplesPerPixel == 3) {
- // Set color conversion flag.
- colorConvertJPEG = param.getJPEGDecompressYCbCrToRGB();
- // Set type to RGB if color converting.
- imageType = colorConvertJPEG ? TYPE_RGB : TYPE_GENERIC;
- } else {
- TIFFField chromaField = dir.getField(TIFF_YCBCR_SUBSAMPLING);
- if(chromaField != null) {
- chromaSubH = chromaField.getAsInt(0);
- chromaSubV = chromaField.getAsInt(1);
- } else {
- chromaSubH = chromaSubV = 2;
- }
- if(chromaSubH*chromaSubV == 1) {
- imageType = TYPE_GENERIC;
- } else if(sampleSize == 8 && samplesPerPixel == 3) {
- imageType = TYPE_YCBCR_SUB;
- }
- }
- break;
- default: // Other including CMYK, CIE L*a*b*, unknown.
- if(sampleSize % 8 == 0) {
- imageType = TYPE_GENERIC;
- }
- }
- // Bail out if not one of the supported types.
- if(imageType == TYPE_UNSUPPORTED) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage4"));
- }
- // Set basic image layout
- minX = minY = 0;
- width = (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_WIDTH);
- height = (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_LENGTH);
- // Set a preliminary band count. This may be changed later as needed.
- numBands = samplesPerPixel;
- // Figure out if any extra samples are present.
- TIFFField efield = dir.getField(TIFFImageDecoder.TIFF_EXTRA_SAMPLES);
- int extraSamples = efield == null ? 0 : (int)efield.getAsLong(0);
- if (dir.getField(TIFFImageDecoder.TIFF_TILE_OFFSETS) != null) {
- // Image is in tiled format
- tileWidth =
- (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_WIDTH);
- tileHeight =
- (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_LENGTH);
- tileOffsets =
- (dir.getField(TIFFImageDecoder.TIFF_TILE_OFFSETS)).getAsLongs();
- tileByteCounts =
- getFieldAsLongs(dir.getField(TIFFImageDecoder.TIFF_TILE_BYTE_COUNTS));
- } else {
- // Image is in stripped format, looks like tiles to us
- // Note: Some legacy files may have tile width and height
- // written but use the strip offsets and byte counts fields
- // instead of the tile offsets and byte counts. Therefore
- // we default here to the tile dimensions if they are written.
- tileWidth =
- dir.getField(TIFFImageDecoder.TIFF_TILE_WIDTH) != null ?
- (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_WIDTH) :
- width;
- TIFFField field =
- dir.getField(TIFFImageDecoder.TIFF_ROWS_PER_STRIP);
- if (field == null) {
- // Default is infinity (2^32 -1), basically the entire image
-
- tileHeight =
- dir.getField(TIFFImageDecoder.TIFF_TILE_LENGTH) != null ?
- (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_TILE_LENGTH):
- height;
- } else {
- long l = field.getAsLong(0);
- long infinity = 1;
- infinity = (infinity << 32) - 1;
- if (l == infinity) {
- // 2^32 - 1 (effectively infinity, entire image is 1 strip)
- tileHeight = height;
- } else {
- tileHeight = (int)l;
- }
- }
- TIFFField tileOffsetsField =
- dir.getField(TIFFImageDecoder.TIFF_STRIP_OFFSETS);
- if (tileOffsetsField == null) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage5"));
- } else {
- tileOffsets = getFieldAsLongs(tileOffsetsField);
- }
- TIFFField tileByteCountsField =
- dir.getField(TIFFImageDecoder.TIFF_STRIP_BYTE_COUNTS);
- if (tileByteCountsField == null) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage6"));
- } else {
- tileByteCounts = getFieldAsLongs(tileByteCountsField);
- }
- }
- // Calculate number of tiles and the tileSize in bytes
- tilesX = (width + tileWidth - 1)/tileWidth;
- tilesY = (height + tileHeight - 1)/tileHeight;
- tileSize = tileWidth * tileHeight * numBands;
- // Check whether big endian or little endian format is used.
- isBigEndian = dir.isBigEndian();
- TIFFField fillOrderField =
- dir.getField(TIFFImageDecoder.TIFF_FILL_ORDER);
- if (fillOrderField != null) {
- fillOrder = fillOrderField.getAsInt(0);
- } else {
- // Default Fill Order
- fillOrder = 1;
- }
- switch(compression) {
- case COMP_NONE:
- case COMP_PACKBITS:
- // Do nothing.
- break;
- case COMP_DEFLATE:
- inflater = new Inflater();
- break;
- case COMP_FAX_G3_1D:
- case COMP_FAX_G3_2D:
- case COMP_FAX_G4_2D:
- if(sampleSize != 1) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage7"));
- }
- // Fax T.4 compression options
- if (compression == 3) {
- TIFFField t4OptionsField =
- dir.getField(TIFFImageDecoder.TIFF_T4_OPTIONS);
- if (t4OptionsField != null) {
- tiffT4Options = t4OptionsField.getAsLong(0);
- } else {
- // Use default value
- tiffT4Options = 0;
- }
- }
- // Fax T.6 compression options
- if (compression == 4) {
- TIFFField t6OptionsField =
- dir.getField(TIFFImageDecoder.TIFF_T6_OPTIONS);
- if (t6OptionsField != null) {
- tiffT6Options = t6OptionsField.getAsLong(0);
- } else {
- // Use default value
- tiffT6Options = 0;
- }
- }
- // Fax encoding, need to create the Fax decoder.
- decoder = new TIFFFaxDecoder(fillOrder,
- tileWidth, tileHeight);
- break;
- case COMP_LZW:
- // LZW compression used, need to create the LZW decoder.
- TIFFField predictorField =
- dir.getField(TIFFImageDecoder.TIFF_PREDICTOR);
- if (predictorField == null) {
- predictor = 1;
- } else {
- predictor = predictorField.getAsInt(0);
-
- if (predictor != 1 && predictor != 2) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage8"));
- }
-
- if (predictor == 2 && sampleSize != 8) {
- throw new RuntimeException(sampleSize +
- JaiI18N.getString("TIFFImage9"));
- }
- }
-
- lzwDecoder = new TIFFLZWDecoder(tileWidth, predictor,
- samplesPerPixel);
- break;
- case COMP_JPEG_OLD:
- throw new RuntimeException(JaiI18N.getString("TIFFImage15"));
- case COMP_JPEG_TTN2:
- if(!(sampleSize == 8 &&
- ((imageType == TYPE_GRAY && samplesPerPixel == 1) ||
- (imageType == TYPE_PALETTE && samplesPerPixel == 1) ||
- (imageType == TYPE_RGB && samplesPerPixel == 3)))) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage16"));
- }
- // Create decodeParam from JPEGTables field if present.
- if(dir.isTagPresent(TIFF_JPEG_TABLES)) {
- TIFFField jpegTableField = dir.getField(TIFF_JPEG_TABLES);
- byte[] jpegTable = jpegTableField.getAsBytes();
- ByteArrayInputStream tableStream =
- new ByteArrayInputStream(jpegTable);
- JPEGImageDecoder decoder =
- JPEGCodec.createJPEGDecoder(tableStream);
- decoder.decodeAsRaster();
- decodeParam = decoder.getJPEGDecodeParam();
- }
- break;
- default:
- throw new RuntimeException(JaiI18N.getString("TIFFImage10"));
- }
- switch(imageType) {
- case TYPE_BILEVEL:
- case TYPE_GRAY_4BIT:
- sampleModel =
- new MultiPixelPackedSampleModel(dataType,
- tileWidth,
- tileHeight,
- sampleSize);
- if(imageType == TYPE_BILEVEL) {
- byte[] map = new byte[] {(byte)(isWhiteZero ? 255 : 0),
- (byte)(isWhiteZero ? 0 : 255)};
- colorModel = new IndexColorModel(1, 2, map, map, map);
- } else {
- colorModel =
- ImageCodec.createGrayIndexColorModel(sampleModel,
- !isWhiteZero);
- }
- break;
- case TYPE_GRAY:
- case TYPE_GRAY_ALPHA:
- case TYPE_RGB:
- case TYPE_RGB_ALPHA:
- // Create a pixel interleaved SampleModel with decreasing
- // band offsets.
- int[] reverseOffsets = new int[numBands];
- for (int i=0; i<numBands; i++) {
- reverseOffsets[i] = numBands - 1 - i;
- }
- sampleModel = createPixelInterleavedSampleModel(dataType,
- tileWidth,
- tileHeight,
- numBands,
- numBands*tileWidth,
- reverseOffsets);
- if(imageType == TYPE_GRAY || imageType == TYPE_RGB) {
- colorModel =
- ImageCodec.createComponentColorModel(sampleModel);
- } else { // hasAlpha
- // Transparency.OPAQUE signifies image data that is
- // completely opaque, meaning that all pixels have an alpha
- // value of 1.0. So the extra band gets ignored, which is
- // what we want.
- int transparency = Transparency.OPAQUE;
- if(extraSamples == 1) { // associated (premultiplied) alpha
- transparency = Transparency.TRANSLUCENT;
- } else if(extraSamples == 2) { // unassociated alpha
- transparency = Transparency.BITMASK;
- }
- colorModel =
- createAlphaComponentColorModel(dataType,
- numBands,
- extraSamples == 1,
- transparency);
- }
- break;
- case TYPE_GENERIC:
- case TYPE_YCBCR_SUB:
- // For this case we can't display the image, so we create a
- // SampleModel with increasing bandOffsets, and keep the
- // ColorModel as null, as there is no appropriate ColorModel.
- int[] bandOffsets = new int[numBands];
- for (int i=0; i<numBands; i++) {
- bandOffsets[i] = i;
- }
-
- sampleModel =
- createPixelInterleavedSampleModel(dataType,
- tileWidth, tileHeight,
- numBands, numBands * tileWidth,
- bandOffsets);
- colorModel = null;
- break;
- case TYPE_PALETTE:
- // Get the colormap
- TIFFField cfield = dir.getField(TIFFImageDecoder.TIFF_COLORMAP);
- if (cfield == null) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage11"));
- } else {
- colormap = cfield.getAsChars();
- }
- // Could be either 1 or 3 bands depending on whether we use
- // IndexColorModel or not.
- if (decodePaletteAsShorts) {
- numBands = 3;
- // If no SampleFormat tag was specified and if the
- // sampleSize is less than or equal to 8, then the
- // dataType was initially set to byte, but now we want to
- // expand the palette as shorts, so the dataType should
- // be ushort.
- if (dataType == DataBuffer.TYPE_BYTE) {
- dataType = DataBuffer.TYPE_USHORT;
- }
- // Data will have to be unpacked into a 3 band short image
- // as we do not have a IndexColorModel that can deal with
- // a colormodel whose entries are of short data type.
- sampleModel =
- RasterFactory.createPixelInterleavedSampleModel(dataType,
- tileWidth,
- tileHeight,
- numBands);
- colorModel = ImageCodec.createComponentColorModel(sampleModel);
- } else {
- numBands = 1;
-
- if (sampleSize == 4) {
- // Pixel data will not be unpacked, will use MPPSM to store
- // packed data and IndexColorModel to do the unpacking.
- sampleModel =
- new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
- tileWidth,
- tileHeight,
- sampleSize);
- } else if (sampleSize == 8) {
- sampleModel =
- RasterFactory.createPixelInterleavedSampleModel(
- DataBuffer.TYPE_BYTE,
- tileWidth,
- tileHeight,
- numBands);
- } else if (sampleSize == 16) {
- // Here datatype has to be unsigned since we are storing
- // indices into the IndexColorModel palette. Ofcourse
- // the actual palette entries are allowed to be negative.
- dataType = DataBuffer.TYPE_USHORT;
- sampleModel =
- RasterFactory.createPixelInterleavedSampleModel(
- DataBuffer.TYPE_USHORT,
- tileWidth,
- tileHeight,
- numBands);
- }
-
- int bandLength = colormap.length/3;
- byte r[] = new byte[bandLength];
- byte g[] = new byte[bandLength];
- byte b[] = new byte[bandLength];
-
- int gIndex = bandLength;
- int bIndex = bandLength * 2;
- if (dataType == DataBuffer.TYPE_SHORT) {
- for (int i=0; i<bandLength; i++) {
- r[i] = param.decodeSigned16BitsTo8Bits(
- (short)colormap[i]);
- g[i] = param.decodeSigned16BitsTo8Bits(
- (short)colormap[gIndex+i]);
- b[i] = param.decodeSigned16BitsTo8Bits(
- (short)colormap[bIndex+i]);
- }
- } else {
- for (int i=0; i<bandLength; i++) {
- r[i] = param.decode16BitsTo8Bits(colormap[i] & 0xffff);
- g[i] = param.decode16BitsTo8Bits(colormap[gIndex+i] &
- 0xffff);
- b[i] = param.decode16BitsTo8Bits(colormap[bIndex+i] &
- 0xffff);
- }
-
- }
- colorModel = new IndexColorModel(sampleSize,
- bandLength, r, g, b);
- }
- break;
- default:
- throw new RuntimeException("TIFFImage4");
- }
- }
- /**
- * Reads a private IFD from a given offset in the stream. This
- * method may be used to obtain IFDs that are referenced
- * only by private tag values.
- */
- public TIFFDirectory getPrivateIFD(long offset) throws IOException {
- return new TIFFDirectory(stream, offset, 0);
- }
- /**
- * Returns tile (tileX, tileY) as a Raster.
- */
- public synchronized Raster getTile(int tileX, int tileY) {
- if ((tileX < 0) || (tileX >= tilesX) ||
- (tileY < 0) || (tileY >= tilesY)) {
- throw new IllegalArgumentException(JaiI18N.getString("TIFFImage12"));
- }
- // Get the data array out of the DataBuffer
- byte bdata[] = null;
- short sdata[] = null;
- int idata[] = null;
- float fdata[] = null;
- DataBuffer buffer = sampleModel.createDataBuffer();
- int dataType = sampleModel.getDataType();
- if (dataType == DataBuffer.TYPE_BYTE) {
- bdata = ((DataBufferByte)buffer).getData();
- } else if (dataType == DataBuffer.TYPE_USHORT) {
- sdata = ((DataBufferUShort)buffer).getData();
- } else if (dataType == DataBuffer.TYPE_SHORT) {
- sdata = ((DataBufferShort)buffer).getData();
- } else if (dataType == DataBuffer.TYPE_INT) {
- idata = ((DataBufferInt)buffer).getData();
- } else if (dataType == DataBuffer.TYPE_FLOAT) {
- if(buffer instanceof DataBufferFloat) {
- fdata = ((DataBufferFloat)buffer).getData();
- } else {
- // This is a hack to make this work with JAI which in some
- // cases downcasts the DataBuffer to a type-specific class.
- // In the case of float data this current means the JAI class
- // javax.media.jai.DataBufferFloat.
- try {
- Method getDataMethod =
- buffer.getClass().getMethod("getData", null);
- fdata = (float[])getDataMethod.invoke(buffer, null);
- } catch(Exception e) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage18"));
- }
- }
- }
- WritableRaster tile =
- (WritableRaster)RasterFactory.createWritableRaster(sampleModel,
- buffer,
- new Point(tileXToX(tileX),
- tileYToY(tileY)));
- // Variables used for swapping when converting from RGB to BGR
- byte bswap;
- short sswap;
- int iswap;
- float fswap;
- // Save original file pointer position and seek to tile data location.
- long save_offset = 0;
- try {
- save_offset = stream.getFilePointer();
- stream.seek(tileOffsets[tileY*tilesX + tileX]);
- } catch (IOException ioe) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage13"));
- }
- // Number of bytes in this tile (strip) after compression.
- int byteCount = (int)tileByteCounts[tileY*tilesX + tileX];
- // Find out the number of bytes in the current tile
- Rectangle tileRect = new Rectangle(tileXToX(tileX), tileYToY(tileY),
- tileWidth, tileHeight);
- Rectangle newRect = tileRect.intersection(getBounds());
- int unitsInThisTile = newRect.width * newRect.height * numBands;
- // Allocate read buffer if needed.
- byte data[] = compression != COMP_NONE || imageType == TYPE_PALETTE ?
- new byte[byteCount] : null;
- // Read the data, uncompressing as needed. There are four cases:
- // bilevel, palette-RGB, 4-bit grayscale, and everything else.
- if(imageType == TYPE_BILEVEL) { // bilevel
- try {
- if (compression == COMP_PACKBITS) {
- stream.readFully(data, 0, byteCount);
- // Since the decompressed data will still be packed
- // 8 pixels into 1 byte, calculate bytesInThisTile
- int bytesInThisTile;
- if ((newRect.width % 8) == 0) {
- bytesInThisTile = (newRect.width/8) * newRect.height;
- } else {
- bytesInThisTile =
- (newRect.width/8 + 1) * newRect.height;
- }
- decodePackbits(data, bytesInThisTile, bdata);
- } else if (compression == COMP_LZW) {
- stream.readFully(data, 0, byteCount);
- lzwDecoder.decode(data, bdata, newRect.height);
- } else if (compression == COMP_FAX_G3_1D) {
- stream.readFully(data, 0, byteCount);
- decoder.decode1D(bdata, data, 0, newRect.height);
- } else if (compression == COMP_FAX_G3_2D) {
- stream.readFully(data, 0, byteCount);
- decoder.decode2D(bdata, data, 0, newRect.height,
- tiffT4Options);
- } else if (compression == COMP_FAX_G4_2D) {
- stream.readFully(data, 0, byteCount);
- decoder.decodeT6(bdata, data, 0, newRect.height,
- tiffT6Options);
- } else if (compression == COMP_DEFLATE) {
- stream.readFully(data, 0, byteCount);
- inflate(data, bdata);
- } else if (compression == COMP_NONE) {
- stream.readFully(bdata, 0, byteCount);
- }
- stream.seek(save_offset);
- } catch (IOException ioe) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage13"));
- }
- } else if(imageType == TYPE_PALETTE) { // palette-RGB
- if (sampleSize == 16) {
- if (decodePaletteAsShorts) {
-
- short tempData[]= null;
-
- // At this point the data is 1 banded and will
- // become 3 banded only after we've done the palette
- // lookup, since unitsInThisTile was calculated with
- // 3 bands, we need to divide this by 3.
- int unitsBeforeLookup = unitsInThisTile / 3;
-
- // Since unitsBeforeLookup is the number of shorts,
- // but we do our decompression in terms of bytes, we
- // need to multiply it by 2 in order to figure out
- // how many bytes we'll get after decompression.
- int entries = unitsBeforeLookup * 2;
-
- // Read the data, if compressed, decode it, reset the pointer
- try {
-
- if (compression == COMP_PACKBITS) {
- stream.readFully(data, 0, byteCount);
-
- byte byteArray[] = new byte[entries];
- decodePackbits(data, entries, byteArray);
- tempData = new short[unitsBeforeLookup];
- interpretBytesAsShorts(byteArray, tempData,
- unitsBeforeLookup);
-
- } else if (compression == COMP_LZW) {
- // Read in all the compressed data for this tile
- stream.readFully(data, 0, byteCount);
- byte byteArray[] = new byte[entries];
- lzwDecoder.decode(data, byteArray, newRect.height);
- tempData = new short[unitsBeforeLookup];
- interpretBytesAsShorts(byteArray, tempData,
- unitsBeforeLookup);
- } else if (compression == COMP_DEFLATE) {
- stream.readFully(data, 0, byteCount);
- byte byteArray[] = new byte[entries];
- inflate(data, byteArray);
- tempData = new short[unitsBeforeLookup];
- interpretBytesAsShorts(byteArray, tempData,
- unitsBeforeLookup);
- } else if (compression == COMP_NONE) {
- // byteCount tells us how many bytes are there
- // in this tile, but we need to read in shorts,
- // which will take half the space, so while
- // allocating we divide byteCount by 2.
- tempData = new short[byteCount/2];
- readShorts(byteCount/2, tempData);
- }
- stream.seek(save_offset);
- } catch (IOException ioe) {
- throw new RuntimeException(
- JaiI18N.getString("TIFFImage13"));
- }
- if (dataType == DataBuffer.TYPE_USHORT) {
-
- // Expand the palette image into an rgb image with ushort
- // data type.
- int cmapValue;
- int count = 0, lookup, len = colormap.length/3;
- int len2 = len * 2;
- for (int i=0; i<unitsBeforeLookup; i++) {
- // Get the index into the colormap
- lookup = tempData[i] & 0xffff;
- // Get the blue value
- cmapValue = colormap[lookup+len2];
- sdata[count++] = (short)(cmapValue & 0xffff);
- // Get the green value
- cmapValue = colormap[lookup+len];
- sdata[count++] = (short)(cmapValue & 0xffff);
- // Get the red value
- cmapValue = colormap[lookup];
- sdata[count++] = (short)(cmapValue & 0xffff);
- }
- } else if (dataType == DataBuffer.TYPE_SHORT) {
- // Expand the palette image into an rgb image with
- // short data type.
- int cmapValue;
- int count = 0, lookup, len = colormap.length/3;
- int len2 = len * 2;
- for (int i=0; i<unitsBeforeLookup; i++) {
- // Get the index into the colormap
- lookup = tempData[i] & 0xffff;
- // Get the blue value
- cmapValue = colormap[lookup+len2];
- sdata[count++] = (short)cmapValue;
- // Get the green value
- cmapValue = colormap[lookup+len];
- sdata[count++] = (short)cmapValue;
- // Get the red value
- cmapValue = colormap[lookup];
- sdata[count++] = (short)cmapValue;
- }
- }
- } else {
-
- // No lookup being done here, when RGB values are needed,
- // the associated IndexColorModel can be used to get them.
-
- try {
- if (compression == COMP_PACKBITS) {
- stream.readFully(data, 0, byteCount);
- // Since unitsInThisTile is the number of shorts,
- // but we do our decompression in terms of bytes, we
- // need to multiply unitsInThisTile by 2 in order to
- // figure out how many bytes we'll get after
- // decompression.
- int bytesInThisTile = unitsInThisTile * 2;
- byte byteArray[] = new byte[bytesInThisTile];
- decodePackbits(data, bytesInThisTile, byteArray);
- interpretBytesAsShorts(byteArray, sdata,
- unitsInThisTile);
-
- } else if (compression == COMP_LZW) {
-
- stream.readFully(data, 0, byteCount);
- // Since unitsInThisTile is the number of shorts,
- // but we do our decompression in terms of bytes, we
- // need to multiply unitsInThisTile by 2 in order to
- // figure out how many bytes we'll get after
- // decompression.
- byte byteArray[] = new byte[unitsInThisTile * 2];
- lzwDecoder.decode(data, byteArray, newRect.height);
- interpretBytesAsShorts(byteArray, sdata,
- unitsInThisTile);
- } else if (compression == COMP_DEFLATE) {
- stream.readFully(data, 0, byteCount);
- byte byteArray[] = new byte[unitsInThisTile * 2];
- inflate(data, byteArray);
- interpretBytesAsShorts(byteArray, sdata,
- unitsInThisTile);
- } else if (compression == COMP_NONE) {
- readShorts(byteCount/2, sdata);
- }
-
- stream.seek(save_offset);
- } catch (IOException ioe) {
- throw new RuntimeException(
- JaiI18N.getString("TIFFImage13"));
- }
- }
-
- } else if (sampleSize == 8) {
- if (decodePaletteAsShorts) {
-
- byte tempData[]= null;
- // At this point the data is 1 banded and will
- // become 3 banded only after we've done the palette
- // lookup, since unitsInThisTile was calculated with
- // 3 bands, we need to divide this by 3.
- int unitsBeforeLookup = unitsInThisTile / 3;
-
- // Read the data, if compressed, decode it, reset the pointer
- try {
-
- if (compression == COMP_PACKBITS) {
-
- stream.readFully(data, 0, byteCount);
- tempData = new byte[unitsBeforeLookup];
- decodePackbits(data, unitsBeforeLookup, tempData);
-
- } else if (compression == COMP_LZW) {
-
- stream.readFully(data, 0, byteCount);
- tempData = new byte[unitsBeforeLookup];
- lzwDecoder.decode(data, tempData, newRect.height);
-
- } else if (compression == COMP_JPEG_TTN2) {
- stream.readFully(data, 0, byteCount);
- Raster tempTile = decodeJPEG(data,
- decodeParam,
- colorConvertJPEG,
- tile.getMinX(),
- tile.getMinY());
- int[] tempPixels = new int[unitsBeforeLookup];
- tempTile.getPixels(tile.getMinX(),
- tile.getMinY(),
- tile.getWidth(),
- tile.getHeight(),
- tempPixels);
- tempData = new byte[unitsBeforeLookup];
- for(int i = 0; i < unitsBeforeLookup; i++) {
- tempData[i] = (byte)tempPixels[i];
- }
- } else if (compression == COMP_DEFLATE) {
- stream.readFully(data, 0, byteCount);
- tempData = new byte[unitsBeforeLookup];
- inflate(data, tempData);
- } else if (compression == COMP_NONE) {
- tempData = new byte[byteCount];
- stream.readFully(tempData, 0, byteCount);
- }
- stream.seek(save_offset);
- } catch (IOException ioe) {
- throw new RuntimeException(
- JaiI18N.getString("TIFFImage13"));
- }
- // Expand the palette image into an rgb image with ushort
- // data type.
- int cmapValue;
- int count = 0, lookup, len = colormap.length/3;
- int len2 = len * 2;
- for (int i=0; i<unitsBeforeLookup; i++) {
- // Get the index into the colormap
- lookup = tempData[i] & 0xff;
- // Get the blue value
- cmapValue = colormap[lookup+len2];
- sdata[count++] = (short)(cmapValue & 0xffff);
- // Get the green value
- cmapValue = colormap[lookup+len];
- sdata[count++] = (short)(cmapValue & 0xffff);
- // Get the red value
- cmapValue = colormap[lookup];
- sdata[count++] = (short)(cmapValue & 0xffff);
- }
- } else {
-
- // No lookup being done here, when RGB values are needed,
- // the associated IndexColorModel can be used to get them.
-
- try {
- if (compression == COMP_PACKBITS) {
-
- stream.readFully(data, 0, byteCount);
- decodePackbits(data, unitsInThisTile, bdata);
-
- } else if (compression == COMP_LZW) {
-
- stream.readFully(data, 0, byteCount);
- lzwDecoder.decode(data, bdata, newRect.height);
-
- } else if (compression == COMP_JPEG_TTN2) {
- stream.readFully(data, 0, byteCount);
- tile.setRect(decodeJPEG(data,
- decodeParam,
- colorConvertJPEG,
- tile.getMinX(),
- tile.getMinY()));
- } else if (compression == COMP_DEFLATE) {
-
- stream.readFully(data, 0, byteCount);
- inflate(data, bdata);
- } else if (compression == COMP_NONE) {
- stream.readFully(bdata, 0, byteCount);
- }
-
- stream.seek(save_offset);
- } catch (IOException ioe) {
- throw new RuntimeException(
- JaiI18N.getString("TIFFImage13"));
- }
- }
-
- } else if (sampleSize == 4) {
-
- int padding = (newRect.width % 2 == 0) ? 0 : 1;
- int bytesPostDecoding = ((newRect.width/2 + padding) *
- newRect.height);
-
- // Output short images
- if (decodePaletteAsShorts) {
-
- byte tempData[] = null;
- try {
- stream.readFully(data, 0, byteCount);
- stream.seek(save_offset);
- } catch (IOException ioe) {
- throw new RuntimeException(
- JaiI18N.getString("TIFFImage13"));
- }
-
- // If compressed, decode the data.
- if (compression == COMP_PACKBITS) {
-
- tempData = new byte[bytesPostDecoding];
- decodePackbits(data, bytesPostDecoding, tempData);
-
- } else if (compression == COMP_LZW) {
-
- tempData = new byte[bytesPostDecoding];
- lzwDecoder.decode(data, tempData, newRect.height);
-
- } else if (compression == COMP_DEFLATE) {
- tempData = new byte[bytesPostDecoding];
- inflate(data, tempData);
-
- } else if (compression == COMP_NONE) {
-
- tempData = data;
- }
-
- int bytes = unitsInThisTile / 3;
-
- // Unpack the 2 pixels packed into each byte.
- data = new byte[bytes];
-
- int srcCount = 0, dstCount = 0;
- for (int j=0; j<newRect.height; j++) {
- for (int i=0; i<newRect.width/2; i++) {
- data[dstCount++] =
- (byte)((tempData[srcCount] & 0xf0) >> 4);
- data[dstCount++] =
- (byte)(tempData[srcCount++] & 0x0f);
- }
-
- if (padding == 1) {
- data[dstCount++] =
- (byte)((tempData[srcCount++] & 0xf0) >> 4);
- }
- }
-
- int len = colormap.length/3;
- int len2 = len*2;
- int cmapValue, lookup;
- int count = 0;
- for (int i=0; i<bytes; i++) {
- lookup = data[i] & 0xff;
- cmapValue = colormap[lookup+len2];
- sdata[count++] = (short)(cmapValue & 0xffff);
- cmapValue = colormap[lookup+len];
- sdata[count++] = (short)(cmapValue & 0xffff);
- cmapValue = colormap[lookup];
- sdata[count++] = (short)(cmapValue & 0xffff);
- }
- } else {
-
- // Output byte values, use IndexColorModel for unpacking
- try {
-
- // If compressed, decode the data.
- if (compression == COMP_PACKBITS) {
-
- stream.readFully(data, 0, byteCount);
- decodePackbits(data, bytesPostDecoding, bdata);
-
- } else if (compression == COMP_LZW) {
-
- stream.readFully(data, 0, byteCount);
- lzwDecoder.decode(data, bdata, newRect.height);
-
- } else if (compression == COMP_DEFLATE) {
- stream.readFully(data, 0, byteCount);
- inflate(data, bdata);
- } else if (compression == COMP_NONE) {
-
- stream.readFully(bdata, 0, byteCount);
- }
-
- stream.seek(save_offset);
- } catch (IOException ioe) {
- throw new RuntimeException(
- JaiI18N.getString("TIFFImage13"));
- }
- }
- }
- } else if(imageType == TYPE_GRAY_4BIT) { // 4-bit gray
- try {
- if (compression == COMP_PACKBITS) {
-
- stream.readFully(data, 0, byteCount);
-
- // Since the decompressed data will still be packed
- // 2 pixels into 1 byte, calculate bytesInThisTile
- int bytesInThisTile;
- if ((newRect.width % 8) == 0) {
- bytesInThisTile = (newRect.width/2) * newRect.height;
- } else {
- bytesInThisTile = (newRect.width/2 + 1) *
- newRect.height;
- }
-
- decodePackbits(data, bytesInThisTile, bdata);
-
- } else if (compression == COMP_LZW) {
-
- stream.readFully(data, 0, byteCount);
- lzwDecoder.decode(data, bdata, newRect.height);
-
- } else if (compression == COMP_DEFLATE) {
-
- stream.readFully(data, 0, byteCount);
- inflate(data, bdata);
-
- } else {
- stream.readFully(bdata, 0, byteCount);
- }
-
- stream.seek(save_offset);
- } catch (IOException ioe) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage13"));
- }
- } else { // everything else
- try {
-
- if (sampleSize == 8) {
-
- if (compression == COMP_NONE) {
-
- stream.readFully(bdata, 0, byteCount);
-
- } else if (compression == COMP_LZW) {
-
- stream.readFully(data, 0, byteCount);
- lzwDecoder.decode(data, bdata, newRect.height);
-
- } else if (compression == COMP_PACKBITS) {
-
- stream.readFully(data, 0, byteCount);
- decodePackbits(data, unitsInThisTile, bdata);
-
- } else if (compression == COMP_JPEG_TTN2) {
- stream.readFully(data, 0, byteCount);
- tile.setRect(decodeJPEG(data,
- decodeParam,
- colorConvertJPEG,
- tile.getMinX(),
- tile.getMinY()));
- } else if (compression == COMP_DEFLATE) {
- stream.readFully(data, 0, byteCount);
- inflate(data, bdata);
- }
-
- } else if (sampleSize == 16) {
-
- if (compression == COMP_NONE) {
-
- readShorts(byteCount/2, sdata);
-
- } else if (compression == COMP_LZW) {
-
- stream.readFully(data, 0, byteCount);
- // Since unitsInThisTile is the number of shorts,
- // but we do our decompression in terms of bytes, we
- // need to multiply unitsInThisTile by 2 in order to
- // figure out how many bytes we'll get after
- // decompression.
- byte byteArray[] = new byte[unitsInThisTile * 2];
- lzwDecoder.decode(data, byteArray, newRect.height);
- interpretBytesAsShorts(byteArray, sdata,
- unitsInThisTile);
-
- } else if (compression == COMP_PACKBITS) {
-
- stream.readFully(data, 0, byteCount);
- // Since unitsInThisTile is the number of shorts,
- // but we do our decompression in terms of bytes, we
- // need to multiply unitsInThisTile by 2 in order to
- // figure out how many bytes we'll get after
- // decompression.
- int bytesInThisTile = unitsInThisTile * 2;
- byte byteArray[] = new byte[bytesInThisTile];
- decodePackbits(data, bytesInThisTile, byteArray);
- interpretBytesAsShorts(byteArray, sdata,
- unitsInThisTile);
- } else if (compression == COMP_DEFLATE) {
- stream.readFully(data, 0, byteCount);
- byte byteArray[] = new byte[unitsInThisTile * 2];
- inflate(data, byteArray);
- interpretBytesAsShorts(byteArray, sdata,
- unitsInThisTile);
- }
- } else if (sampleSize == 32 &&
- dataType == DataBuffer.TYPE_INT) { // redundant
- if (compression == COMP_NONE) {
-
- readInts(byteCount/4, idata);
- } else if (compression == COMP_LZW) {
-
- stream.readFully(data, 0, byteCount);
- // Since unitsInThisTile is the number of ints,
- // but we do our decompression in terms of bytes, we
- // need to multiply unitsInThisTile by 4 in order to
- // figure out how many bytes we'll get after
- // decompression.
- byte byteArray[] = new byte[unitsInThisTile * 4];
- lzwDecoder.decode(data, byteArray, newRect.height);
- interpretBytesAsInts(byteArray, idata,
- unitsInThisTile);
-
- } else if (compression == COMP_PACKBITS) {
-
- stream.readFully(data, 0, byteCount);
- // Since unitsInThisTile is the number of ints,
- // but we do our decompression in terms of bytes, we
- // need to multiply unitsInThisTile by 4 in order to
- // figure out how many bytes we'll get after
- // decompression.
- int bytesInThisTile = unitsInThisTile * 4;
- byte byteArray[] = new byte[bytesInThisTile];
- decodePackbits(data, bytesInThisTile, byteArray);
- interpretBytesAsInts(byteArray, idata,
- unitsInThisTile);
- } else if (compression == COMP_DEFLATE) {
- stream.readFully(data, 0, byteCount);
- byte byteArray[] = new byte[unitsInThisTile * 4];
- inflate(data, byteArray);
- interpretBytesAsInts(byteArray, idata,
- unitsInThisTile);
- }
- } else if (sampleSize == 32 &&
- dataType == DataBuffer.TYPE_FLOAT) { // redundant
- if (compression == COMP_NONE) {
-
- readFloats(byteCount/4, fdata);
- } else if (compression == COMP_LZW) {
-
- stream.readFully(data, 0, byteCount);
- // Since unitsInThisTile is the number of floats,
- // but we do our decompression in terms of bytes, we
- // need to multiply unitsInThisTile by 4 in order to
- // figure out how many bytes we'll get after
- // decompression.
- byte byteArray[] = new byte[unitsInThisTile * 4];
- lzwDecoder.decode(data, byteArray, newRect.height);
- interpretBytesAsFloats(byteArray, fdata,
- unitsInThisTile);
-
- } else if (compression == COMP_PACKBITS) {
-
- stream.readFully(data, 0, byteCount);
- // Since unitsInThisTile is the number of floats,
- // but we do our decompression in terms of bytes, we
- // need to multiply unitsInThisTile by 4 in order to
- // figure out how many bytes we'll get after
- // decompression.
- int bytesInThisTile = unitsInThisTile * 4;
- byte byteArray[] = new byte[bytesInThisTile];
- decodePackbits(data, bytesInThisTile, byteArray);
- interpretBytesAsFloats(byteArray, fdata,
- unitsInThisTile);
- } else if (compression == COMP_DEFLATE) {
- stream.readFully(data, 0, byteCount);
- byte byteArray[] = new byte[unitsInThisTile * 4];
- inflate(data, byteArray);
- interpretBytesAsFloats(byteArray, fdata,
- unitsInThisTile);
- }
- }
- stream.seek(save_offset);
- } catch (IOException ioe) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage13"));
- }
- // Modify the data for certain special cases.
- switch(imageType) {
- case TYPE_GRAY:
- case TYPE_GRAY_ALPHA:
- if(isWhiteZero) {
- // Since we are using a ComponentColorModel with this
- // image, we need to change the WhiteIsZero data to
- // BlackIsZero data so it will display properly.
- if (dataType == DataBuffer.TYPE_BYTE &&
- !(colorModel instanceof IndexColorModel)) {
- for (int l = 0; l < bdata.length; l += numBands) {
- bdata[l] = (byte)(255 - bdata[l]);
- }
- } else if (dataType == DataBuffer.TYPE_USHORT) {
- int ushortMax = Short.MAX_VALUE - Short.MIN_VALUE;
- for (int l = 0; l < sdata.length; l += numBands) {
- sdata[l] = (short)(ushortMax - sdata[l]);
- }
- } else if (dataType == DataBuffer.TYPE_SHORT) {
- for (int l = 0; l < sdata.length; l += numBands) {
- sdata[l] = (short)(~sdata[l]);
- }
- } else if (dataType == DataBuffer.TYPE_INT) {
- long uintMax = Integer.MAX_VALUE - Integer.MIN_VALUE;
- for (int l = 0; l < idata.length; l += numBands) {
- idata[l] = (int)(uintMax - (long)idata[l]);
- }
- }
- }
- break;
- case TYPE_RGB:
- // Change RGB to BGR order, as Java2D displays that faster.
- // Unnecessary for JPEG-in-TIFF as the decoder handles it.
- if (sampleSize == 8 && compression != COMP_JPEG_TTN2) {
- for (int i=0; i<unitsInThisTile; i+=3) {
- bswap = bdata[i];
- bdata[i] = bdata[i+2];
- bdata[i+2] = bswap;
- }
- } else if (sampleSize == 16) {
- for (int i=0; i<unitsInThisTile; i+=3) {
- sswap = sdata[i];
- sdata[i] = sdata[i+2];
- sdata[i+2] = sswap;
- }
- } else if (sampleSize == 32) {
- if(dataType == DataBuffer.TYPE_INT) {
- for (int i=0; i<unitsInThisTile; i+=3) {
- iswap = idata[i];
- idata[i] = idata[i+2];
- idata[i+2] = iswap;
- }
- } else if(dataType == DataBuffer.TYPE_FLOAT) {
- for (int i=0; i<unitsInThisTile; i+=3) {
- fswap = fdata[i];
- fdata[i] = fdata[i+2];
- fdata[i+2] = fswap;
- }
- }
- }
- break;
- case TYPE_RGB_ALPHA:
- // Convert from RGBA to ABGR for Java2D
- if (sampleSize == 8) {
- for (int i=0; i<unitsInThisTile; i+=4) {
- // Swap R and A
- bswap = bdata[i];
- bdata[i] = bdata[i+3];
- bdata[i+3] = bswap;
-
- // Swap G and B
- bswap = bdata[i+1];
- bdata[i+1] = bdata[i+2];
- bdata[i+2] = bswap;
- }
- } else if (sampleSize == 16) {
- for (int i=0; i<unitsInThisTile; i+=4) {
- // Swap R and A
- sswap = sdata[i];
- sdata[i] = sdata[i+3];
- sdata[i+3] = sswap;
- // Swap G and B
- sswap = sdata[i+1];
- sdata[i+1] = sdata[i+2];
- sdata[i+2] = sswap;
- }
- } else if (sampleSize == 32) {
- if(dataType == DataBuffer.TYPE_INT) {
- for (int i=0; i<unitsInThisTile; i+=4) {
- // Swap R and A
- iswap = idata[i];
- idata[i] = idata[i+3];
- idata[i+3] = iswap;
- // Swap G and B
- iswap = idata[i+1];
- idata[i+1] = idata[i+2];
- idata[i+2] = iswap;
- }
- } else if(dataType == DataBuffer.TYPE_FLOAT) {
- for (int i=0; i<unitsInThisTile; i+=4) {
- // Swap R and A
- fswap = fdata[i];
- fdata[i] = fdata[i+3];
- fdata[i+3] = fswap;
- // Swap G and B
- fswap = fdata[i+1];
- fdata[i+1] = fdata[i+2];
- fdata[i+2] = fswap;
- }
- }
- }
- break;
- case TYPE_YCBCR_SUB:
- // Post-processing for YCbCr with subsampled chrominance:
- // simply replicate the chroma channels for displayability.
- int pixelsPerDataUnit = chromaSubH*chromaSubV;
- int numH = newRect.width/chromaSubH;
- int numV = newRect.height/chromaSubV;
- byte[] tempData = new byte[numH*numV*(pixelsPerDataUnit + 2)];
- System.arraycopy(bdata, 0, tempData, 0, tempData.length);
- int samplesPerDataUnit = pixelsPerDataUnit*3;
- int[] pixels = new int[samplesPerDataUnit];
- int bOffset = 0;
- int offsetCb = pixelsPerDataUnit;
- int offsetCr = offsetCb + 1;
- int y = newRect.y;
- for(int j = 0; j < numV; j++) {
- int x = newRect.x;
- for(int i = 0; i < numH; i++) {
- int Cb = tempData[bOffset + offsetCb];
- int Cr = tempData[bOffset + offsetCr];
- int k = 0;
- while(k < samplesPerDataUnit) {
- pixels[k++] = tempData[bOffset++];
- pixels[k++] = Cb;
- pixels[k++] = Cr;
- }
- bOffset += 2;
- tile.setPixels(x, y, chromaSubH, chromaSubV, pixels);
- x += chromaSubH;
- }
- y += chromaSubV;
- }
- break;
- }
- }
-
- return tile;
- }
- private void readShorts(int shortCount, short shortArray[]) {
-
- // Since each short consists of 2 bytes, we need a
- // byte array of double size
- int byteCount = 2 * shortCount;
- byte byteArray[] = new byte[byteCount];
- try {
- stream.readFully(byteArray, 0, byteCount);
- } catch (IOException ioe) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage13"));
- }
- interpretBytesAsShorts(byteArray, shortArray, shortCount);
- }
- private void readInts(int intCount, int intArray[]) {
-
- // Since each int consists of 4 bytes, we need a
- // byte array of quadruple size
- int byteCount = 4 * intCount;
- byte byteArray[] = new byte[byteCount];
- try {
- stream.readFully(byteArray, 0, byteCount);
- } catch (IOException ioe) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage13"));
- }
- interpretBytesAsInts(byteArray, intArray, intCount);
- }
- private void readFloats(int floatCount, float floatArray[]) {
-
- // Since each float consists of 4 bytes, we need a
- // byte array of quadruple size
- int byteCount = 4 * floatCount;
- byte byteArray[] = new byte[byteCount];
- try {
- stream.readFully(byteArray, 0, byteCount);
- } catch (IOException ioe) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage13"));
- }
- interpretBytesAsFloats(byteArray, floatArray, floatCount);
- }
- // Method to interpret a byte array to a short array, depending on
- // whether the bytes are stored in a big endian or little endian format.
- private void interpretBytesAsShorts(byte byteArray[],
- short shortArray[],
- int shortCount) {
- int j = 0;
- int firstByte, secondByte;
- if (isBigEndian) {
- for (int i=0; i<shortCount; i++) {
- firstByte = byteArray[j++] & 0xff;
- secondByte = byteArray[j++] & 0xff;
- shortArray[i] = (short)((firstByte << 8) + secondByte);
- }
-
- } else {
-
- for (int i=0; i<shortCount; i++) {
- firstByte = byteArray[j++] & 0xff;
- secondByte = byteArray[j++] & 0xff;
- shortArray[i] = (short)((secondByte << 8) + firstByte);
- }
- }
- }
- // Method to interpret a byte array to a int array, depending on
- // whether the bytes are stored in a big endian or little endian format.
- private void interpretBytesAsInts(byte byteArray[],
- int intArray[],
- int intCount) {
- int j = 0;
- if (isBigEndian) {
-
- for (int i=0; i<intCount; i++) {
- intArray[i] =
- (int)(((byteArray[j++] & 0xff) << 24) |
- ((byteArray[j++] & 0xff) << 16) |
- ((byteArray[j++] & 0xff) << 8) |
- (byteArray[j++] & 0xff));
- }
-
- } else {
-
- for (int i=0; i<intCount; i++) {
- intArray[i] =
- (int)((byteArray[j++] & 0xff) |
- ((byteArray[j++] & 0xff) << 8) |
- ((byteArray[j++] & 0xff) << 16) |
- ((byteArray[j++] & 0xff) << 24));
- }
- }
- }
- // Method to interpret a byte array to a float array, depending on
- // whether the bytes are stored in a big endian or little endian format.
- private void interpretBytesAsFloats(byte byteArray[],
- float floatArray[],
- int floatCount) {
- int j = 0;
- if (isBigEndian) {
-
- for (int i=0; i<floatCount; i++) {
- int value = (int)(((byteArray[j++] & 0xff) << 24) |
- ((byteArray[j++] & 0xff) << 16) |
- ((byteArray[j++] & 0xff) << 8) |
- (byteArray[j++] & 0xff));
- floatArray[i] = Float.intBitsToFloat(value);
- }
-
- } else {
-
- for (int i=0; i<floatCount; i++) {
- int value = (int)((byteArray[j++] & 0xff) |
- ((byteArray[j++] & 0xff) << 8) |
- ((byteArray[j++] & 0xff) << 16) |
- ((byteArray[j++] & 0xff) << 24));
- floatArray[i] = Float.intBitsToFloat(value);
- }
- }
- }
- // Uncompress packbits compressed image data.
- private byte[] decodePackbits(byte data[], int arraySize, byte[] dst) {
- if (dst == null) {
- dst = new byte[arraySize];
- }
- int srcCount = 0, dstCount = 0;
- byte repeat, b;
- try {
-
- while (dstCount < arraySize) {
-
- b = data[srcCount++];
-
- if (b >= 0 && b <= 127) {
-
- // literal run packet
- for (int i=0; i<(b + 1); i++) {
- dst[dstCount++] = data[srcCount++];
- }
-
- } else if (b <= -1 && b >= -127) {
-
- // 2 byte encoded run packet
- repeat = data[srcCount++];
- for (int i=0; i<(-b + 1); i++) {
- dst[dstCount++] = repeat;
- }
-
- } else {
- // no-op packet. Do nothing
- srcCount++;
- }
- }
- } catch (java.lang.ArrayIndexOutOfBoundsException ae) {
- throw new RuntimeException(JaiI18N.getString("TIFFImage14"));
- }
- return dst;
- }
- // Need a createColorModel().
- // Create ComponentColorModel for TYPE_RGB images
- private ComponentColorModel createAlphaComponentColorModel(
- int dataType,
- int numBands,
- boolean isAlphaPremultiplied,
- int transparency) {
-
- ComponentColorModel ccm = null;
- int RGBBits[] = null;
- ColorSpace cs = null;
- switch(numBands) {
- case 2: // gray+alpha
- cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
- break;
- case 4: // RGB+alpha
- cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
- break;
- default:
- throw new IllegalArgumentException();
- }
- if(dataType == DataBuffer.TYPE_FLOAT) {
- ccm = new FloatDoubleColorModel(cs,
- true,
- isAlphaPremultiplied,
- transparency,
- dataType);
- } else { // all other types
- int componentSize = 0;
- switch(dataType) {
- case DataBuffer.TYPE_BYTE:
- componentSize = 8;
- break;
- case DataBuffer.TYPE_USHORT:
- case DataBuffer.TYPE_SHORT:
- componentSize = 16;
- break;
- case DataBuffer.TYPE_INT:
- componentSize = 32;
- break;
- default:
- throw new IllegalArgumentException();
- }
- RGBBits = new int[numBands];
- for(int i = 0; i < numBands; i++) {
- RGBBits[i] = componentSize;
- }
- ccm = new ComponentColorModel(cs,
- RGBBits,
- true,
- isAlphaPremultiplied,
- transparency,
- dataType);
- }
- return ccm;
- }
- }