diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/ImageUtil.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/ImageUtil.java
index d3a4ba4a6f9..ddaad6e78d6 100644
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/ImageUtil.java
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/ImageUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@ import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
@@ -47,64 +48,15 @@ import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
import java.util.Arrays;
-
-//import javax.imageio.ImageTypeSpecifier;
-
+import java.util.Iterator;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
+import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.spi.ImageWriterSpi;
public class ImageUtil {
- /* XXX testing only
- public static void main(String[] args) {
- ImageTypeSpecifier bilevel =
- ImageTypeSpecifier.createIndexed(new byte[] {(byte)0, (byte)255},
- new byte[] {(byte)0, (byte)255},
- new byte[] {(byte)0, (byte)255},
- null, 1,
- DataBuffer.TYPE_BYTE);
- ImageTypeSpecifier gray =
- ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false);
- ImageTypeSpecifier grayAlpha =
- ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false,
- false);
- ImageTypeSpecifier rgb =
- ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
- new int[] {0, 1, 2},
- DataBuffer.TYPE_BYTE,
- false,
- false);
- ImageTypeSpecifier rgba =
- ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
- new int[] {0, 1, 2, 3},
- DataBuffer.TYPE_BYTE,
- true,
- false);
- ImageTypeSpecifier packed =
- ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
- 0xff000000,
- 0x00ff0000,
- 0x0000ff00,
- 0x000000ff,
- DataBuffer.TYPE_BYTE,
- false);
-
- SampleModel bandedSM =
- new java.awt.image.BandedSampleModel(DataBuffer.TYPE_BYTE,
- 1, 1, 15);
-
- System.out.println(createColorModel(bilevel.getSampleModel()));
- System.out.println(createColorModel(gray.getSampleModel()));
- System.out.println(createColorModel(grayAlpha.getSampleModel()));
- System.out.println(createColorModel(rgb.getSampleModel()));
- System.out.println(createColorModel(rgba.getSampleModel()));
- System.out.println(createColorModel(packed.getSampleModel()));
- System.out.println(createColorModel(bandedSM));
- }
- */
-
/**
* Creates a ColorModel that may be used with the
* specified SampleModel. If a suitable
@@ -1162,4 +1114,78 @@ public class ImageUtil {
// pixel stride.
return ImageUtil.isBinary(sm);
}
+
+ /**
+ * Gets the destination image type.
+ */
+ public static final ImageTypeSpecifier
+ getDestinationType(ImageReadParam param,
+ Iterator imageTypes) throws IIOException {
+
+ if (imageTypes == null || !imageTypes.hasNext()) {
+ throw new IllegalArgumentException("imageTypes null or empty!");
+ }
+
+ ImageTypeSpecifier imageType = null;
+
+ // If param is non-null, use it
+ if (param != null) {
+ imageType = param.getDestinationType();
+ }
+
+ // No info from param, use fallback image type
+ if (imageType == null) {
+ Object o = imageTypes.next();
+ if (!(o instanceof ImageTypeSpecifier)) {
+ throw new IllegalArgumentException
+ ("Non-ImageTypeSpecifier retrieved from imageTypes!");
+ }
+ imageType = (ImageTypeSpecifier)o;
+ } else {
+ boolean foundIt = false;
+ while (imageTypes.hasNext()) {
+ ImageTypeSpecifier type =
+ imageTypes.next();
+ if (type.equals(imageType)) {
+ foundIt = true;
+ break;
+ }
+ }
+
+ if (!foundIt) {
+ throw new IIOException
+ ("Destination type from ImageReadParam does not match!");
+ }
+ }
+
+ return imageType;
+ }
+
+ /**
+ * Returns true if the given ColorSpace object is
+ * an instance of ICC_ColorSpace but is not one of the standard
+ * ColorSpaces returned by ColorSpace.getInstance().
+ *
+ * @param cs The ColorSpace to test.
+ */
+ public static boolean isNonStandardICCColorSpace(ColorSpace cs) {
+ boolean retval = false;
+
+ try {
+ // Check the standard ColorSpaces in decreasing order of
+ // likelihood except check CS_PYCC last as in some JREs
+ // PYCC.pf used not to be installed.
+ retval =
+ (cs instanceof ICC_ColorSpace) &&
+ !(cs.isCS_sRGB() ||
+ cs.equals(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB)) ||
+ cs.equals(ColorSpace.getInstance(ColorSpace.CS_GRAY)) ||
+ cs.equals(ColorSpace.getInstance(ColorSpace.CS_CIEXYZ)) ||
+ cs.equals(ColorSpace.getInstance(ColorSpace.CS_PYCC)));
+ } catch(IllegalArgumentException e) {
+ // PYCC.pf not installed: ignore it - 'retval' is still 'false'.
+ }
+
+ return retval;
+ }
}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleCMYKColorSpace.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleCMYKColorSpace.java
new file mode 100644
index 00000000000..a1097b3853e
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleCMYKColorSpace.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.common;
+
+import java.awt.color.ColorSpace;
+
+/**
+ * Singleton class representing a simple, mathematically defined CMYK
+ * color space.
+ */
+public final class SimpleCMYKColorSpace extends ColorSpace {
+ private static final long serialVersionUID = 666L; // XXX Revise UID value
+
+ private static ColorSpace theInstance = null;
+ private ColorSpace csRGB;
+
+ /** The exponent for gamma correction. */
+ private static final double power1 = 1.0 / 2.4;
+
+ public static final synchronized ColorSpace getInstance() {
+ if(theInstance == null) {
+ theInstance = new SimpleCMYKColorSpace();
+ }
+ return theInstance;
+ }
+
+ private SimpleCMYKColorSpace() {
+ super(TYPE_CMYK, 4);
+ csRGB = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
+ }
+
+ public boolean equals(Object o) {
+ return o != null && o instanceof SimpleCMYKColorSpace;
+ }
+
+ public int hashCode() {
+ return theInstance.hashCode();
+ }
+
+ public float[] toRGB(float[] colorvalue) {
+ float C = colorvalue[0];
+ float M = colorvalue[1];
+ float Y = colorvalue[2];
+ float K = colorvalue[3];
+
+ float K1 = 1.0F - K;
+
+ // Convert from CMYK to linear RGB.
+ float[] rgbvalue = new float[] {K1*(1.0F - C),
+ K1*(1.0F - M),
+ K1*(1.0F - Y)};
+
+ // Convert from linear RGB to sRGB.
+ for (int i = 0; i < 3; i++) {
+ float v = rgbvalue[i];
+
+ if (v < 0.0F) v = 0.0F;
+
+ if (v < 0.0031308F) {
+ rgbvalue[i] = 12.92F * v;
+ } else {
+ if (v > 1.0F) v = 1.0F;
+
+ rgbvalue[i] = (float)(1.055 * Math.pow(v, power1) - 0.055);
+ }
+ }
+
+ return rgbvalue;
+ }
+
+ public float[] fromRGB(float[] rgbvalue) {
+ // Convert from sRGB to linear RGB.
+ for (int i = 0; i < 3; i++) {
+ if (rgbvalue[i] < 0.040449936F) {
+ rgbvalue[i] /= 12.92F;
+ } else {
+ rgbvalue[i] =
+ (float)(Math.pow((rgbvalue[i] + 0.055)/1.055, 2.4));
+ }
+ }
+
+ // Convert from linear RGB to CMYK.
+ float C = 1.0F - rgbvalue[0];
+ float M = 1.0F - rgbvalue[1];
+ float Y = 1.0F - rgbvalue[2];
+ float K = Math.min(C, Math.min(M, Y));
+
+ // If K == 1.0F, then C = M = Y = 1.0F.
+ if(K != 1.0F) {
+ float K1 = 1.0F - K;
+
+ C = (C - K)/K1;
+ M = (M - K)/K1;
+ Y = (Y - K)/K1;
+ } else {
+ C = M = Y = 0.0F;
+ }
+
+ return new float[] {C, M, Y, K};
+ }
+
+ public float[] toCIEXYZ(float[] colorvalue) {
+ return csRGB.toCIEXYZ(toRGB(colorvalue));
+ }
+
+ public float[] fromCIEXYZ(float[] xyzvalue) {
+ return fromRGB(csRGB.fromCIEXYZ(xyzvalue));
+ }
+}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleRenderedImage.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleRenderedImage.java
new file mode 100644
index 00000000000..04d545ba72b
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleRenderedImage.java
@@ -0,0 +1,571 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.common;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.image.RenderedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.SampleModel;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+
+public abstract class SimpleRenderedImage implements RenderedImage {
+ /** The X coordinate of the image's upper-left pixel. */
+ protected int minX;
+
+ /** The Y coordinate of the image's upper-left pixel. */
+ protected int minY;
+
+ /** The image's width in pixels. */
+ protected int width;
+
+ /** The image's height in pixels. */
+ protected int height;
+
+ /** The width of a tile. */
+ protected int tileWidth;
+
+ /** The height of a tile. */
+ protected int tileHeight;
+
+ /** The X coordinate of the upper-left pixel of tile (0, 0). */
+ protected int tileGridXOffset = 0;
+
+ /** The Y coordinate of the upper-left pixel of tile (0, 0). */
+ protected int tileGridYOffset = 0;
+
+ /** The image's SampleModel. */
+ protected SampleModel sampleModel;
+
+ /** The image's ColorModel. */
+ protected ColorModel colorModel;
+
+ /** The image's sources, stored in a Vector. */
+ protected Vector sources = new Vector();
+
+ /** A Hashtable containing the image properties. */
+ protected Hashtable properties = new Hashtable();
+
+ /** Returns the X coordinate of the leftmost column of the image. */
+ public int getMinX() {
+ return minX;
+ }
+
+ /**
+ * Returns the X coordinate of the column immediatetely to the
+ * right of the rightmost column of the image. getMaxX() is
+ * implemented in terms of getMinX() and getWidth() and so does
+ * not need to be implemented by subclasses.
+ */
+ public final int getMaxX() {
+ return getMinX() + getWidth();
+ }
+
+ /** Returns the X coordinate of the uppermost row of the image. */
+ public int getMinY() {
+ return minY;
+ }
+
+ /**
+ * Returns the Y coordinate of the row immediately below the
+ * bottom row of the image. getMaxY() is implemented in terms of
+ * getMinY() and getHeight() and so does not need to be
+ * implemented by subclasses.
+ */
+ public final int getMaxY() {
+ return getMinY() + getHeight();
+ }
+
+ /** Returns the width of the image. */
+ public int getWidth() {
+ return width;
+ }
+
+ /** Returns the height of the image. */
+ public int getHeight() {
+ return height;
+ }
+
+ /** Returns a Rectangle indicating the image bounds. */
+ public Rectangle getBounds() {
+ return new Rectangle(getMinX(), getMinY(), getWidth(), getHeight());
+ }
+
+ /** Returns the width of a tile. */
+ public int getTileWidth() {
+ return tileWidth;
+ }
+
+ /** Returns the height of a tile. */
+ public int getTileHeight() {
+ return tileHeight;
+ }
+
+ /**
+ * Returns the X coordinate of the upper-left pixel of tile (0, 0).
+ */
+ public int getTileGridXOffset() {
+ return tileGridXOffset;
+ }
+
+ /**
+ * Returns the Y coordinate of the upper-left pixel of tile (0, 0).
+ */
+ public int getTileGridYOffset() {
+ return tileGridYOffset;
+ }
+
+ /**
+ * Returns the horizontal index of the leftmost column of tiles.
+ * getMinTileX() is implemented in terms of getMinX()
+ * and so does not need to be implemented by subclasses.
+ */
+ public int getMinTileX() {
+ return XToTileX(getMinX());
+ }
+
+ /**
+ * Returns the horizontal index of the rightmost column of tiles.
+ * getMaxTileX() is implemented in terms of getMaxX()
+ * and so does not need to be implemented by subclasses.
+ */
+ public int getMaxTileX() {
+ return XToTileX(getMaxX() - 1);
+ }
+
+ /**
+ * Returns the number of tiles along the tile grid in the
+ * horizontal direction. getNumXTiles() is implemented in terms
+ * of getMinTileX() and getMaxTileX() and so does not need to be
+ * implemented by subclasses.
+ */
+ public int getNumXTiles() {
+ return getMaxTileX() - getMinTileX() + 1;
+ }
+
+ /**
+ * Returns the vertical index of the uppermost row of tiles. getMinTileY()
+ * is implemented in terms of getMinY() and so does not need to be
+ * implemented by subclasses.
+ */
+ public int getMinTileY() {
+ return YToTileY(getMinY());
+ }
+
+ /**
+ * Returns the vertical index of the bottom row of tiles. getMaxTileY()
+ * is implemented in terms of getMaxY() and so does not need to
+ * be implemented by subclasses.
+ */
+ public int getMaxTileY() {
+ return YToTileY(getMaxY() - 1);
+ }
+
+ /**
+ * Returns the number of tiles along the tile grid in the vertical
+ * direction. getNumYTiles() is implemented in terms
+ * of getMinTileY() and getMaxTileY() and so does not need to be
+ * implemented by subclasses.
+ */
+ public int getNumYTiles() {
+ return getMaxTileY() - getMinTileY() + 1;
+ }
+
+ /** Returns the SampleModel of the image. */
+ public SampleModel getSampleModel() {
+ return sampleModel;
+ }
+
+ /** Returns the ColorModel of the image. */
+ public ColorModel getColorModel() {
+ return colorModel;
+ }
+
+ /**
+ * Gets a property from the property set of this image. If the
+ * property name is not recognized,
+ * java.awt.Image.UndefinedProperty will be returned.
+ *
+ * @param name the name of the property to get, as a
+ * String. @return a reference to the property
+ * Object, or the value
+ * java.awt.Image.UndefinedProperty.
+ */
+ public Object getProperty(String name) {
+ name = name.toLowerCase();
+ Object value = properties.get(name);
+ return value != null ? value : java.awt.Image.UndefinedProperty;
+ }
+
+ /**
+ * Returns a list of the properties recognized by this image. If
+ * no properties are available, null will be
+ * returned.
+ *
+ * @return an array of Strings representing valid
+ * property names.
+ */
+ public String[] getPropertyNames() {
+ String[] names = null;
+
+ if(properties.size() > 0) {
+ names = new String[properties.size()];
+ int index = 0;
+
+ Enumeration e = properties.keys();
+ while (e.hasMoreElements()) {
+ String name = e.nextElement();
+ names[index++] = name;
+ }
+ }
+
+ return names;
+ }
+
+ /**
+ * Returns an array of Strings recognized as names by
+ * this property source that begin with the supplied prefix. If
+ * no property names match, null will be returned.
+ * The comparison is done in a case-independent manner.
+ *
+ *
The default implementation calls
+ * getPropertyNames() and searches the list of names
+ * for matches.
+ *
+ * @return an array of Strings giving the valid
+ * property names.
+ */
+ public String[] getPropertyNames(String prefix) {
+ String propertyNames[] = getPropertyNames();
+ if (propertyNames == null) {
+ return null;
+ }
+
+ prefix = prefix.toLowerCase();
+
+ Vector names = new Vector();
+ for (int i = 0; i < propertyNames.length; i++) {
+ if (propertyNames[i].startsWith(prefix)) {
+ names.addElement(propertyNames[i]);
+ }
+ }
+
+ if (names.size() == 0) {
+ return null;
+ }
+
+ // Copy the strings from the Vector over to a String array.
+ String prefixNames[] = new String[names.size()];
+ int count = 0;
+ for (Iterator it = names.iterator(); it.hasNext(); ) {
+ prefixNames[count++] = it.next();
+ }
+
+ return prefixNames;
+ }
+
+ // Utility methods.
+
+ /**
+ * Converts a pixel's X coordinate into a horizontal tile index
+ * relative to a given tile grid layout specified by its X offset
+ * and tile width.
+ */
+ public static int XToTileX(int x, int tileGridXOffset, int tileWidth) {
+ x -= tileGridXOffset;
+ if (x < 0) {
+ x += 1 - tileWidth; // Force round to -infinity
+ }
+ return x/tileWidth;
+ }
+
+ /**
+ * Converts a pixel's Y coordinate into a vertical tile index
+ * relative to a given tile grid layout specified by its Y offset
+ * and tile height.
+ */
+ public static int YToTileY(int y, int tileGridYOffset, int tileHeight) {
+ y -= tileGridYOffset;
+ if (y < 0) {
+ y += 1 - tileHeight; // Force round to -infinity
+ }
+ return y/tileHeight;
+ }
+
+ /**
+ * Converts a pixel's X coordinate into a horizontal tile index.
+ * This is a convenience method. No attempt is made to detect
+ * out-of-range coordinates.
+ *
+ * @param x the X coordinate of a pixel.
+ * @return the X index of the tile containing the pixel.
+ */
+ public int XToTileX(int x) {
+ return XToTileX(x, getTileGridXOffset(), getTileWidth());
+ }
+
+ /**
+ * Converts a pixel's Y coordinate into a vertical tile index.
+ * This is a convenience method. No attempt is made to detect
+ * out-of-range coordinates.
+ *
+ * @param y the Y coordinate of a pixel.
+ * @return the Y index of the tile containing the pixel.
+ */
+ public int YToTileY(int y) {
+ return YToTileY(y, getTileGridYOffset(), getTileHeight());
+ }
+
+ /**
+ * Converts a horizontal tile index into the X coordinate of its
+ * upper left pixel relative to a given tile grid layout specified
+ * by its X offset and tile width.
+ */
+ public static int tileXToX(int tx, int tileGridXOffset, int tileWidth) {
+ return tx*tileWidth + tileGridXOffset;
+ }
+
+ /**
+ * Converts a vertical tile index into the Y coordinate of
+ * its upper left pixel relative to a given tile grid layout
+ * specified by its Y offset and tile height.
+ */
+ public static int tileYToY(int ty, int tileGridYOffset, int tileHeight) {
+ return ty*tileHeight + tileGridYOffset;
+ }
+
+ /**
+ * Converts a horizontal tile index into the X coordinate of its
+ * upper left pixel. This is a convenience method. No attempt is made
+ * to detect out-of-range indices.
+ *
+ * @param tx the horizontal index of a tile.
+ * @return the X coordinate of the tile's upper left pixel.
+ */
+ public int tileXToX(int tx) {
+ return tx*tileWidth + tileGridXOffset;
+ }
+
+ /**
+ * Converts a vertical tile index into the Y coordinate of its
+ * upper left pixel. This is a convenience method. No attempt is made
+ * to detect out-of-range indices.
+ *
+ * @param ty the vertical index of a tile.
+ * @return the Y coordinate of the tile's upper left pixel.
+ */
+ public int tileYToY(int ty) {
+ return ty*tileHeight + tileGridYOffset;
+ }
+
+ public Vector getSources() {
+ return null;
+ }
+
+ /**
+ * Returns the entire image in a single Raster. For images with
+ * multiple tiles this will require making a copy.
+ *
+ *
The returned Raster is semantically a copy. This means
+ * that updates to the source image will not be reflected in the
+ * returned Raster. For non-writable (immutable) source images,
+ * the returned value may be a reference to the image's internal
+ * data. The returned Raster should be considered non-writable;
+ * any attempt to alter its pixel data (such as by casting it to
+ * WritableRaster or obtaining and modifying its DataBuffer) may
+ * result in undefined behavior. The copyData method should be
+ * used if the returned Raster is to be modified.
+ *
+ * @return a Raster containing a copy of this image's data.
+ */
+ public Raster getData() {
+ Rectangle rect = new Rectangle(getMinX(), getMinY(),
+ getWidth(), getHeight());
+ return getData(rect);
+ }
+
+ /**
+ * Returns an arbitrary rectangular region of the RenderedImage
+ * in a Raster. The rectangle of interest will be clipped against
+ * the image bounds.
+ *
+ *
The returned Raster is semantically a copy. This means
+ * that updates to the source image will not be reflected in the
+ * returned Raster. For non-writable (immutable) source images,
+ * the returned value may be a reference to the image's internal
+ * data. The returned Raster should be considered non-writable;
+ * any attempt to alter its pixel data (such as by casting it to
+ * WritableRaster or obtaining and modifying its DataBuffer) may
+ * result in undefined behavior. The copyData method should be
+ * used if the returned Raster is to be modified.
+ *
+ * @param bounds the region of the RenderedImage to be returned.
+ */
+ public Raster getData(Rectangle bounds) {
+ // Get the image bounds.
+ Rectangle imageBounds = getBounds();
+
+ // Check for parameter validity.
+ if(bounds == null) {
+ bounds = imageBounds;
+ } else if(!bounds.intersects(imageBounds)) {
+ throw new IllegalArgumentException("The provided region doesn't intersect with the image bounds.");
+ }
+
+ // Determine tile limits for the prescribed bounds.
+ int startX = XToTileX(bounds.x);
+ int startY = YToTileY(bounds.y);
+ int endX = XToTileX(bounds.x + bounds.width - 1);
+ int endY = YToTileY(bounds.y + bounds.height - 1);
+
+ // If the bounds are contained in a single tile, return a child
+ // of that tile's Raster.
+ if ((startX == endX) && (startY == endY)) {
+ Raster tile = getTile(startX, startY);
+ return tile.createChild(bounds.x, bounds.y,
+ bounds.width, bounds.height,
+ bounds.x, bounds.y, null);
+ } else {
+ // Recalculate the tile limits if the data bounds are not a
+ // subset of the image bounds.
+ if(!imageBounds.contains(bounds)) {
+ Rectangle xsect = bounds.intersection(imageBounds);
+ startX = XToTileX(xsect.x);
+ startY = YToTileY(xsect.y);
+ endX = XToTileX(xsect.x + xsect.width - 1);
+ endY = YToTileY(xsect.y + xsect.height - 1);
+ }
+
+ // Create a WritableRaster of the desired size
+ SampleModel sm =
+ sampleModel.createCompatibleSampleModel(bounds.width,
+ bounds.height);
+
+ // Translate it
+ WritableRaster dest =
+ Raster.createWritableRaster(sm, bounds.getLocation());
+
+ // Loop over the tiles in the intersection.
+ for (int j = startY; j <= endY; j++) {
+ for (int i = startX; i <= endX; i++) {
+ // Retrieve the tile.
+ Raster tile = getTile(i, j);
+
+ // Create a child of the tile for the intersection of
+ // the tile bounds and the bounds of the requested area.
+ Rectangle tileRect = tile.getBounds();
+ Rectangle intersectRect =
+ bounds.intersection(tile.getBounds());
+ Raster liveRaster = tile.createChild(intersectRect.x,
+ intersectRect.y,
+ intersectRect.width,
+ intersectRect.height,
+ intersectRect.x,
+ intersectRect.y,
+ null);
+
+ // Copy the data from the child.
+ dest.setRect(liveRaster);
+ }
+ }
+
+ return dest;
+ }
+ }
+
+ /**
+ * Copies an arbitrary rectangular region of the RenderedImage
+ * into a caller-supplied WritableRaster. The region to be
+ * computed is determined by clipping the bounds of the supplied
+ * WritableRaster against the bounds of the image. The supplied
+ * WritableRaster must have a SampleModel that is compatible with
+ * that of the image.
+ *
+ *
If the raster argument is null, the entire image will
+ * be copied into a newly-created WritableRaster with a SampleModel
+ * that is compatible with that of the image.
+ *
+ * @param dest a WritableRaster to hold the returned portion of
+ * the image.
+ * @return a reference to the supplied WritableRaster, or to a
+ * new WritableRaster if the supplied one was null.
+ */
+ public WritableRaster copyData(WritableRaster dest) {
+ // Get the image bounds.
+ Rectangle imageBounds = getBounds();
+
+ Rectangle bounds;
+ if (dest == null) {
+ // Create a WritableRaster for the entire image.
+ bounds = imageBounds;
+ Point p = new Point(minX, minY);
+ SampleModel sm =
+ sampleModel.createCompatibleSampleModel(width, height);
+ dest = Raster.createWritableRaster(sm, p);
+ } else {
+ bounds = dest.getBounds();
+ }
+
+ // Determine tile limits for the intersection of the prescribed
+ // bounds with the image bounds.
+ Rectangle xsect = imageBounds.contains(bounds) ?
+ bounds : bounds.intersection(imageBounds);
+ int startX = XToTileX(xsect.x);
+ int startY = YToTileY(xsect.y);
+ int endX = XToTileX(xsect.x + xsect.width - 1);
+ int endY = YToTileY(xsect.y + xsect.height - 1);
+
+ // Loop over the tiles in the intersection.
+ for (int j = startY; j <= endY; j++) {
+ for (int i = startX; i <= endX; i++) {
+ // Retrieve the tile.
+ Raster tile = getTile(i, j);
+
+ // Create a child of the tile for the intersection of
+ // the tile bounds and the bounds of the requested area.
+ Rectangle tileRect = tile.getBounds();
+ Rectangle intersectRect =
+ bounds.intersection(tile.getBounds());
+ Raster liveRaster = tile.createChild(intersectRect.x,
+ intersectRect.y,
+ intersectRect.width,
+ intersectRect.height,
+ intersectRect.x,
+ intersectRect.y,
+ null);
+
+ // Copy the data from the child.
+ dest.setRect(liveRaster);
+ }
+ }
+
+ return dest;
+ }
+}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SingleTileRenderedImage.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SingleTileRenderedImage.java
new file mode 100644
index 00000000000..5dbd5b4a6a2
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SingleTileRenderedImage.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.imageio.plugins.common;
+
+import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+
+/**
+ * A simple class that provides RenderedImage functionality
+ * given a Raster and a ColorModel.
+ */
+public class SingleTileRenderedImage extends SimpleRenderedImage {
+
+ Raster ras;
+
+ /**
+ * Constructs a SingleTileRenderedImage based on a Raster
+ * and a ColorModel.
+ *
+ * @param ras A Raster that will define tile (0, 0) of the image.
+ * @param cm A ColorModel that will serve as the image's
+ * ColorModel.
+ */
+ public SingleTileRenderedImage(Raster ras, ColorModel colorModel) {
+ this.ras = ras;
+
+ this.tileGridXOffset = this.minX = ras.getMinX();
+ this.tileGridYOffset = this.minY = ras.getMinY();
+ this.tileWidth = this.width = ras.getWidth();
+ this.tileHeight = this.height = ras.getHeight();
+ this.sampleModel = ras.getSampleModel();
+ this.colorModel = colorModel;
+ }
+
+ /**
+ * Returns the image's Raster as tile (0, 0).
+ */
+ public Raster getTile(int tileX, int tileY) {
+ if (tileX != 0 || tileY != 0) {
+ throw new IllegalArgumentException("tileX != 0 || tileY != 0");
+ }
+ return ras;
+ }
+}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/iio-plugin.properties b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/iio-plugin.properties
index 181446bca21..0705b99eee8 100644
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/iio-plugin.properties
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/iio-plugin.properties
@@ -8,7 +8,7 @@
# Common properties
ImageUtil0=The supplied Raster does not represent a binary data set.
ImageUtil1=The provided sample model is null.
-SimpleRenderedImage0=The provided region doesn't intersect with the image bounds.
+ImageUtil2=The provided image cannot be encoded using:
GetNumImages0=Input has not been set.
GetNumImages1=seekForwardOnly and allowSearch cannot both be true.
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFAttrInfo.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFAttrInfo.java
new file mode 100644
index 00000000000..8faeaa60785
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFAttrInfo.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.tiff;
+
+import javax.imageio.metadata.IIOMetadataFormat;
+
+public class TIFFAttrInfo {
+ int valueType = IIOMetadataFormat.VALUE_ARBITRARY;
+ int dataType;
+ boolean isRequired = false;
+ int listMinLength = 0;
+ int listMaxLength = Integer.MAX_VALUE;
+
+ public TIFFAttrInfo() { }
+}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFBaseJPEGCompressor.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFBaseJPEGCompressor.java
new file mode 100644
index 00000000000..5bb49a5bef7
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFBaseJPEGCompressor.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.tiff;
+
+import java.awt.Point;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Iterator;
+import javax.imageio.IIOException;
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.metadata.IIOInvalidTreeException;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.spi.ImageWriterSpi;
+import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
+import javax.imageio.stream.ImageOutputStream;
+import javax.imageio.stream.MemoryCacheImageOutputStream;
+import org.w3c.dom.Node;
+
+/**
+ * Base class for all possible forms of JPEG compression in TIFF.
+ */
+public abstract class TIFFBaseJPEGCompressor extends TIFFCompressor {
+
+ // Stream metadata format.
+ protected static final String STREAM_METADATA_NAME =
+ "javax_imageio_jpeg_stream_1.0";
+
+ // Image metadata format.
+ protected static final String IMAGE_METADATA_NAME =
+ "javax_imageio_jpeg_image_1.0";
+
+ // ImageWriteParam passed in.
+ private ImageWriteParam param = null;
+
+ /**
+ * ImageWriteParam for JPEG writer.
+ * May be initialized by {@link #initJPEGWriter()}.
+ */
+ protected JPEGImageWriteParam JPEGParam = null;
+
+ /**
+ * The JPEG writer.
+ * May be initialized by {@link #initJPEGWriter()}.
+ */
+ protected ImageWriter JPEGWriter = null;
+
+ /**
+ * Whether to write abbreviated JPEG streams (default == false).
+ * A subclass which sets this to true should also
+ * initialized {@link #JPEGStreamMetadata}.
+ */
+ protected boolean writeAbbreviatedStream = false;
+
+ /**
+ * Stream metadata equivalent to a tables-only stream such as in
+ * the JPEGTables. Default value is null.
+ * This should be set by any subclass which sets
+ * {@link writeAbbreviatedStream} to true.
+ */
+ protected IIOMetadata JPEGStreamMetadata = null;
+
+ // A pruned image metadata object containing only essential nodes.
+ private IIOMetadata JPEGImageMetadata = null;
+
+ // Array-based output stream.
+ private IIOByteArrayOutputStream baos;
+
+ /**
+ * Removes nonessential nodes from a JPEG native image metadata tree.
+ * All nodes derived from JPEG marker segments other than DHT, DQT,
+ * SOF, SOS segments are removed unless pruneTables is
+ * true in which case the nodes derived from the DHT and
+ * DQT marker segments are also removed.
+ *
+ * @param tree A javax_imageio_jpeg_image_1.0 tree.
+ * @param pruneTables Whether to prune Huffman and quantization tables.
+ * @throws NullPointerException if tree is
+ * null.
+ * @throws IllegalArgumentException if tree is not the root
+ * of a JPEG native image metadata tree.
+ */
+ private static void pruneNodes(Node tree, boolean pruneTables) {
+ if(tree == null) {
+ throw new NullPointerException("tree == null!");
+ }
+ if(!tree.getNodeName().equals(IMAGE_METADATA_NAME)) {
+ throw new IllegalArgumentException
+ ("root node name is not "+IMAGE_METADATA_NAME+"!");
+ }
+
+ // Create list of required nodes.
+ List wantedNodes = new ArrayList();
+ wantedNodes.addAll(Arrays.asList(new String[] {
+ "JPEGvariety", "markerSequence",
+ "sof", "componentSpec",
+ "sos", "scanComponentSpec"
+ }));
+
+ // Add Huffman and quantization table nodes if not pruning tables.
+ if(!pruneTables) {
+ wantedNodes.add("dht");
+ wantedNodes.add("dhtable");
+ wantedNodes.add("dqt");
+ wantedNodes.add("dqtable");
+ }
+
+ IIOMetadataNode iioTree = (IIOMetadataNode)tree;
+
+ List nodes = getAllNodes(iioTree, null);
+ int numNodes = nodes.size();
+
+ for(int i = 0; i < numNodes; i++) {
+ Node node = nodes.get(i);
+ if(!wantedNodes.contains(node.getNodeName())) {
+ node.getParentNode().removeChild(node);
+ }
+ }
+ }
+
+ private static List getAllNodes(IIOMetadataNode root, List nodes) {
+ if(nodes == null) nodes = new ArrayList();
+
+ if(root.hasChildNodes()) {
+ Node sibling = root.getFirstChild();
+ while(sibling != null) {
+ nodes.add(sibling);
+ nodes = getAllNodes((IIOMetadataNode)sibling, nodes);
+ sibling = sibling.getNextSibling();
+ }
+ }
+
+ return nodes;
+ }
+
+ public TIFFBaseJPEGCompressor(String compressionType,
+ int compressionTagValue,
+ boolean isCompressionLossless,
+ ImageWriteParam param) {
+ super(compressionType, compressionTagValue, isCompressionLossless);
+
+ this.param = param;
+ }
+
+ /**
+ * A ByteArrayOutputStream which allows writing to an
+ * ImageOutputStream.
+ */
+ private static class IIOByteArrayOutputStream extends ByteArrayOutputStream {
+ IIOByteArrayOutputStream() {
+ super();
+ }
+
+ IIOByteArrayOutputStream(int size) {
+ super(size);
+ }
+
+ public synchronized void writeTo(ImageOutputStream ios)
+ throws IOException {
+ ios.write(buf, 0, count);
+ }
+ }
+
+ /**
+ * Initializes the JPEGWriter and JPEGParam instance variables.
+ * This method must be called before encode() is invoked.
+ *
+ * @param supportsStreamMetadata Whether the JPEG writer must
+ * support JPEG native stream metadata, i.e., be capable of writing
+ * abbreviated streams.
+ * @param supportsImageMetadata Whether the JPEG writer must
+ * support JPEG native image metadata.
+ */
+ protected void initJPEGWriter(boolean supportsStreamMetadata,
+ boolean supportsImageMetadata) {
+ // Reset the writer to null if it does not match preferences.
+ if(this.JPEGWriter != null &&
+ (supportsStreamMetadata || supportsImageMetadata)) {
+ ImageWriterSpi spi = this.JPEGWriter.getOriginatingProvider();
+ if(supportsStreamMetadata) {
+ String smName = spi.getNativeStreamMetadataFormatName();
+ if(smName == null || !smName.equals(STREAM_METADATA_NAME)) {
+ this.JPEGWriter = null;
+ }
+ }
+ if(this.JPEGWriter != null && supportsImageMetadata) {
+ String imName = spi.getNativeImageMetadataFormatName();
+ if(imName == null || !imName.equals(IMAGE_METADATA_NAME)) {
+ this.JPEGWriter = null;
+ }
+ }
+ }
+
+ // Set the writer.
+ if(this.JPEGWriter == null) {
+ Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
+
+ while(iter.hasNext()) {
+ // Get a writer.
+ ImageWriter writer = iter.next();
+
+ // Verify its metadata support level.
+ if(supportsStreamMetadata || supportsImageMetadata) {
+ ImageWriterSpi spi = writer.getOriginatingProvider();
+ if(supportsStreamMetadata) {
+ String smName =
+ spi.getNativeStreamMetadataFormatName();
+ if(smName == null ||
+ !smName.equals(STREAM_METADATA_NAME)) {
+ // Try the next one.
+ continue;
+ }
+ }
+ if(supportsImageMetadata) {
+ String imName =
+ spi.getNativeImageMetadataFormatName();
+ if(imName == null ||
+ !imName.equals(IMAGE_METADATA_NAME)) {
+ // Try the next one.
+ continue;
+ }
+ }
+ }
+
+ // Set the writer.
+ this.JPEGWriter = writer;
+ break;
+ }
+
+ if(this.JPEGWriter == null) {
+ throw new NullPointerException
+ ("No appropriate JPEG writers found!");
+ }
+ }
+
+ // Initialize the ImageWriteParam.
+ if(this.JPEGParam == null) {
+ if(param != null && param instanceof JPEGImageWriteParam) {
+ JPEGParam = (JPEGImageWriteParam)param;
+ } else {
+ JPEGParam =
+ new JPEGImageWriteParam(writer != null ?
+ writer.getLocale() : null);
+ if (param != null && param.getCompressionMode()
+ == ImageWriteParam.MODE_EXPLICIT) {
+ JPEGParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+ JPEGParam.setCompressionQuality(param.getCompressionQuality());
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieves image metadata with non-core nodes removed.
+ */
+ private IIOMetadata getImageMetadata(boolean pruneTables)
+ throws IIOException {
+ if(JPEGImageMetadata == null &&
+ IMAGE_METADATA_NAME.equals(JPEGWriter.getOriginatingProvider().getNativeImageMetadataFormatName())) {
+ TIFFImageWriter tiffWriter = (TIFFImageWriter)this.writer;
+
+ // Get default image metadata.
+ JPEGImageMetadata =
+ JPEGWriter.getDefaultImageMetadata(tiffWriter.getImageType(),
+ JPEGParam);
+
+ // Get the DOM tree.
+ Node tree = JPEGImageMetadata.getAsTree(IMAGE_METADATA_NAME);
+
+ // Remove unwanted marker segments.
+ try {
+ pruneNodes(tree, pruneTables);
+ } catch(IllegalArgumentException e) {
+ throw new IIOException("Error pruning unwanted nodes", e);
+ }
+
+ // Set the DOM back into the metadata.
+ try {
+ JPEGImageMetadata.setFromTree(IMAGE_METADATA_NAME, tree);
+ } catch(IIOInvalidTreeException e) {
+ throw new IIOException
+ ("Cannot set pruned image metadata!", e);
+ }
+ }
+
+ return JPEGImageMetadata;
+ }
+
+ public final int encode(byte[] b, int off,
+ int width, int height,
+ int[] bitsPerSample,
+ int scanlineStride) throws IOException {
+ if (this.JPEGWriter == null) {
+ throw new IIOException("JPEG writer has not been initialized!");
+ }
+ if (!((bitsPerSample.length == 3
+ && bitsPerSample[0] == 8
+ && bitsPerSample[1] == 8
+ && bitsPerSample[2] == 8)
+ || (bitsPerSample.length == 1
+ && bitsPerSample[0] == 8))) {
+ throw new IIOException("Can only JPEG compress 8- and 24-bit images!");
+ }
+
+ // Set the stream.
+ // The stream has to be wrapped as the Java Image I/O JPEG
+ // ImageWriter flushes the stream at the end of each write()
+ // and this causes problems for the TIFF writer.
+ if (baos == null) {
+ baos = new IIOByteArrayOutputStream();
+ } else {
+ baos.reset();
+ }
+ ImageOutputStream ios = new MemoryCacheImageOutputStream(baos);
+ JPEGWriter.setOutput(ios);
+
+ // Create a DataBuffer.
+ DataBufferByte dbb;
+ if (off == 0) {
+ dbb = new DataBufferByte(b, b.length);
+ } else {
+ //
+ // Workaround for bug in core Java Image I/O JPEG
+ // ImageWriter which cannot handle non-zero offsets.
+ //
+ int bytesPerSegment = scanlineStride * height;
+ byte[] btmp = new byte[bytesPerSegment];
+ System.arraycopy(b, off, btmp, 0, bytesPerSegment);
+ dbb = new DataBufferByte(btmp, bytesPerSegment);
+ off = 0;
+ }
+
+ // Set up the ColorSpace.
+ int[] offsets;
+ ColorSpace cs;
+ if (bitsPerSample.length == 3) {
+ offsets = new int[]{off, off + 1, off + 2};
+ cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ } else {
+ offsets = new int[]{off};
+ cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+ }
+
+ // Create the ColorModel.
+ ColorModel cm = new ComponentColorModel(cs,
+ false,
+ false,
+ Transparency.OPAQUE,
+ DataBuffer.TYPE_BYTE);
+
+ // Create the SampleModel.
+ SampleModel sm
+ = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
+ width, height,
+ bitsPerSample.length,
+ scanlineStride,
+ offsets);
+
+ // Create the WritableRaster.
+ WritableRaster wras
+ = Raster.createWritableRaster(sm, dbb, new Point(0, 0));
+
+ // Create the BufferedImage.
+ BufferedImage bi = new BufferedImage(cm, wras, false, null);
+
+ // Get the pruned JPEG image metadata (may be null).
+ IIOMetadata imageMetadata = getImageMetadata(writeAbbreviatedStream);
+
+ // Compress the image into the output stream.
+ int compDataLength;
+ if (writeAbbreviatedStream) {
+ // Write abbreviated JPEG stream
+
+ // First write the tables-only data.
+ JPEGWriter.prepareWriteSequence(JPEGStreamMetadata);
+ ios.flush();
+
+ // Rewind to the beginning of the byte array.
+ baos.reset();
+
+ // Write the abbreviated image data.
+ IIOImage image = new IIOImage(bi, null, imageMetadata);
+ JPEGWriter.writeToSequence(image, JPEGParam);
+ JPEGWriter.endWriteSequence();
+ } else {
+ // Write complete JPEG stream
+ JPEGWriter.write(null,
+ new IIOImage(bi, null, imageMetadata),
+ JPEGParam);
+ }
+
+ compDataLength = baos.size();
+ baos.writeTo(stream);
+ baos.reset();
+
+ return compDataLength;
+ }
+
+ protected void finalize() throws Throwable {
+ super.finalize();
+ if(JPEGWriter != null) {
+ JPEGWriter.dispose();
+ }
+ }
+}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFCIELabColorConverter.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFCIELabColorConverter.java
new file mode 100644
index 00000000000..bc20c57da69
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFCIELabColorConverter.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.tiff;
+
+public class TIFFCIELabColorConverter extends TIFFColorConverter {
+
+ // XYZ coordinate or reference white (CIE D65)
+ private static final float Xn = 95.047f;
+ private static final float Yn = 100.0f;
+ private static final float Zn = 108.883f;
+
+ private static final float THRESHOLD = (float)Math.pow(0.008856, 1.0/3.0);
+
+ public TIFFCIELabColorConverter() {}
+
+
+ private float clamp(float x) {
+ if (x < 0.0f) {
+ return 0.0f;
+ } else if (x > 100.0f) {
+ return 255.0f;
+ } else {
+ return x*(255.0f/100.0f);
+ }
+ }
+
+ private float clamp2(float x) {
+ if (x < 0.0f) {
+ return 0.0f;
+ } else if (x > 255.0f) {
+ return 255.0f;
+ } else {
+ return x;
+ }
+ }
+
+ public void fromRGB(float r, float g, float b, float[] result) {
+ float X = 0.412453f*r + 0.357580f*g + 0.180423f*b;
+ float Y = 0.212671f*r + 0.715160f*g + 0.072169f*b;
+ float Z = 0.019334f*r + 0.119193f*g + 0.950227f*b;
+
+ float YYn = Y/Yn;
+ float XXn = X/Xn;
+ float ZZn = Z/Zn;
+
+ if (YYn < 0.008856f) {
+ YYn = 7.787f*YYn + 16.0f/116.0f;
+ } else {
+ YYn = (float)Math.pow(YYn, 1.0/3.0);
+ }
+
+ if (XXn < 0.008856f) {
+ XXn = 7.787f*XXn + 16.0f/116.0f;
+ } else {
+ XXn = (float)Math.pow(XXn, 1.0/3.0);
+ }
+
+ if (ZZn < 0.008856f) {
+ ZZn = 7.787f*ZZn + 16.0f/116.0f;
+ } else {
+ ZZn = (float)Math.pow(ZZn, 1.0/3.0);
+ }
+
+ float LStar = 116.0f*YYn - 16.0f;
+ float aStar = 500.0f*(XXn - YYn);
+ float bStar = 200.0f*(YYn - ZZn);
+
+ LStar *= 255.0f/100.0f;
+ if (aStar < 0.0f) {
+ aStar += 256.0f;
+ }
+ if (bStar < 0.0f) {
+ bStar += 256.0f;
+ }
+
+ result[0] = clamp2(LStar);
+ result[1] = clamp2(aStar);
+ result[2] = clamp2(bStar);
+ }
+
+ public void toRGB(float x0, float x1, float x2, float[] rgb) {
+ float LStar = x0*100.0f/255.0f;
+ float aStar = (x1 > 128.0f) ? (x1 - 256.0f) : x1;
+ float bStar = (x2 > 128.0f) ? (x2 - 256.0f) : x2;
+
+ float YYn; // Y/Yn
+ float fY; // 'F' value for Y
+
+ if (LStar < 8.0f) {
+ YYn = LStar/903.3f;
+ fY = 7.787f*YYn + 16.0f/116.0f;
+ } else {
+ float YYn_cubeRoot = (LStar + 16.0f)/116.0f;
+ YYn = YYn_cubeRoot*YYn_cubeRoot*YYn_cubeRoot;
+ fY = (float)Math.pow(YYn, 1.0/3.0);
+ }
+ float Y = YYn*Yn;
+
+ float fX = fY + (aStar/500.0f);
+ float X;
+ if (fX <= THRESHOLD) {
+ X = Xn*(fX - 16.0f/116.0f)/7.787f;
+ } else {
+ X = Xn*fX*fX*fX;
+ }
+
+ float fZ = fY - bStar/200.0f;
+ float Z;
+ if (fZ <= THRESHOLD) {
+ Z = Zn*(fZ - 16.0f/116.0f)/7.787f;
+ } else {
+ Z = Zn*fZ*fZ*fZ;
+ }
+
+ float R = 3.240479f*X - 1.537150f*Y - 0.498535f*Z;
+ float G = -0.969256f*X + 1.875992f*Y + 0.041556f*Z;
+ float B = 0.055648f*X - 0.204043f*Y + 1.057311f*Z;
+
+ rgb[0] = clamp(R);
+ rgb[1] = clamp(G);
+ rgb[2] = clamp(B);
+ }
+}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFColorConverter.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFColorConverter.java
new file mode 100644
index 00000000000..3138974ad00
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFColorConverter.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.tiff;
+
+/**
+ * An abstract class that performs simple color conversion on 3-banded source
+ * images, for use with the TIFF Image I/O plug-in.
+ */
+public abstract class TIFFColorConverter {
+
+ /**
+ * Constructs an instance of a TIFFColorConverter.
+ */
+ public TIFFColorConverter() {}
+
+ /**
+ * Converts an RGB triple into the native color space of this
+ * TIFFColorConverter, and stores the result in the first three
+ * entries of the result array.
+ *
+ * @param r the red value.
+ * @param g the green value.
+ * @param b the blue value.
+ * @param result an array of floats containing three elements.
+ * @throws NullPointerException if result is
+ * null.
+ * @throws ArrayIndexOutOfBoundsException if
+ * result.length < 3.
+ */
+ public abstract void fromRGB(float r, float g, float b, float[] result);
+
+ /**
+ * Converts a triple in the native color space of this
+ * TIFFColorConverter into an RGB triple, and stores the result in
+ * the first three entries of the rgb array.
+ *
+ * @param x0 the value of channel 0.
+ * @param x1 the value of channel 1.
+ * @param x2 the value of channel 2.
+ * @param rgb an array of floats containing three elements.
+ * @throws NullPointerException if rgb is
+ * null.
+ * @throws ArrayIndexOutOfBoundsException if
+ * rgb.length < 3.
+ */
+ public abstract void toRGB(float x0, float x1, float x2, float[] rgb);
+}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFCompressor.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFCompressor.java
new file mode 100644
index 00000000000..cc72f85cea8
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFCompressor.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.tiff;
+
+import java.io.IOException;
+import javax.imageio.ImageWriter;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.stream.ImageOutputStream;
+
+/**
+ * An abstract superclass for pluggable TIFF compressors.
+ */
+public abstract class TIFFCompressor {
+
+ /**
+ * The ImageWriter calling this
+ * TIFFCompressor.
+ */
+ protected ImageWriter writer;
+
+ /**
+ * The IIOMetadata object containing metadata for the
+ * current image.
+ */
+ protected IIOMetadata metadata;
+
+ /**
+ * The name of the compression type supported by this compressor.
+ */
+ protected String compressionType;
+
+ /**
+ * The value to be assigned to the TIFF Compression tag in the
+ * TIFF image metadata.
+ */
+ protected int compressionTagValue;
+
+ /**
+ * Whether the compression is lossless.
+ */
+ protected boolean isCompressionLossless;
+
+ /**
+ * The ImageOutputStream to be written.
+ */
+ protected ImageOutputStream stream;
+
+ /**
+ * Creates a compressor object for use in compressing TIFF data. This
+ * object may be passed to the
+ * {@link TIFFImageWriteParam#setTIFFCompressor(TIFFCompressor)}
+ * method to override the compressor of a supported compression type or
+ * to provide the implementation of the compression algorithm of an
+ * unsupported compression type.
+ *
+ *
The parameters compressionTagValue and
+ * isCompressionLossless are provided to accomodate
+ * compression types which are unknown. A compression type is
+ * "known" if it is either among those already supported by the
+ * TIFF writer (see {@link TIFFImageWriteParam}), or is listed in
+ * the TIFF 6.0 specification but not supported. If the compression
+ * type is unknown, the compressionTagValue and
+ * isCompressionLossless parameters are ignored.
+ *
+ * @param compressionType The name of the compression type.
+ * @param compressionTagValue The value to be assigned to the TIFF
+ * Compression tag in the TIFF image metadata; ignored if
+ * compressionType is a known type.
+ * @param isCompressionLossless Whether the compression is lossless;
+ * ignored if compressionType is a known type.
+ *
+ * @throws NullPointerException if compressionType is
+ * null.
+ * @throws IllegalArgumentException if compressionTagValue is
+ * less 1.
+ */
+ public TIFFCompressor(String compressionType,
+ int compressionTagValue,
+ boolean isCompressionLossless) {
+ if(compressionType == null) {
+ throw new NullPointerException("compressionType == null");
+ } else if(compressionTagValue < 1) {
+ throw new IllegalArgumentException("compressionTagValue < 1");
+ }
+
+ // Set the compression type.
+ this.compressionType = compressionType;
+
+ // Determine whether this type is either defined in the TIFF 6.0
+ // specification or is already supported.
+ int compressionIndex = -1;
+ String[] compressionTypes = TIFFImageWriter.compressionTypes;
+ int len = compressionTypes.length;
+ for(int i = 0; i < len; i++) {
+ if(compressionTypes[i].equals(compressionType)) {
+ // Save the index of the supported type.
+ compressionIndex = i;
+ break;
+ }
+ }
+
+ if(compressionIndex != -1) {
+ // Known compression type.
+ this.compressionTagValue =
+ TIFFImageWriter.compressionNumbers[compressionIndex];
+ this.isCompressionLossless =
+ TIFFImageWriter.isCompressionLossless[compressionIndex];
+ } else {
+ // Unknown compression type.
+ this.compressionTagValue = compressionTagValue;
+ this.isCompressionLossless = isCompressionLossless;
+ }
+ }
+
+ /**
+ * Retrieve the name of the compression type supported by this compressor.
+ *
+ * @return The compression type name.
+ */
+ public String getCompressionType() {
+ return compressionType;
+ }
+
+ /**
+ * Retrieve the value to be assigned to the TIFF Compression tag
+ * in the TIFF image metadata.
+ *
+ * @return The Compression tag value.
+ */
+ public int getCompressionTagValue() {
+ return compressionTagValue;
+ }
+
+ /**
+ * Retrieves a value indicating whether the compression is lossless.
+ *
+ * @return Whether the compression is lossless.
+ */
+ public boolean isCompressionLossless() {
+ return isCompressionLossless;
+ }
+
+ /**
+ * Sets the ImageOutputStream to be written.
+ *
+ * @param stream an ImageOutputStream to be written.
+ *
+ * @see #getStream
+ */
+ public void setStream(ImageOutputStream stream) {
+ this.stream = stream;
+ }
+
+ /**
+ * Returns the ImageOutputStream that will be written.
+ *
+ * @return an ImageOutputStream.
+ *
+ * @see #setStream(ImageOutputStream)
+ */
+ public ImageOutputStream getStream() {
+ return stream;
+ }
+
+ /**
+ * Sets the value of the writer field.
+ *
+ * @param writer the current ImageWriter.
+ *
+ * @see #getWriter()
+ */
+ public void setWriter(ImageWriter writer) {
+ this.writer = writer;
+ }
+
+ /**
+ * Returns the current ImageWriter.
+ *
+ * @return an ImageWriter.
+ *
+ * @see #setWriter(ImageWriter)
+ */
+ public ImageWriter getWriter() {
+ return this.writer;
+ }
+
+ /**
+ * Sets the value of the metadata field.
+ *
+ * @param metadata the IIOMetadata object for the
+ * image being written.
+ *
+ * @see #getMetadata()
+ */
+ public void setMetadata(IIOMetadata metadata) {
+ this.metadata = metadata;
+ }
+
+ /**
+ * Returns the current IIOMetadata object.
+ *
+ * @return the IIOMetadata object for the image being
+ * written.
+ *
+ * @see #setMetadata(IIOMetadata)
+ */
+ public IIOMetadata getMetadata() {
+ return this.metadata;
+ }
+
+ /**
+ * Encodes the supplied image data, writing to the currently set
+ * ImageOutputStream.
+ *
+ * @param b an array of bytes containing the packed
+ * but uncompressed image data.
+ * @param off the starting offset of the data to be written in the
+ * array b.
+ * @param width the width of the rectangle of pixels to be written.
+ * @param height the height of the rectangle of pixels to be written.
+ * @param bitsPerSample an array of ints indicting
+ * the number of bits used to represent each image sample within
+ * a pixel.
+ * @param scanlineStride the number of bytes separating each
+ * row of the input data.
+ *
+ * @return the number of bytes written.
+ *
+ * @throws IOException if the supplied data cannot be encoded by
+ * this TIFFCompressor, or if any I/O error occurs
+ * during writing.
+ */
+ public abstract int encode(byte[] b, int off,
+ int width, int height,
+ int[] bitsPerSample,
+ int scanlineStride) throws IOException;
+
+}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java
new file mode 100644
index 00000000000..fdc29f26016
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java
@@ -0,0 +1,2816 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.tiff;
+
+import java.awt.Rectangle;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.ComponentSampleModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferDouble;
+import java.awt.image.DataBufferFloat;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DataBufferShort;
+import java.awt.image.DataBufferUShort;
+import java.awt.image.MultiPixelPackedSampleModel;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.WritableRaster;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.ByteOrder;
+import javax.imageio.IIOException;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.MemoryCacheImageInputStream;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import com.sun.imageio.plugins.common.ImageUtil;
+import com.sun.imageio.plugins.common.BogusColorSpace;
+import com.sun.imageio.plugins.common.SimpleCMYKColorSpace;
+
+/**
+ * A class defining a pluggable TIFF decompressor.
+ *
+ *
The mapping between source and destination Y coordinates is
+ * given by the equations:
+ *
+ *
+ *
+ * Note that the mapping from source coordinates to destination
+ * coordinates is not one-to-one if subsampling is being used, since
+ * only certain source pixels are to be copied to the
+ * destination. However, * the inverse mapping is always one-to-one:
+ *
+ *
Decompressors may be written with various levels of complexity.
+ * The most complex decompressors will override the
+ * decode method, and will perform all the work of
+ * decoding, subsampling, offsetting, clipping, and format conversion.
+ * This approach may be the most efficient, since it is possible to
+ * avoid the use of extra image buffers, and it may be possible to
+ * avoid decoding portions of the image that will not be copied into
+ * the destination.
+ *
+ *
Less ambitious decompressors may override the
+ * decodeRaw method, which is responsible for
+ * decompressing the entire tile or strip into a byte array (or other
+ * appropriate datatype). The default implementation of
+ * decode will perform all necessary setup of buffers,
+ * call decodeRaw to perform the actual decoding, perform
+ * subsampling, and copy the results into the final destination image.
+ * Where possible, it will pass the real image buffer to
+ * decodeRaw in order to avoid making an extra copy.
+ *
+ *
Slightly more ambitious decompressors may override
+ * decodeRaw, but avoid writing pixels that will be
+ * discarded in the subsampling phase.
+ */
+public abstract class TIFFDecompressor {
+
+ /**
+ * The ImageReader calling this
+ * TIFFDecompressor.
+ */
+ protected ImageReader reader;
+
+ /**
+ * The IIOMetadata object containing metadata for the
+ * current image.
+ */
+ protected IIOMetadata metadata;
+
+ /**
+ * The value of the PhotometricInterpretation tag.
+ * Legal values are {@link
+ * BaselineTIFFTagSet#PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO },
+ * {@link
+ * BaselineTIFFTagSet#PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO},
+ * {@link BaselineTIFFTagSet#PHOTOMETRIC_INTERPRETATION_RGB},
+ * {@link
+ * BaselineTIFFTagSet#PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR},
+ * {@link
+ * BaselineTIFFTagSet#PHOTOMETRIC_INTERPRETATION_TRANSPARENCY_MASK},
+ * {@link BaselineTIFFTagSet#PHOTOMETRIC_INTERPRETATION_Y_CB_CR},
+ * {@link BaselineTIFFTagSet#PHOTOMETRIC_INTERPRETATION_CIELAB},
+ * {@link BaselineTIFFTagSet#PHOTOMETRIC_INTERPRETATION_ICCLAB},
+ * or other value defined by a TIFF extension.
+ */
+ protected int photometricInterpretation;
+
+ /**
+ * The value of the Compression tag. Legal values are
+ * {@link BaselineTIFFTagSet#COMPRESSION_NONE}, {@link
+ * BaselineTIFFTagSet#COMPRESSION_CCITT_RLE}, {@link
+ * BaselineTIFFTagSet#COMPRESSION_CCITT_T_4}, {@link
+ * BaselineTIFFTagSet#COMPRESSION_CCITT_T_6}, {@link
+ * BaselineTIFFTagSet#COMPRESSION_LZW}, {@link
+ * BaselineTIFFTagSet#COMPRESSION_OLD_JPEG}, {@link
+ * BaselineTIFFTagSet#COMPRESSION_JPEG}, {@link
+ * BaselineTIFFTagSet#COMPRESSION_ZLIB}, {@link
+ * BaselineTIFFTagSet#COMPRESSION_PACKBITS}, {@link
+ * BaselineTIFFTagSet#COMPRESSION_DEFLATE}, or other value
+ * defined by a TIFF extension.
+ */
+ protected int compression;
+
+ /**
+ * true if the image is encoded using separate planes.
+ */
+ protected boolean planar;
+
+ /**
+ * The value of the SamplesPerPixel tag.
+ */
+ protected int samplesPerPixel;
+
+ /**
+ * The value of the BitsPerSample tag.
+ *
+ */
+ protected int[] bitsPerSample;
+
+ /**
+ * The value of the SampleFormat tag. Legal values
+ * are {@link BaselineTIFFTagSet#SAMPLE_FORMAT_UNSIGNED_INTEGER},
+ * {@link BaselineTIFFTagSet#SAMPLE_FORMAT_SIGNED_INTEGER}, {@link
+ * BaselineTIFFTagSet#SAMPLE_FORMAT_FLOATING_POINT}, {@link
+ * BaselineTIFFTagSet#SAMPLE_FORMAT_UNDEFINED}, or other value
+ * defined by a TIFF extension.
+ */
+ protected int[] sampleFormat =
+ new int[] {BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER};
+
+ /**
+ * The value of the ExtraSamples tag. Legal values
+ * are {@link BaselineTIFFTagSet#EXTRA_SAMPLES_UNSPECIFIED},
+ * {@link BaselineTIFFTagSet#EXTRA_SAMPLES_ASSOCIATED_ALPHA},
+ * {@link BaselineTIFFTagSet#EXTRA_SAMPLES_UNASSOCIATED_ALPHA},
+ * or other value defined by a TIFF extension.
+ */
+ protected int[] extraSamples;
+
+ /**
+ * The value of the ColorMap tag.
+ *
+ */
+ protected char[] colorMap;
+
+ // Region of input stream containing the data
+
+ /**
+ * The ImageInputStream containing the TIFF source
+ * data.
+ */
+ protected ImageInputStream stream;
+
+ /**
+ * The offset in the source ImageInputStream of the
+ * start of the data to be decompressed.
+ */
+ protected long offset;
+
+ /**
+ * The number of bytes of data from the source
+ * ImageInputStream to be decompressed.
+ */
+ protected int byteCount;
+
+ // Region of the file image represented in the stream
+ // This is unaffected by subsampling
+
+ /**
+ * The X coordinate of the upper-left pixel of the source region
+ * being decoded from the source stream. This value is not affected
+ * by source subsampling.
+ */
+ protected int srcMinX;
+
+ /**
+ * The Y coordinate of the upper-left pixel of the source region
+ * being decoded from the source stream. This value is not affected
+ * by source subsampling.
+ */
+ protected int srcMinY;
+
+ /**
+ * The width of the source region being decoded from the source
+ * stream. This value is not affected by source subsampling.
+ */
+ protected int srcWidth;
+
+ /**
+ * The height of the source region being decoded from the source
+ * stream. This value is not affected by source subsampling.
+ */
+ protected int srcHeight;
+
+ // Subsampling to be performed
+
+ /**
+ * The source X offset used, along with dstXOffset
+ * and subsampleX, to map between horizontal source
+ * and destination pixel coordinates.
+ */
+ protected int sourceXOffset;
+
+ /**
+ * The horizontal destination offset used, along with
+ * sourceXOffset and subsampleX, to map
+ * between horizontal source and destination pixel coordinates.
+ * See the comment for {@link #sourceXOffset sourceXOffset} for
+ * the mapping equations.
+ */
+ protected int dstXOffset;
+
+ /**
+ * The source Y offset used, along with dstYOffset
+ * and subsampleY, to map between vertical source and
+ * destination pixel coordinates.
+ */
+ protected int sourceYOffset;
+
+ /**
+ * The vertical destination offset used, along with
+ * sourceYOffset and subsampleY, to map
+ * between horizontal source and destination pixel coordinates.
+ * See the comment for {@link #sourceYOffset sourceYOffset} for
+ * the mapping equations.
+ */
+ protected int dstYOffset;
+
+ /**
+ * The horizontal subsampling factor. A factor of 1 means that
+ * every column is copied to the destination; a factor of 2 means
+ * that every second column is copied, etc.
+ */
+ protected int subsampleX;
+
+ /**
+ * The vertical subsampling factor. A factor of 1 means that
+ * every row is copied to the destination; a factor of 2 means
+ * that every second row is copied, etc.
+ */
+ protected int subsampleY;
+
+ // Band subsetting/rearrangement
+
+ /**
+ * The sequence of source bands that are to be copied into the
+ * destination.
+ */
+ protected int[] sourceBands;
+
+ /**
+ * The sequence of destination bands to receive the source data.
+ */
+ protected int[] destinationBands;
+
+ // Destination for decodeRaw
+
+ /**
+ * A BufferedImage for the decodeRaw
+ * method to write into.
+ */
+ protected BufferedImage rawImage;
+
+ // Destination
+
+ /**
+ * The final destination image.
+ */
+ protected BufferedImage image;
+
+ /**
+ * The X coordinate of the upper left pixel to be written in the
+ * destination image.
+ */
+ protected int dstMinX;
+
+ /**
+ * The Y coordinate of the upper left pixel to be written in the
+ * destination image.
+ */
+ protected int dstMinY;
+
+ /**
+ * The width of the region of the destination image to be written.
+ */
+ protected int dstWidth;
+
+ /**
+ * The height of the region of the destination image to be written.
+ */
+ protected int dstHeight;
+
+ // Region of source contributing to the destination
+
+ /**
+ * The X coordinate of the upper-left source pixel that will
+ * actually be copied into the destination image, taking into
+ * account all subsampling, offsetting, and clipping. That is,
+ * the pixel at (activeSrcMinX,
+ * activeSrcMinY) is to be copied into the
+ * destination pixel at (dstMinX,
+ * dstMinY).
+ *
+ *
The pixels in the source region to be copied are
+ * those with X coordinates of the form activeSrcMinX +
+ * k*subsampleX, where k is an integer such
+ * that 0 ≤ k < dstWidth.
+ */
+ protected int activeSrcMinX;
+
+ /**
+ * The Y coordinate of the upper-left source pixel that will
+ * actually be copied into the destination image, taking into account
+ * all subsampling, offsetting, and clipping.
+ *
+ *
The pixels in the source region to be copied are
+ * those with Y coordinates of the form activeSrcMinY +
+ * k*subsampleY, where k is an integer such
+ * that 0 ≤ k < dstHeight.
+ */
+ protected int activeSrcMinY;
+
+ /**
+ * The width of the source region that will actually be copied
+ * into the destination image, taking into account all
+ * susbampling, offsetting, and clipping.
+ *
+ *
The active source width will always be equal to
+ * (dstWidth - 1)*subsampleX + 1.
+ */
+ protected int activeSrcWidth;
+
+ /**
+ * The height of the source region that will actually be copied
+ * into the destination image, taking into account all
+ * susbampling, offsetting, and clipping.
+ *
+ *
The active source height will always be equal to
+ * (dstHeight - 1)*subsampleY + 1.
+ */
+ protected int activeSrcHeight;
+
+ /**
+ * A TIFFColorConverter object describing the color space of
+ * the encoded pixel data, or null.
+ */
+ protected TIFFColorConverter colorConverter;
+
+ private boolean isBilevel;
+ private boolean isContiguous;
+ private boolean isImageSimple;
+ private boolean adjustBitDepths;
+ private int[][] bitDepthScale;
+
+ // source pixel at (sx, sy) should map to dst pixel (dx, dy), where:
+ //
+ // dx = (sx - sourceXOffset)/subsampleX + dstXOffset;
+ // dy = (sy - sourceYOffset)/subsampleY + dstYOffset;
+ //
+ // Note that this mapping is many-to-one. Source pixels such that
+ // (sx - sourceXOffset) % subsampleX != 0 should not be copied
+ // (and similarly for y).
+ //
+ // The backwards mapping from dest to source is one-to-one:
+ //
+ // sx = (dx - dstXOffset)*subsampleX + sourceXOffset;
+ // sy = (dy - dstYOffset)*subsampleY + sourceYOffset;
+ //
+ // The reader will always hand us the full source region as it
+ // exists in the file. It will take care of clipping the dest region
+ // to exactly those dest pixels that are present in the source region.
+
+ /**
+ * Create a PixelInterleavedSampleModel for use in creating
+ * an ImageTypeSpecifier. Its dimensions will be 1x1 and
+ * it will have ascending band offsets as {0, 1, 2, ..., numBands}.
+ *
+ * @param dataType The data type (DataBuffer.TYPE_*).
+ * @param numBands The number of bands.
+ * @return A PixelInterleavedSampleModel.
+ */
+ static SampleModel createInterleavedSM(int dataType,
+ int numBands) {
+ int[] bandOffsets = new int[numBands];
+ for(int i = 0; i < numBands; i++) {
+ bandOffsets[i] = i;
+ }
+ return new PixelInterleavedSampleModel(dataType,
+ 1, // width
+ 1, // height
+ numBands, // pixelStride,
+ numBands, // scanlineStride
+ bandOffsets);
+ }
+
+ /**
+ * Create a ComponentColorModel for use in creating
+ * an ImageTypeSpecifier.
+ */
+ // This code was copied from javax.imageio.ImageTypeSpecifier.
+ static ColorModel createComponentCM(ColorSpace colorSpace,
+ int numBands,
+ int dataType,
+ boolean hasAlpha,
+ boolean isAlphaPremultiplied) {
+ int transparency =
+ hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE;
+
+ int[] numBits = new int[numBands];
+ int bits = DataBuffer.getDataTypeSize(dataType);
+
+ for (int i = 0; i < numBands; i++) {
+ numBits[i] = bits;
+ }
+
+ return new ComponentColorModel(colorSpace,
+ numBits,
+ hasAlpha,
+ isAlphaPremultiplied,
+ transparency,
+ dataType);
+ }
+
+ private static int createMask(int[] bitsPerSample, int band) {
+ int mask = (1 << bitsPerSample[band]) - 1;
+ for (int i = band + 1; i < bitsPerSample.length; i++) {
+ mask <<= bitsPerSample[i];
+ }
+
+ return mask;
+ }
+
+ private static int getDataTypeFromNumBits(int numBits, boolean isSigned) {
+ int dataType;
+
+ if (numBits <= 8) {
+ dataType = DataBuffer.TYPE_BYTE;
+ } else if (numBits <= 16) {
+ dataType = isSigned ?
+ DataBuffer.TYPE_SHORT : DataBuffer.TYPE_USHORT;
+ } else {
+ dataType = DataBuffer.TYPE_INT;
+ }
+
+ return dataType;
+ }
+
+ private static boolean areIntArraysEqual(int[] a, int[] b) {
+ if(a == null || b == null) {
+ if(a == null && b == null) {
+ return true;
+ } else { // one is null and one is not
+ return false;
+ }
+ }
+
+ if(a.length != b.length) {
+ return false;
+ }
+
+ int length = a.length;
+ for(int i = 0; i < length; i++) {
+ if(a[i] != b[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Return the number of bits occupied by dataType
+ * which must be one of the DataBufferTYPEs.
+ */
+ private static int getDataTypeSize(int dataType) throws IIOException {
+ int dataTypeSize = 0;
+ switch(dataType) {
+ case DataBuffer.TYPE_BYTE:
+ dataTypeSize = 8;
+ break;
+ case DataBuffer.TYPE_SHORT:
+ case DataBuffer.TYPE_USHORT:
+ dataTypeSize = 16;
+ break;
+ case DataBuffer.TYPE_INT:
+ case DataBuffer.TYPE_FLOAT:
+ dataTypeSize = 32;
+ break;
+ case DataBuffer.TYPE_DOUBLE:
+ dataTypeSize = 64;
+ break;
+ default:
+ throw new IIOException("Unknown data type "+dataType);
+ }
+
+ return dataTypeSize;
+ }
+
+ /**
+ * Returns the number of bits per pixel.
+ */
+ private static int getBitsPerPixel(SampleModel sm) {
+ int bitsPerPixel = 0;
+ int[] sampleSize = sm.getSampleSize();
+ int numBands = sampleSize.length;
+ for(int i = 0; i < numBands; i++) {
+ bitsPerPixel += sampleSize[i];
+ }
+ return bitsPerPixel;
+ }
+
+ /**
+ * Returns whether all samples have the same number of bits.
+ */
+ private static boolean areSampleSizesEqual(SampleModel sm) {
+ boolean allSameSize = true;
+ int[] sampleSize = sm.getSampleSize();
+ int sampleSize0 = sampleSize[0];
+ int numBands = sampleSize.length;
+
+ for(int i = 1; i < numBands; i++) {
+ if(sampleSize[i] != sampleSize0) {
+ allSameSize = false;
+ break;
+ }
+ }
+
+ return allSameSize;
+ }
+
+ /**
+ * Determines whether the DataBuffer is filled without
+ * any interspersed padding bits.
+ */
+ private static boolean isDataBufferBitContiguous(SampleModel sm)
+ throws IIOException {
+ int dataTypeSize = getDataTypeSize(sm.getDataType());
+
+ if(sm instanceof ComponentSampleModel) {
+ int numBands = sm.getNumBands();
+ for(int i = 0; i < numBands; i++) {
+ if(sm.getSampleSize(i) != dataTypeSize) {
+ // Sample does not fill data element.
+ return false;
+ }
+ }
+ } else if(sm instanceof MultiPixelPackedSampleModel) {
+ MultiPixelPackedSampleModel mppsm =
+ (MultiPixelPackedSampleModel)sm;
+ if(dataTypeSize % mppsm.getPixelBitStride() != 0) {
+ // Pixels do not fill the data element.
+ return false;
+ }
+ } else if(sm instanceof SinglePixelPackedSampleModel) {
+ SinglePixelPackedSampleModel sppsm =
+ (SinglePixelPackedSampleModel)sm;
+ int numBands = sm.getNumBands();
+ int numBits = 0;
+ for(int i = 0; i < numBands; i++) {
+ numBits += sm.getSampleSize(i);
+ }
+ if(numBits != dataTypeSize) {
+ // Pixel does not fill the data element.
+ return false;
+ }
+ } else {
+ // Unknown SampleModel class.
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Reformats data read as bytes into a short or int buffer.
+ */
+ private static void reformatData(byte[] buf,
+ int bytesPerRow,
+ int numRows,
+ short[] shortData,
+ int[] intData,
+ int outOffset,
+ int outStride)
+ throws IIOException {
+
+ if(shortData != null) {
+ int inOffset = 0;
+ int shortsPerRow = bytesPerRow/2;
+ int numExtraBytes = bytesPerRow % 2;
+ for(int j = 0; j < numRows; j++) {
+ int k = outOffset;
+ for(int i = 0; i < shortsPerRow; i++) {
+ shortData[k++] =
+ (short)(((buf[inOffset++]&0xff) << 8) |
+ (buf[inOffset++]&0xff));
+ }
+ if(numExtraBytes != 0) {
+ shortData[k++] = (short)((buf[inOffset++]&0xff) << 8);
+ }
+ outOffset += outStride;
+ }
+ } else if(intData != null) {
+ int inOffset = 0;
+ int intsPerRow = bytesPerRow/4;
+ int numExtraBytes = bytesPerRow % 4;
+ for(int j = 0; j < numRows; j++) {
+ int k = outOffset;
+ for(int i = 0; i < intsPerRow; i++) {
+ intData[k++] =
+ ((buf[inOffset++]&0xff) << 24) |
+ ((buf[inOffset++]&0xff) << 16) |
+ ((buf[inOffset++]&0xff) << 8) |
+ (buf[inOffset++]&0xff);
+ }
+ if(numExtraBytes != 0) {
+ int shift = 24;
+ int ival = 0;
+ for(int b = 0; b < numExtraBytes; b++) {
+ ival |= (buf[inOffset++]&0xff) << shift;
+ shift -= 8;
+ }
+ intData[k++] = ival;
+ }
+ outOffset += outStride;
+ }
+ } else {
+ throw new IIOException("shortData == null && intData == null!");
+ }
+ }
+
+ /**
+ * Reformats bit-discontiguous data into the DataBuffer
+ * of the supplied WritableRaster.
+ */
+ private static void reformatDiscontiguousData(byte[] buf,
+ int stride,
+ int w,
+ int h,
+ WritableRaster raster)
+ throws IOException {
+
+ // Get SampleModel info.
+ SampleModel sm = raster.getSampleModel();
+ int numBands = sm.getNumBands();
+ int[] sampleSize = sm.getSampleSize();
+
+ // Initialize input stream.
+ ByteArrayInputStream is = new ByteArrayInputStream(buf);
+ ImageInputStream iis = new MemoryCacheImageInputStream(is);
+
+ // Reformat.
+ long iisPosition = 0L;
+ int y = raster.getMinY();
+ for(int j = 0; j < h; j++, y++) {
+ iis.seek(iisPosition);
+ int x = raster.getMinX();
+ for(int i = 0; i < w; i++, x++) {
+ for(int b = 0; b < numBands; b++) {
+ long bits = iis.readBits(sampleSize[b]);
+ raster.setSample(x, y, b, (int)bits);
+ }
+ }
+ iisPosition += stride;
+ }
+ }
+
+ /**
+ * A utility method that returns an
+ * ImageTypeSpecifier suitable for decoding an image
+ * with the given parameters.
+ *
+ * @param photometricInterpretation the value of the
+ * PhotometricInterpretation field.
+ * @param compression the value of the Compression field.
+ * @param samplesPerPixel the value of the
+ * SamplesPerPixel field.
+ * @param bitsPerSample the value of the BitsPerSample field.
+ * @param sampleFormat the value of the SampleFormat field.
+ * @param extraSamples the value of the ExtraSamples field.
+ * @param colorMap the value of the ColorMap field.
+ *
+ * @return a suitable ImageTypeSpecifier, or
+ * null if it is not possible to create one.
+ */
+ public static ImageTypeSpecifier
+ getRawImageTypeSpecifier(int photometricInterpretation,
+ int compression,
+ int samplesPerPixel,
+ int[] bitsPerSample,
+ int[] sampleFormat,
+ int[] extraSamples,
+ char[] colorMap) {
+
+ //
+ // Types to support:
+ //
+ // 1, 2, 4, 8, or 16 bit grayscale or indexed
+ // 8,8-bit gray+alpha
+ // 16,16-bit gray+alpha
+ // 8,8,8-bit RGB
+ // 8,8,8,8-bit RGB+alpha
+ // 16,16,16-bit RGB
+ // 16,16,16,16-bit RGB+alpha
+ // R+G+B = 8-bit RGB
+ // R+G+B+A = 8-bit RGB
+ // R+G+B = 16-bit RGB
+ // R+G+B+A = 16-bit RGB
+ // 8X-bits/sample, arbitrary numBands.
+ // Arbitrary non-indexed, non-float layouts (discontiguous).
+ //
+
+ // Band-sequential
+
+ // 1, 2, 4, 8, or 16 bit grayscale or indexed images
+ if (samplesPerPixel == 1 &&
+ (bitsPerSample[0] == 1 ||
+ bitsPerSample[0] == 2 ||
+ bitsPerSample[0] == 4 ||
+ bitsPerSample[0] == 8 ||
+ bitsPerSample[0] == 16)) {
+
+ // 2 and 16 bits images are not in the baseline
+ // specification, but we will allow them anyway
+ // since they fit well into Java2D
+ //
+ // this raises the issue of how to write such images...
+
+ if (colorMap == null) {
+ // Grayscale
+ boolean isSigned = (sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER);
+ int dataType;
+ if (bitsPerSample[0] <= 8) {
+ dataType = DataBuffer.TYPE_BYTE;
+ } else {
+ dataType = sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER ?
+ DataBuffer.TYPE_SHORT :
+ DataBuffer.TYPE_USHORT;
+ }
+
+ return ImageTypeSpecifier.createGrayscale(bitsPerSample[0],
+ dataType,
+ isSigned);
+ } else {
+ // Indexed
+ int mapSize = 1 << bitsPerSample[0];
+ byte[] redLut = new byte[mapSize];
+ byte[] greenLut = new byte[mapSize];
+ byte[] blueLut = new byte[mapSize];
+ byte[] alphaLut = null;
+
+ int idx = 0;
+ for (int i = 0; i < mapSize; i++) {
+ redLut[i] = (byte)((colorMap[i]*255)/65535);
+ greenLut[i] = (byte)((colorMap[mapSize + i]*255)/65535);
+ blueLut[i] = (byte)((colorMap[2*mapSize + i]*255)/65535);
+ }
+
+ int dataType = bitsPerSample[0] == 8 ?
+ DataBuffer.TYPE_BYTE : DataBuffer.TYPE_USHORT;
+ return ImageTypeSpecifier.createIndexed(redLut,
+ greenLut,
+ blueLut,
+ alphaLut,
+ bitsPerSample[0],
+ dataType);
+ }
+ }
+
+ // 8-bit gray-alpha
+ if (samplesPerPixel == 2 &&
+ bitsPerSample[0] == 8 &&
+ bitsPerSample[1] == 8) {
+ int dataType = DataBuffer.TYPE_BYTE;
+ boolean alphaPremultiplied = false;
+ if (extraSamples != null &&
+ extraSamples[0] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) {
+ alphaPremultiplied = true;
+ }
+ return ImageTypeSpecifier.createGrayscale(8,
+ dataType,
+ false,
+ alphaPremultiplied);
+ }
+
+ // 16-bit gray-alpha
+ if (samplesPerPixel == 2 &&
+ bitsPerSample[0] == 16 &&
+ bitsPerSample[1] == 16) {
+ int dataType = sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER ?
+ DataBuffer.TYPE_SHORT :
+ DataBuffer.TYPE_USHORT;
+ boolean alphaPremultiplied = false;
+ if (extraSamples != null &&
+ extraSamples[0] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) {
+ alphaPremultiplied = true;
+ }
+ boolean isSigned = dataType == DataBuffer.TYPE_SHORT;
+ return ImageTypeSpecifier.createGrayscale(16,
+ dataType,
+ isSigned,
+ alphaPremultiplied);
+ }
+
+ ColorSpace rgb = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+
+ // 8-bit RGB
+ if (samplesPerPixel == 3 &&
+ bitsPerSample[0] == 8 &&
+ bitsPerSample[1] == 8 &&
+ bitsPerSample[2] == 8) {
+ int[] bandOffsets = new int[3];
+ bandOffsets[0] = 0;
+ bandOffsets[1] = 1;
+ bandOffsets[2] = 2;
+ int dataType = DataBuffer.TYPE_BYTE;
+ ColorSpace theColorSpace;
+ if((photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR &&
+ compression != BaselineTIFFTagSet.COMPRESSION_JPEG &&
+ compression != BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) ||
+ photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB) {
+ theColorSpace =
+ ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
+ } else {
+ theColorSpace = rgb;
+ }
+ return ImageTypeSpecifier.createInterleaved(theColorSpace,
+ bandOffsets,
+ dataType,
+ false,
+ false);
+ }
+
+ // 8-bit RGBA
+ if (samplesPerPixel == 4 &&
+ bitsPerSample[0] == 8 &&
+ bitsPerSample[1] == 8 &&
+ bitsPerSample[2] == 8 &&
+ bitsPerSample[3] == 8) {
+ int[] bandOffsets = new int[4];
+ bandOffsets[0] = 0;
+ bandOffsets[1] = 1;
+ bandOffsets[2] = 2;
+ bandOffsets[3] = 3;
+ int dataType = DataBuffer.TYPE_BYTE;
+
+ ColorSpace theColorSpace;
+ boolean hasAlpha;
+ boolean alphaPremultiplied = false;
+ if(photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CMYK) {
+ theColorSpace = SimpleCMYKColorSpace.getInstance();
+ hasAlpha = false;
+ } else {
+ theColorSpace = rgb;
+ hasAlpha = true;
+ if (extraSamples != null &&
+ extraSamples[0] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) {
+ alphaPremultiplied = true;
+ }
+ }
+
+ return ImageTypeSpecifier.createInterleaved(theColorSpace,
+ bandOffsets,
+ dataType,
+ hasAlpha,
+ alphaPremultiplied);
+ }
+
+ // 16-bit RGB
+ if (samplesPerPixel == 3 &&
+ bitsPerSample[0] == 16 &&
+ bitsPerSample[1] == 16 &&
+ bitsPerSample[2] == 16) {
+ int[] bandOffsets = new int[3];
+ bandOffsets[0] = 0;
+ bandOffsets[1] = 1;
+ bandOffsets[2] = 2;
+ int dataType = sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER ?
+ DataBuffer.TYPE_SHORT :
+ DataBuffer.TYPE_USHORT;
+ return ImageTypeSpecifier.createInterleaved(rgb,
+ bandOffsets,
+ dataType,
+ false,
+ false);
+ }
+
+ // 16-bit RGBA
+ if (samplesPerPixel == 4 &&
+ bitsPerSample[0] == 16 &&
+ bitsPerSample[1] == 16 &&
+ bitsPerSample[2] == 16 &&
+ bitsPerSample[3] == 16) {
+ int[] bandOffsets = new int[4];
+ bandOffsets[0] = 0;
+ bandOffsets[1] = 1;
+ bandOffsets[2] = 2;
+ bandOffsets[3] = 3;
+ int dataType = sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER ?
+ DataBuffer.TYPE_SHORT :
+ DataBuffer.TYPE_USHORT;
+
+ boolean alphaPremultiplied = false;
+ if (extraSamples != null &&
+ extraSamples[0] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) {
+ alphaPremultiplied = true;
+ }
+ return ImageTypeSpecifier.createInterleaved(rgb,
+ bandOffsets,
+ dataType,
+ true,
+ alphaPremultiplied);
+ }
+
+ // Compute bits per pixel.
+ int totalBits = 0;
+ for (int i = 0; i < bitsPerSample.length; i++) {
+ totalBits += bitsPerSample[i];
+ }
+
+ // Packed: 3- or 4-band, 8- or 16-bit.
+ if ((samplesPerPixel == 3 || samplesPerPixel == 4) &&
+ (totalBits == 8 || totalBits == 16)) {
+ int redMask = createMask(bitsPerSample, 0);
+ int greenMask = createMask(bitsPerSample, 1);
+ int blueMask = createMask(bitsPerSample, 2);
+ int alphaMask = (samplesPerPixel == 4) ?
+ createMask(bitsPerSample, 3) : 0;
+ int transferType = totalBits == 8 ?
+ DataBuffer.TYPE_BYTE : DataBuffer.TYPE_USHORT;
+ boolean alphaPremultiplied = false;
+ if (extraSamples != null &&
+ extraSamples[0] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) {
+ alphaPremultiplied = true;
+ }
+ return ImageTypeSpecifier.createPacked(rgb,
+ redMask,
+ greenMask,
+ blueMask,
+ alphaMask,
+ transferType,
+ alphaPremultiplied);
+ }
+
+ // Generic components with 8X bits per sample.
+ if(bitsPerSample[0] % 8 == 0) {
+ // Check whether all bands have same bit depth.
+ boolean allSameBitDepth = true;
+ for(int i = 1; i < bitsPerSample.length; i++) {
+ if(bitsPerSample[i] != bitsPerSample[i-1]) {
+ allSameBitDepth = false;
+ break;
+ }
+ }
+
+ // Proceed if all bands have same bit depth.
+ if(allSameBitDepth) {
+ // Determine the data type.
+ int dataType = -1;
+ boolean isDataTypeSet = false;
+ switch(bitsPerSample[0]) {
+ case 8:
+ if(sampleFormat[0] !=
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ // Ignore whether signed or unsigned:
+ // treat all as unsigned.
+ dataType = DataBuffer.TYPE_BYTE;
+ isDataTypeSet = true;
+ }
+ break;
+ case 16:
+ if(sampleFormat[0] !=
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ if(sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER) {
+ dataType = DataBuffer.TYPE_SHORT;
+ } else {
+ dataType = DataBuffer.TYPE_USHORT;
+ }
+ isDataTypeSet = true;
+ }
+ break;
+ case 32:
+ if(sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ dataType = DataBuffer.TYPE_FLOAT;
+ } else {
+ dataType = DataBuffer.TYPE_INT;
+ }
+ isDataTypeSet = true;
+ break;
+ case 64:
+ if(sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ dataType = DataBuffer.TYPE_DOUBLE;
+ isDataTypeSet = true;
+ }
+ break;
+ }
+
+ if(isDataTypeSet) {
+ // Create the SampleModel.
+ SampleModel sm = createInterleavedSM(dataType,
+ samplesPerPixel);
+
+ // Create the ColorModel.
+ ColorModel cm;
+ if(samplesPerPixel >= 1 && samplesPerPixel <= 4 &&
+ (dataType == DataBuffer.TYPE_INT ||
+ dataType == DataBuffer.TYPE_FLOAT)) {
+ // Handle the 32-bit cases for 1-4 bands.
+ ColorSpace cs = samplesPerPixel <= 2 ?
+ ColorSpace.getInstance(ColorSpace.CS_GRAY) : rgb;
+ boolean hasAlpha = ((samplesPerPixel % 2) == 0);
+ boolean alphaPremultiplied = false;
+ if(hasAlpha && extraSamples != null &&
+ extraSamples[0] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) {
+ alphaPremultiplied = true;
+ }
+
+ cm = createComponentCM(cs,
+ samplesPerPixel,
+ dataType,
+ hasAlpha,
+ alphaPremultiplied);
+ } else {
+ ColorSpace cs = new BogusColorSpace(samplesPerPixel);
+ cm = createComponentCM(cs,
+ samplesPerPixel,
+ dataType,
+ false, // hasAlpha
+ false); // alphaPremultiplied
+ }
+ return new ImageTypeSpecifier(cm, sm);
+ }
+ }
+ }
+
+ // Other more bizarre cases including discontiguous DataBuffers
+ // such as for the image in bug 4918959.
+
+ if(colorMap == null &&
+ sampleFormat[0] !=
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+
+ // Determine size of largest sample.
+ int maxBitsPerSample = 0;
+ for(int i = 0; i < bitsPerSample.length; i++) {
+ if(bitsPerSample[i] > maxBitsPerSample) {
+ maxBitsPerSample = bitsPerSample[i];
+ }
+ }
+
+ // Determine whether data are signed.
+ boolean isSigned =
+ (sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER);
+
+ // Grayscale
+ if(samplesPerPixel == 1) {
+ int dataType =
+ getDataTypeFromNumBits(maxBitsPerSample, isSigned);
+
+ return ImageTypeSpecifier.createGrayscale(maxBitsPerSample,
+ dataType,
+ isSigned);
+ }
+
+ // Gray-alpha
+ if (samplesPerPixel == 2) {
+ boolean alphaPremultiplied = false;
+ if (extraSamples != null &&
+ extraSamples[0] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) {
+ alphaPremultiplied = true;
+ }
+
+ int dataType =
+ getDataTypeFromNumBits(maxBitsPerSample, isSigned);
+
+ return ImageTypeSpecifier.createGrayscale(maxBitsPerSample,
+ dataType,
+ false,
+ alphaPremultiplied);
+ }
+
+ if (samplesPerPixel == 3 || samplesPerPixel == 4) {
+ if(totalBits <= 32 && !isSigned) {
+ // Packed RGB or RGBA
+ int redMask = createMask(bitsPerSample, 0);
+ int greenMask = createMask(bitsPerSample, 1);
+ int blueMask = createMask(bitsPerSample, 2);
+ int alphaMask = (samplesPerPixel == 4) ?
+ createMask(bitsPerSample, 3) : 0;
+ int transferType =
+ getDataTypeFromNumBits(totalBits, false);
+ boolean alphaPremultiplied = false;
+ if (extraSamples != null &&
+ extraSamples[0] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) {
+ alphaPremultiplied = true;
+ }
+ return ImageTypeSpecifier.createPacked(rgb,
+ redMask,
+ greenMask,
+ blueMask,
+ alphaMask,
+ transferType,
+ alphaPremultiplied);
+ } else if(samplesPerPixel == 3) {
+ // Interleaved RGB
+ int[] bandOffsets = new int[] {0, 1, 2};
+ int dataType =
+ getDataTypeFromNumBits(maxBitsPerSample, isSigned);
+ return ImageTypeSpecifier.createInterleaved(rgb,
+ bandOffsets,
+ dataType,
+ false,
+ false);
+ } else if(samplesPerPixel == 4) {
+ // Interleaved RGBA
+ int[] bandOffsets = new int[] {0, 1, 2, 3};
+ int dataType =
+ getDataTypeFromNumBits(maxBitsPerSample, isSigned);
+ boolean alphaPremultiplied = false;
+ if (extraSamples != null &&
+ extraSamples[0] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) {
+ alphaPremultiplied = true;
+ }
+ return ImageTypeSpecifier.createInterleaved(rgb,
+ bandOffsets,
+ dataType,
+ true,
+ alphaPremultiplied);
+ }
+ } else {
+ // Arbitrary Interleaved.
+ int dataType =
+ getDataTypeFromNumBits(maxBitsPerSample, isSigned);
+ SampleModel sm = createInterleavedSM(dataType,
+ samplesPerPixel);
+ ColorSpace cs = new BogusColorSpace(samplesPerPixel);
+ ColorModel cm = createComponentCM(cs,
+ samplesPerPixel,
+ dataType,
+ false, // hasAlpha
+ false); // alphaPremultiplied
+ return new ImageTypeSpecifier(cm, sm);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Sets the value of the reader field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param reader the current ImageReader.
+ */
+ public void setReader(ImageReader reader) {
+ this.reader = reader;
+ }
+
+ /**
+ * Sets the value of the metadata field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param metadata the IIOMetadata object for the
+ * image being read.
+ */
+ public void setMetadata(IIOMetadata metadata) {
+ this.metadata = metadata;
+ }
+
+ /**
+ * Sets the value of the photometricInterpretation
+ * field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param photometricInterpretation the photometric interpretation
+ * value.
+ */
+ public void setPhotometricInterpretation(int photometricInterpretation) {
+ this.photometricInterpretation = photometricInterpretation;
+ }
+
+ /**
+ * Sets the value of the compression field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param compression the compression type.
+ */
+ public void setCompression(int compression) {
+ this.compression = compression;
+ }
+
+ /**
+ * Sets the value of the planar field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param planar true if the image to be decoded is
+ * stored in planar format.
+ */
+ public void setPlanar(boolean planar) {
+ this.planar = planar;
+ }
+
+ /**
+ * Sets the value of the samplesPerPixel field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param samplesPerPixel the number of samples in each source
+ * pixel.
+ */
+ public void setSamplesPerPixel(int samplesPerPixel) {
+ this.samplesPerPixel = samplesPerPixel;
+ }
+
+ /**
+ * Sets the value of the bitsPerSample field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param bitsPerSample the number of bits for each source image
+ * sample.
+ */
+ public void setBitsPerSample(int[] bitsPerSample) {
+ this.bitsPerSample = bitsPerSample == null ?
+ null : bitsPerSample.clone();
+ }
+
+ /**
+ * Sets the value of the sampleFormat field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param sampleFormat the format of the source image data,
+ * for example unsigned integer or floating-point.
+ */
+ public void setSampleFormat(int[] sampleFormat) {
+ this.sampleFormat = sampleFormat == null ?
+ new int[] {BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER} :
+ sampleFormat.clone();
+ }
+
+ /**
+ * Sets the value of the extraSamples field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param extraSamples the interpretation of any samples in the
+ * source file beyond those used for basic color or grayscale
+ * information.
+ */
+ public void setExtraSamples(int[] extraSamples) {
+ this.extraSamples = extraSamples == null ?
+ null : extraSamples.clone();
+ }
+
+ /**
+ * Sets the value of the colorMap field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param colorMap the color map to apply to the source data,
+ * as an array of chars.
+ */
+ public void setColorMap(char[] colorMap) {
+ this.colorMap = colorMap == null ?
+ null : colorMap.clone();
+ }
+
+ /**
+ * Sets the value of the stream field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param stream the ImageInputStream to be read.
+ */
+ public void setStream(ImageInputStream stream) {
+ this.stream = stream;
+ }
+
+ /**
+ * Sets the value of the offset field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param offset the offset of the beginning of the compressed
+ * data.
+ */
+ public void setOffset(long offset) {
+ this.offset = offset;
+ }
+
+ /**
+ * Sets the value of the byteCount field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param byteCount the number of bytes of compressed data.
+ */
+ public void setByteCount(int byteCount) {
+ this.byteCount = byteCount;
+ }
+
+ // Region of the file image represented in the stream
+
+ /**
+ * Sets the value of the srcMinX field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param srcMinX the minimum X coordinate of the source region
+ * being decoded, irrespective of how it will be copied into the
+ * destination.
+ */
+ public void setSrcMinX(int srcMinX) {
+ this.srcMinX = srcMinX;
+ }
+
+ /**
+ * Sets the value of the srcMinY field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param srcMinY the minimum Y coordinate of the source region
+ * being decoded, irrespective of how it will be copied into the
+ * destination.
+ */
+ public void setSrcMinY(int srcMinY) {
+ this.srcMinY = srcMinY;
+ }
+
+ /**
+ * Sets the value of the srcWidth field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param srcWidth the width of the source region being decoded,
+ * irrespective of how it will be copied into the destination.
+ */
+ public void setSrcWidth(int srcWidth) {
+ this.srcWidth = srcWidth;
+ }
+
+ /**
+ * Sets the value of the srcHeight field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param srcHeight the height of the source region being decoded,
+ * irrespective of how it will be copied into the destination.
+ */
+ public void setSrcHeight(int srcHeight) {
+ this.srcHeight = srcHeight;
+ }
+
+ // First source pixel to be read
+
+ /**
+ * Sets the value of the sourceXOffset field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param sourceXOffset the horizontal source offset to be used when
+ * mapping between source and destination coordinates.
+ */
+ public void setSourceXOffset(int sourceXOffset) {
+ this.sourceXOffset = sourceXOffset;
+ }
+
+ /**
+ * Sets the value of the dstXOffset field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param dstXOffset the horizontal destination offset to be
+ * used when mapping between source and destination coordinates.
+ */
+ public void setDstXOffset(int dstXOffset) {
+ this.dstXOffset = dstXOffset;
+ }
+
+ /**
+ * Sets the value of the sourceYOffset.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param sourceYOffset the vertical source offset to be used when
+ * mapping between source and destination coordinates.
+ */
+ public void setSourceYOffset(int sourceYOffset) {
+ this.sourceYOffset = sourceYOffset;
+ }
+
+ /**
+ * Sets the value of the dstYOffset field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param dstYOffset the vertical destination offset to be
+ * used when mapping between source and destination coordinates.
+ */
+ public void setDstYOffset(int dstYOffset) {
+ this.dstYOffset = dstYOffset;
+ }
+
+ // Subsampling to be performed
+
+ /**
+ * Sets the value of the subsampleX field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param subsampleX the horizontal subsampling factor.
+ *
+ * @throws IllegalArgumentException if subsampleX is
+ * less than or equal to 0.
+ */
+ public void setSubsampleX(int subsampleX) {
+ if (subsampleX <= 0) {
+ throw new IllegalArgumentException("subsampleX <= 0!");
+ }
+ this.subsampleX = subsampleX;
+ }
+
+ /**
+ * Sets the value of the subsampleY field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param subsampleY the vertical subsampling factor.
+ *
+ * @throws IllegalArgumentException if subsampleY is
+ * less than or equal to 0.
+ */
+ public void setSubsampleY(int subsampleY) {
+ if (subsampleY <= 0) {
+ throw new IllegalArgumentException("subsampleY <= 0!");
+ }
+ this.subsampleY = subsampleY;
+ }
+
+ // Band subsetting/rearrangement
+
+ /**
+ * Sets the value of the sourceBands field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param sourceBands an array of ints
+ * specifying the source bands to be read.
+ */
+ public void setSourceBands(int[] sourceBands) {
+ this.sourceBands = sourceBands == null ?
+ null : sourceBands.clone();
+ }
+
+ /**
+ * Sets the value of the destinationBands field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param destinationBands an array of ints
+ * specifying the destination bands to be written.
+ */
+ public void setDestinationBands(int[] destinationBands) {
+ this.destinationBands = destinationBands == null ?
+ null : destinationBands.clone();
+ }
+
+ // Destination image and region
+
+ /**
+ * Sets the value of the image field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param image the destination BufferedImage.
+ */
+ public void setImage(BufferedImage image) {
+ this.image = image;
+ }
+
+ /**
+ * Sets the value of the dstMinX field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param dstMinX the minimum X coordinate of the destination
+ * region.
+ */
+ public void setDstMinX(int dstMinX) {
+ this.dstMinX = dstMinX;
+ }
+
+ /**
+ * Sets the value of the dstMinY field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param dstMinY the minimum Y coordinate of the destination
+ * region.
+ */
+ public void setDstMinY(int dstMinY) {
+ this.dstMinY = dstMinY;
+ }
+
+ /**
+ * Sets the value of the dstWidth field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param dstWidth the width of the destination region.
+ */
+ public void setDstWidth(int dstWidth) {
+ this.dstWidth = dstWidth;
+ }
+
+ /**
+ * Sets the value of the dstHeight field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param dstHeight the height of the destination region.
+ */
+ public void setDstHeight(int dstHeight) {
+ this.dstHeight = dstHeight;
+ }
+
+ // Active source region
+
+ /**
+ * Sets the value of the activeSrcMinX field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param activeSrcMinX the minimum X coordinate of the active
+ * source region.
+ */
+ public void setActiveSrcMinX(int activeSrcMinX) {
+ this.activeSrcMinX = activeSrcMinX;
+ }
+
+ /**
+ * Sets the value of the activeSrcMinY field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param activeSrcMinY the minimum Y coordinate of the active
+ * source region.
+ */
+ public void setActiveSrcMinY(int activeSrcMinY) {
+ this.activeSrcMinY = activeSrcMinY;
+ }
+
+ /**
+ * Sets the value of the activeSrcWidth field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param activeSrcWidth the width of the active source region.
+ */
+ public void setActiveSrcWidth(int activeSrcWidth) {
+ this.activeSrcWidth = activeSrcWidth;
+ }
+
+ /**
+ * Sets the value of the activeSrcHeight field.
+ *
+ *
If this method is called, the beginDecoding
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param activeSrcHeight the height of the active source region.
+ */
+ public void setActiveSrcHeight(int activeSrcHeight) {
+ this.activeSrcHeight = activeSrcHeight;
+ }
+
+ /**
+ * Sets the TIFFColorConverter object describing the color
+ * space of the encoded data in the input stream. If no
+ * TIFFColorConverter is set, no conversion will be performed.
+ *
+ * @param colorConverter a TIFFColorConverter object, or
+ * null.
+ */
+ public void setColorConverter(TIFFColorConverter colorConverter) {
+ this.colorConverter = colorConverter;
+ }
+
+ /**
+ * Returns an ImageTypeSpecifier describing an image
+ * whose underlying data array has the same format as the raw
+ * source pixel data.
+ *
+ * @return an ImageTypeSpecifier.
+ */
+ public ImageTypeSpecifier getRawImageType() {
+ ImageTypeSpecifier its =
+ getRawImageTypeSpecifier(photometricInterpretation,
+ compression,
+ samplesPerPixel,
+ bitsPerSample,
+ sampleFormat,
+ extraSamples,
+ colorMap);
+ return its;
+ }
+
+ /**
+ * Creates a BufferedImage whose underlying data
+ * array will be suitable for holding the raw decoded output of
+ * the decodeRaw method.
+ *
+ *
The default implementation calls
+ * getRawImageType, and calls the resulting
+ * ImageTypeSpecifier's
+ * createBufferedImage method.
+ *
+ * @return a BufferedImage whose underlying data
+ * array has the same format as the raw source pixel data, or
+ * null if it is not possible to create such an
+ * image.
+ */
+ public BufferedImage createRawImage() {
+ if (planar) {
+ // Create a single-banded image of the appropriate data type.
+
+ // Get the number of bits per sample.
+ int bps = bitsPerSample[sourceBands[0]];
+
+ // Determine the data type.
+ int dataType;
+ if(sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ if(bps <= 32) {
+ dataType = DataBuffer.TYPE_FLOAT;
+ } else {
+ dataType = DataBuffer.TYPE_DOUBLE;
+ }
+ } else if(bps <= 8) {
+ dataType = DataBuffer.TYPE_BYTE;
+ } else if(bps <= 16) {
+ if(sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER) {
+ dataType = DataBuffer.TYPE_SHORT;
+ } else {
+ dataType = DataBuffer.TYPE_USHORT;
+ }
+ } else {
+ dataType = DataBuffer.TYPE_INT;
+ }
+
+ ColorSpace csGray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+ ImageTypeSpecifier its =
+ ImageTypeSpecifier.createInterleaved(csGray,
+ new int[] {0},
+ dataType,
+ false,
+ false);
+
+ return its.createBufferedImage(srcWidth, srcHeight);
+ } else {
+ ImageTypeSpecifier its = getRawImageType();
+ if (its == null) {
+ return null;
+ }
+
+ BufferedImage bi = its.createBufferedImage(srcWidth, srcHeight);
+ return bi;
+ }
+ }
+
+ /**
+ * Decodes the source data into the provided byte
+ * array b, starting at the offset given by
+ * dstOffset. Each pixel occupies
+ * bitsPerPixel bits, with no padding between pixels.
+ * Scanlines are separated by scanlineStride
+ * bytes.
+ *
+ * @param b a byte array to be written.
+ * @param dstOffset the starting offset in b to be
+ * written.
+ * @param bitsPerPixel the number of bits for each pixel.
+ * @param scanlineStride the number of bytes to
+ * advance between that starting pixels of each scanline.
+ *
+ * @throws IOException if an error occurs reading from the source
+ * ImageInputStream.
+ */
+ public abstract void decodeRaw(byte[] b,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException;
+
+ /**
+ * Decodes the source data into the provided short
+ * array s, starting at the offset given by
+ * dstOffset. Each pixel occupies
+ * bitsPerPixel bits, with no padding between pixels.
+ * Scanlines are separated by scanlineStride
+ * shorts
+ *
+ *
The default implementation calls decodeRaw(byte[] b,
+ * ...) and copies the resulting data into s.
+ *
+ * @param s a short array to be written.
+ * @param dstOffset the starting offset in s to be
+ * written.
+ * @param bitsPerPixel the number of bits for each pixel.
+ * @param scanlineStride the number of shorts to
+ * advance between that starting pixels of each scanline.
+ *
+ * @throws IOException if an error occurs reading from the source
+ * ImageInputStream.
+ */
+ public void decodeRaw(short[] s,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException {
+ int bytesPerRow = (srcWidth*bitsPerPixel + 7)/8;
+ int shortsPerRow = bytesPerRow/2;
+
+ byte[] b = new byte[bytesPerRow*srcHeight];
+ decodeRaw(b, 0, bitsPerPixel, bytesPerRow);
+
+ int bOffset = 0;
+ if(stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
+ for (int j = 0; j < srcHeight; j++) {
+ for (int i = 0; i < shortsPerRow; i++) {
+ short hiVal = b[bOffset++];
+ short loVal = b[bOffset++];
+ short sval = (short)((hiVal << 8) | (loVal & 0xff));
+ s[dstOffset + i] = sval;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ } else { // ByteOrder.LITLE_ENDIAN
+ for (int j = 0; j < srcHeight; j++) {
+ for (int i = 0; i < shortsPerRow; i++) {
+ short loVal = b[bOffset++];
+ short hiVal = b[bOffset++];
+ short sval = (short)((hiVal << 8) | (loVal & 0xff));
+ s[dstOffset + i] = sval;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ }
+ }
+
+ /**
+ * Decodes the source data into the provided int
+ * array i, starting at the offset given by
+ * dstOffset. Each pixel occupies
+ * bitsPerPixel bits, with no padding between pixels.
+ * Scanlines are separated by scanlineStride
+ * ints.
+ *
+ *
The default implementation calls decodeRaw(byte[] b,
+ * ...) and copies the resulting data into i.
+ *
+ * @param i an int array to be written.
+ * @param dstOffset the starting offset in i to be
+ * written.
+ * @param bitsPerPixel the number of bits for each pixel.
+ * @param scanlineStride the number of ints to
+ * advance between that starting pixels of each scanline.
+ *
+ * @throws IOException if an error occurs reading from the source
+ * ImageInputStream.
+ */
+ public void decodeRaw(int[] i,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException {
+ int numBands = bitsPerPixel/32;
+ int intsPerRow = srcWidth*numBands;
+ int bytesPerRow = intsPerRow*4;
+
+ byte[] b = new byte[bytesPerRow*srcHeight];
+ decodeRaw(b, 0, bitsPerPixel, bytesPerRow);
+
+ int bOffset = 0;
+ if(stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
+ for (int j = 0; j < srcHeight; j++) {
+ for (int k = 0; k < intsPerRow; k++) {
+ int v0 = b[bOffset++] & 0xff;
+ int v1 = b[bOffset++] & 0xff;
+ int v2 = b[bOffset++] & 0xff;
+ int v3 = b[bOffset++] & 0xff;
+ int ival = (v0 << 24) | (v1 << 16) | (v2 << 8) | v3;
+ i[dstOffset + k] = ival;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ } else { // ByteOrder.LITLE_ENDIAN
+ for (int j = 0; j < srcHeight; j++) {
+ for (int k = 0; k < intsPerRow; k++) {
+ int v3 = b[bOffset++] & 0xff;
+ int v2 = b[bOffset++] & 0xff;
+ int v1 = b[bOffset++] & 0xff;
+ int v0 = b[bOffset++] & 0xff;
+ int ival = (v0 << 24) | (v1 << 16) | (v2 << 8) | v3;
+ i[dstOffset + k] = ival;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ }
+ }
+
+ /**
+ * Decodes the source data into the provided float
+ * array f, starting at the offset given by
+ * dstOffset. Each pixel occupies
+ * bitsPerPixel bits, with no padding between pixels.
+ * Scanlines are separated by scanlineStride
+ * floats.
+ *
+ *
The default implementation calls decodeRaw(byte[] b,
+ * ...) and copies the resulting data into f.
+ *
+ * @param f a float array to be written.
+ * @param dstOffset the starting offset in f to be
+ * written.
+ * @param bitsPerPixel the number of bits for each pixel.
+ * @param scanlineStride the number of floats to
+ * advance between that starting pixels of each scanline.
+ *
+ * @throws IOException if an error occurs reading from the source
+ * ImageInputStream.
+ */
+ public void decodeRaw(float[] f,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException {
+ int numBands = bitsPerPixel/32;
+ int floatsPerRow = srcWidth*numBands;
+ int bytesPerRow = floatsPerRow*4;
+
+ byte[] b = new byte[bytesPerRow*srcHeight];
+ decodeRaw(b, 0, bitsPerPixel, bytesPerRow);
+
+ int bOffset = 0;
+ if(stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
+ for (int j = 0; j < srcHeight; j++) {
+ for (int i = 0; i < floatsPerRow; i++) {
+ int v0 = b[bOffset++] & 0xff;
+ int v1 = b[bOffset++] & 0xff;
+ int v2 = b[bOffset++] & 0xff;
+ int v3 = b[bOffset++] & 0xff;
+ int ival = (v0 << 24) | (v1 << 16) | (v2 << 8) | v3;
+ float fval = Float.intBitsToFloat(ival);
+ f[dstOffset + i] = fval;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ } else { // ByteOrder.LITLE_ENDIAN
+ for (int j = 0; j < srcHeight; j++) {
+ for (int i = 0; i < floatsPerRow; i++) {
+ int v3 = b[bOffset++] & 0xff;
+ int v2 = b[bOffset++] & 0xff;
+ int v1 = b[bOffset++] & 0xff;
+ int v0 = b[bOffset++] & 0xff;
+ int ival = (v0 << 24) | (v1 << 16) | (v2 << 8) | v3;
+ float fval = Float.intBitsToFloat(ival);
+ f[dstOffset + i] = fval;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ }
+ }
+
+ /**
+ * Decodes the source data into the provided double
+ * array f, starting at the offset given by
+ * dstOffset. Each pixel occupies
+ * bitsPerPixel bits, with no padding between pixels.
+ * Scanlines are separated by scanlineStride
+ * doubles.
+ *
+ *
The default implementation calls decodeRaw(byte[] b,
+ * ...) and copies the resulting data into f.
+ *
+ * @param f a double array to be written.
+ * @param dstOffset the starting offset in f to be
+ * written.
+ * @param bitsPerPixel the number of bits for each pixel.
+ * @param scanlineStride the number of doubles to
+ * advance between that starting pixels of each scanline.
+ *
+ * @throws IOException if an error occurs reading from the source
+ * ImageInputStream.
+ */
+ public void decodeRaw(double[] d,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException {
+ int numBands = bitsPerPixel/64;
+ int doublesPerRow = srcWidth*numBands;
+ int bytesPerRow = doublesPerRow*8;
+
+ byte[] b = new byte[bytesPerRow*srcHeight];
+ decodeRaw(b, 0, bitsPerPixel, bytesPerRow);
+
+ int bOffset = 0;
+ if(stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
+ for (int j = 0; j < srcHeight; j++) {
+ for (int i = 0; i < doublesPerRow; i++) {
+ long v0 = b[bOffset++] & 0xff;
+ long v1 = b[bOffset++] & 0xff;
+ long v2 = b[bOffset++] & 0xff;
+ long v3 = b[bOffset++] & 0xff;
+ long v4 = b[bOffset++] & 0xff;
+ long v5 = b[bOffset++] & 0xff;
+ long v6 = b[bOffset++] & 0xff;
+ long v7 = b[bOffset++] & 0xff;
+ long lval =
+ (v0 << 56) | (v1 << 48) | (v2 << 40) | (v3 << 32)
+ | (v4 << 24) | (v5 << 16) | (v6 << 8) | v7;
+ double dval = Double.longBitsToDouble(lval);
+ d[dstOffset + i] = dval;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ } else { // ByteOrder.LITLE_ENDIAN
+ for (int j = 0; j < srcHeight; j++) {
+ for (int i = 0; i < doublesPerRow; i++) {
+ long v7 = b[bOffset++] & 0xff;
+ long v6 = b[bOffset++] & 0xff;
+ long v5 = b[bOffset++] & 0xff;
+ long v4 = b[bOffset++] & 0xff;
+ long v3 = b[bOffset++] & 0xff;
+ long v2 = b[bOffset++] & 0xff;
+ long v1 = b[bOffset++] & 0xff;
+ long v0 = b[bOffset++] & 0xff;
+ long lval =
+ (v0 << 56) | (v1 << 48) | (v2 << 40) | (v3 << 32)
+ | (v4 << 24) | (v5 << 16) | (v6 << 8) | v7;
+ double dval = Double.longBitsToDouble(lval);
+ d[dstOffset + i] = dval;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ }
+ }
+
+ //
+ // Values used to prevent unneeded recalculation of bit adjustment table.
+ //
+ private boolean isFirstBitDepthTable = true;
+ private boolean planarCache = false;
+ private int[] destBitsPerSampleCache = null;
+ private int[] sourceBandsCache = null;
+ private int[] bitsPerSampleCache = null;
+ private int[] destinationBandsCache = null;
+
+ /**
+ * This routine is called prior to a sequence of calls to the
+ * decode method, in order to allow any necessary
+ * tables or other structures to be initialized based on metadata
+ * values. This routine is guaranteed to be called any time the
+ * metadata values have changed.
+ *
+ *
The default implementation computes tables used by the
+ * decode method to rescale components to different
+ * bit depths. Thus, if this method is overridden, it is
+ * important for the subclass method to call super(),
+ * unless it overrides decode as well.
+ */
+ public void beginDecoding() {
+ // Note: This method assumes that sourceBands, destinationBands,
+ // and bitsPerSample are all non-null which is true as they are
+ // set up that way in TIFFImageReader. Also the lengths and content
+ // of sourceBands and destinationBands are checked in TIFFImageReader
+ // before the present method is invoked.
+
+ // Determine if all of the relevant output bands have the
+ // same bit depth as the source data
+ this.adjustBitDepths = false;
+ int numBands = destinationBands.length;
+ int[] destBitsPerSample = null;
+ if(planar) {
+ int totalNumBands = bitsPerSample.length;
+ destBitsPerSample = new int[totalNumBands];
+ int dbps = image.getSampleModel().getSampleSize(0);
+ for(int b = 0; b < totalNumBands; b++) {
+ destBitsPerSample[b] = dbps;
+ }
+ } else {
+ destBitsPerSample = image.getSampleModel().getSampleSize();
+ }
+
+ for (int b = 0; b < numBands; b++) {
+ if (destBitsPerSample[destinationBands[b]] !=
+ bitsPerSample[sourceBands[b]]) {
+ adjustBitDepths = true;
+ break;
+ }
+ }
+
+ // If the bit depths differ, create a lookup table
+ // per band to perform the conversion
+ if(adjustBitDepths) {
+ // Compute the table only if this is the first time one is
+ // being computed or if any of the variables on which the
+ // table is based have changed.
+ if(this.isFirstBitDepthTable ||
+ planar != planarCache ||
+ !areIntArraysEqual(destBitsPerSample,
+ destBitsPerSampleCache) ||
+ !areIntArraysEqual(sourceBands,
+ sourceBandsCache) ||
+ !areIntArraysEqual(bitsPerSample,
+ bitsPerSampleCache) ||
+ !areIntArraysEqual(destinationBands,
+ destinationBandsCache)) {
+
+ this.isFirstBitDepthTable = false;
+
+ // Cache some variables.
+ this.planarCache = planar;
+ this.destBitsPerSampleCache =
+ destBitsPerSample.clone(); // never null ...
+ this.sourceBandsCache = sourceBands == null ?
+ null : sourceBands.clone();
+ this.bitsPerSampleCache = bitsPerSample == null ?
+ null : bitsPerSample.clone();
+ this.destinationBandsCache = destinationBands.clone();
+
+ // Allocate and fill the table.
+ bitDepthScale = new int[numBands][];
+ for (int b = 0; b < numBands; b++) {
+ int maxInSample = (1 << bitsPerSample[sourceBands[b]]) - 1;
+ int halfMaxInSample = maxInSample/2;
+
+ int maxOutSample =
+ (1 << destBitsPerSample[destinationBands[b]]) - 1;
+
+ bitDepthScale[b] = new int[maxInSample + 1];
+ for (int s = 0; s <= maxInSample; s++) {
+ bitDepthScale[b][s] =
+ (s*maxOutSample + halfMaxInSample)/
+ maxInSample;
+ }
+ }
+ }
+ } else { // !adjustBitDepths
+ // Clear any prior table.
+ this.bitDepthScale = null;
+ }
+
+ // Determine whether source and destination band lists are ramps.
+ // Note that these conditions will be true for planar images if
+ // and only if samplesPerPixel == 1, sourceBands[0] == 0, and
+ // destinationBands[0] == 0. For the purposes of this method, the
+ // only difference between such a planar image and a chunky image
+ // is the setting of the PlanarConfiguration field.
+ boolean sourceBandsNormal = false;
+ boolean destinationBandsNormal = false;
+ if (numBands == samplesPerPixel) {
+ sourceBandsNormal = true;
+ destinationBandsNormal = true;
+ for (int i = 0; i < numBands; i++) {
+ if (sourceBands[i] != i) {
+ sourceBandsNormal = false;
+ }
+ if (destinationBands[i] != i) {
+ destinationBandsNormal = false;
+ }
+ }
+ }
+
+ // Determine whether the image is bilevel and/or contiguous.
+ // Note that a planar image could be bilevel but it will not
+ // be contiguous unless it has a single component band stored
+ // in a single bank.
+ this.isBilevel =
+ ImageUtil.isBinary(this.image.getRaster().getSampleModel());
+ this.isContiguous = this.isBilevel ?
+ true : ImageUtil.imageIsContiguous(this.image);
+
+ // Analyze destination image to see if we can copy into it
+ // directly
+
+ this.isImageSimple =
+ (colorConverter == null) &&
+ (subsampleX == 1) && (subsampleY == 1) &&
+ (srcWidth == dstWidth) && (srcHeight == dstHeight) &&
+ ((dstMinX + dstWidth) <= image.getWidth()) &&
+ ((dstMinY + dstHeight) <= image.getHeight()) &&
+ sourceBandsNormal && destinationBandsNormal &&
+ !adjustBitDepths;
+ }
+
+ /**
+ * Decodes the input bit stream (located in the
+ * ImageInputStreamstream, at offset
+ * offset, and continuing for byteCount
+ * bytes) into the output BufferedImage
+ * image.
+ *
+ *
The default implementation analyzes the destination image
+ * to determine if it is suitable as the destination for the
+ * decodeRaw method. If not, a suitable image is
+ * created. Next, decodeRaw is called to perform the
+ * actual decoding, and the results are copied into the
+ * destination image if necessary. Subsampling and offsetting are
+ * performed automatically.
+ *
+ *
The precise responsibilities of this routine are as
+ * follows. The input bit stream is defined by the instance
+ * variables stream, offset, and
+ * byteCount. These bits contain the data for the
+ * region of the source image defined by srcMinX,
+ * srcMinY, srcWidth, and
+ * srcHeight.
+ *
+ *
The source data is required to be subsampling, starting at
+ * the sourceXOffsetth column and including
+ * every subsampleXth pixel thereafter (and similarly
+ * for sourceYOffset and
+ * subsampleY).
+ *
+ *
Pixels are copied into the destination with an addition shift of
+ * (dstXOffset, dstYOffset). The complete
+ * set of formulas relating the source and destination coordinate spaces
+ * are:
+ *
+ *
The region of the destination image to be updated is given
+ * by the instance variables dstMinX,
+ * dstMinY, dstWidth, and
+ * dstHeight.
+ *
+ *
It is possible that not all of the source data being read
+ * will contribute to the destination image. For example, the
+ * destination offsets could be set such that some of the source
+ * pixels land outside of the bounds of the image. As a
+ * convenience, the bounds of the active source region (that is,
+ * the region of the strip or tile being read that actually
+ * contributes to the destination image, taking clipping into
+ * account) are available as activeSrcMinX,
+ * activeSrcMinY, activeSrcWidth and
+ * activeSrcHeight. Thus, the source pixel at
+ * (activeSrcMinX, activeSrcMinY) will
+ * map to the destination pixel (dstMinX,
+ * dstMinY).
+ *
+ *
The sequence of source bands given by
+ * sourceBands are to be copied into the sequence of
+ * bands in the destination given by
+ * destinationBands.
+ *
+ *
Some standard tag information is provided the instance
+ * variables photometricInterpretation,
+ * compression, samplesPerPixel,
+ * bitsPerSample, sampleFormat,
+ * extraSamples, and colorMap.
+ *
+ *
In practice, unless there is a significant performance
+ * advantage to be gained by overriding this routine, most users
+ * will prefer to use the default implementation of this routine,
+ * and instead override the decodeRaw and/or
+ * getRawImageType methods.
+ *
+ * @exception IOException if an error occurs in
+ * decodeRaw.
+ */
+ public void decode() throws IOException {
+ byte[] byteData = null;
+ short[] shortData = null;
+ int[] intData = null;
+ float[] floatData = null;
+ double[] doubleData = null;
+
+ int dstOffset = 0;
+ int pixelBitStride = 1;
+ int scanlineStride = 0;
+
+ // Analyze raw image
+
+ this.rawImage = null;
+ if(isImageSimple) {
+ if(isBilevel) {
+ rawImage = this.image;
+ } else if (isContiguous) {
+ rawImage =
+ image.getSubimage(dstMinX, dstMinY, dstWidth, dstHeight);
+ }
+ }
+
+ boolean isDirectCopy = rawImage != null;
+
+ if(rawImage == null) {
+ rawImage = createRawImage();
+ if (rawImage == null) {
+ throw new IIOException("Couldn't create image buffer!");
+ }
+ }
+
+ WritableRaster ras = rawImage.getRaster();
+
+ if(isBilevel) {
+ Rectangle rect = isImageSimple ?
+ new Rectangle(dstMinX, dstMinY, dstWidth, dstHeight) :
+ ras.getBounds();
+ byteData = ImageUtil.getPackedBinaryData(ras, rect);
+ dstOffset = 0;
+ pixelBitStride = 1;
+ scanlineStride = (rect.width + 7)/8;
+ } else {
+ SampleModel sm = ras.getSampleModel();
+ DataBuffer db = ras.getDataBuffer();
+
+ boolean isSupportedType = false;
+
+ if (sm instanceof ComponentSampleModel) {
+ ComponentSampleModel csm = (ComponentSampleModel)sm;
+ dstOffset = csm.getOffset(-ras.getSampleModelTranslateX(),
+ -ras.getSampleModelTranslateY());
+ scanlineStride = csm.getScanlineStride();
+ if(db instanceof DataBufferByte) {
+ DataBufferByte dbb = (DataBufferByte)db;
+
+ byteData = dbb.getData();
+ pixelBitStride = csm.getPixelStride()*8;
+ isSupportedType = true;
+ } else if(db instanceof DataBufferUShort) {
+ DataBufferUShort dbus = (DataBufferUShort)db;
+
+ shortData = dbus.getData();
+ pixelBitStride = csm.getPixelStride()*16;
+ isSupportedType = true;
+ } else if(db instanceof DataBufferShort) {
+ DataBufferShort dbs = (DataBufferShort)db;
+
+ shortData = dbs.getData();
+ pixelBitStride = csm.getPixelStride()*16;
+ isSupportedType = true;
+ } else if(db instanceof DataBufferInt) {
+ DataBufferInt dbi = (DataBufferInt)db;
+
+ intData = dbi.getData();
+ pixelBitStride = csm.getPixelStride()*32;
+ isSupportedType = true;
+ } else if(db instanceof DataBufferFloat) {
+ DataBufferFloat dbf = (DataBufferFloat)db;
+
+ floatData = dbf.getData();
+ pixelBitStride = csm.getPixelStride()*32;
+ isSupportedType = true;
+ } else if(db instanceof DataBufferDouble) {
+ DataBufferDouble dbd = (DataBufferDouble)db;
+
+ doubleData = dbd.getData();
+ pixelBitStride = csm.getPixelStride()*64;
+ isSupportedType = true;
+ }
+ } else if (sm instanceof MultiPixelPackedSampleModel) {
+ MultiPixelPackedSampleModel mppsm =
+ (MultiPixelPackedSampleModel)sm;
+ dstOffset =
+ mppsm.getOffset(-ras.getSampleModelTranslateX(),
+ -ras.getSampleModelTranslateY());
+ pixelBitStride = mppsm.getPixelBitStride();
+ scanlineStride = mppsm.getScanlineStride();
+ if(db instanceof DataBufferByte) {
+ DataBufferByte dbb = (DataBufferByte)db;
+
+ byteData = dbb.getData();
+ isSupportedType = true;
+ } else if(db instanceof DataBufferUShort) {
+ DataBufferUShort dbus = (DataBufferUShort)db;
+
+ shortData = dbus.getData();
+ isSupportedType = true;
+ } else if(db instanceof DataBufferInt) {
+ DataBufferInt dbi = (DataBufferInt)db;
+
+ intData = dbi.getData();
+ isSupportedType = true;
+ }
+ } else if (sm instanceof SinglePixelPackedSampleModel) {
+ SinglePixelPackedSampleModel sppsm =
+ (SinglePixelPackedSampleModel)sm;
+ dstOffset =
+ sppsm.getOffset(-ras.getSampleModelTranslateX(),
+ -ras.getSampleModelTranslateY());
+ scanlineStride = sppsm.getScanlineStride();
+ if(db instanceof DataBufferByte) {
+ DataBufferByte dbb = (DataBufferByte)db;
+
+ byteData = dbb.getData();
+ pixelBitStride = 8;
+ isSupportedType = true;
+ } else if(db instanceof DataBufferUShort) {
+ DataBufferUShort dbus = (DataBufferUShort)db;
+
+ shortData = dbus.getData();
+ pixelBitStride = 16;
+ isSupportedType = true;
+ } else if(db instanceof DataBufferInt) {
+ DataBufferInt dbi = (DataBufferInt)db;
+
+ intData = dbi.getData();
+ pixelBitStride = 32;
+ isSupportedType = true;
+ }
+ }
+
+ if(!isSupportedType) {
+ throw new IIOException
+ ("Unsupported raw image type: SampleModel = "+sm+
+ "; DataBuffer = "+db);
+ }
+ }
+
+ if(isBilevel) {
+ // Bilevel data are always in a contiguous byte buffer.
+ decodeRaw(byteData, dstOffset, pixelBitStride, scanlineStride);
+ } else {
+ SampleModel sm = ras.getSampleModel();
+
+ // Branch based on whether data are bit-contiguous, i.e.,
+ // data are packaed as tightly as possible leaving no unused
+ // bits except at the end of a row.
+ if(isDataBufferBitContiguous(sm)) {
+ // Use byte or float data directly.
+ if (byteData != null) {
+ decodeRaw(byteData, dstOffset,
+ pixelBitStride, scanlineStride);
+ } else if (floatData != null) {
+ decodeRaw(floatData, dstOffset,
+ pixelBitStride, scanlineStride);
+ } else if (doubleData != null) {
+ decodeRaw(doubleData, dstOffset,
+ pixelBitStride, scanlineStride);
+ } else {
+ if (shortData != null) {
+ if(areSampleSizesEqual(sm) &&
+ sm.getSampleSize(0) == 16) {
+ // Decode directly into short data.
+ decodeRaw(shortData, dstOffset,
+ pixelBitStride, scanlineStride);
+ } else {
+ // Decode into bytes and reformat into shorts.
+ int bpp = getBitsPerPixel(sm);
+ int bytesPerRow = (bpp*srcWidth + 7)/8;
+ byte[] buf = new byte[bytesPerRow*srcHeight];
+ decodeRaw(buf, 0, bpp, bytesPerRow);
+ reformatData(buf, bytesPerRow, srcHeight,
+ shortData, null,
+ dstOffset, scanlineStride);
+ }
+ } else if (intData != null) {
+ if(areSampleSizesEqual(sm) &&
+ sm.getSampleSize(0) == 32) {
+ // Decode directly into int data.
+ decodeRaw(intData, dstOffset,
+ pixelBitStride, scanlineStride);
+ } else {
+ // Decode into bytes and reformat into ints.
+ int bpp = getBitsPerPixel(sm);
+ int bytesPerRow = (bpp*srcWidth + 7)/8;
+ byte[] buf = new byte[bytesPerRow*srcHeight];
+ decodeRaw(buf, 0, bpp, bytesPerRow);
+ reformatData(buf, bytesPerRow, srcHeight,
+ null, intData,
+ dstOffset, scanlineStride);
+ }
+ }
+ }
+ } else {
+ // Read discontiguous data into bytes and set the samples
+ // into the Raster.
+ int bpp = getBitsPerPixel(sm);
+ int bytesPerRow = (bpp*srcWidth + 7)/8;
+ byte[] buf = new byte[bytesPerRow*srcHeight];
+ decodeRaw(buf, 0, bpp, bytesPerRow);
+ reformatDiscontiguousData(buf, bytesPerRow,
+ srcWidth, srcHeight,
+ ras);
+ }
+ }
+
+ if (colorConverter != null) {
+ float[] rgb = new float[3];
+
+ if(byteData != null) {
+ for (int j = 0; j < dstHeight; j++) {
+ int idx = dstOffset;
+ for (int i = 0; i < dstWidth; i++) {
+ float x0 = (float)(byteData[idx] & 0xff);
+ float x1 = (float)(byteData[idx + 1] & 0xff);
+ float x2 = (float)(byteData[idx + 2] & 0xff);
+
+ colorConverter.toRGB(x0, x1, x2, rgb);
+
+ byteData[idx] = (byte)(rgb[0]);
+ byteData[idx + 1] = (byte)(rgb[1]);
+ byteData[idx + 2] = (byte)(rgb[2]);
+
+ idx += 3;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ } else if(shortData != null) {
+ if(sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER) {
+ for (int j = 0; j < dstHeight; j++) {
+ int idx = dstOffset;
+ for (int i = 0; i < dstWidth; i++) {
+ float x0 = (float)shortData[idx];
+ float x1 = (float)shortData[idx + 1];
+ float x2 = (float)shortData[idx + 2];
+
+ colorConverter.toRGB(x0, x1, x2, rgb);
+
+ shortData[idx] = (short)(rgb[0]);
+ shortData[idx + 1] = (short)(rgb[1]);
+ shortData[idx + 2] = (short)(rgb[2]);
+
+ idx += 3;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ } else {
+ for (int j = 0; j < dstHeight; j++) {
+ int idx = dstOffset;
+ for (int i = 0; i < dstWidth; i++) {
+ float x0 = (float)(shortData[idx] & 0xffff);
+ float x1 = (float)(shortData[idx + 1] & 0xffff);
+ float x2 = (float)(shortData[idx + 2] & 0xffff);
+
+ colorConverter.toRGB(x0, x1, x2, rgb);
+
+ shortData[idx] = (short)(rgb[0]);
+ shortData[idx + 1] = (short)(rgb[1]);
+ shortData[idx + 2] = (short)(rgb[2]);
+
+ idx += 3;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ }
+ } else if(intData != null) {
+ for (int j = 0; j < dstHeight; j++) {
+ int idx = dstOffset;
+ for (int i = 0; i < dstWidth; i++) {
+ float x0 = (float)intData[idx];
+ float x1 = (float)intData[idx + 1];
+ float x2 = (float)intData[idx + 2];
+
+ colorConverter.toRGB(x0, x1, x2, rgb);
+
+ intData[idx] = (int)(rgb[0]);
+ intData[idx + 1] = (int)(rgb[1]);
+ intData[idx + 2] = (int)(rgb[2]);
+
+ idx += 3;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ } else if(floatData != null) {
+ for (int j = 0; j < dstHeight; j++) {
+ int idx = dstOffset;
+ for (int i = 0; i < dstWidth; i++) {
+ float x0 = floatData[idx];
+ float x1 = floatData[idx + 1];
+ float x2 = floatData[idx + 2];
+
+ colorConverter.toRGB(x0, x1, x2, rgb);
+
+ floatData[idx] = rgb[0];
+ floatData[idx + 1] = rgb[1];
+ floatData[idx + 2] = rgb[2];
+
+ idx += 3;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ } else if(doubleData != null) {
+ for (int j = 0; j < dstHeight; j++) {
+ int idx = dstOffset;
+ for (int i = 0; i < dstWidth; i++) {
+ // Note: Possible loss of precision.
+ float x0 = (float)doubleData[idx];
+ float x1 = (float)doubleData[idx + 1];
+ float x2 = (float)doubleData[idx + 2];
+
+ colorConverter.toRGB(x0, x1, x2, rgb);
+
+ doubleData[idx] = rgb[0];
+ doubleData[idx + 1] = rgb[1];
+ doubleData[idx + 2] = rgb[2];
+
+ idx += 3;
+ }
+
+ dstOffset += scanlineStride;
+ }
+ }
+ }
+
+ if (photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO) {
+ if(byteData != null) {
+ int bytesPerRow = (srcWidth*pixelBitStride + 7)/8;
+ for (int y = 0; y < srcHeight; y++) {
+ int offset = dstOffset + y*scanlineStride;
+ for (int i = 0; i < bytesPerRow; i++) {
+ byteData[offset + i] ^= 0xff;
+ }
+ }
+ } else if(shortData != null) {
+ int shortsPerRow = (srcWidth*pixelBitStride + 15)/16;
+ if(sampleFormat[0] ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER) {
+ for (int y = 0; y < srcHeight; y++) {
+ int offset = dstOffset + y*scanlineStride;
+ for (int i = 0; i < shortsPerRow; i++) {
+ int shortOffset = offset + i;
+ shortData[shortOffset] =
+ (short)(Short.MAX_VALUE -
+ shortData[shortOffset]);
+ }
+ }
+ } else {
+ for (int y = 0; y < srcHeight; y++) {
+ int offset = dstOffset + y*scanlineStride;
+ for (int i = 0; i < shortsPerRow; i++) {
+ shortData[offset + i] ^= 0xffff;
+ }
+ }
+ }
+ } else if(intData != null) {
+ int intsPerRow = (srcWidth*pixelBitStride + 31)/32;
+ for (int y = 0; y < srcHeight; y++) {
+ int offset = dstOffset + y*scanlineStride;
+ for (int i = 0; i < intsPerRow; i++) {
+ int intOffset = offset + i;
+ intData[intOffset] =
+ Integer.MAX_VALUE - intData[intOffset];
+ }
+ }
+ } else if(floatData != null) {
+ int floatsPerRow = (srcWidth*pixelBitStride + 31)/32;
+ for (int y = 0; y < srcHeight; y++) {
+ int offset = dstOffset + y*scanlineStride;
+ for (int i = 0; i < floatsPerRow; i++) {
+ int floatOffset = offset + i;
+ floatData[floatOffset] =
+ 1.0F - floatData[floatOffset];
+ }
+ }
+ } else if(doubleData != null) {
+ int doublesPerRow = (srcWidth*pixelBitStride + 63)/64;
+ for (int y = 0; y < srcHeight; y++) {
+ int offset = dstOffset + y*scanlineStride;
+ for (int i = 0; i < doublesPerRow; i++) {
+ int doubleOffset = offset + i;
+ doubleData[doubleOffset] =
+ 1.0F - doubleData[doubleOffset];
+ }
+ }
+ }
+ }
+
+ if(isBilevel) {
+ Rectangle rect = isImageSimple ?
+ new Rectangle(dstMinX, dstMinY, dstWidth, dstHeight) :
+ ras.getBounds();
+ ImageUtil.setPackedBinaryData(byteData, ras, rect);
+ }
+
+ if (isDirectCopy) { // rawImage == image) {
+ return;
+ }
+
+ // Copy the raw image data into the true destination image
+ Raster src = rawImage.getRaster();
+
+ // Create band child of source
+ Raster srcChild = src.createChild(0, 0,
+ srcWidth, srcHeight,
+ srcMinX, srcMinY,
+ planar ? null : sourceBands);
+
+ WritableRaster dst = image.getRaster();
+
+ // Create dst child covering area and bands to be written
+ WritableRaster dstChild = dst.createWritableChild(dstMinX, dstMinY,
+ dstWidth, dstHeight,
+ dstMinX, dstMinY,
+ destinationBands);
+
+ if (subsampleX == 1 && subsampleY == 1 && !adjustBitDepths) {
+ srcChild = srcChild.createChild(activeSrcMinX,
+ activeSrcMinY,
+ activeSrcWidth, activeSrcHeight,
+ dstMinX, dstMinY,
+ null);
+
+ dstChild.setRect(srcChild);
+ } else if (subsampleX == 1 && !adjustBitDepths) {
+ int sy = activeSrcMinY;
+ int dy = dstMinY;
+ while (sy < srcMinY + srcHeight) {
+ Raster srcRow = srcChild.createChild(activeSrcMinX, sy,
+ activeSrcWidth, 1,
+ dstMinX, dy,
+ null);
+ dstChild.setRect(srcRow);
+
+ sy += subsampleY;
+ ++dy;
+ }
+ } else {
+ int[] p = srcChild.getPixel(srcMinX, srcMinY, (int[])null);
+ int numBands = p.length;
+
+ int sy = activeSrcMinY;
+ int dy = dstMinY;
+
+ while (sy < activeSrcMinY + activeSrcHeight) {
+ int sx = activeSrcMinX;
+ int dx = dstMinX;
+
+ while (sx < activeSrcMinX + activeSrcWidth) {
+ srcChild.getPixel(sx, sy, p);
+ if (adjustBitDepths) {
+ for (int band = 0; band < numBands; band++) {
+ p[band] = bitDepthScale[band][p[band]];
+ }
+ }
+ dstChild.setPixel(dx, dy, p);
+
+ sx += subsampleX;
+ ++dx;
+ }
+
+ sy += subsampleY;
+ ++dy;
+ }
+ }
+ }
+}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDeflateCompressor.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDeflateCompressor.java
new file mode 100644
index 00000000000..af37c5c1aab
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDeflateCompressor.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.tiff;
+
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.ImageWriteParam;
+
+/**
+ * Compressor for Deflate compression.
+ */
+public class TIFFDeflateCompressor extends TIFFDeflater {
+ public TIFFDeflateCompressor(ImageWriteParam param, int predictor) {
+ super("Deflate", BaselineTIFFTagSet.COMPRESSION_DEFLATE, param,
+ predictor);
+ }
+}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDeflateDecompressor.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDeflateDecompressor.java
new file mode 100644
index 00000000000..ecbd38a4d3a
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDeflateDecompressor.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.tiff;
+
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+import javax.imageio.IIOException;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+
+public class TIFFDeflateDecompressor extends TIFFDecompressor {
+
+ Inflater inflater = null;
+ int predictor;
+
+ public TIFFDeflateDecompressor(int predictor) throws IIOException {
+ inflater = new Inflater();
+
+ if (predictor != BaselineTIFFTagSet.PREDICTOR_NONE &&
+ predictor !=
+ BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
+ throw new IIOException("Illegal value for Predictor in " +
+ "TIFF file");
+ }
+
+ this.predictor = predictor;
+ }
+
+ public synchronized void decodeRaw(byte[] b,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException {
+
+ // Check bitsPerSample.
+ if (predictor ==
+ BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
+ int len = bitsPerSample.length;
+ for(int i = 0; i < len; i++) {
+ if(bitsPerSample[i] != 8) {
+ throw new IIOException
+ (bitsPerSample[i] + "-bit samples "+
+ "are not supported for Horizontal "+
+ "differencing Predictor");
+ }
+ }
+ }
+
+ // Seek to current tile data offset.
+ stream.seek(offset);
+
+ // Read the deflated data.
+ byte[] srcData = new byte[byteCount];
+ stream.readFully(srcData);
+
+ int bytesPerRow = (srcWidth*bitsPerPixel + 7)/8;
+ byte[] buf;
+ int bufOffset;
+ if(bytesPerRow == scanlineStride) {
+ buf = b;
+ bufOffset = dstOffset;
+ } else {
+ buf = new byte[bytesPerRow*srcHeight];
+ bufOffset = 0;
+ }
+
+ // Set the input to the Inflater.
+ inflater.setInput(srcData);
+
+ // Inflate the data.
+ try {
+ inflater.inflate(buf, bufOffset, bytesPerRow*srcHeight);
+ } catch(DataFormatException dfe) {
+ throw new IIOException("Error inflating data",
+ dfe);
+ }
+
+ // Reset the Inflater.
+ inflater.reset();
+
+ if (predictor ==
+ BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
+
+ for (int j = 0; j < srcHeight; j++) {
+ int count = bufOffset + samplesPerPixel * (j * srcWidth + 1);
+ for (int i=samplesPerPixel; i= samplesPerPixel; j--) {
+ rowBuf[j] -= rowBuf[j - samplesPerPixel];
+ }
+
+ deflater.setInput(rowBuf);
+ if(i == maxRow) {
+ deflater.finish();
+ }
+
+ int numBytes = 0;
+ while((numBytes = deflater.deflate(compData,
+ numCompressedBytes,
+ compData.length -
+ numCompressedBytes)) != 0) {
+ numCompressedBytes += numBytes;
+ }
+
+ off += scanlineStride;
+ }
+ } else {
+ deflater.setInput(b, off, height*scanlineStride);
+ deflater.finish();
+
+ numCompressedBytes = deflater.deflate(compData);
+ }
+
+ deflater.reset();
+
+ stream.write(compData, 0, numCompressedBytes);
+
+ return numCompressedBytes;
+ }
+}
diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFElementInfo.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFElementInfo.java
new file mode 100644
index 00000000000..dbe8ecbe76e
--- /dev/null
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFElementInfo.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.imageio.plugins.tiff;
+
+import javax.imageio.metadata.IIOMetadataFormat;
+
+class TIFFElementInfo {
+ String[] childNames;
+ String[] attributeNames;
+ int childPolicy;
+
+ int minChildren = 0;
+ int maxChildren = Integer.MAX_VALUE;
+
+ int objectValueType = IIOMetadataFormat.VALUE_NONE;
+ Class> objectClass = null;
+ Object objectDefaultValue = null;
+ Object[] objectEnumerations = null;
+ Comparable
+
+
If the inferred color space not based on the ICC Profile field is compatible
+with the ICC profile-based color space, then a second
+ImageTypeSpecifier derived from this inferred color
+space will be included in the
+Iterator returned by
+ImageReader.getImageTypes. If the iterator contains
+more than one type, the first one will be based on the ICC profile and the
+second on the inferred color space.
+
+
Metadata Issues
+
+By default all fields in the TIFF image file directory (IFD) are loaded into
+the native image metadata object. In cases where the IFD includes fields which
+contain large amounts of data this could be very inefficient. Which fields
+are loaded may be controlled by setting which TIFF tags the reader is allowed
+to recognize and whether it is ignoring metadata. The reader is informed to
+disregard metadata as usual via the ignoreMetadata parameter of
+ImageReader.setInput(Object,boolean,boolean). It is
+informed of which TIFFTags to
+recognize or not to recognize via
+TIFFImageReadParam.addAllowedTagSet(TIFFTagSet)
+and
+TIFFImageReadParam.removeAllowedTagSet(TIFFTagSet).
+If ignoreMetadata is true, then the reader will
+load into the native image metadata object only those fields which have a
+TIFFTag contained in the one of the allowed
+TIFFTagSets.
+
+
Use of a TIFFDirectory
+object may simplify gaining access to metadata values. An instance of
+TIFFDirectory may be created from the IIOMetadata
+object returned by the TIFF reader using the
+TIFFDirectory.createFromMetadata method.
+
+
+Mapping of TIFF Native Image Metadata to the Standard Metadata Format
PhotometricInterpretation PaletteColor => "Index";
+SampleFormat unsigned integer data => "UnsignedIntegral";
+SampleFormat two's complement signed integer data => "SignedIntegral";
+SampleFormat IEEE floating point data => "Real";
+otherwise element not emitted.
+
+
+
+
/Data/BitsPerSample@value
+
BitsPerSample as a space-separated list.
+
+
+
/Data/SampleMSB@value
+
FillOrder: left-to-right => space-separated list of BitsPerSample-1;
+right-to-left => space-separated list of 0s.
+
+
+
/Dimension/PixelAspectRatio@value
+
(1/XResolution)/(1/YResolution)
+
+
+
/Dimension/ImageOrientation@value
+
Orientation
+
+
+
/Dimension/HorizontalPixelSize@value
+
1/XResolution in millimeters if ResolutionUnit is not None.
+
+
+
/Dimension/VerticalPixelSize@value
+
1/YResolution in millimeters if ResolutionUnit is not None.
+
+
+
/Dimension/HorizontalPosition@value
+
XPosition in millimeters if ResolutionUnit is not None.
+
+
+
/Dimension/VerticalPosition@value
+
YPosition in millimeters if ResolutionUnit is not None.
DocumentName, ImageDescription, Make, Model, PageName, Software,
+Artist, HostComputer, InkNames, Copyright:
+/Text/TextEntry@keyword = field name,
+/Text/TextEntry@value = field value.
+Example: TIFF Software field => /Text/TextEntry@keyword = "Software",
+/Text/TextEntry@value = Name and version number of the software package(s)
+used to create the image.
+
+The TIFF reader may be used to read an uncompressed Exif image or the
+contents of the APP1 marker segment of a compressed Exif image.
+
+
Reading Uncompressed Exif Images
+
+An uncompressed Exif image is a one- or two-page uncompressed TIFF image
+with a specific ordering of its IFD and image data content. Each pixel
+has three 8-bit samples with photometric interpretation RGB or YCbCr.
+The image stream must contain a single primary image and may contain a
+single thumbnail which if present must also be uncompressed. The usual
+ImageReader methods may be used to read the image
+data and metadata:
+
+
+
+Note that the Exif thumbnail is treated as a separate page in the TIFF
+stream and not as a thumbnail, i.e.,
+tiffReader.hasThumbnails(0) will return false.
+
+
Reading Compressed Exif Images
+
+A compressed Exif image is a 3-band ISO/IEC 10918-1 baseline DCT JPEG stream
+with an inserted APP1 marker segment. The parameters of the marker
+segment after the length are the 6-byte sequence
+{'E', 'x', 'i', 'f', 0x00, 0x00}
+followed by a complete TIFF stream. The embedded TIFF stream contains a primary
+IFD describing the JPEG image optionally followed by a thumbnail IFD and
+compressed or uncompressed thumbnail image data. Note that the embedded TIFF
+stream does not contain any image data associated with the primary IFD
+nor any descriptive fields which duplicate information found in the JPEG
+stream itself.
+
+
The parameter content of the APP1 marker segment may be obtained
+from the user object of the associated Node in a
+javax_imageio_jpeg_image_1.0 native image metadata tree extracted
+from the image metadata object returned by the JPEG reader. This APP1 Exif
+node will be a child of the node named "markerSequence" and will
+have name unknown and an attribute named MarkerTag with
+integral value 0xE1 (String value
+"225"). The user object of this node will be a byte array
+which starts with the six bytes {'E', 'x', 'i', 'f', '0', '0'}.
+The primary IFD and the thumbnail IFD and image may be
+read from the user object by the usual ImageReader
+methods:
+
+
+ ImageReader jpegReader;
+ ImageReader tiffReader;
+
+ // Obtain the APP1 Exif marker data from the JPEG image metadata.
+ IIOMetadata jpegImageMetadata = jpegReader.getImageMetadata(0);
+ String nativeFormat = jpegImageMetadata.getNativeMetadataFormatName();
+ Node jpegImageMetadataTree = jpegImageMetadata.getAsTree(nativeFormat);
+
+ // getExifMarkerData() returns the byte array which is the user object
+ // of the APP1 Exif marker node.
+ byte[] app1Params = getExifMarkerData(jpegImageMetadataTree);
+ if (app1Params == null) {
+ throw new IIOException("APP1 Exif marker not found.");
+ }
+
+ // Set up input, skipping Exif ID 6-byte sequence.
+ MemoryCacheImageInputStream app1ExifInput
+ = new MemoryCacheImageInputStream
+ (new ByteArrayInputStream(app1Params, 6, app1Params.length - 6));
+ tiffReader.setInput(app1ExifInput);
+
+ // Read primary IFD.
+ IIOMetadata primaryIFD = tiffReader.getImageMetadata(0);
+
+ // Read thumbnail if present.
+ BufferedImage thumbnail = null;
+ if (tiffReader.getNumImages(true) > 1) {
+ thumbnail = tiffReader.read(1, tiffReadParam);
+ }
+
+ // Read the primary image.
+ BufferedImage image = jpegReader.read(0);
+
+
+Note that tiffReader.getNumImages(true) returns the number of
+IFDs in the embedded TIFF stream including those corresponding to empty
+images. Calling tiffReader.read(0, readParam) will throw
+an exception as the primary image in the embedded TIFF stream is always
+empty; the primary image should be obtained using the JPEG reader itself.
+
+
+
Writing Images
+
+TIFF images are written by a ImageWriter which may be
+controlled by its public interface as well as via a supplied
+ImageWriteParam. For an ImageWriteParam returned
+by the getDefaultWriteParam() method of the TIFF ImageWriter,
+the canWriteTiles() and canWriteCompressed() methods
+will return true; the canOffsetTiles() and
+canWriteProgressive() methods will return false.
+
+The TIFF writer supports many optional capabilities including writing tiled
+images, inserting images, writing or inserting empty images, and replacing image
+data. Pixels may be replaced in either empty or non-empty images but if and
+only if the data are not compressed.
+
+
If tiles are being written, then each of their dimensions will be
+rounded to the nearest multiple of 16 per the TIFF specification. If
+JPEG-in-TIFF compression is being used, and tiles are being written
+each tile dimension will be rounded to the nearest multiple of 8 times
+the JPEG minimum coded unit (MCU) in that dimension. If JPEG-in-TIFF
+compression is being used and strips are being written, the number of
+rows per strip is rounded to a multiple of 8 times the maximum MCU over
+both dimensions.
+
+
+
+
+
+
Compression
+
+The compression type may be set via the setCompressionType() method of
+the ImageWriteParam after setting the compression mode to
+MODE_EXPLICIT. The set of innately
+supported compression types is listed in the following table:
+
+
+Old-style JPEG compression as described in section 22 of the TIFF 6.0
+Specification is not supported.
+
+
+
The CCITT compression types are applicable to bilevel (1-bit)
+images only. The JPEG compression type is applicable to byte
+grayscale (1-band) and RGB (3-band) images only.
+
+
+ZLib and Deflate compression are identical except for the value of the
+TIFF Compression field: for ZLib the Compression field has value 8
+whereas for Deflate it has value 32946 (0x80b2). In both cases each
+image segment (strip or tile) is written as a single complete zlib data
+stream.
+
+
+
+"Exif JPEG" is a compression type used when writing the contents of an
+APP1 Exif marker segment for inclusion in a JPEG native image metadata
+tree. The contents appended to the output when this compression type is
+used are a function of whether an empty or non-empty image is written.
+If the image is empty, then a TIFF IFD adhering to the specification of
+a compressed Exif primary IFD is appended. If the image is non-empty,
+then a complete IFD and image adhering to the specification of a
+compressed Exif thumbnail IFD and image are appended. Note that the
+data of the empty image may not later be appended using the pixel
+replacement capability of the TIFF writer.
+
+
+
If ZLib/Deflate or JPEG compression is used, the compression quality
+may be set. For ZLib/Deflate the supplied floating point quality value is
+rescaled to the range [1, 9] and truncated to an integer
+to derive the Deflate compression level. For JPEG the floating point
+quality value is passed directly to the JPEG writer plug-in which
+interprets it in the usual way.
+
+
Color Conversion
+
+
If the source image data
+color space type is RGB, and the destination photometric type is CIE L*a*b* or
+YCbCr, then the source image data will be automatically converted from
+RGB using an internal color converter.
+
+
ICC Profiles
+
+An ICC Profile field will be written if either:
+
+
one is present in the native image metadata
+IIOMetadata instance supplied to the writer,
+or
+
the ColorSpace
+of the destination ImageTypeSpecifier is an instance of
+ICC_ColorSpace which is not one of the standard
+color spaces defined by the CS_* constants in the
+ColorSpace class. The destination type is set via
+ImageWriteParam.setDestinationType(ImageTypeSpecifier) and defaults
+to the ImageTypeSpecifier of the image being written.
+
+
+
+
Metadata Issues
+
+Some behavior of the writer is affected by or may affect the contents of
+the image metadata which may be supplied by the user.
+
+
For bilevel images, the FillOrder, and T4Options
+fields affect the output data. The data will be filled right-to-left if
+FillOrder is present with a value of 2
+(BaselineTIFFTagSet.FILL_ORDER_RIGHT_TO_LEFT)
+and will be filled left-to-right otherwise. The value of T4Options
+specifies whether the data should be 1D- or 2D-encoded and whether EOL
+padding should be used.
+
+
For all images the value of the RowsPerStrip field is used
+to the set the number of rows per strip if the image is not tiled. The
+default number of rows per strip is either 8 or the number of rows which
+would fill no more than 8 kilobytes, whichever is larger.
+
+
For all images the tile dimensions may be set using the TileWidth
+and TileLength field values if the tiling mode is
+ImageWriteParam.MODE_COPY_FROM_METADATA. If this mode
+is set but the fields are not, their respective default values are the image
+width and height.
+
+
When using JPEG-in-TIFF compression, a JPEGTables field will be
+written to the IFD and abbreviated JPEG streams to each strip or tile if and
+only if a JPEGTables field is contained in the metadata object
+provided to the writer. If the contents of the JPEGTables field is
+a valid tables-only JPEG stream, then it will be used; otherwise the contents
+of the field will be replaced with default visually lossless tables. If no
+such JPEGTables field is present in the metadata, then no
+JPEGTables field will be written to the output and each strip or
+tile will be written as a separate, self-contained JPEG stream.
+
+
When using Deflate/ZLib or LZW compression, if the image has 8 bits per
+sample, a horizontal differencing predictor will be used if the
+Predictor field is present with a value of 2
+(BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING).
+If prediction is so requested but the image does not have
+8 bits per sample the field will be reset to have the value 1
+(BaselineTIFFTagSet.PREDICTOR_NONE).
+
+
+
Some fields may be added or modified:
+
+
+
PhotometricInterpretation if not present.
+
PlanarConfiguration if this field is present with value
+Planar is is reset to Chunky.
+
Compression always.
+
BitsPerSample if the image is not bilevel.
+
SamplesPerPixel always.
+
ExtraSamples if an alpha channel is present.
+
SampleFormat if not present and the data are 16- or 32-bit
+integers or floating point.
+
ColorMap if the PhotometricInterpretation is
+RGBPalette.
+
ImageWidth and ImageLength always.
+
TileWidth, TileLength, TileOffsets, and
+TileByteCounts if a tiled image is being written.
+
RowsPerStrip, StripOffsets, and StripByteCounts
+if a tiled image is not being written.
+
XResolution, YResolution, and ResolutionUnit
+if none of these is present.
+
YCbCrSubsampling and YCbCrPositioning if the
+photometric interpretation is YCbCr and the compression type is not JPEG
+(only [1, 1] subsampling and cosited positioning are supported for
+non-JPEG YCbCr output).
+
YCbCrSubsampling, YCbCrPositioning, and
+ReferenceBlackWhite: if the compression type is JPEG and the color
+space is RGB these will be reset to [2, 2] centered subsampling with no
+headroom/footroom (0:255,128:255,128:255).
+
+
+
Some fields may be removed:
+
+
+
BitsPerSample if the image is bilevel.
+
ExtraSamples if the image does not have an alpha channel.
+
ColorMap if the photometric interpretation is not
+RGBPalette.
+
TileWidth, TileLength, TileOffsets, and
+TileByteCounts if tiling is not being used.
+
RowsPerStrip, StripOffsets, and StripByteCounts
+if tiling is being used.
+
YCbCrSubsampling, YCbCrPositioning, and
+ReferenceBlackWhite if the compression type is JPEG and the
+color space is grayscale.
+
JPEGProc, JPEGInterchangeFormat,
+JPEGInterchangeFormatLength, JPEGRestartInterval,
+JPEGLosslessPredictors, JPEGPointTransforms,
+JPEGQTables, JPEGDCTables, and
+JPEGACTables if the compression type is JPEG.
+
+
+
+
Other fields present in the supplied metadata are uninterpreted and will
+be written as supplied.
+
+
If an Exif image is being written, the set of fields present and their
+values will be modified such that the result is in accord with the Exif 2.2
+specification.
+
+
Setting up the image metadata to write to a TIFF stream may be simplified
+by using the TIFFDirectory class
+which represents a TIFF IFD. A field in a TIFF IFD is represented by an
+instance of TIFFField. For each
+field to be written a TIFFField may be added to the
+TIFFDirectory and the latter converted to an
+IIOMetadata object by invoking
+TIFFDirectory.getAsMetadata. The
+IIOMetadata object so obtained may then be passed to the TIFF
+writer.
+
+
+Mapping of the Standard Metadata Format to TIFF Native Image Metadata
/Text/TextEntry: if /Text/TextEntry@keyword is the name of any of the
+TIFF Fields, e.g., "Software", then the field is added with content
+/Text/TextEntry@value and count 1.
+
+The TIFF writer may be used to write an uncompressed Exif image or the
+contents of the APP1 marker segment of a compressed Exif image.
+
+
Writing Uncompressed Exif Images
+
+When writing a sequence of images each image is normally recorded as
+{IFD, IFD Value, Image Data}. The Exif specification requires
+that an uncompressed Exif image be structured as follows:
+
+
+
+
Image File Header
+
Primary IFD
+
Primary IFD Value
+
Thumbnail IFD
+
Thumbnail IFD Value
+
Thumbnail Image Data
+
Primary Image Data
+
+
+To meet the requirement of the primary image data being recorded last, the
+primary image must be written initially as an empty image and have its data
+added via pixel replacement after the thumbnail IFD and image data have been
+written:
+
+
+
+The structure of the embedded TIFF stream in the APP1 segment of a
+compressed Exif image is identical to the
+uncompressed Exif image structure except that there are no primary
+image data, i.e., the primary IFD does not refer to any image data.
+
+
+ ImageWriter tiffWriter;
+ ImageWriteParam tiffWriteParam;
+ IIOMetadata tiffStreamMetadata;
+ BufferedImage image;
+ BufferedImage thumbnail;
+ IIOMetadata primaryIFD;
+ ImageOutputStream output;
+
+ // Set up an output to contain the APP1 Exif TIFF stream.
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ MemoryCacheImageOutputStream app1ExifOutput =
+ new MemoryCacheImageOutputStream(baos);
+ tiffWriter.setOutput(app1ExifOutput);
+
+ // Set compression for the thumbnail.
+ tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+ tiffWriteParam.setCompressionType("Exif JPEG");
+
+ // Write the APP1 Exif TIFF stream.
+ if (thumbnail != null) {
+ // Write the TIFF header.
+ tiffWriter.prepareWriteSequence(tiffStreamMetadata);
+
+ // Append the primary IFD.
+ tiffWriter.prepareInsertEmpty(-1, // append
+ new ImageTypeSpecifier(image),
+ image.getWidth(),
+ image.getHeight(),
+ primaryIFD,
+ null, // thumbnails
+ tiffWriteParam);
+ tiffWriter.endInsertEmpty();
+
+ // Append the thumbnail IFD and image data.
+ tiffWriter.writeToSequence(new IIOImage(thumbnail, null,
+ null), tiffWriteParam);
+
+ // End writing.
+ tiffWriter.endWriteSequence();
+ } else {
+ // Write only the primary IFD.
+ tiffWriter.prepareWriteEmpty(tiffStreamMetadata,
+ new ImageTypeSpecifier(image),
+ image.getWidth(),
+ image.getHeight(),
+ primaryIFD,
+ null, // thumbnails
+ tiffWriteParam);
+ tiffWriter.endWriteEmpty();
+ }
+
+ // Flush data into byte stream.
+ app1ExifOutput.flush();
+
+ // Create APP1 parameter array.
+ byte[] app1Parameters = new byte[6 + baos.size()];
+
+ // Add APP1 Exif ID bytes.
+ app1Parameters[0] = (byte) 'E';
+ app1Parameters[1] = (byte) 'x';
+ app1Parameters[2] = (byte) 'i';
+ app1Parameters[3] = (byte) 'f';
+ app1Parameters[4] = app1Parameters[5] = (byte) 0;
+
+ // Append TIFF stream to APP1 parameters.
+ System.arraycopy(baos.toByteArray(), 0, app1Parameters, 6, baos.size());
+
+ // Create the APP1 Exif node to be added to native JPEG image metadata.
+ IIOMetadataNode app1Node = new IIOMetadataNode("unknown");
+ app1Node.setAttribute("MarkerTag", String.valueOf(0xE1));
+ app1Node.setUserObject(app1Parameters);
+
+ // Append the APP1 Exif marker to the "markerSequence" node.
+ IIOMetadata jpegImageMetadata =
+ jpegWriter.getDefaultImageMetadata(new ImageTypeSpecifier(image),
+ jpegWriteParam);
+ String nativeFormat = jpegImageMetadata.getNativeMetadataFormatName();
+ Node tree = jpegImageMetadata.getAsTree(nativeFormat);
+ NodeList children = tree.getChildNodes();
+ int numChildren = children.getLength();
+ for (int i = 0; i < numChildren; i++) {
+ Node child = children.item(i);
+ if (child.getNodeName().equals("markerSequence")) {
+ child.appendChild(app1Node);
+ break;
+ }
+ }
+ jpegImageMetadata.setFromTree(nativeFormat, tree);
+
+ // Write the JPEG image data including the APP1 Exif marker.
+ jpegWriter.setOutput(output);
+ jpegWriter.write(new IIOImage(image, null, jpegImageMetadata));
+
+
+The "unknown" node created above would be appended to the
+"markerSequence" node of the native JPEG image metadata
+and written to the JPEG stream when the primary image is written using
+the JPEG writer.
+
+
Stream Metadata
+
+The DTD for the TIFF native stream metadata format is as follows:
+
+
+<!DOCTYPE "javax_imageio_tiff_stream_1.0" [
+
+ <!ELEMENT "javax_imageio_tiff_stream_1.0" (ByteOrder)>
+
+ <!ELEMENT "ByteOrder" EMPTY>
+ <!-- The stream byte order -->
+ <!ATTLIST "ByteOrder" "value" #CDATA #REQUIRED>
+ <!-- One of "BIG_ENDIAN" or "LITTLE_ENDIAN" -->
+ <!-- Data type: String -->
+]>
+
+
+
Image Metadata
+
+The DTD for the TIFF native image metadata format is as follows:
+
+
+<!DOCTYPE "javax_imageio_tiff_image_1.0" [
+
+ <!ELEMENT "javax_imageio_tiff_image_1.0" (TIFFIFD)*>
+
+ <!ELEMENT "TIFFIFD" (TIFFField | TIFFIFD)*>
+ <!-- An IFD (directory) containing fields -->
+ <!ATTLIST "TIFFIFD" "tagSets" #CDATA #REQUIRED>
+ <!-- Data type: String -->
+ <!ATTLIST "TIFFIFD" "parentTagNumber" #CDATA #IMPLIED>
+ <!-- The tag number of the field pointing to this IFD -->
+ <!-- Data type: Integer -->
+ <!ATTLIST "TIFFIFD" "parentTagName" #CDATA #IMPLIED>
+ <!-- A mnemonic name for the field pointing to this IFD, if known
+ -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFField" (TIFFBytes | TIFFAsciis |
+ TIFFShorts | TIFFSShorts | TIFFLongs | TIFFSLongs |
+ TIFFRationals | TIFFSRationals |
+ TIFFFloats | TIFFDoubles | TIFFUndefined)>
+ <!-- A field containing data -->
+ <!ATTLIST "TIFFField" "number" #CDATA #REQUIRED>
+ <!-- The tag number asociated with the field -->
+ <!-- Data type: String -->
+ <!ATTLIST "TIFFField" "name" #CDATA #IMPLIED>
+ <!-- A mnemonic name associated with the field, if known -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFBytes" (TIFFByte)*>
+ <!-- A sequence of TIFFByte nodes -->
+
+ <!ELEMENT "TIFFByte" EMPTY>
+ <!-- An integral value between 0 and 255 -->
+ <!ATTLIST "TIFFByte" "value" #CDATA #IMPLIED>
+ <!-- The value -->
+ <!-- Data type: String -->
+ <!ATTLIST "TIFFByte" "description" #CDATA #IMPLIED>
+ <!-- A description, if available -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFAsciis" (TIFFAscii)*>
+ <!-- A sequence of TIFFAscii nodes -->
+
+ <!ELEMENT "TIFFAscii" EMPTY>
+ <!-- A String value -->
+ <!ATTLIST "TIFFAscii" "value" #CDATA #IMPLIED>
+ <!-- The value -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFShorts" (TIFFShort)*>
+ <!-- A sequence of TIFFShort nodes -->
+
+ <!ELEMENT "TIFFShort" EMPTY>
+ <!-- An integral value between 0 and 65535 -->
+ <!ATTLIST "TIFFShort" "value" #CDATA #IMPLIED>
+ <!-- The value -->
+ <!-- Data type: String -->
+ <!ATTLIST "TIFFShort" "description" #CDATA #IMPLIED>
+ <!-- A description, if available -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFSShorts" (TIFFSShort)*>
+ <!-- A sequence of TIFFSShort nodes -->
+
+ <!ELEMENT "TIFFSShort" EMPTY>
+ <!-- An integral value between -32768 and 32767 -->
+ <!ATTLIST "TIFFSShort" "value" #CDATA #IMPLIED>
+ <!-- The value -->
+ <!-- Data type: String -->
+ <!ATTLIST "TIFFSShort" "description" #CDATA #IMPLIED>
+ <!-- A description, if available -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFLongs" (TIFFLong)*>
+ <!-- A sequence of TIFFLong nodes -->
+
+ <!ELEMENT "TIFFLong" EMPTY>
+ <!-- An integral value between 0 and 4294967295 -->
+ <!ATTLIST "TIFFLong" "value" #CDATA #IMPLIED>
+ <!-- The value -->
+ <!-- Data type: String -->
+ <!ATTLIST "TIFFLong" "description" #CDATA #IMPLIED>
+ <!-- A description, if available -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFSLongs" (TIFFSLong)*>
+ <!-- A sequence of TIFFSLong nodes -->
+
+ <!ELEMENT "TIFFSLong" EMPTY>
+ <!-- An integral value between -2147483648 and 2147482647 -->
+ <!ATTLIST "TIFFSLong" "value" #CDATA #IMPLIED>
+ <!-- The value -->
+ <!-- Data type: String -->
+ <!ATTLIST "TIFFSLong" "description" #CDATA #IMPLIED>
+ <!-- A description, if available -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFRationals" (TIFFRational)*>
+ <!-- A sequence of TIFFRational nodes -->
+
+ <!ELEMENT "TIFFRational" EMPTY>
+ <!-- A rational value consisting of an unsigned numerator and
+ denominator -->
+ <!ATTLIST "TIFFRational" "value" #CDATA #IMPLIED>
+ <!-- The numerator and denominator, separated by a slash -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFSRationals" (TIFFSRational)*>
+ <!-- A sequence of TIFFSRational nodes -->
+
+ <!ELEMENT "TIFFSRational" EMPTY>
+ <!-- A rational value consisting of a signed numerator and
+ denominator -->
+ <!ATTLIST "TIFFSRational" "value" #CDATA #IMPLIED>
+ <!-- The numerator and denominator, separated by a slash -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFFloats" (TIFFFloat)*>
+ <!-- A sequence of TIFFFloat nodes -->
+
+ <!ELEMENT "TIFFFloat" EMPTY>
+ <!-- A single-precision floating-point value -->
+ <!ATTLIST "TIFFFloat" "value" #CDATA #IMPLIED>
+ <!-- The value -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFDoubles" (TIFFDouble)*>
+ <!-- A sequence of TIFFDouble nodes -->
+
+ <!ELEMENT "TIFFDouble" EMPTY>
+ <!-- A double-precision floating-point value -->
+ <!ATTLIST "TIFFDouble" "value" #CDATA #IMPLIED>
+ <!-- The value -->
+ <!-- Data type: String -->
+
+ <!ELEMENT "TIFFUndefined" EMPTY>
+ <!-- Uninterpreted byte data -->
+ <!ATTLIST "TIFFUndefined" "value" #CDATA #IMPLIED>
+ <!-- A list of comma-separated byte values -->
+ <!-- Data type: String -->
+]>
+
+
+@since 1.9
+
+
+
diff --git a/jdk/src/java.desktop/share/classes/javax/imageio/metadata/package.html b/jdk/src/java.desktop/share/classes/javax/imageio/metadata/package.html
index 8fc6561d600..da369489951 100644
--- a/jdk/src/java.desktop/share/classes/javax/imageio/metadata/package.html
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/metadata/package.html
@@ -2,7 +2,7 @@
+
+
+
+
+
+Public classes used by the built-in TIFF plug-ins.
+
+
+This package contains classes supporting the built-in TIFF reader and writer
+plug-ins. Classes are provided for simplifying interaction with metadata,
+including Exif metadata common in digital photography, and an extension of
+{@link javax.imageio.ImageReadParam} which permits specifying which metadata
+tags are allowed to be read. For more information about the operation of the
+built-in TIFF plug-ins, see the
+TIFF metadata format
+specification and usage notes.
+
+
+
+
+
+@since 1.9
+
+
diff --git a/jdk/src/java.desktop/share/classes/javax/imageio/spi/IIORegistry.java b/jdk/src/java.desktop/share/classes/javax/imageio/spi/IIORegistry.java
index 445f03323d7..a643218f861 100644
--- a/jdk/src/java.desktop/share/classes/javax/imageio/spi/IIORegistry.java
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/spi/IIORegistry.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -49,6 +49,8 @@ import com.sun.imageio.plugins.bmp.BMPImageReaderSpi;
import com.sun.imageio.plugins.bmp.BMPImageWriterSpi;
import com.sun.imageio.plugins.wbmp.WBMPImageReaderSpi;
import com.sun.imageio.plugins.wbmp.WBMPImageWriterSpi;
+import com.sun.imageio.plugins.tiff.TIFFImageReaderSpi;
+import com.sun.imageio.plugins.tiff.TIFFImageWriterSpi;
import sun.awt.AppContext;
import java.util.ServiceLoader;
import java.util.ServiceConfigurationError;
@@ -168,6 +170,8 @@ public final class IIORegistry extends ServiceRegistry {
registerServiceProvider(new BMPImageWriterSpi());
registerServiceProvider(new WBMPImageReaderSpi());
registerServiceProvider(new WBMPImageWriterSpi());
+ registerServiceProvider(new TIFFImageReaderSpi());
+ registerServiceProvider(new TIFFImageWriterSpi());
registerServiceProvider(new PNGImageReaderSpi());
registerServiceProvider(new PNGImageWriterSpi());
registerServiceProvider(new JPEGImageReaderSpi());