You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by cf...@apache.org on 2012/10/25 21:01:49 UTC
svn commit: r1402274 [17/31] - in
/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext:
./ awt/ awt/color/ awt/font/ awt/g2d/ awt/geom/ awt/image/ awt/image/codec/
awt/image/codec/jpeg/ awt/image/codec/p...
Added: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/codec/tiff/TIFFImage.java
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/codec/tiff/TIFFImage.java?rev=1402274&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/codec/tiff/TIFFImage.java (added)
+++ incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/codec/tiff/TIFFImage.java Thu Oct 25 19:01:43 2012
@@ -0,0 +1,1818 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ */
+package org.apache.flex.forks.batik.ext.awt.image.codec.tiff;
+
+import java.awt.Rectangle;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DataBufferShort;
+import java.awt.image.DataBufferUShort;
+import java.awt.image.IndexColorModel;
+import java.awt.image.MultiPixelPackedSampleModel;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+import org.apache.flex.forks.batik.ext.awt.image.codec.util.SeekableStream;
+import org.apache.flex.forks.batik.ext.awt.image.rendered.AbstractRed;
+import org.apache.flex.forks.batik.ext.awt.image.rendered.CachableRed;
+
+import com.sun.image.codec.jpeg.JPEGCodec;
+import com.sun.image.codec.jpeg.JPEGDecodeParam;
+import com.sun.image.codec.jpeg.JPEGImageDecoder;
+
+/**
+ *
+ * @version $Id: TIFFImage.java 498740 2007-01-22 18:35:57Z dvholten $
+ */
+public class TIFFImage extends AbstractRed {
+
+ // 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;
+ boolean tiled;
+
+ // 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("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("TIFFImage17"+": "+
+ dfe.getMessage());
+ }
+ inflater.reset();
+ }
+
+ private static SampleModel createPixelInterleavedSampleModel
+ (int dataType, int tileWidth, int tileHeight, int bands) {
+ int [] bandOffsets = new int[bands];
+ for (int i=0; i<bands; i++)
+ bandOffsets[i] = i;
+ return new PixelInterleavedSampleModel
+ (dataType, tileWidth, tileHeight, bands,
+ tileWidth*bands, bandOffsets);
+ }
+
+ /**
+ * Return as a long[] the value of a TIFF_LONG or TIFF_SHORT field.
+ */
+ private 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);
+
+ // 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("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("TIFFImage1");
+ }
+ }
+ }
+ sampleSize = 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("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:
+ if (sampleFormat[0] == 3)
+ isValidDataFormat = false;
+ else {
+ dataType = DataBuffer.TYPE_INT;
+ isValidDataFormat = true;
+ }
+ break;
+ }
+
+ if(!isValidDataFormat) {
+ throw new RuntimeException("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("TIFFImage4");
+ }
+
+ // Set basic image layout
+ Rectangle bounds = new Rectangle
+ (0, 0,
+ (int)dir.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_WIDTH),
+ (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);
+
+ int tileWidth, tileHeight;
+ if (dir.getField(TIFFImageDecoder.TIFF_TILE_OFFSETS) != null) {
+ tiled = true;
+ // 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 {
+ tiled = false;
+
+ // 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) :
+ bounds.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):
+ bounds.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 = bounds.height;
+ } else {
+ tileHeight = (int)l;
+ }
+ }
+
+ TIFFField tileOffsetsField =
+ dir.getField(TIFFImageDecoder.TIFF_STRIP_OFFSETS);
+ if (tileOffsetsField == null) {
+ throw new RuntimeException("TIFFImage5");
+ } else {
+ tileOffsets = getFieldAsLongs(tileOffsetsField);
+ }
+
+ TIFFField tileByteCountsField =
+ dir.getField(TIFFImageDecoder.TIFF_STRIP_BYTE_COUNTS);
+ if (tileByteCountsField == null) {
+ throw new RuntimeException("TIFFImage6");
+ } else {
+ tileByteCounts = getFieldAsLongs(tileByteCountsField);
+ }
+ }
+
+ // Calculate number of tiles and the tileSize in bytes
+ tilesX = (bounds.width + tileWidth - 1)/tileWidth;
+ tilesY = (bounds.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("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("TIFFImage8");
+ }
+
+ if (predictor == 2 && sampleSize != 8) {
+ throw new RuntimeException(sampleSize +
+ "TIFFImage9");
+ }
+ }
+
+ lzwDecoder = new TIFFLZWDecoder(tileWidth, predictor,
+ samplesPerPixel);
+ break;
+
+ case COMP_JPEG_OLD:
+ throw new RuntimeException("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("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("TIFFImage10");
+ }
+
+ ColorModel colorModel = null;
+ SampleModel sampleModel = null;
+ 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 {
+ byte [] map = new byte[16];
+ if (isWhiteZero) {
+ for (int i=0; i<map.length; i++)
+ map[i] = (byte)(255-(16*i));
+ } else {
+ for (int i=0; i<map.length; i++)
+ map[i] = (byte)(16*i);
+ }
+ colorModel = new IndexColorModel(4, 16, map, map, map);
+ }
+ 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 = new PixelInterleavedSampleModel
+ (dataType, tileWidth, tileHeight,
+ numBands, numBands*tileWidth, reverseOffsets);
+
+ if(imageType == TYPE_GRAY) {
+ colorModel = new ComponentColorModel
+ (ColorSpace.getInstance(ColorSpace.CS_GRAY),
+ new int[] { sampleSize }, false, false,
+ Transparency.OPAQUE, dataType);
+ } else if (imageType == TYPE_RGB) {
+ colorModel = new ComponentColorModel
+ (ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ new int[] { sampleSize, sampleSize, sampleSize },
+ false, false, Transparency.OPAQUE, dataType);
+ } 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 = new PixelInterleavedSampleModel
+ (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("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 = createPixelInterleavedSampleModel
+ (dataType, tileWidth, tileHeight, numBands);
+
+ colorModel = new ComponentColorModel
+ (ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ new int[] { 16, 16, 16 }, false, false,
+ Transparency.OPAQUE, dataType);
+
+ } 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 = 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 = 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");
+ }
+
+ Map properties = new HashMap();
+ // Set a property "tiff_directory".
+ properties.put("tiff_directory", dir);
+
+ // System.out.println("Constructed TIFF");
+
+ init((CachableRed)null, bounds, colorModel, sampleModel,
+ 0, 0, properties);
+ }
+
+ /**
+ * 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);
+ }
+
+
+ public WritableRaster copyData(WritableRaster wr) {
+ copyToRaster(wr);
+ return wr;
+ }
+
+
+ /**
+ * 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("TIFFImage12");
+ }
+
+ // System.out.println("Called TIFF getTile:" + tileX + "," + tileY);
+
+
+ // Get the data array out of the DataBuffer
+ byte[] bdata = null;
+ short[] sdata = null;
+ int[] idata = null;
+
+ SampleModel sampleModel = getSampleModel();
+ WritableRaster tile = makeTile(tileX,tileY);
+
+ DataBuffer buffer = tile.getDataBuffer();
+
+ 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();
+ }
+
+ // Variables used for swapping when converting from RGB to BGR
+ byte bswap;
+ short sswap;
+ int iswap;
+
+ // 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("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 newRect;
+ if (!tiled)
+ newRect = tile.getBounds();
+ else
+ newRect = new Rectangle(tile.getMinX(), tile.getMinY(),
+ tileWidth, tileHeight);
+
+ 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("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("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("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("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("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("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("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("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);
+
+ }
+ }
+
+ stream.seek(save_offset);
+
+ } catch (IOException ioe) {
+ throw new RuntimeException("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 &&
+ !(getColorModel() 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 = ((long)Integer.MAX_VALUE -
+ (long)Integer.MIN_VALUE);
+ for (int l = 0; l < idata.length; l += numBands) {
+ idata[l] = (int)(uintMax - 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;
+ }
+ }
+ }
+ 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;
+ }
+ }
+ }
+ 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("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("TIFFImage13");
+ }
+
+ interpretBytesAsInts(byteArray, intArray, intCount);
+ }
+
+ // 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] = (((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] = ((byteArray[j++] & 0xff) |
+ ((byteArray[j++] & 0xff) << 8) |
+ ((byteArray[j++] & 0xff) << 16) |
+ ((byteArray[j++] & 0xff) << 24));
+ }
+ }
+ }
+
+ // 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("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();
+ }
+
+ 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;
+ }
+
+}
Propchange: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/codec/tiff/TIFFImage.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/codec/tiff/TIFFImageDecoder.java
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/codec/tiff/TIFFImageDecoder.java?rev=1402274&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/codec/tiff/TIFFImageDecoder.java (added)
+++ incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/codec/tiff/TIFFImageDecoder.java Thu Oct 25 19:01:43 2012
@@ -0,0 +1,88 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ */
+package org.apache.flex.forks.batik.ext.awt.image.codec.tiff;
+
+import java.awt.image.RenderedImage;
+import java.io.IOException;
+
+import org.apache.flex.forks.batik.ext.awt.image.codec.util.ImageDecodeParam;
+import org.apache.flex.forks.batik.ext.awt.image.codec.util.ImageDecoderImpl;
+import org.apache.flex.forks.batik.ext.awt.image.codec.util.SeekableStream;
+
+/**
+ * A baseline TIFF reader. The reader has some functionality in addition to
+ * the baseline specifications for Bilevel images, for which the group 3 and
+ * group 4 decompression schemes have been implemented. Support for LZW
+ * decompression has also been added. Support for Horizontal differencing
+ * predictor decoding is also included, when used with LZW compression.
+ * However, this support is limited to data with bitsPerSample value of 8.
+ * When reading in RGB images, support for alpha and extraSamples being
+ * present has been added. Support for reading in images with 16 bit samples
+ * has been added. Support for the SampleFormat tag (signed samples as well
+ * as floating-point samples) has also been added. In all other cases, support
+ * is limited to Baseline specifications.
+ *
+ * @version $Id: TIFFImageDecoder.java 498740 2007-01-22 18:35:57Z dvholten $
+ */
+public class TIFFImageDecoder extends ImageDecoderImpl {
+
+ // All the TIFF tags that we care about
+ public static final int TIFF_IMAGE_WIDTH = 256;
+ public static final int TIFF_IMAGE_LENGTH = 257;
+ public static final int TIFF_BITS_PER_SAMPLE = 258;
+ public static final int TIFF_COMPRESSION = 259;
+ public static final int TIFF_PHOTOMETRIC_INTERPRETATION = 262;
+ public static final int TIFF_FILL_ORDER = 266;
+ public static final int TIFF_STRIP_OFFSETS = 273;
+ public static final int TIFF_SAMPLES_PER_PIXEL = 277;
+ public static final int TIFF_ROWS_PER_STRIP = 278;
+ public static final int TIFF_STRIP_BYTE_COUNTS = 279;
+ public static final int TIFF_X_RESOLUTION = 282;
+ public static final int TIFF_Y_RESOLUTION = 283;
+ public static final int TIFF_PLANAR_CONFIGURATION = 284;
+ public static final int TIFF_T4_OPTIONS = 292;
+ public static final int TIFF_T6_OPTIONS = 293;
+ public static final int TIFF_RESOLUTION_UNIT = 296;
+ public static final int TIFF_PREDICTOR = 317;
+ public static final int TIFF_COLORMAP = 320;
+ public static final int TIFF_TILE_WIDTH = 322;
+ public static final int TIFF_TILE_LENGTH = 323;
+ public static final int TIFF_TILE_OFFSETS = 324;
+ public static final int TIFF_TILE_BYTE_COUNTS = 325;
+ public static final int TIFF_EXTRA_SAMPLES = 338;
+ public static final int TIFF_SAMPLE_FORMAT = 339;
+ public static final int TIFF_S_MIN_SAMPLE_VALUE = 340;
+ public static final int TIFF_S_MAX_SAMPLE_VALUE = 341;
+
+ public TIFFImageDecoder(SeekableStream input,
+ ImageDecodeParam param) {
+ super(input, param);
+ }
+
+ public int getNumPages() throws IOException {
+ return TIFFDirectory.getNumDirectories(input);
+ }
+
+ public RenderedImage decodeAsRenderedImage(int page) throws IOException {
+ if ((page < 0) || (page >= getNumPages())) {
+ throw new IOException("TIFFImageDecoder0");
+ }
+ return new TIFFImage(input, (TIFFDecodeParam)param, page);
+ }
+}
Propchange: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/codec/tiff/TIFFImageDecoder.java
------------------------------------------------------------------------------
svn:eol-style = native