8143342: Integrate Java Image I/O support for TIFF per JEP 262
Summary: Port TIFF reader and writer plugins from JAI Image I/O Tools to JDK 9
Reviewed-by: prr, serb
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/ImageUtil.java Mon Nov 23 10:00:50 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/ImageUtil.java Mon Nov 23 12:26:12 2015 -0800
@@ -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.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.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 <code>ColorModel</code> that may be used with the
* specified <code>SampleModel</code>. If a suitable
@@ -1162,4 +1114,78 @@
// pixel stride.
return ImageUtil.isBinary(sm);
}
+
+ /**
+ * Gets the destination image type.
+ */
+ public static final ImageTypeSpecifier
+ getDestinationType(ImageReadParam param,
+ Iterator<ImageTypeSpecifier> 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 <code>true</code> if the given <code>ColorSpace</code> object is
+ * an instance of <code>ICC_ColorSpace</code> but is not one of the standard
+ * <code>ColorSpace</code>s returned by <code>ColorSpace.getInstance()</code>.
+ *
+ * @param cs The <code>ColorSpace</code> 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;
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleCMYKColorSpace.java Mon Nov 23 12:26:12 2015 -0800
@@ -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));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SimpleRenderedImage.java Mon Nov 23 12:26:12 2015 -0800
@@ -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<RenderedImage> sources = new Vector<RenderedImage>();
+
+ /** A Hashtable containing the image properties. */
+ protected Hashtable<String,Object> properties = new Hashtable<String,Object>();
+
+ /** 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,
+ * <code>java.awt.Image.UndefinedProperty</code> will be returned.
+ *
+ * @param name the name of the property to get, as a
+ * <code>String</code>. @return a reference to the property
+ * <code>Object</code>, or the value
+ * <code>java.awt.Image.UndefinedProperty.</code>
+ */
+ 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, <code>null</code> will be
+ * returned.
+ *
+ * @return an array of <code>String</code>s representing valid
+ * property names.
+ */
+ public String[] getPropertyNames() {
+ String[] names = null;
+
+ if(properties.size() > 0) {
+ names = new String[properties.size()];
+ int index = 0;
+
+ Enumeration<String> e = properties.keys();
+ while (e.hasMoreElements()) {
+ String name = e.nextElement();
+ names[index++] = name;
+ }
+ }
+
+ return names;
+ }
+
+ /**
+ * Returns an array of <code>String</code>s recognized as names by
+ * this property source that begin with the supplied prefix. If
+ * no property names match, <code>null</code> will be returned.
+ * The comparison is done in a case-independent manner.
+ *
+ * <p> The default implementation calls
+ * <code>getPropertyNames()</code> and searches the list of names
+ * for matches.
+ *
+ * @return an array of <code>String</code>s giving the valid
+ * property names.
+ */
+ public String[] getPropertyNames(String prefix) {
+ String propertyNames[] = getPropertyNames();
+ if (propertyNames == null) {
+ return null;
+ }
+
+ prefix = prefix.toLowerCase();
+
+ Vector<String> names = new Vector<String>();
+ 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<String> 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<RenderedImage> getSources() {
+ return null;
+ }
+
+ /**
+ * Returns the entire image in a single Raster. For images with
+ * multiple tiles this will require making a copy.
+ *
+ * <p> 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.
+ *
+ * <p> 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.
+ *
+ * <p> 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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/SingleTileRenderedImage.java Mon Nov 23 12:26:12 2015 -0800
@@ -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;
+ }
+}
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/iio-plugin.properties Mon Nov 23 10:00:50 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/common/iio-plugin.properties Mon Nov 23 12:26:12 2015 -0800
@@ -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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFAttrInfo.java Mon Nov 23 12:26:12 2015 -0800
@@ -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() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFBaseJPEGCompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -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 <code>true</code> should also
+ * initialized {@link #JPEGStreamMetadata}.
+ */
+ protected boolean writeAbbreviatedStream = false;
+
+ /**
+ * Stream metadata equivalent to a tables-only stream such as in
+ * the <code>JPEGTables</code>. Default value is <code>null</code>.
+ * This should be set by any subclass which sets
+ * {@link writeAbbreviatedStream} to <code>true</code>.
+ */
+ 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 <code>pruneTables</code> is
+ * <code>true</code> in which case the nodes derived from the DHT and
+ * DQT marker segments are also removed.
+ *
+ * @param tree A <tt>javax_imageio_jpeg_image_1.0</tt> tree.
+ * @param pruneTables Whether to prune Huffman and quantization tables.
+ * @throws NullPointerException if <code>tree</code> is
+ * <code>null</code>.
+ * @throws IllegalArgumentException if <code>tree</code> 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<String> wantedNodes = new ArrayList<String>();
+ 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<Node> 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<Node> getAllNodes(IIOMetadataNode root, List<Node> nodes) {
+ if(nodes == null) nodes = new ArrayList<Node>();
+
+ 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 <code>ByteArrayOutputStream</code> which allows writing to an
+ * <code>ImageOutputStream</code>.
+ */
+ 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<ImageWriter> 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();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFCIELabColorConverter.java Mon Nov 23 12:26:12 2015 -0800
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFColorConverter.java Mon Nov 23 12:26:12 2015 -0800
@@ -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 <code>TIFFColorConverter</code>.
+ */
+ 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 <code>result</code> array.
+ *
+ * @param r the red value.
+ * @param g the green value.
+ * @param b the blue value.
+ * @param result an array of <code>float</code>s containing three elements.
+ * @throws NullPointerException if <code>result</code> is
+ * <code>null</code>.
+ * @throws ArrayIndexOutOfBoundsException if
+ * <code>result.length < 3</code>.
+ */
+ 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 <code>rgb</code> 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 <code>float</code>s containing three elements.
+ * @throws NullPointerException if <code>rgb</code> is
+ * <code>null</code>.
+ * @throws ArrayIndexOutOfBoundsException if
+ * <code>rgb.length < 3</code>.
+ */
+ public abstract void toRGB(float x0, float x1, float x2, float[] rgb);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFCompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -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 <code>ImageWriter</code> calling this
+ * <code>TIFFCompressor</code>.
+ */
+ protected ImageWriter writer;
+
+ /**
+ * The <code>IIOMetadata</code> 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 <i>Compression</i> tag in the
+ * TIFF image metadata.
+ */
+ protected int compressionTagValue;
+
+ /**
+ * Whether the compression is lossless.
+ */
+ protected boolean isCompressionLossless;
+
+ /**
+ * The <code>ImageOutputStream</code> 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.
+ *
+ * <p>The parameters <code>compressionTagValue</code> and
+ * <code>isCompressionLossless</code> 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 <code>compressionTagValue</code> and
+ * <code>isCompressionLossless</code> parameters are ignored.</p>
+ *
+ * @param compressionType The name of the compression type.
+ * @param compressionTagValue The value to be assigned to the TIFF
+ * <i>Compression</i> tag in the TIFF image metadata; ignored if
+ * <code>compressionType</code> is a known type.
+ * @param isCompressionLossless Whether the compression is lossless;
+ * ignored if <code>compressionType</code> is a known type.
+ *
+ * @throws NullPointerException if <code>compressionType</code> is
+ * <code>null</code>.
+ * @throws IllegalArgumentException if <code>compressionTagValue</code> is
+ * less <code>1</code>.
+ */
+ 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 <i>Compression</i> tag
+ * in the TIFF image metadata.
+ *
+ * @return The <i>Compression</i> 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 <code>ImageOutputStream</code> to be written.
+ *
+ * @param stream an <code>ImageOutputStream</code> to be written.
+ *
+ * @see #getStream
+ */
+ public void setStream(ImageOutputStream stream) {
+ this.stream = stream;
+ }
+
+ /**
+ * Returns the <code>ImageOutputStream</code> that will be written.
+ *
+ * @return an <code>ImageOutputStream</code>.
+ *
+ * @see #setStream(ImageOutputStream)
+ */
+ public ImageOutputStream getStream() {
+ return stream;
+ }
+
+ /**
+ * Sets the value of the <code>writer</code> field.
+ *
+ * @param writer the current <code>ImageWriter</code>.
+ *
+ * @see #getWriter()
+ */
+ public void setWriter(ImageWriter writer) {
+ this.writer = writer;
+ }
+
+ /**
+ * Returns the current <code>ImageWriter</code>.
+ *
+ * @return an <code>ImageWriter</code>.
+ *
+ * @see #setWriter(ImageWriter)
+ */
+ public ImageWriter getWriter() {
+ return this.writer;
+ }
+
+ /**
+ * Sets the value of the <code>metadata</code> field.
+ *
+ * @param metadata the <code>IIOMetadata</code> object for the
+ * image being written.
+ *
+ * @see #getMetadata()
+ */
+ public void setMetadata(IIOMetadata metadata) {
+ this.metadata = metadata;
+ }
+
+ /**
+ * Returns the current <code>IIOMetadata</code> object.
+ *
+ * @return the <code>IIOMetadata</code> 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
+ * <code>ImageOutputStream</code>.
+ *
+ * @param b an array of <code>byte</code>s containing the packed
+ * but uncompressed image data.
+ * @param off the starting offset of the data to be written in the
+ * array <code>b</code>.
+ * @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 <code>int</code>s 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 <code>TIFFCompressor</code>, 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;
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -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.
+ *
+ * <p> The mapping between source and destination Y coordinates is
+ * given by the equations:
+ *
+ * <pre>
+ * dx = (sx - sourceXOffset)/subsampleX + dstXOffset;
+ * dy = (sy - sourceYOffset)/subsampleY + dstYOffset;
+ * </pre>
+ *
+ * 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:
+ *
+ * <pre>
+ * sx = (dx - dstXOffset)*subsampleX + sourceXOffset;
+ * sy = (dy - dstYOffset)*subsampleY + sourceYOffset;
+ * </pre>
+ *
+ * <p> Decompressors may be written with various levels of complexity.
+ * The most complex decompressors will override the
+ * <code>decode</code> 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.
+ *
+ * <p> Less ambitious decompressors may override the
+ * <code>decodeRaw</code> method, which is responsible for
+ * decompressing the entire tile or strip into a byte array (or other
+ * appropriate datatype). The default implementation of
+ * <code>decode</code> will perform all necessary setup of buffers,
+ * call <code>decodeRaw</code> 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
+ * <code>decodeRaw</code> in order to avoid making an extra copy.
+ *
+ * <p> Slightly more ambitious decompressors may override
+ * <code>decodeRaw</code>, but avoid writing pixels that will be
+ * discarded in the subsampling phase.
+ */
+public abstract class TIFFDecompressor {
+
+ /**
+ * The <code>ImageReader</code> calling this
+ * <code>TIFFDecompressor</code>.
+ */
+ protected ImageReader reader;
+
+ /**
+ * The <code>IIOMetadata</code> object containing metadata for the
+ * current image.
+ */
+ protected IIOMetadata metadata;
+
+ /**
+ * The value of the <code>PhotometricInterpretation</code> 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 <code>Compression</code> 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;
+
+ /**
+ * <code>true</code> if the image is encoded using separate planes.
+ */
+ protected boolean planar;
+
+ /**
+ * The value of the <code>SamplesPerPixel</code> tag.
+ */
+ protected int samplesPerPixel;
+
+ /**
+ * The value of the <code>BitsPerSample</code> tag.
+ *
+ */
+ protected int[] bitsPerSample;
+
+ /**
+ * The value of the <code>SampleFormat</code> 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 <code>ExtraSamples</code> 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 <code>ColorMap</code> tag.
+ *
+ */
+ protected char[] colorMap;
+
+ // Region of input stream containing the data
+
+ /**
+ * The <code>ImageInputStream</code> containing the TIFF source
+ * data.
+ */
+ protected ImageInputStream stream;
+
+ /**
+ * The offset in the source <code>ImageInputStream</code> of the
+ * start of the data to be decompressed.
+ */
+ protected long offset;
+
+ /**
+ * The number of bytes of data from the source
+ * <code>ImageInputStream</code> 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 <code>dstXOffset</code>
+ * and <code>subsampleX</code>, to map between horizontal source
+ * and destination pixel coordinates.
+ */
+ protected int sourceXOffset;
+
+ /**
+ * The horizontal destination offset used, along with
+ * <code>sourceXOffset</code> and <code>subsampleX</code>, 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 <code>dstYOffset</code>
+ * and <code>subsampleY</code>, to map between vertical source and
+ * destination pixel coordinates.
+ */
+ protected int sourceYOffset;
+
+ /**
+ * The vertical destination offset used, along with
+ * <code>sourceYOffset</code> and <code>subsampleY</code>, 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 <code>BufferedImage</code> for the <code>decodeRaw</code>
+ * 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 (<code>activeSrcMinX</code>,
+ * <code>activeSrcMinY</code>) is to be copied into the
+ * destination pixel at (<code>dstMinX</code>,
+ * <code>dstMinY</code>).
+ *
+ * <p> The pixels in the source region to be copied are
+ * those with X coordinates of the form <code>activeSrcMinX +
+ * k*subsampleX</code>, where <code>k</code> is an integer such
+ * that <code>0 ≤ k < dstWidth</code>.
+ */
+ 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.
+ *
+ * <p> The pixels in the source region to be copied are
+ * those with Y coordinates of the form <code>activeSrcMinY +
+ * k*subsampleY</code>, where <code>k</code> is an integer such
+ * that <code>0 ≤ k < dstHeight</code>.
+ */
+ 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.
+ *
+ * <p> The active source width will always be equal to
+ * <code>(dstWidth - 1)*subsampleX + 1</code>.
+ */
+ 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.
+ *
+ * <p> The active source height will always be equal to
+ * <code>(dstHeight - 1)*subsampleY + 1</code>.
+ */
+ protected int activeSrcHeight;
+
+ /**
+ * A <code>TIFFColorConverter</code> object describing the color space of
+ * the encoded pixel data, or <code>null</code>.
+ */
+ 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 <code>PixelInterleavedSampleModel</code> for use in creating
+ * an <code>ImageTypeSpecifier</code>. 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 <code>PixelInterleavedSampleModel</code>.
+ */
+ 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 <code>ComponentColorModel</code> for use in creating
+ * an <code>ImageTypeSpecifier</code>.
+ */
+ // 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 <code>dataType</code>
+ * which must be one of the <code>DataBuffer</code> <code>TYPE</code>s.
+ */
+ 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 <code>DataBuffer</code> 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 <code>DataBuffer</code>
+ * of the supplied <code>WritableRaster</code>.
+ */
+ 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
+ * <code>ImageTypeSpecifier</code> suitable for decoding an image
+ * with the given parameters.
+ *
+ * @param photometricInterpretation the value of the
+ * <code>PhotometricInterpretation</code> field.
+ * @param compression the value of the <code>Compression</code> field.
+ * @param samplesPerPixel the value of the
+ * <code>SamplesPerPixel</code> field.
+ * @param bitsPerSample the value of the <code>BitsPerSample</code> field.
+ * @param sampleFormat the value of the <code>SampleFormat</code> field.
+ * @param extraSamples the value of the <code>ExtraSamples</code> field.
+ * @param colorMap the value of the <code>ColorMap</code> field.
+ *
+ * @return a suitable <code>ImageTypeSpecifier</code>, or
+ * <code>null</code> 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 <code>reader</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param reader the current <code>ImageReader</code>.
+ */
+ public void setReader(ImageReader reader) {
+ this.reader = reader;
+ }
+
+ /**
+ * Sets the value of the <code>metadata</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param metadata the <code>IIOMetadata</code> object for the
+ * image being read.
+ */
+ public void setMetadata(IIOMetadata metadata) {
+ this.metadata = metadata;
+ }
+
+ /**
+ * Sets the value of the <code>photometricInterpretation</code>
+ * field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>compression</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>planar</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param planar <code>true</code> if the image to be decoded is
+ * stored in planar format.
+ */
+ public void setPlanar(boolean planar) {
+ this.planar = planar;
+ }
+
+ /**
+ * Sets the value of the <code>samplesPerPixel</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>bitsPerSample</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>sampleFormat</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>extraSamples</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>colorMap</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>char</code>s.
+ */
+ public void setColorMap(char[] colorMap) {
+ this.colorMap = colorMap == null ?
+ null : colorMap.clone();
+ }
+
+ /**
+ * Sets the value of the <code>stream</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param stream the <code>ImageInputStream</code> to be read.
+ */
+ public void setStream(ImageInputStream stream) {
+ this.stream = stream;
+ }
+
+ /**
+ * Sets the value of the <code>offset</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>byteCount</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>srcMinX</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>srcMinY</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>srcWidth</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>srcHeight</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>sourceXOffset</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>dstXOffset</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>sourceYOffset</code>.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>dstYOffset</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>subsampleX</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param subsampleX the horizontal subsampling factor.
+ *
+ * @throws IllegalArgumentException if <code>subsampleX</code> 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 <code>subsampleY</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param subsampleY the vertical subsampling factor.
+ *
+ * @throws IllegalArgumentException if <code>subsampleY</code> 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 <code>sourceBands</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param sourceBands an array of <code>int</code>s
+ * specifying the source bands to be read.
+ */
+ public void setSourceBands(int[] sourceBands) {
+ this.sourceBands = sourceBands == null ?
+ null : sourceBands.clone();
+ }
+
+ /**
+ * Sets the value of the <code>destinationBands</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param destinationBands an array of <code>int</code>s
+ * 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 <code>image</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * method must be called prior to calling any of the decode
+ * methods.
+ *
+ * @param image the destination <code>BufferedImage</code>.
+ */
+ public void setImage(BufferedImage image) {
+ this.image = image;
+ }
+
+ /**
+ * Sets the value of the <code>dstMinX</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>dstMinY</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>dstWidth</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>dstHeight</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>activeSrcMinX</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>activeSrcMinY</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>activeSrcWidth</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>activeSrcHeight</code> field.
+ *
+ * <p> If this method is called, the <code>beginDecoding</code>
+ * 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 <code>TIFFColorConverter</code> object describing the color
+ * space of the encoded data in the input stream. If no
+ * <code>TIFFColorConverter</code> is set, no conversion will be performed.
+ *
+ * @param colorConverter a <code>TIFFColorConverter</code> object, or
+ * <code>null</code>.
+ */
+ public void setColorConverter(TIFFColorConverter colorConverter) {
+ this.colorConverter = colorConverter;
+ }
+
+ /**
+ * Returns an <code>ImageTypeSpecifier</code> describing an image
+ * whose underlying data array has the same format as the raw
+ * source pixel data.
+ *
+ * @return an <code>ImageTypeSpecifier</code>.
+ */
+ public ImageTypeSpecifier getRawImageType() {
+ ImageTypeSpecifier its =
+ getRawImageTypeSpecifier(photometricInterpretation,
+ compression,
+ samplesPerPixel,
+ bitsPerSample,
+ sampleFormat,
+ extraSamples,
+ colorMap);
+ return its;
+ }
+
+ /**
+ * Creates a <code>BufferedImage</code> whose underlying data
+ * array will be suitable for holding the raw decoded output of
+ * the <code>decodeRaw</code> method.
+ *
+ * <p> The default implementation calls
+ * <code>getRawImageType</code>, and calls the resulting
+ * <code>ImageTypeSpecifier</code>'s
+ * <code>createBufferedImage</code> method.
+ *
+ * @return a <code>BufferedImage</code> whose underlying data
+ * array has the same format as the raw source pixel data, or
+ * <code>null</code> 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 <code>byte</code>
+ * array <code>b</code>, starting at the offset given by
+ * <code>dstOffset</code>. Each pixel occupies
+ * <code>bitsPerPixel</code> bits, with no padding between pixels.
+ * Scanlines are separated by <code>scanlineStride</code>
+ * <code>byte</code>s.
+ *
+ * @param b a <code>byte</code> array to be written.
+ * @param dstOffset the starting offset in <code>b</code> to be
+ * written.
+ * @param bitsPerPixel the number of bits for each pixel.
+ * @param scanlineStride the number of <code>byte</code>s to
+ * advance between that starting pixels of each scanline.
+ *
+ * @throws IOException if an error occurs reading from the source
+ * <code>ImageInputStream</code>.
+ */
+ public abstract void decodeRaw(byte[] b,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException;
+
+ /**
+ * Decodes the source data into the provided <code>short</code>
+ * array <code>s</code>, starting at the offset given by
+ * <code>dstOffset</code>. Each pixel occupies
+ * <code>bitsPerPixel</code> bits, with no padding between pixels.
+ * Scanlines are separated by <code>scanlineStride</code>
+ * <code>short</code>s
+ *
+ * <p> The default implementation calls <code>decodeRaw(byte[] b,
+ * ...)</code> and copies the resulting data into <code>s</code>.
+ *
+ * @param s a <code>short</code> array to be written.
+ * @param dstOffset the starting offset in <code>s</code> to be
+ * written.
+ * @param bitsPerPixel the number of bits for each pixel.
+ * @param scanlineStride the number of <code>short</code>s to
+ * advance between that starting pixels of each scanline.
+ *
+ * @throws IOException if an error occurs reading from the source
+ * <code>ImageInputStream</code>.
+ */
+ 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 <code>int</code>
+ * array <code>i</code>, starting at the offset given by
+ * <code>dstOffset</code>. Each pixel occupies
+ * <code>bitsPerPixel</code> bits, with no padding between pixels.
+ * Scanlines are separated by <code>scanlineStride</code>
+ * <code>int</code>s.
+ *
+ * <p> The default implementation calls <code>decodeRaw(byte[] b,
+ * ...)</code> and copies the resulting data into <code>i</code>.
+ *
+ * @param i an <code>int</code> array to be written.
+ * @param dstOffset the starting offset in <code>i</code> to be
+ * written.
+ * @param bitsPerPixel the number of bits for each pixel.
+ * @param scanlineStride the number of <code>int</code>s to
+ * advance between that starting pixels of each scanline.
+ *
+ * @throws IOException if an error occurs reading from the source
+ * <code>ImageInputStream</code>.
+ */
+ 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 <code>float</code>
+ * array <code>f</code>, starting at the offset given by
+ * <code>dstOffset</code>. Each pixel occupies
+ * <code>bitsPerPixel</code> bits, with no padding between pixels.
+ * Scanlines are separated by <code>scanlineStride</code>
+ * <code>float</code>s.
+ *
+ * <p> The default implementation calls <code>decodeRaw(byte[] b,
+ * ...)</code> and copies the resulting data into <code>f</code>.
+ *
+ * @param f a <code>float</code> array to be written.
+ * @param dstOffset the starting offset in <code>f</code> to be
+ * written.
+ * @param bitsPerPixel the number of bits for each pixel.
+ * @param scanlineStride the number of <code>float</code>s to
+ * advance between that starting pixels of each scanline.
+ *
+ * @throws IOException if an error occurs reading from the source
+ * <code>ImageInputStream</code>.
+ */
+ 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 <code>double</code>
+ * array <code>f</code>, starting at the offset given by
+ * <code>dstOffset</code>. Each pixel occupies
+ * <code>bitsPerPixel</code> bits, with no padding between pixels.
+ * Scanlines are separated by <code>scanlineStride</code>
+ * <code>double</code>s.
+ *
+ * <p> The default implementation calls <code>decodeRaw(byte[] b,
+ * ...)</code> and copies the resulting data into <code>f</code>.
+ *
+ * @param f a <code>double</code> array to be written.
+ * @param dstOffset the starting offset in <code>f</code> to be
+ * written.
+ * @param bitsPerPixel the number of bits for each pixel.
+ * @param scanlineStride the number of <code>double</code>s to
+ * advance between that starting pixels of each scanline.
+ *
+ * @throws IOException if an error occurs reading from the source
+ * <code>ImageInputStream</code>.
+ */
+ 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
+ * <code>decode</code> 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.
+ *
+ * <p> The default implementation computes tables used by the
+ * <code>decode</code> method to rescale components to different
+ * bit depths. Thus, if this method is overridden, it is
+ * important for the subclass method to call <code>super()</code>,
+ * unless it overrides <code>decode</code> 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
+ * <code>ImageInputStream</code> <code>stream</code>, at offset
+ * <code>offset</code>, and continuing for <code>byteCount</code>
+ * bytes) into the output <code>BufferedImage</code>
+ * <code>image</code>.
+ *
+ * <p> The default implementation analyzes the destination image
+ * to determine if it is suitable as the destination for the
+ * <code>decodeRaw</code> method. If not, a suitable image is
+ * created. Next, <code>decodeRaw</code> is called to perform the
+ * actual decoding, and the results are copied into the
+ * destination image if necessary. Subsampling and offsetting are
+ * performed automatically.
+ *
+ * <p> The precise responsibilities of this routine are as
+ * follows. The input bit stream is defined by the instance
+ * variables <code>stream</code>, <code>offset</code>, and
+ * <code>byteCount</code>. These bits contain the data for the
+ * region of the source image defined by <code>srcMinX</code>,
+ * <code>srcMinY</code>, <code>srcWidth</code>, and
+ * <code>srcHeight</code>.
+ *
+ * <p> The source data is required to be subsampling, starting at
+ * the <code>sourceXOffset</code>th column and including
+ * every <code>subsampleX</code>th pixel thereafter (and similarly
+ * for <code>sourceYOffset</code> and
+ * <code>subsampleY</code>).
+ *
+ * <p> Pixels are copied into the destination with an addition shift of
+ * (<code>dstXOffset</code>, <code>dstYOffset</code>). The complete
+ * set of formulas relating the source and destination coordinate spaces
+ * are:
+ *
+ * <pre>
+ * dx = (sx - sourceXOffset)/subsampleX + dstXOffset;
+ * dy = (sy - sourceYOffset)/subsampleY + dstYOffset;
+ * </pre>
+ *
+ * Only source pixels such that <code>(sx - sourceXOffset) %
+ * subsampleX == 0</code> and <code>(sy - sourceYOffset) %
+ * subsampleY == 0</code> are copied.
+ *
+ * <p> The inverse mapping, from destination to source coordinates,
+ * is one-to-one:
+ *
+ * <pre>
+ * sx = (dx - dstXOffset)*subsampleX + sourceXOffset;
+ * sy = (dy - dstYOffset)*subsampleY + sourceYOffset;
+ * </pre>
+ *
+ * <p> The region of the destination image to be updated is given
+ * by the instance variables <code>dstMinX</code>,
+ * <code>dstMinY</code>, <code>dstWidth</code>, and
+ * <code>dstHeight</code>.
+ *
+ * <p> 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 <code>activeSrcMinX</code>,
+ * <code>activeSrcMinY</code>, <code>activeSrcWidth</code> and
+ * <code>activeSrcHeight</code>. Thus, the source pixel at
+ * (<code>activeSrcMinX</code>, <code>activeSrcMinY</code>) will
+ * map to the destination pixel (<code>dstMinX</code>,
+ * <code>dstMinY</code>).
+ *
+ * <p> The sequence of source bands given by
+ * <code>sourceBands</code> are to be copied into the sequence of
+ * bands in the destination given by
+ * <code>destinationBands</code>.
+ *
+ * <p> Some standard tag information is provided the instance
+ * variables <code>photometricInterpretation</code>,
+ * <code>compression</code>, <code>samplesPerPixel</code>,
+ * <code>bitsPerSample</code>, <code>sampleFormat</code>,
+ * <code>extraSamples</code>, and <code>colorMap</code>.
+ *
+ * <p> 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 <code>decodeRaw</code> and/or
+ * <code>getRawImageType</code> methods.
+ *
+ * @exception IOException if an error occurs in
+ * <code>decodeRaw</code>.
+ */
+ 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;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDeflateCompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDeflateDecompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -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<srcWidth*samplesPerPixel; i++) {
+ buf[count] += buf[count - samplesPerPixel];
+ count++;
+ }
+ }
+ }
+
+ if(bytesPerRow != scanlineStride) {
+ int off = 0;
+ for (int y = 0; y < srcHeight; y++) {
+ System.arraycopy(buf, off, b, dstOffset, bytesPerRow);
+ off += bytesPerRow;
+ dstOffset += scanlineStride;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDeflater.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,120 @@
+/*
+ * 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 java.io.IOException;
+import java.util.zip.Deflater;
+import javax.imageio.ImageWriteParam;
+
+/**
+ * Compressor superclass for Deflate and ZLib compression.
+ */
+public class TIFFDeflater extends TIFFCompressor {
+
+ Deflater deflater;
+ int predictor;
+
+ public TIFFDeflater(String compressionType,
+ int compressionTagValue,
+ ImageWriteParam param,
+ int predictorValue) {
+ super(compressionType, compressionTagValue, true);
+
+ this.predictor = predictorValue;
+
+ // Set the deflate level.
+ int deflateLevel;
+ if(param != null &&
+ param.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) {
+ float quality = param.getCompressionQuality();
+ deflateLevel = (int)(1 + 8*quality);
+ } else {
+ deflateLevel = Deflater.DEFAULT_COMPRESSION;
+ }
+
+ this.deflater = new Deflater(deflateLevel);
+ }
+
+ public int encode(byte[] b, int off,
+ int width, int height,
+ int[] bitsPerSample,
+ int scanlineStride) throws IOException {
+
+ int inputSize = height*scanlineStride;
+ int blocks = (inputSize + 32767)/32768;
+
+ // Worst case for Zlib deflate is input size + 5 bytes per 32k
+ // block, plus 6 header bytes
+ byte[] compData = new byte[inputSize + 5*blocks + 6];
+
+ int numCompressedBytes = 0;
+ if(predictor == BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
+ int samplesPerPixel = bitsPerSample.length;
+ int bitsPerPixel = 0;
+ for (int i = 0; i < samplesPerPixel; i++) {
+ bitsPerPixel += bitsPerSample[i];
+ }
+ int bytesPerRow = (bitsPerPixel*width + 7)/8;
+ byte[] rowBuf = new byte[bytesPerRow];
+
+ int maxRow = height - 1;
+ for(int i = 0; i < height; i++) {
+ // Cannot modify b[] in place as it might be a data
+ // array from the image being written so make a copy.
+ System.arraycopy(b, off, rowBuf, 0, bytesPerRow);
+ for(int j = bytesPerRow - 1; j >= 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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFElementInfo.java Mon Nov 23 12:26:12 2015 -0800
@@ -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<Object> objectMinValue = null;
+ Comparable<Object> objectMaxValue = null;
+ int objectArrayMinLength = 0;
+ int objectArrayMaxLength = 0;
+
+ public TIFFElementInfo(String[] childNames,
+ String[] attributeNames,
+ int childPolicy) {
+ this.childNames = childNames;
+ this.attributeNames = attributeNames;
+ this.childPolicy = childPolicy;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFExifJPEGCompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,50 @@
+/*
+ * 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.ImageWriteParam;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+
+/**
+ * A <code>TIFFCompressor</code> for the JPEG variant of Exif.
+ */
+public class TIFFExifJPEGCompressor extends TIFFBaseJPEGCompressor {
+ public TIFFExifJPEGCompressor(ImageWriteParam param) {
+ super(TIFFImageWriter.EXIF_JPEG_COMPRESSION_TYPE,
+ BaselineTIFFTagSet.COMPRESSION_OLD_JPEG,
+ false,
+ param);
+ }
+
+ public void setMetadata(IIOMetadata metadata) {
+ // Set the metadata.
+ super.setMetadata(metadata);
+
+ // Initialize the JPEG writer and writeparam.
+ initJPEGWriter(false, // No stream metadata (not writing abbreviated)
+ true); // Yes image metadata (remove APPn markers)
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFFaxCompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,513 @@
+/*
+ * 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.IIOMetadata;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.plugins.tiff.TIFFField;
+
+/**
+ *
+ */
+abstract class TIFFFaxCompressor extends TIFFCompressor {
+
+ /**
+ * The CCITT numerical definition of white.
+ */
+ protected static final int WHITE = 0;
+
+ /**
+ * The CCITT numerical definition of black.
+ */
+ protected static final int BLACK = 1;
+
+ // --- Begin tables for CCITT compression ---
+
+ protected static final byte[] byteTable = new byte[] {
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, // 0 to 15
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16 to 31
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 32 to 47
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 48 to 63
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 to 79
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 to 95
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 to 111
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 112 to 127
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 to 143
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144 to 159
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160 to 175
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176 to 191
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192 to 207
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208 to 223
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224 to 239
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240 to 255
+ };
+
+ /**
+ * Terminating codes for black runs.
+ */
+ protected static final int[] termCodesBlack = new int[] {
+ /* 0 0x0000 */ 0x0dc0000a, 0x40000003, 0xc0000002, 0x80000002,
+ /* 4 0x0004 */ 0x60000003, 0x30000004, 0x20000004, 0x18000005,
+ /* 8 0x0008 */ 0x14000006, 0x10000006, 0x08000007, 0x0a000007,
+ /* 12 0x000c */ 0x0e000007, 0x04000008, 0x07000008, 0x0c000009,
+ /* 16 0x0010 */ 0x05c0000a, 0x0600000a, 0x0200000a, 0x0ce0000b,
+ /* 20 0x0014 */ 0x0d00000b, 0x0d80000b, 0x06e0000b, 0x0500000b,
+ /* 24 0x0018 */ 0x02e0000b, 0x0300000b, 0x0ca0000c, 0x0cb0000c,
+ /* 28 0x001c */ 0x0cc0000c, 0x0cd0000c, 0x0680000c, 0x0690000c,
+ /* 32 0x0020 */ 0x06a0000c, 0x06b0000c, 0x0d20000c, 0x0d30000c,
+ /* 36 0x0024 */ 0x0d40000c, 0x0d50000c, 0x0d60000c, 0x0d70000c,
+ /* 40 0x0028 */ 0x06c0000c, 0x06d0000c, 0x0da0000c, 0x0db0000c,
+ /* 44 0x002c */ 0x0540000c, 0x0550000c, 0x0560000c, 0x0570000c,
+ /* 48 0x0030 */ 0x0640000c, 0x0650000c, 0x0520000c, 0x0530000c,
+ /* 52 0x0034 */ 0x0240000c, 0x0370000c, 0x0380000c, 0x0270000c,
+ /* 56 0x0038 */ 0x0280000c, 0x0580000c, 0x0590000c, 0x02b0000c,
+ /* 60 0x003c */ 0x02c0000c, 0x05a0000c, 0x0660000c, 0x0670000c
+ };
+
+ /**
+ * Terminating codes for white runs.
+ */
+ protected static final int[] termCodesWhite = new int[] {
+ /* 0 0x0000 */ 0x35000008, 0x1c000006, 0x70000004, 0x80000004,
+ /* 4 0x0004 */ 0xb0000004, 0xc0000004, 0xe0000004, 0xf0000004,
+ /* 8 0x0008 */ 0x98000005, 0xa0000005, 0x38000005, 0x40000005,
+ /* 12 0x000c */ 0x20000006, 0x0c000006, 0xd0000006, 0xd4000006,
+ /* 16 0x0010 */ 0xa8000006, 0xac000006, 0x4e000007, 0x18000007,
+ /* 20 0x0014 */ 0x10000007, 0x2e000007, 0x06000007, 0x08000007,
+ /* 24 0x0018 */ 0x50000007, 0x56000007, 0x26000007, 0x48000007,
+ /* 28 0x001c */ 0x30000007, 0x02000008, 0x03000008, 0x1a000008,
+ /* 32 0x0020 */ 0x1b000008, 0x12000008, 0x13000008, 0x14000008,
+ /* 36 0x0024 */ 0x15000008, 0x16000008, 0x17000008, 0x28000008,
+ /* 40 0x0028 */ 0x29000008, 0x2a000008, 0x2b000008, 0x2c000008,
+ /* 44 0x002c */ 0x2d000008, 0x04000008, 0x05000008, 0x0a000008,
+ /* 48 0x0030 */ 0x0b000008, 0x52000008, 0x53000008, 0x54000008,
+ /* 52 0x0034 */ 0x55000008, 0x24000008, 0x25000008, 0x58000008,
+ /* 56 0x0038 */ 0x59000008, 0x5a000008, 0x5b000008, 0x4a000008,
+ /* 60 0x003c */ 0x4b000008, 0x32000008, 0x33000008, 0x34000008
+ };
+
+ /**
+ * Make-up codes for black runs.
+ */
+ protected static final int[] makeupCodesBlack = new int[] {
+ /* 0 0x0000 */ 0x00000000, 0x03c0000a, 0x0c80000c, 0x0c90000c,
+ /* 4 0x0004 */ 0x05b0000c, 0x0330000c, 0x0340000c, 0x0350000c,
+ /* 8 0x0008 */ 0x0360000d, 0x0368000d, 0x0250000d, 0x0258000d,
+ /* 12 0x000c */ 0x0260000d, 0x0268000d, 0x0390000d, 0x0398000d,
+ /* 16 0x0010 */ 0x03a0000d, 0x03a8000d, 0x03b0000d, 0x03b8000d,
+ /* 20 0x0014 */ 0x0290000d, 0x0298000d, 0x02a0000d, 0x02a8000d,
+ /* 24 0x0018 */ 0x02d0000d, 0x02d8000d, 0x0320000d, 0x0328000d,
+ /* 28 0x001c */ 0x0100000b, 0x0180000b, 0x01a0000b, 0x0120000c,
+ /* 32 0x0020 */ 0x0130000c, 0x0140000c, 0x0150000c, 0x0160000c,
+ /* 36 0x0024 */ 0x0170000c, 0x01c0000c, 0x01d0000c, 0x01e0000c,
+ /* 40 0x0028 */ 0x01f0000c, 0x00000000, 0x00000000, 0x00000000,
+ /* 44 0x002c */ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /* 48 0x0030 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /* 52 0x0034 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /* 56 0x0038 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000
+ };
+
+ /**
+ * Make-up codes for white runs.
+ */
+ protected static final int[] makeupCodesWhite = new int[] {
+ /* 0 0x0000 */ 0x00000000, 0xd8000005, 0x90000005, 0x5c000006,
+ /* 4 0x0004 */ 0x6e000007, 0x36000008, 0x37000008, 0x64000008,
+ /* 8 0x0008 */ 0x65000008, 0x68000008, 0x67000008, 0x66000009,
+ /* 12 0x000c */ 0x66800009, 0x69000009, 0x69800009, 0x6a000009,
+ /* 16 0x0010 */ 0x6a800009, 0x6b000009, 0x6b800009, 0x6c000009,
+ /* 20 0x0014 */ 0x6c800009, 0x6d000009, 0x6d800009, 0x4c000009,
+ /* 24 0x0018 */ 0x4c800009, 0x4d000009, 0x60000006, 0x4d800009,
+ /* 28 0x001c */ 0x0100000b, 0x0180000b, 0x01a0000b, 0x0120000c,
+ /* 32 0x0020 */ 0x0130000c, 0x0140000c, 0x0150000c, 0x0160000c,
+ /* 36 0x0024 */ 0x0170000c, 0x01c0000c, 0x01d0000c, 0x01e0000c,
+ /* 40 0x0028 */ 0x01f0000c, 0x00000000, 0x00000000, 0x00000000,
+ /* 44 0x002c */ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /* 48 0x0030 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /* 52 0x0034 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ /* 56 0x0038 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000
+ };
+
+ /**
+ * Pass mode table.
+ */
+ protected static final int[] passMode = new int[] {
+ 0x10000004 // 0001
+ };
+
+ /**
+ * Vertical mode table.
+ */
+ protected static final int[] vertMode = new int[] {
+ 0x06000007, // 0000011
+ 0x0c000006, // 000011
+ 0x60000003, // 011
+ 0x80000001, // 1
+ 0x40000003, // 010
+ 0x08000006, // 000010
+ 0x04000007 // 0000010
+ };
+
+ /**
+ * Horizontal mode table.
+ */
+ protected static final int[] horzMode = new int[] {
+ 0x20000003 // 001
+ };
+
+ /**
+ * Black and white terminating code table.
+ */
+ protected static final int[][] termCodes =
+ new int[][] {termCodesWhite, termCodesBlack};
+
+ /**
+ * Black and white make-up code table.
+ */
+ protected static final int[][] makeupCodes =
+ new int[][] {makeupCodesWhite, makeupCodesBlack};
+
+ /**
+ * Black and white pass mode table.
+ */
+ protected static final int[][] pass = new int[][] {passMode, passMode};
+
+ /**
+ * Black and white vertical mode table.
+ */
+ protected static final int[][] vert = new int[][] {vertMode, vertMode};
+
+ /**
+ * Black and white horizontal mode table.
+ */
+ protected static final int[][] horz = new int[][] {horzMode, horzMode};
+
+ // --- End tables for CCITT compression ---
+
+ /**
+ * Whether bits are inserted in reverse order (TIFF FillOrder 2).
+ */
+ protected boolean inverseFill = false;
+
+ /**
+ * Output bit buffer.
+ */
+ protected int bits;
+
+ /**
+ * Number of bits in the output bit buffer.
+ */
+ protected int ndex;
+
+ /**
+ * Constructor. The superclass constructor is merely invoked with the
+ * same parameters.
+ */
+ protected TIFFFaxCompressor(String compressionType,
+ int compressionTagValue,
+ boolean isCompressionLossless) {
+ super(compressionType, compressionTagValue, isCompressionLossless);
+ }
+
+ /**
+ * Sets the value of the <code>metadata</code> field.
+ *
+ * <p> The implementation in this class also sets local options
+ * from the FILL_ORDER field if it exists.</p>
+ *
+ * @param metadata the <code>IIOMetadata</code> object for the
+ * image being written.
+ *
+ * @see #getMetadata()
+ */
+ public void setMetadata(IIOMetadata metadata) {
+ super.setMetadata(metadata);
+
+ if (metadata instanceof TIFFImageMetadata) {
+ TIFFImageMetadata tim = (TIFFImageMetadata)metadata;
+ TIFFField f = tim.getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER);
+ inverseFill = (f != null && f.getAsInt(0) == 2);
+ }
+ }
+
+ /**
+ * Return min of <code>maxOffset</code> or offset of first pixel
+ * different from pixel at <code>bitOffset</code>.
+ */
+ public int nextState(byte[] data,
+ int base,
+ int bitOffset,
+ int maxOffset)
+ {
+ if(data == null) {
+ return maxOffset;
+ }
+
+ int next = base + (bitOffset>>>3);
+ // If the offset is beyond the data already then the minimum of the
+ // current offset and maxOffset must be maxOffset.
+ if(next >= data.length) {
+ return maxOffset;
+ }
+ int end = base + (maxOffset>>>3);
+ if(end == data.length) { // Prevents out of bounds exception below
+ end--;
+ }
+ int extra = bitOffset & 0x7;
+
+ int testbyte;
+
+ if((data[next] & (0x80 >>> extra)) != 0) { // look for "0"
+ testbyte = ~(data[next]) & (0xff >>> extra);
+ while (next < end) {
+ if (testbyte != 0) {
+ break;
+ }
+ testbyte = ~(data[++next]) & 0xff;
+ }
+ } else { // look for "1"
+ if ((testbyte = (data[next] & (0xff >>> extra))) != 0) {
+ bitOffset = (next-base)*8 + byteTable[testbyte];
+ return ((bitOffset < maxOffset) ? bitOffset : maxOffset);
+ }
+ while (next < end) {
+ if ((testbyte = data[++next]&0xff) != 0) {
+ // "1" is in current byte
+ bitOffset = (next-base)*8 + byteTable[testbyte];
+ return ((bitOffset < maxOffset) ? bitOffset : maxOffset);
+ }
+ }
+ }
+ bitOffset = (next-base)*8 + byteTable[testbyte];
+ return ((bitOffset < maxOffset) ? bitOffset : maxOffset);
+ }
+
+ /**
+ * Initialize bit buffer machinery.
+ */
+ public void initBitBuf()
+ {
+ ndex = 0;
+ bits = 0x00000000;
+ }
+
+ /**
+ * Get code for run and add to compressed bitstream.
+ */
+ public int add1DBits(byte[] buf,
+ int where, // byte offs
+ int count, // #pixels in run
+ int color) // color of run
+ {
+ int sixtyfours;
+ int mask;
+ int len = where;
+
+ sixtyfours = count >>> 6; // count / 64;
+ count = count & 0x3f; // count % 64
+ if (sixtyfours != 0) {
+ for ( ; sixtyfours > 40; sixtyfours -= 40) {
+ mask = makeupCodes[color][40];
+ bits |= (mask & 0xfff80000) >>> ndex;
+ ndex += (mask & 0x0000ffff);
+ while (ndex > 7) {
+ buf[len++] = (byte)(bits >>> 24);
+ bits <<= 8;
+ ndex -= 8;
+ }
+ }
+
+ mask = makeupCodes[color][sixtyfours];
+ bits |= (mask & 0xfff80000) >>> ndex;
+ ndex += (mask & 0x0000ffff);
+ while (ndex > 7) {
+ buf[len++] = (byte)(bits >>> 24);
+ bits <<= 8;
+ ndex -= 8;
+ }
+ }
+
+ mask = termCodes[color][count];
+ bits |= (mask & 0xfff80000) >>> ndex;
+ ndex += (mask & 0x0000ffff);
+ while (ndex > 7) {
+ buf[len++] = (byte)(bits >>> 24);
+ bits <<= 8;
+ ndex -= 8;
+ }
+
+ return(len - where);
+ }
+
+ /**
+ * Place entry from mode table into compressed bitstream.
+ */
+ public int add2DBits(byte[] buf, // compressed buffer
+ int where, // byte offset into compressed buffer
+ int[][] mode, // 2-D mode to be encoded
+ int entry) // mode entry (0 unless vertical)
+ {
+ int mask;
+ int len = where;
+ int color = 0;
+
+ mask = mode[color][entry];
+ bits |= (mask & 0xfff80000) >>> ndex;
+ ndex += (mask & 0x0000ffff);
+ while (ndex > 7) {
+ buf[len++] = (byte)(bits >>> 24);
+ bits <<= 8;
+ ndex -= 8;
+ }
+
+ return(len - where);
+ }
+
+ /**
+ * Add an End-of-Line (EOL == 0x001) to the compressed bitstream
+ * with optional byte alignment.
+ */
+ public int addEOL(boolean is1DMode,// 1D encoding
+ boolean addFill, // byte aligned EOLs
+ boolean add1, // add1 ? EOL+1 : EOL+0
+ byte[] buf, // compressed buffer address
+ int where) // current byte offset into buffer
+ {
+ int len = where;
+
+ //
+ // Add zero-valued fill bits such that the EOL is aligned as
+ //
+ // xxxx 0000 0000 0001
+ //
+ if(addFill) {
+ //
+ // Simply increment the bit count. No need to feed bits into
+ // the output buffer at this point as there are at most 7 bits
+ // in the bit buffer, at most 7 are added here, and at most
+ // 13 below making the total 7+7+13 = 27 before the bit feed
+ // at the end of this routine.
+ //
+ ndex += ((ndex <= 4) ? 4 - ndex : 12 - ndex);
+ }
+
+ //
+ // Write EOL into buffer
+ //
+ if(is1DMode) {
+ bits |= 0x00100000 >>> ndex;
+ ndex += 12;
+ } else {
+ bits |= (add1 ? 0x00180000 : 0x00100000) >>> ndex;
+ ndex += 13;
+ }
+
+ while (ndex > 7) {
+ buf[len++] = (byte)(bits >>> 24);
+ bits <<= 8;
+ ndex -= 8;
+ }
+
+ return(len - where);
+ }
+
+ /**
+ * Add an End-of-Facsimile-Block (EOFB == 0x001001) to the compressed
+ * bitstream.
+ */
+ public int addEOFB(byte[] buf, // compressed buffer
+ int where) // byte offset into compressed buffer
+ {
+ int len = where;
+
+ //
+ // eofb code
+ //
+ bits |= 0x00100100 >>> ndex;
+
+ //
+ // eofb code length
+ //
+ ndex += 24;
+
+ //
+ // flush all pending bits
+ //
+ while(ndex > 0) {
+ buf[len++] = (byte)(bits >>> 24);
+ bits <<= 8;
+ ndex -= 8;
+ }
+
+ return(len - where);
+ }
+
+ /**
+ * One-dimensionally encode a row of data using CCITT Huffman compression.
+ * The bit buffer should be initialized as required before invoking this
+ * method and should be flushed after the method returns. The fill order
+ * is always highest-order to lowest-order bit so the calling routine
+ * should handle bit inversion.
+ */
+ public int encode1D(byte[] data,
+ int rowOffset,
+ int colOffset,
+ int rowLength,
+ byte[] compData,
+ int compOffset) {
+ int lineAddr = rowOffset;
+ int bitIndex = colOffset;
+
+ int last = bitIndex + rowLength;
+ int outIndex = compOffset;
+
+ //
+ // Is first pixel black
+ //
+ int testbit =
+ ((data[lineAddr + (bitIndex>>>3)]&0xff) >>>
+ (7-(bitIndex & 0x7))) & 0x1;
+ int currentColor = BLACK;
+ if (testbit != 0) {
+ outIndex += add1DBits(compData, outIndex, 0, WHITE);
+ } else {
+ currentColor = WHITE;
+ }
+
+ //
+ // Run-length encode line
+ //
+ while (bitIndex < last) {
+ int bitCount =
+ nextState(data, lineAddr, bitIndex, last) - bitIndex;
+ outIndex +=
+ add1DBits(compData, outIndex, bitCount, currentColor);
+ bitIndex += bitCount;
+ currentColor ^= 0x00000001;
+ }
+
+ return outIndex - compOffset;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFFaxDecompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,1594 @@
+/*
+ * 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.io.EOFException;
+import javax.imageio.IIOException;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.plugins.tiff.TIFFField;
+
+class TIFFFaxDecompressor extends TIFFDecompressor {
+
+ /**
+ * The logical order of bits within a byte.
+ * <pre>
+ * 1 = MSB-to-LSB
+ * 2 = LSB-to-MSB (flipped)
+ * </pre>
+ */
+ private int fillOrder;
+ private int t4Options;
+ private int t6Options;
+
+ // Variables set by T4Options
+ /**
+ * Uncompressed mode flag: 1 if uncompressed, 0 if not.
+ */
+ private int uncompressedMode = 0;
+
+ /**
+ * EOL padding flag: 1 if fill bits have been added before an EOL such
+ * that the EOL ends on a byte boundary, 0 otherwise.
+ */
+ private int fillBits = 0;
+
+ /**
+ * Coding dimensionality: 1 for 2-dimensional, 0 for 1-dimensional.
+ */
+ private int oneD;
+
+ private byte[] data;
+ private int bitPointer, bytePointer;
+
+ // Output image buffer
+ private byte[] buffer;
+ private int w, h, bitsPerScanline;
+ private int lineBitNum;
+
+ // Data structures needed to store changing elements for the previous
+ // and the current scanline
+ private int changingElemSize = 0;
+ private int prevChangingElems[];
+ private int currChangingElems[];
+
+ // Element at which to start search in getNextChangingElement
+ private int lastChangingElement = 0;
+
+ private static int table1[] = {
+ 0x00, // 0 bits are left in first byte - SHOULD NOT HAPPEN
+ 0x01, // 1 bits are left in first byte
+ 0x03, // 2 bits are left in first byte
+ 0x07, // 3 bits are left in first byte
+ 0x0f, // 4 bits are left in first byte
+ 0x1f, // 5 bits are left in first byte
+ 0x3f, // 6 bits are left in first byte
+ 0x7f, // 7 bits are left in first byte
+ 0xff // 8 bits are left in first byte
+ };
+
+ private static int table2[] = {
+ 0x00, // 0
+ 0x80, // 1
+ 0xc0, // 2
+ 0xe0, // 3
+ 0xf0, // 4
+ 0xf8, // 5
+ 0xfc, // 6
+ 0xfe, // 7
+ 0xff // 8
+ };
+
+ // Table to be used when fillOrder = 2, for flipping bytes.
+ static byte flipTable[] = {
+ 0, -128, 64, -64, 32, -96, 96, -32,
+ 16, -112, 80, -48, 48, -80, 112, -16,
+ 8, -120, 72, -56, 40, -88, 104, -24,
+ 24, -104, 88, -40, 56, -72, 120, -8,
+ 4, -124, 68, -60, 36, -92, 100, -28,
+ 20, -108, 84, -44, 52, -76, 116, -12,
+ 12, -116, 76, -52, 44, -84, 108, -20,
+ 28, -100, 92, -36, 60, -68, 124, -4,
+ 2, -126, 66, -62, 34, -94, 98, -30,
+ 18, -110, 82, -46, 50, -78, 114, -14,
+ 10, -118, 74, -54, 42, -86, 106, -22,
+ 26, -102, 90, -38, 58, -70, 122, -6,
+ 6, -122, 70, -58, 38, -90, 102, -26,
+ 22, -106, 86, -42, 54, -74, 118, -10,
+ 14, -114, 78, -50, 46, -82, 110, -18,
+ 30, -98, 94, -34, 62, -66, 126, -2,
+ 1, -127, 65, -63, 33, -95, 97, -31,
+ 17, -111, 81, -47, 49, -79, 113, -15,
+ 9, -119, 73, -55, 41, -87, 105, -23,
+ 25, -103, 89, -39, 57, -71, 121, -7,
+ 5, -123, 69, -59, 37, -91, 101, -27,
+ 21, -107, 85, -43, 53, -75, 117, -11,
+ 13, -115, 77, -51, 45, -83, 109, -19,
+ 29, -99, 93, -35, 61, -67, 125, -3,
+ 3, -125, 67, -61, 35, -93, 99, -29,
+ 19, -109, 83, -45, 51, -77, 115, -13,
+ 11, -117, 75, -53, 43, -85, 107, -21,
+ 27, -101, 91, -37, 59, -69, 123, -5,
+ 7, -121, 71, -57, 39, -89, 103, -25,
+ 23, -105, 87, -41, 55, -73, 119, -9,
+ 15, -113, 79, -49, 47, -81, 111, -17,
+ 31, -97, 95, -33, 63, -65, 127, -1,
+ };
+
+ // The main 10 bit white runs lookup table
+ private static short white[] = {
+ // 0 - 7
+ 6430, 6400, 6400, 6400, 3225, 3225, 3225, 3225,
+ // 8 - 15
+ 944, 944, 944, 944, 976, 976, 976, 976,
+ // 16 - 23
+ 1456, 1456, 1456, 1456, 1488, 1488, 1488, 1488,
+ // 24 - 31
+ 718, 718, 718, 718, 718, 718, 718, 718,
+ // 32 - 39
+ 750, 750, 750, 750, 750, 750, 750, 750,
+ // 40 - 47
+ 1520, 1520, 1520, 1520, 1552, 1552, 1552, 1552,
+ // 48 - 55
+ 428, 428, 428, 428, 428, 428, 428, 428,
+ // 56 - 63
+ 428, 428, 428, 428, 428, 428, 428, 428,
+ // 64 - 71
+ 654, 654, 654, 654, 654, 654, 654, 654,
+ // 72 - 79
+ 1072, 1072, 1072, 1072, 1104, 1104, 1104, 1104,
+ // 80 - 87
+ 1136, 1136, 1136, 1136, 1168, 1168, 1168, 1168,
+ // 88 - 95
+ 1200, 1200, 1200, 1200, 1232, 1232, 1232, 1232,
+ // 96 - 103
+ 622, 622, 622, 622, 622, 622, 622, 622,
+ // 104 - 111
+ 1008, 1008, 1008, 1008, 1040, 1040, 1040, 1040,
+ // 112 - 119
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ // 120 - 127
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ // 128 - 135
+ 396, 396, 396, 396, 396, 396, 396, 396,
+ // 136 - 143
+ 396, 396, 396, 396, 396, 396, 396, 396,
+ // 144 - 151
+ 1712, 1712, 1712, 1712, 1744, 1744, 1744, 1744,
+ // 152 - 159
+ 846, 846, 846, 846, 846, 846, 846, 846,
+ // 160 - 167
+ 1264, 1264, 1264, 1264, 1296, 1296, 1296, 1296,
+ // 168 - 175
+ 1328, 1328, 1328, 1328, 1360, 1360, 1360, 1360,
+ // 176 - 183
+ 1392, 1392, 1392, 1392, 1424, 1424, 1424, 1424,
+ // 184 - 191
+ 686, 686, 686, 686, 686, 686, 686, 686,
+ // 192 - 199
+ 910, 910, 910, 910, 910, 910, 910, 910,
+ // 200 - 207
+ 1968, 1968, 1968, 1968, 2000, 2000, 2000, 2000,
+ // 208 - 215
+ 2032, 2032, 2032, 2032, 16, 16, 16, 16,
+ // 216 - 223
+ 10257, 10257, 10257, 10257, 12305, 12305, 12305, 12305,
+ // 224 - 231
+ 330, 330, 330, 330, 330, 330, 330, 330,
+ // 232 - 239
+ 330, 330, 330, 330, 330, 330, 330, 330,
+ // 240 - 247
+ 330, 330, 330, 330, 330, 330, 330, 330,
+ // 248 - 255
+ 330, 330, 330, 330, 330, 330, 330, 330,
+ // 256 - 263
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ // 264 - 271
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ // 272 - 279
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ // 280 - 287
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ // 288 - 295
+ 878, 878, 878, 878, 878, 878, 878, 878,
+ // 296 - 303
+ 1904, 1904, 1904, 1904, 1936, 1936, 1936, 1936,
+ // 304 - 311
+ -18413, -18413, -16365, -16365, -14317, -14317, -10221, -10221,
+ // 312 - 319
+ 590, 590, 590, 590, 590, 590, 590, 590,
+ // 320 - 327
+ 782, 782, 782, 782, 782, 782, 782, 782,
+ // 328 - 335
+ 1584, 1584, 1584, 1584, 1616, 1616, 1616, 1616,
+ // 336 - 343
+ 1648, 1648, 1648, 1648, 1680, 1680, 1680, 1680,
+ // 344 - 351
+ 814, 814, 814, 814, 814, 814, 814, 814,
+ // 352 - 359
+ 1776, 1776, 1776, 1776, 1808, 1808, 1808, 1808,
+ // 360 - 367
+ 1840, 1840, 1840, 1840, 1872, 1872, 1872, 1872,
+ // 368 - 375
+ 6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157,
+ // 376 - 383
+ 6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157,
+ // 384 - 391
+ -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275,
+ // 392 - 399
+ -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275,
+ // 400 - 407
+ 14353, 14353, 14353, 14353, 16401, 16401, 16401, 16401,
+ // 408 - 415
+ 22547, 22547, 24595, 24595, 20497, 20497, 20497, 20497,
+ // 416 - 423
+ 18449, 18449, 18449, 18449, 26643, 26643, 28691, 28691,
+ // 424 - 431
+ 30739, 30739, -32749, -32749, -30701, -30701, -28653, -28653,
+ // 432 - 439
+ -26605, -26605, -24557, -24557, -22509, -22509, -20461, -20461,
+ // 440 - 447
+ 8207, 8207, 8207, 8207, 8207, 8207, 8207, 8207,
+ // 448 - 455
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 456 - 463
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 464 - 471
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 472 - 479
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 480 - 487
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 488 - 495
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 496 - 503
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 504 - 511
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 512 - 519
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 520 - 527
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 528 - 535
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 536 - 543
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 544 - 551
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 552 - 559
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 560 - 567
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 568 - 575
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 576 - 583
+ 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
+ // 584 - 591
+ 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
+ // 592 - 599
+ 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
+ // 600 - 607
+ 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
+ // 608 - 615
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ // 616 - 623
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ // 624 - 631
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ // 632 - 639
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ // 640 - 647
+ 298, 298, 298, 298, 298, 298, 298, 298,
+ // 648 - 655
+ 298, 298, 298, 298, 298, 298, 298, 298,
+ // 656 - 663
+ 298, 298, 298, 298, 298, 298, 298, 298,
+ // 664 - 671
+ 298, 298, 298, 298, 298, 298, 298, 298,
+ // 672 - 679
+ 524, 524, 524, 524, 524, 524, 524, 524,
+ // 680 - 687
+ 524, 524, 524, 524, 524, 524, 524, 524,
+ // 688 - 695
+ 556, 556, 556, 556, 556, 556, 556, 556,
+ // 696 - 703
+ 556, 556, 556, 556, 556, 556, 556, 556,
+ // 704 - 711
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 712 - 719
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 720 - 727
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 728 - 735
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 736 - 743
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 744 - 751
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 752 - 759
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 760 - 767
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 768 - 775
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 776 - 783
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 784 - 791
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 792 - 799
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 800 - 807
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 808 - 815
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 816 - 823
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 824 - 831
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 832 - 839
+ 460, 460, 460, 460, 460, 460, 460, 460,
+ // 840 - 847
+ 460, 460, 460, 460, 460, 460, 460, 460,
+ // 848 - 855
+ 492, 492, 492, 492, 492, 492, 492, 492,
+ // 856 - 863
+ 492, 492, 492, 492, 492, 492, 492, 492,
+ // 864 - 871
+ 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
+ // 872 - 879
+ 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
+ // 880 - 887
+ 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
+ // 888 - 895
+ 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
+ // 896 - 903
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 904 - 911
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 912 - 919
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 920 - 927
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 928 - 935
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 936 - 943
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 944 - 951
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 952 - 959
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 960 - 967
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 968 - 975
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 976 - 983
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 984 - 991
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 992 - 999
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 1000 - 1007
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 1008 - 1015
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 1016 - 1023
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ };
+
+ // Additional make up codes for both White and Black runs
+ private static short additionalMakeup[] = {
+ 28679, 28679, 31752, (short)32777,
+ (short)33801, (short)34825, (short)35849, (short)36873,
+ (short)29703, (short)29703, (short)30727, (short)30727,
+ (short)37897, (short)38921, (short)39945, (short)40969
+ };
+
+ // Initial black run look up table, uses the first 4 bits of a code
+ private static short initBlack[] = {
+ // 0 - 7
+ 3226, 6412, 200, 168, 38, 38, 134, 134,
+ // 8 - 15
+ 100, 100, 100, 100, 68, 68, 68, 68
+ };
+
+ //
+ private static short twoBitBlack[] = {292, 260, 226, 226}; // 0 - 3
+
+ // Main black run table, using the last 9 bits of possible 13 bit code
+ private static short black[] = {
+ // 0 - 7
+ 62, 62, 30, 30, 0, 0, 0, 0,
+ // 8 - 15
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ // 16 - 23
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ // 24 - 31
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ // 32 - 39
+ 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
+ // 40 - 47
+ 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
+ // 48 - 55
+ 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
+ // 56 - 63
+ 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
+ // 64 - 71
+ 588, 588, 588, 588, 588, 588, 588, 588,
+ // 72 - 79
+ 1680, 1680, 20499, 22547, 24595, 26643, 1776, 1776,
+ // 80 - 87
+ 1808, 1808, -24557, -22509, -20461, -18413, 1904, 1904,
+ // 88 - 95
+ 1936, 1936, -16365, -14317, 782, 782, 782, 782,
+ // 96 - 103
+ 814, 814, 814, 814, -12269, -10221, 10257, 10257,
+ // 104 - 111
+ 12305, 12305, 14353, 14353, 16403, 18451, 1712, 1712,
+ // 112 - 119
+ 1744, 1744, 28691, 30739, -32749, -30701, -28653, -26605,
+ // 120 - 127
+ 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061,
+ // 128 - 135
+ 424, 424, 424, 424, 424, 424, 424, 424,
+ // 136 - 143
+ 424, 424, 424, 424, 424, 424, 424, 424,
+ // 144 - 151
+ 424, 424, 424, 424, 424, 424, 424, 424,
+ // 152 - 159
+ 424, 424, 424, 424, 424, 424, 424, 424,
+ // 160 - 167
+ 750, 750, 750, 750, 1616, 1616, 1648, 1648,
+ // 168 - 175
+ 1424, 1424, 1456, 1456, 1488, 1488, 1520, 1520,
+ // 176 - 183
+ 1840, 1840, 1872, 1872, 1968, 1968, 8209, 8209,
+ // 184 - 191
+ 524, 524, 524, 524, 524, 524, 524, 524,
+ // 192 - 199
+ 556, 556, 556, 556, 556, 556, 556, 556,
+ // 200 - 207
+ 1552, 1552, 1584, 1584, 2000, 2000, 2032, 2032,
+ // 208 - 215
+ 976, 976, 1008, 1008, 1040, 1040, 1072, 1072,
+ // 216 - 223
+ 1296, 1296, 1328, 1328, 718, 718, 718, 718,
+ // 224 - 231
+ 456, 456, 456, 456, 456, 456, 456, 456,
+ // 232 - 239
+ 456, 456, 456, 456, 456, 456, 456, 456,
+ // 240 - 247
+ 456, 456, 456, 456, 456, 456, 456, 456,
+ // 248 - 255
+ 456, 456, 456, 456, 456, 456, 456, 456,
+ // 256 - 263
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 264 - 271
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 272 - 279
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 280 - 287
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 288 - 295
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 296 - 303
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 304 - 311
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 312 - 319
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 320 - 327
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 328 - 335
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 336 - 343
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 344 - 351
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 352 - 359
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 360 - 367
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 368 - 375
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 376 - 383
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 384 - 391
+ 490, 490, 490, 490, 490, 490, 490, 490,
+ // 392 - 399
+ 490, 490, 490, 490, 490, 490, 490, 490,
+ // 400 - 407
+ 4113, 4113, 6161, 6161, 848, 848, 880, 880,
+ // 408 - 415
+ 912, 912, 944, 944, 622, 622, 622, 622,
+ // 416 - 423
+ 654, 654, 654, 654, 1104, 1104, 1136, 1136,
+ // 424 - 431
+ 1168, 1168, 1200, 1200, 1232, 1232, 1264, 1264,
+ // 432 - 439
+ 686, 686, 686, 686, 1360, 1360, 1392, 1392,
+ // 440 - 447
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ // 448 - 455
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 456 - 463
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 464 - 471
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 472 - 479
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 480 - 487
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 488 - 495
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 496 - 503
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 504 - 511
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ };
+
+ private static byte twoDCodes[] = {
+ // 0 - 7
+ 80, 88, 23, 71, 30, 30, 62, 62,
+ // 8 - 15
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ // 16 - 23
+ 11, 11, 11, 11, 11, 11, 11, 11,
+ // 24 - 31
+ 11, 11, 11, 11, 11, 11, 11, 11,
+ // 32 - 39
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ // 40 - 47
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ // 48 - 55
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ // 56 - 63
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ // 64 - 71
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 72 - 79
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 80 - 87
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 88 - 95
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 96 - 103
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 104 - 111
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 112 - 119
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 120 - 127
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ };
+
+ public TIFFFaxDecompressor() {}
+
+ /**
+ * Invokes the superclass method and then sets instance variables on
+ * the basis of the metadata set on this decompressor.
+ */
+ public void beginDecoding() {
+ super.beginDecoding();
+
+ if(metadata instanceof TIFFImageMetadata) {
+ TIFFImageMetadata tmetadata = (TIFFImageMetadata)metadata;
+ TIFFField f;
+
+ f = tmetadata.getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER);
+ this.fillOrder = f == null ? 1 : f.getAsInt(0);
+
+ f = tmetadata.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
+ this.compression = f == null ?
+ BaselineTIFFTagSet.COMPRESSION_CCITT_RLE : f.getAsInt(0);
+
+ f = tmetadata.getTIFFField(BaselineTIFFTagSet.TAG_T4_OPTIONS);
+ this.t4Options = f == null ? 0 : f.getAsInt(0);
+ this.oneD = (t4Options & 0x01);
+ // uncompressedMode - haven't dealt with this yet.
+ this.uncompressedMode = ((t4Options & 0x02) >> 1);
+ this.fillBits = ((t4Options & 0x04) >> 2);
+ f = tmetadata.getTIFFField(BaselineTIFFTagSet.TAG_T6_OPTIONS);
+ this.t6Options = f == null ? 0 : f.getAsInt(0);
+ } else {
+ this.fillOrder = 1; // MSB-to-LSB
+
+ this.compression = BaselineTIFFTagSet.COMPRESSION_CCITT_RLE; // RLE
+
+ this.t4Options = 0; // Irrelevant as applies to T.4 only
+ this.oneD = 0; // One-dimensional
+ this.uncompressedMode = 0; // Not uncompressed mode
+ this.fillBits = 0; // No fill bits
+ this.t6Options = 0;
+ }
+ }
+
+ public void decodeRaw(byte[] b, int dstOffset,
+ int pixelBitStride, // will always be 1
+ int scanlineStride) throws IOException {
+
+ this.buffer = b;
+
+ this.w = srcWidth;
+ this.h = srcHeight;
+ this.bitsPerScanline = scanlineStride*8;
+ this.lineBitNum = 8*dstOffset;
+
+ this.data = new byte[byteCount];
+ this.bitPointer = 0;
+ this.bytePointer = 0;
+ this.prevChangingElems = new int[w + 1];
+ this.currChangingElems = new int[w + 1];
+
+ stream.seek(offset);
+ stream.readFully(data);
+
+ if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_RLE) {
+ decodeRLE();
+ } else if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_4) {
+ decodeT4();
+ } else if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_6) {
+ this.uncompressedMode = ((t6Options & 0x02) >> 1);
+ decodeT6();
+ } else {
+ throw new IIOException("Unknown compression type " + compression);
+ }
+ }
+
+ public void decodeRLE() throws IIOException {
+ for (int i = 0; i < h; i++) {
+ // Decode the line.
+ decodeNextScanline(srcMinY + i);
+
+ // Advance to the next byte boundary if not already there.
+ if (bitPointer != 0) {
+ bytePointer++;
+ bitPointer = 0;
+ }
+
+ // Update the total number of bits.
+ lineBitNum += bitsPerScanline;
+ }
+ }
+
+ public void decodeNextScanline(int lineIndex) throws IIOException {
+ int bits = 0, code = 0, isT = 0;
+ int current, entry, twoBits;
+ boolean isWhite = true;
+ int dstEnd = 0;
+
+ int bitOffset = 0;
+
+ // Initialize starting of the changing elements array
+ changingElemSize = 0;
+
+ // While scanline not complete
+ while (bitOffset < w) {
+
+ // Mark start of white run.
+ int runOffset = bitOffset;
+
+ while (isWhite && bitOffset < w) {
+ // White run
+ current = nextNBits(10);
+ entry = white[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x0f;
+
+ if (bits == 12) { // Additional Make up code
+ // Get the next 2 bits
+ twoBits = nextLesserThan8Bits(2);
+ // Consolidate the 2 new bits and last 2 bits into 4 bits
+ current = ((current << 2) & 0x000c) | twoBits;
+ entry = additionalMakeup[current];
+ bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111
+ code = (entry >>> 4) & 0x0fff; // 12 bits
+ bitOffset += code; // Skip white run
+
+ updatePointer(4 - bits);
+ } else if (bits == 0) { // ERROR
+ warning("Error 0");
+ } else if (bits == 15) { // EOL
+ //
+ // Instead of throwing an exception, assume that the
+ // EOL was premature; emit a warning and return.
+ //
+ warning("Premature EOL in white run of line "+lineIndex+
+ ": read "+bitOffset+" of "+w+" expected pixels.");
+ return;
+ } else {
+ // 11 bits - 0000 0111 1111 1111 = 0x07ff
+ code = (entry >>> 5) & 0x07ff;
+ bitOffset += code;
+
+ updatePointer(10 - bits);
+ if (isT == 0) {
+ isWhite = false;
+ currChangingElems[changingElemSize++] = bitOffset;
+ }
+ }
+ }
+
+ // Check whether this run completed one width
+ if (bitOffset == w) {
+ // If the white run has not been terminated then ensure that
+ // the next code word is a terminating code for a white run
+ // of length zero.
+ int runLength = bitOffset - runOffset;
+ if(isWhite &&
+ runLength != 0 && runLength % 64 == 0 &&
+ nextNBits(8) != 0x35) {
+ warning("Missing zero white run length terminating code!");
+ updatePointer(8);
+ }
+ break;
+ }
+
+ // Mark start of black run.
+ runOffset = bitOffset;
+
+ while (isWhite == false && bitOffset < w) {
+ // Black run
+ current = nextLesserThan8Bits(4);
+ entry = initBlack[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x000f;
+ code = (entry >>> 5) & 0x07ff;
+
+ if (code == 100) {
+ current = nextNBits(9);
+ entry = black[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x000f;
+ code = (entry >>> 5) & 0x07ff;
+
+ if (bits == 12) {
+ // Additional makeup codes
+ updatePointer(5);
+ current = nextLesserThan8Bits(4);
+ entry = additionalMakeup[current];
+ bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111
+ code = (entry >>> 4) & 0x0fff; // 12 bits
+
+ setToBlack(bitOffset, code);
+ bitOffset += code;
+
+ updatePointer(4 - bits);
+ } else if (bits == 15) {
+ //
+ // Instead of throwing an exception, assume that the
+ // EOL was premature; emit a warning and return.
+ //
+ warning("Premature EOL in black run of line "+
+ lineIndex+": read "+bitOffset+" of "+w+
+ " expected pixels.");
+ return;
+ } else {
+ setToBlack(bitOffset, code);
+ bitOffset += code;
+
+ updatePointer(9 - bits);
+ if (isT == 0) {
+ isWhite = true;
+ currChangingElems[changingElemSize++] = bitOffset;
+ }
+ }
+ } else if (code == 200) {
+ // Is a Terminating code
+ current = nextLesserThan8Bits(2);
+ entry = twoBitBlack[current];
+ code = (entry >>> 5) & 0x07ff;
+ bits = (entry >>> 1) & 0x0f;
+
+ setToBlack(bitOffset, code);
+ bitOffset += code;
+
+ updatePointer(2 - bits);
+ isWhite = true;
+ currChangingElems[changingElemSize++] = bitOffset;
+ } else {
+ // Is a Terminating code
+ setToBlack(bitOffset, code);
+ bitOffset += code;
+
+ updatePointer(4 - bits);
+ isWhite = true;
+ currChangingElems[changingElemSize++] = bitOffset;
+ }
+ }
+
+ // Check whether this run completed one width
+ if (bitOffset == w) {
+ // If the black run has not been terminated then ensure that
+ // the next code word is a terminating code for a black run
+ // of length zero.
+ int runLength = bitOffset - runOffset;
+ if(!isWhite &&
+ runLength != 0 && runLength % 64 == 0 &&
+ nextNBits(10) != 0x37) {
+ warning("Missing zero black run length terminating code!");
+ updatePointer(10);
+ }
+ break;
+ }
+ }
+
+ currChangingElems[changingElemSize++] = bitOffset;
+ }
+
+ public void decodeT4() throws IIOException {
+ int height = h;
+
+ int a0, a1, b1, b2;
+ int[] b = new int[2];
+ int entry, code, bits, color;
+ boolean isWhite;
+ int currIndex = 0;
+ int temp[];
+
+ if(data.length < 2) {
+ throw new IIOException("Insufficient data to read initial EOL.");
+ }
+
+ // The data should start with an EOL code
+ int next12 = nextNBits(12);
+ if(next12 != 1) {
+ warning("T.4 compressed data should begin with EOL.");
+ }
+ updatePointer(12);
+
+ // Find the first one-dimensionally encoded line.
+ int modeFlag = 0;
+ int lines = -1; // indicates imaginary line before first actual line.
+ while(modeFlag != 1) {
+ try {
+ modeFlag = findNextLine();
+ lines++; // Normally 'lines' will be 0 on exiting loop.
+ } catch(EOFException eofe) {
+ throw new IIOException("No reference line present.");
+ }
+ }
+
+ int bitOffset;
+
+ // Then the 1D encoded scanline data will occur, changing elements
+ // array gets set.
+ decodeNextScanline(srcMinY);
+ lines++;
+ lineBitNum += bitsPerScanline;
+
+ while(lines < height) {
+
+ // Every line must begin with an EOL followed by a bit which
+ // indicates whether the following scanline is 1D or 2D encoded.
+ try {
+ modeFlag = findNextLine();
+ } catch(EOFException eofe) {
+ warning("Input exhausted before EOL found at line "+
+ (srcMinY+lines)+": read 0 of "+w+" expected pixels.");
+ break;
+ }
+ if(modeFlag == 0) {
+ // 2D encoded scanline follows
+
+ // Initialize previous scanlines changing elements, and
+ // initialize current scanline's changing elements array
+ temp = prevChangingElems;
+ prevChangingElems = currChangingElems;
+ currChangingElems = temp;
+ currIndex = 0;
+
+ // a0 has to be set just before the start of this scanline.
+ a0 = -1;
+ isWhite = true;
+ bitOffset = 0;
+
+ lastChangingElement = 0;
+
+ while (bitOffset < w) {
+ // Get the next changing element
+ getNextChangingElement(a0, isWhite, b);
+
+ b1 = b[0];
+ b2 = b[1];
+
+ // Get the next seven bits
+ entry = nextLesserThan8Bits(7);
+
+ // Run these through the 2DCodes table
+ entry = (twoDCodes[entry] & 0xff);
+
+ // Get the code and the number of bits used up
+ code = (entry & 0x78) >>> 3;
+ bits = entry & 0x07;
+
+ if (code == 0) {
+ if (!isWhite) {
+ setToBlack(bitOffset, b2 - bitOffset);
+ }
+ bitOffset = a0 = b2;
+
+ // Set pointer to consume the correct number of bits.
+ updatePointer(7 - bits);
+ } else if (code == 1) {
+ // Horizontal
+ updatePointer(7 - bits);
+
+ // identify the next 2 codes.
+ int number;
+ if (isWhite) {
+ number = decodeWhiteCodeWord();
+ bitOffset += number;
+ currChangingElems[currIndex++] = bitOffset;
+
+ number = decodeBlackCodeWord();
+ setToBlack(bitOffset, number);
+ bitOffset += number;
+ currChangingElems[currIndex++] = bitOffset;
+ } else {
+ number = decodeBlackCodeWord();
+ setToBlack(bitOffset, number);
+ bitOffset += number;
+ currChangingElems[currIndex++] = bitOffset;
+
+ number = decodeWhiteCodeWord();
+ bitOffset += number;
+ currChangingElems[currIndex++] = bitOffset;
+ }
+
+ a0 = bitOffset;
+ } else if (code <= 8) {
+ // Vertical
+ a1 = b1 + (code - 5);
+
+ currChangingElems[currIndex++] = a1;
+
+ // We write the current color till a1 - 1 pos,
+ // since a1 is where the next color starts
+ if (!isWhite) {
+ setToBlack(bitOffset, a1 - bitOffset);
+ }
+ bitOffset = a0 = a1;
+ isWhite = !isWhite;
+
+ updatePointer(7 - bits);
+ } else {
+ warning("Unknown coding mode encountered at line "+
+ (srcMinY+lines)+": read "+bitOffset+" of "+w+
+ " expected pixels.");
+
+ // Find the next one-dimensionally encoded line.
+ int numLinesTested = 0;
+ while(modeFlag != 1) {
+ try {
+ modeFlag = findNextLine();
+ numLinesTested++;
+ } catch(EOFException eofe) {
+ warning("Sync loss at line "+
+ (srcMinY+lines)+": read "+
+ lines+" of "+height+" lines.");
+ return;
+ }
+ }
+ lines += numLinesTested - 1;
+ updatePointer(13);
+ break;
+ }
+ }
+
+ // Add the changing element beyond the current scanline for the
+ // other color too
+ currChangingElems[currIndex++] = bitOffset;
+ changingElemSize = currIndex;
+ } else { // modeFlag == 1
+ // 1D encoded scanline follows
+ decodeNextScanline(srcMinY+lines);
+ }
+
+ lineBitNum += bitsPerScanline;
+ lines++;
+ } // while(lines < height)
+ }
+
+ public synchronized void decodeT6() throws IIOException {
+ int height = h;
+
+ int bufferOffset = 0;
+
+ int a0, a1, b1, b2;
+ int entry, code, bits;
+ byte color;
+ boolean isWhite;
+ int currIndex;
+ int temp[];
+
+ // Return values from getNextChangingElement
+ int[] b = new int[2];
+
+ // uncompressedMode - have written some code for this, but this
+ // has not been tested due to lack of test images using this optional
+ // extension. This code is when code == 11. aastha 03/03/1999
+
+ // Local cached reference
+ int[] cce = currChangingElems;
+
+ // Assume invisible preceding row of all white pixels and insert
+ // both black and white changing elements beyond the end of this
+ // imaginary scanline.
+ changingElemSize = 0;
+ cce[changingElemSize++] = w;
+ cce[changingElemSize++] = w;
+
+ int bitOffset;
+
+ for (int lines = 0; lines < height; lines++) {
+ // a0 has to be set just before the start of the scanline.
+ a0 = -1;
+ isWhite = true;
+
+ // Assign the changing elements of the previous scanline to
+ // prevChangingElems and start putting this new scanline's
+ // changing elements into the currChangingElems.
+ temp = prevChangingElems;
+ prevChangingElems = currChangingElems;
+ cce = currChangingElems = temp;
+ currIndex = 0;
+
+ // Start decoding the scanline
+ bitOffset = 0;
+
+ // Reset search start position for getNextChangingElement
+ lastChangingElement = 0;
+
+ // Till one whole scanline is decoded
+ while (bitOffset < w) {
+ // Get the next changing element
+ getNextChangingElement(a0, isWhite, b);
+ b1 = b[0];
+ b2 = b[1];
+
+ // Get the next seven bits
+ entry = nextLesserThan8Bits(7);
+ // Run these through the 2DCodes table
+ entry = (twoDCodes[entry] & 0xff);
+
+ // Get the code and the number of bits used up
+ code = (entry & 0x78) >>> 3;
+ bits = entry & 0x07;
+
+ if (code == 0) { // Pass
+ // We always assume WhiteIsZero format for fax.
+ if (!isWhite) {
+ if(b2 > w) {
+ b2 = w;
+ warning("Decoded row "+(srcMinY+lines)+
+ " too long; ignoring extra samples.");
+ }
+ setToBlack(bitOffset, b2 - bitOffset);
+ }
+ bitOffset = a0 = b2;
+
+ // Set pointer to only consume the correct number of bits.
+ updatePointer(7 - bits);
+ } else if (code == 1) { // Horizontal
+ // Set pointer to only consume the correct number of bits.
+ updatePointer(7 - bits);
+
+ // identify the next 2 alternating color codes.
+ int number;
+ if (isWhite) {
+ // Following are white and black runs
+ number = decodeWhiteCodeWord();
+ bitOffset += number;
+ cce[currIndex++] = bitOffset;
+
+ number = decodeBlackCodeWord();
+ if(number > w - bitOffset) {
+ number = w - bitOffset;
+ warning("Decoded row "+(srcMinY+lines)+
+ " too long; ignoring extra samples.");
+ }
+ setToBlack(bitOffset, number);
+ bitOffset += number;
+ cce[currIndex++] = bitOffset;
+ } else {
+ // First a black run and then a white run follows
+ number = decodeBlackCodeWord();
+ if(number > w - bitOffset) {
+ number = w - bitOffset;
+ warning("Decoded row "+(srcMinY+lines)+
+ " too long; ignoring extra samples.");
+ }
+ setToBlack(bitOffset, number);
+ bitOffset += number;
+ cce[currIndex++] = bitOffset;
+
+ number = decodeWhiteCodeWord();
+ bitOffset += number;
+ cce[currIndex++] = bitOffset;
+ }
+
+ a0 = bitOffset;
+ } else if (code <= 8) { // Vertical
+ a1 = b1 + (code - 5);
+ cce[currIndex++] = a1;
+
+ // We write the current color till a1 - 1 pos,
+ // since a1 is where the next color starts
+ if (!isWhite) {
+ if(a1 > w) {
+ a1 = w;
+ warning("Decoded row "+(srcMinY+lines)+
+ " too long; ignoring extra samples.");
+ }
+ setToBlack(bitOffset, a1 - bitOffset);
+ }
+ bitOffset = a0 = a1;
+ isWhite = !isWhite;
+
+ updatePointer(7 - bits);
+ } else if (code == 11) {
+ int entranceCode = nextLesserThan8Bits(3);
+ if (entranceCode != 7) {
+ String msg =
+ "Unsupported entrance code "+entranceCode+
+ " for extension mode at line "+(srcMinY+lines)+".";
+ warning(msg);
+ }
+
+ int zeros = 0;
+ boolean exit = false;
+
+ while (!exit) {
+ while (nextLesserThan8Bits(1) != 1) {
+ zeros++;
+ }
+
+ if (zeros > 5) {
+ // Exit code
+
+ // Zeros before exit code
+ zeros = zeros - 6;
+
+ if (!isWhite && (zeros > 0)) {
+ cce[currIndex++] = bitOffset;
+ }
+
+ // Zeros before the exit code
+ bitOffset += zeros;
+ if (zeros > 0) {
+ // Some zeros have been written
+ isWhite = true;
+ }
+
+ // Read in the bit which specifies the color of
+ // the following run
+ if (nextLesserThan8Bits(1) == 0) {
+ if (!isWhite) {
+ cce[currIndex++] = bitOffset;
+ }
+ isWhite = true;
+ } else {
+ if (isWhite) {
+ cce[currIndex++] = bitOffset;
+ }
+ isWhite = false;
+ }
+
+ exit = true;
+ }
+
+ if (zeros == 5) {
+ if (!isWhite) {
+ cce[currIndex++] = bitOffset;
+ }
+ bitOffset += zeros;
+
+ // Last thing written was white
+ isWhite = true;
+ } else {
+ bitOffset += zeros;
+
+ cce[currIndex++] = bitOffset;
+ setToBlack(bitOffset, 1);
+ ++bitOffset;
+
+ // Last thing written was black
+ isWhite = false;
+ }
+
+ }
+ } else {
+ String msg =
+ "Unknown coding mode encountered at line "+
+ (srcMinY+lines)+".";
+ warning(msg);
+ }
+ } // while bitOffset < w
+
+ // Add the changing element beyond the current scanline for the
+ // other color too, if not already added previously
+ if (currIndex <= w)
+ cce[currIndex++] = bitOffset;
+
+ // Number of changing elements in this scanline.
+ changingElemSize = currIndex;
+
+ lineBitNum += bitsPerScanline;
+ } // for lines < height
+ }
+
+ private void setToBlack(int bitNum, int numBits) {
+ // bitNum is relative to current scanline so bump it by lineBitNum
+ bitNum += lineBitNum;
+
+ int lastBit = bitNum + numBits;
+ int byteNum = bitNum >> 3;
+
+ // Handle bits in first byte
+ int shift = bitNum & 0x7;
+ if (shift > 0) {
+ int maskVal = 1 << (7 - shift);
+ byte val = buffer[byteNum];
+ while (maskVal > 0 && bitNum < lastBit) {
+ val |= maskVal;
+ maskVal >>= 1;
+ ++bitNum;
+ }
+ buffer[byteNum] = val;
+ }
+
+ // Fill in 8 bits at a time
+ byteNum = bitNum >> 3;
+ while (bitNum < lastBit - 7) {
+ buffer[byteNum++] = (byte)255;
+ bitNum += 8;
+ }
+
+ // Fill in remaining bits
+ while (bitNum < lastBit) {
+ byteNum = bitNum >> 3;
+ buffer[byteNum] |= 1 << (7 - (bitNum & 0x7));
+ ++bitNum;
+ }
+ }
+
+ // Returns run length
+ private int decodeWhiteCodeWord() throws IIOException {
+ int current, entry, bits, isT, twoBits, code = -1;
+ int runLength = 0;
+ boolean isWhite = true;
+
+ while (isWhite) {
+ current = nextNBits(10);
+ entry = white[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x0f;
+
+ if (bits == 12) { // Additional Make up code
+ // Get the next 2 bits
+ twoBits = nextLesserThan8Bits(2);
+ // Consolidate the 2 new bits and last 2 bits into 4 bits
+ current = ((current << 2) & 0x000c) | twoBits;
+ entry = additionalMakeup[current];
+ bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111
+ code = (entry >>> 4) & 0x0fff; // 12 bits
+ runLength += code;
+ updatePointer(4 - bits);
+ } else if (bits == 0) { // ERROR
+ throw new IIOException("Error 0");
+ } else if (bits == 15) { // EOL
+ throw new IIOException("Error 1");
+ } else {
+ // 11 bits - 0000 0111 1111 1111 = 0x07ff
+ code = (entry >>> 5) & 0x07ff;
+ runLength += code;
+ updatePointer(10 - bits);
+ if (isT == 0) {
+ isWhite = false;
+ }
+ }
+ }
+
+ return runLength;
+ }
+
+ // Returns run length
+ private int decodeBlackCodeWord() throws IIOException {
+ int current, entry, bits, isT, twoBits, code = -1;
+ int runLength = 0;
+ boolean isWhite = false;
+
+ while (!isWhite) {
+ current = nextLesserThan8Bits(4);
+ entry = initBlack[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x000f;
+ code = (entry >>> 5) & 0x07ff;
+
+ if (code == 100) {
+ current = nextNBits(9);
+ entry = black[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x000f;
+ code = (entry >>> 5) & 0x07ff;
+
+ if (bits == 12) {
+ // Additional makeup codes
+ updatePointer(5);
+ current = nextLesserThan8Bits(4);
+ entry = additionalMakeup[current];
+ bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111
+ code = (entry >>> 4) & 0x0fff; // 12 bits
+ runLength += code;
+
+ updatePointer(4 - bits);
+ } else if (bits == 15) {
+ // EOL code
+ throw new IIOException("Error 2");
+ } else {
+ runLength += code;
+ updatePointer(9 - bits);
+ if (isT == 0) {
+ isWhite = true;
+ }
+ }
+ } else if (code == 200) {
+ // Is a Terminating code
+ current = nextLesserThan8Bits(2);
+ entry = twoBitBlack[current];
+ code = (entry >>> 5) & 0x07ff;
+ runLength += code;
+ bits = (entry >>> 1) & 0x0f;
+ updatePointer(2 - bits);
+ isWhite = true;
+ } else {
+ // Is a Terminating code
+ runLength += code;
+ updatePointer(4 - bits);
+ isWhite = true;
+ }
+ }
+
+ return runLength;
+ }
+
+ private int findNextLine() throws IIOException, EOFException {
+ // Set maximum and current bit index into the compressed data.
+ int bitIndexMax = data.length*8 - 1;
+ int bitIndexMax12 = bitIndexMax - 12;
+ int bitIndex = bytePointer*8 + bitPointer;
+
+ // Loop while at least 12 bits are available.
+ while(bitIndex <= bitIndexMax12) {
+ // Get the next 12 bits.
+ int next12Bits = nextNBits(12);
+ bitIndex += 12;
+
+ // Loop while the 12 bits are not unity, i.e., while the EOL
+ // has not been reached, and there is at least one bit left.
+ while(next12Bits != 1 && bitIndex < bitIndexMax) {
+ next12Bits =
+ ((next12Bits & 0x000007ff) << 1) |
+ (nextLesserThan8Bits(1) & 0x00000001);
+ bitIndex++;
+ }
+
+ if(next12Bits == 1) { // now positioned just after EOL
+ if(oneD == 1) { // two-dimensional coding
+ if(bitIndex < bitIndexMax) {
+ // check next bit against type of line being sought
+ return nextLesserThan8Bits(1);
+ }
+ } else {
+ return 1;
+ }
+ }
+ }
+
+ // EOL not found.
+ throw new EOFException();
+ }
+
+ private void getNextChangingElement(int a0, boolean isWhite, int[] ret) throws IIOException {
+ // Local copies of instance variables
+ int[] pce = this.prevChangingElems;
+ int ces = this.changingElemSize;
+
+ // If the previous match was at an odd element, we still
+ // have to search the preceeding element.
+ // int start = lastChangingElement & ~0x1;
+ int start = lastChangingElement > 0 ? lastChangingElement - 1 : 0;
+ if (isWhite) {
+ start &= ~0x1; // Search even numbered elements
+ } else {
+ start |= 0x1; // Search odd numbered elements
+ }
+
+ int i = start;
+ for (; i < ces; i += 2) {
+ int temp = pce[i];
+ if (temp > a0) {
+ lastChangingElement = i;
+ ret[0] = temp;
+ break;
+ }
+ }
+
+ if (i + 1 < ces) {
+ ret[1] = pce[i + 1];
+ }
+ }
+
+ private int nextNBits(int bitsToGet) throws IIOException {
+ byte b, next, next2next;
+ int l = data.length - 1;
+ int bp = this.bytePointer;
+
+ if (fillOrder == 1) {
+ b = data[bp];
+
+ if (bp == l) {
+ next = 0x00;
+ next2next = 0x00;
+ } else if ((bp + 1) == l) {
+ next = data[bp + 1];
+ next2next = 0x00;
+ } else {
+ next = data[bp + 1];
+ next2next = data[bp + 2];
+ }
+ } else if (fillOrder == 2) {
+ b = flipTable[data[bp] & 0xff];
+
+ if (bp == l) {
+ next = 0x00;
+ next2next = 0x00;
+ } else if ((bp + 1) == l) {
+ next = flipTable[data[bp + 1] & 0xff];
+ next2next = 0x00;
+ } else {
+ next = flipTable[data[bp + 1] & 0xff];
+ next2next = flipTable[data[bp + 2] & 0xff];
+ }
+ } else {
+ throw new IIOException("Invalid FillOrder");
+ }
+
+ int bitsLeft = 8 - bitPointer;
+ int bitsFromNextByte = bitsToGet - bitsLeft;
+ int bitsFromNext2NextByte = 0;
+ if (bitsFromNextByte > 8) {
+ bitsFromNext2NextByte = bitsFromNextByte - 8;
+ bitsFromNextByte = 8;
+ }
+
+ bytePointer++;
+
+ int i1 = (b & table1[bitsLeft]) << (bitsToGet - bitsLeft);
+ int i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte);
+
+ int i3 = 0;
+ if (bitsFromNext2NextByte != 0) {
+ i2 <<= bitsFromNext2NextByte;
+ i3 = (next2next & table2[bitsFromNext2NextByte]) >>>
+ (8 - bitsFromNext2NextByte);
+ i2 |= i3;
+ bytePointer++;
+ bitPointer = bitsFromNext2NextByte;
+ } else {
+ if (bitsFromNextByte == 8) {
+ bitPointer = 0;
+ bytePointer++;
+ } else {
+ bitPointer = bitsFromNextByte;
+ }
+ }
+
+ int i = i1 | i2;
+ return i;
+ }
+
+ private int nextLesserThan8Bits(int bitsToGet) throws IIOException {
+ byte b, next;
+ int l = data.length - 1;
+ int bp = this.bytePointer;
+
+ if (fillOrder == 1) {
+ b = data[bp];
+ if (bp == l) {
+ next = 0x00;
+ } else {
+ next = data[bp + 1];
+ }
+ } else if (fillOrder == 2) {
+ b = flipTable[data[bp] & 0xff];
+ if (bp == l) {
+ next = 0x00;
+ } else {
+ next = flipTable[data[bp + 1] & 0xff];
+ }
+ } else {
+ throw new IIOException("Invalid FillOrder");
+ }
+
+ int bitsLeft = 8 - bitPointer;
+ int bitsFromNextByte = bitsToGet - bitsLeft;
+
+ int shift = bitsLeft - bitsToGet;
+ int i1, i2;
+ if (shift >= 0) {
+ i1 = (b & table1[bitsLeft]) >>> shift;
+ bitPointer += bitsToGet;
+ if (bitPointer == 8) {
+ bitPointer = 0;
+ bytePointer++;
+ }
+ } else {
+ i1 = (b & table1[bitsLeft]) << (-shift);
+ i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte);
+
+ i1 |= i2;
+ bytePointer++;
+ bitPointer = bitsFromNextByte;
+ }
+
+ return i1;
+ }
+
+ // Move pointer backwards by given amount of bits
+ private void updatePointer(int bitsToMoveBack) {
+ if (bitsToMoveBack > 8) {
+ bytePointer -= bitsToMoveBack/8;
+ bitsToMoveBack %= 8;
+ }
+
+ int i = bitPointer - bitsToMoveBack;
+ if (i < 0) {
+ bytePointer--;
+ bitPointer = 8 + i;
+ } else {
+ bitPointer = i;
+ }
+ }
+
+ // Forward warning message to reader
+ private void warning(String msg) {
+ if(this.reader instanceof TIFFImageReader) {
+ ((TIFFImageReader)reader).forwardWarningMessage(msg);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFFieldNode.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,219 @@
+/*
+ * 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.util.Arrays;
+import java.util.List;
+import javax.imageio.metadata.IIOMetadataNode;
+import org.w3c.dom.Node;
+import javax.imageio.plugins.tiff.TIFFDirectory;
+import javax.imageio.plugins.tiff.TIFFField;
+import javax.imageio.plugins.tiff.TIFFTag;
+import javax.imageio.plugins.tiff.TIFFTagSet;
+
+/**
+ * The <code>Node</code> representation of a <code>TIFFField</code>
+ * wherein the child node is procedural rather than buffered.
+ */
+public class TIFFFieldNode extends IIOMetadataNode {
+ private static String getNodeName(TIFFField f) {
+ return f.getData() instanceof TIFFDirectory ?
+ "TIFFIFD" : "TIFFField";
+ }
+
+ private boolean isIFD;
+
+ private Boolean isInitialized = Boolean.FALSE;
+
+ private TIFFField field;
+
+ public TIFFFieldNode(TIFFField field) {
+ super(getNodeName(field));
+
+ isIFD = field.getData() instanceof TIFFDirectory;
+
+ this.field = field;
+
+ TIFFTag tag = field.getTag();
+ int tagNumber = tag.getNumber();
+ String tagName = tag.getName();
+
+ if(isIFD) {
+ if(tagNumber != 0) {
+ setAttribute("parentTagNumber", Integer.toString(tagNumber));
+ }
+ if(tagName != null) {
+ setAttribute("parentTagName", tagName);
+ }
+
+ TIFFDirectory dir = (TIFFDirectory)field.getData();
+ TIFFTagSet[] tagSets = dir.getTagSets();
+ if(tagSets != null) {
+ StringBuilder tagSetNames = new StringBuilder();
+ for(int i = 0; i < tagSets.length; i++) {
+ tagSetNames.append(tagSets[i].getClass().getName());
+ if(i != tagSets.length - 1) {
+ tagSetNames.append(",");
+ }
+ }
+ setAttribute("tagSets", tagSetNames.toString());
+ }
+ } else {
+ setAttribute("number", Integer.toString(tagNumber));
+ setAttribute("name", tagName);
+ }
+ }
+
+ private synchronized void initialize() {
+ if(isInitialized) return;
+
+ if(isIFD) {
+ TIFFDirectory dir = (TIFFDirectory)field.getData();
+ TIFFField[] fields = dir.getTIFFFields();
+ if(fields != null) {
+ TIFFTagSet[] tagSets = dir.getTagSets();
+ List<TIFFTagSet> tagSetList = Arrays.asList(tagSets);
+ int numFields = fields.length;
+ for(int i = 0; i < numFields; i++) {
+ TIFFField f = fields[i];
+ int tagNumber = f.getTagNumber();
+ TIFFTag tag = TIFFIFD.getTag(tagNumber, tagSetList);
+
+ Node node = f.getAsNativeNode();
+
+ if (node != null) {
+ appendChild(node);
+ }
+ }
+ }
+ } else {
+ IIOMetadataNode child;
+ int count = field.getCount();
+ if (field.getType() == TIFFTag.TIFF_UNDEFINED) {
+ child = new IIOMetadataNode("TIFFUndefined");
+
+ byte[] data = field.getAsBytes();
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < count; i++) {
+ sb.append(Integer.toString(data[i] & 0xff));
+ if (i < count - 1) {
+ sb.append(",");
+ }
+ }
+ child.setAttribute("value", sb.toString());
+ } else {
+ child = new IIOMetadataNode("TIFF" +
+ TIFFField.getTypeName(field.getType()) +
+ "s");
+
+ TIFFTag tag = field.getTag();
+
+ for (int i = 0; i < count; i++) {
+ IIOMetadataNode cchild =
+ new IIOMetadataNode("TIFF" +
+ TIFFField.getTypeName(field.getType()));
+
+ cchild.setAttribute("value", field.getValueAsString(i));
+ if (tag.hasValueNames() && field.isIntegral()) {
+ int value = field.getAsInt(i);
+ String name = tag.getValueName(value);
+ if (name != null) {
+ cchild.setAttribute("description", name);
+ }
+ }
+
+ child.appendChild(cchild);
+ }
+ }
+ appendChild(child);
+ }
+
+ isInitialized = Boolean.TRUE;
+ }
+
+ // Need to override this method to avoid a stack overflow exception
+ // which will occur if super.appendChild is called from initialize().
+ public Node appendChild(Node newChild) {
+ if (newChild == null) {
+ throw new NullPointerException("newChild == null!");
+ }
+
+ return super.insertBefore(newChild, null);
+ }
+
+ // Override all methods which refer to child nodes.
+
+ public boolean hasChildNodes() {
+ initialize();
+ return super.hasChildNodes();
+ }
+
+ public int getLength() {
+ initialize();
+ return super.getLength();
+ }
+
+ public Node getFirstChild() {
+ initialize();
+ return super.getFirstChild();
+ }
+
+ public Node getLastChild() {
+ initialize();
+ return super.getLastChild();
+ }
+
+ public Node getPreviousSibling() {
+ initialize();
+ return super.getPreviousSibling();
+ }
+
+ public Node getNextSibling() {
+ initialize();
+ return super.getNextSibling();
+ }
+
+ public Node insertBefore(Node newChild,
+ Node refChild) {
+ initialize();
+ return super.insertBefore(newChild, refChild);
+ }
+
+ public Node replaceChild(Node newChild,
+ Node oldChild) {
+ initialize();
+ return super.replaceChild(newChild, oldChild);
+ }
+
+ public Node removeChild(Node oldChild) {
+ initialize();
+ return super.removeChild(oldChild);
+ }
+
+ public Node cloneNode(boolean deep) {
+ initialize();
+ return super.cloneNode(deep);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,837 @@
+/*
+ * 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.EOFException;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import javax.imageio.IIOException;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.ImageOutputStream;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.plugins.tiff.TIFFDirectory;
+import javax.imageio.plugins.tiff.TIFFField;
+import javax.imageio.plugins.tiff.TIFFTag;
+import javax.imageio.plugins.tiff.TIFFTagSet;
+
+public class TIFFIFD extends TIFFDirectory {
+ private static final long MAX_SAMPLES_PER_PIXEL = 0xffff;
+ private static final long MAX_ASCII_SIZE = 0xffff;
+
+ private long stripOrTileByteCountsPosition = -1;
+ private long stripOrTileOffsetsPosition = -1;
+ private long lastPosition = -1;
+
+ public static TIFFTag getTag(int tagNumber, List<TIFFTagSet> tagSets) {
+ Iterator<TIFFTagSet> iter = tagSets.iterator();
+ while (iter.hasNext()) {
+ TIFFTagSet tagSet = iter.next();
+ TIFFTag tag = tagSet.getTag(tagNumber);
+ if (tag != null) {
+ return tag;
+ }
+ }
+
+ return null;
+ }
+
+ public static TIFFTag getTag(String tagName, List<TIFFTagSet> tagSets) {
+ Iterator<TIFFTagSet> iter = tagSets.iterator();
+ while (iter.hasNext()) {
+ TIFFTagSet tagSet = iter.next();
+ TIFFTag tag = tagSet.getTag(tagName);
+ if (tag != null) {
+ return tag;
+ }
+ }
+
+ return null;
+ }
+
+ private static void writeTIFFFieldToStream(TIFFField field,
+ ImageOutputStream stream)
+ throws IOException {
+ int count = field.getCount();
+ Object data = field.getData();
+
+ switch (field.getType()) {
+ case TIFFTag.TIFF_ASCII:
+ for (int i = 0; i < count; i++) {
+ String s = ((String[])data)[i];
+ int length = s.length();
+ for (int j = 0; j < length; j++) {
+ stream.writeByte(s.charAt(j) & 0xff);
+ }
+ stream.writeByte(0);
+ }
+ break;
+ case TIFFTag.TIFF_UNDEFINED:
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_SBYTE:
+ stream.write((byte[])data);
+ break;
+ case TIFFTag.TIFF_SHORT:
+ stream.writeChars((char[])data, 0, ((char[])data).length);
+ break;
+ case TIFFTag.TIFF_SSHORT:
+ stream.writeShorts((short[])data, 0, ((short[])data).length);
+ break;
+ case TIFFTag.TIFF_SLONG:
+ stream.writeInts((int[])data, 0, ((int[])data).length);
+ break;
+ case TIFFTag.TIFF_LONG:
+ for (int i = 0; i < count; i++) {
+ stream.writeInt((int)(((long[])data)[i]));
+ }
+ break;
+ case TIFFTag.TIFF_IFD_POINTER:
+ stream.writeInt(0); // will need to be backpatched
+ break;
+ case TIFFTag.TIFF_FLOAT:
+ stream.writeFloats((float[])data, 0, ((float[])data).length);
+ break;
+ case TIFFTag.TIFF_DOUBLE:
+ stream.writeDoubles((double[])data, 0, ((double[])data).length);
+ break;
+ case TIFFTag.TIFF_SRATIONAL:
+ for (int i = 0; i < count; i++) {
+ stream.writeInt(((int[][])data)[i][0]);
+ stream.writeInt(((int[][])data)[i][1]);
+ }
+ break;
+ case TIFFTag.TIFF_RATIONAL:
+ for (int i = 0; i < count; i++) {
+ long num = ((long[][])data)[i][0];
+ long den = ((long[][])data)[i][1];
+ stream.writeInt((int)num);
+ stream.writeInt((int)den);
+ }
+ break;
+ default:
+ // error
+ }
+ }
+
+ public TIFFIFD(List<TIFFTagSet> tagSets, TIFFTag parentTag) {
+ super(tagSets.toArray(new TIFFTagSet[tagSets.size()]),
+ parentTag);
+ }
+
+ public TIFFIFD(List<TIFFTagSet> tagSets) {
+ this(tagSets, null);
+ }
+
+ public List<TIFFTagSet> getTagSetList() {
+ return Arrays.asList(getTagSets());
+ }
+
+ /**
+ * Returns an <code>Iterator</code> over the TIFF fields. The
+ * traversal is in the order of increasing tag number.
+ */
+ // Note: the sort is guaranteed for low fields by the use of an
+ // array wherein the index corresponds to the tag number and for
+ // the high fields by the use of a TreeMap with tag number keys.
+ public Iterator<TIFFField> iterator() {
+ return Arrays.asList(getTIFFFields()).iterator();
+ }
+
+ /**
+ * Read the value of a field. The <code>data</code> parameter should be
+ * an array of length 1 of Object.
+ *
+ * @param stream the input stream
+ * @param type the type as read from the stream
+ * @param count the count read from the stream
+ * @param data a container for the data
+ * @return the updated count
+ * @throws IOException
+ */
+ private static int readFieldValue(ImageInputStream stream,
+ int type, int count, Object[] data) throws IOException {
+ Object obj;
+
+ switch (type) {
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_SBYTE:
+ case TIFFTag.TIFF_UNDEFINED:
+ case TIFFTag.TIFF_ASCII:
+ byte[] bvalues = new byte[count];
+ stream.readFully(bvalues, 0, count);
+
+ if (type == TIFFTag.TIFF_ASCII) {
+ // Can be multiple strings
+ ArrayList<String> v = new ArrayList<>();
+ boolean inString = false;
+ int prevIndex = 0;
+ for (int index = 0; index <= count; index++) {
+ if (index < count && bvalues[index] != 0) {
+ if (!inString) {
+ // start of string
+ prevIndex = index;
+ inString = true;
+ }
+ } else { // null or special case at end of string
+ if (inString) {
+ // end of string
+ String s = new String(bvalues, prevIndex,
+ index - prevIndex,
+ StandardCharsets.US_ASCII);
+ v.add(s);
+ inString = false;
+ }
+ }
+ }
+
+ count = v.size();
+ String[] strings;
+ if (count != 0) {
+ strings = new String[count];
+ for (int c = 0; c < count; c++) {
+ strings[c] = v.get(c);
+ }
+ } else {
+ // This case has been observed when the value of
+ // 'count' recorded in the field is non-zero but
+ // the value portion contains all nulls.
+ count = 1;
+ strings = new String[]{""};
+ }
+
+ obj = strings;
+ } else {
+ obj = bvalues;
+ }
+ break;
+
+ case TIFFTag.TIFF_SHORT:
+ char[] cvalues = new char[count];
+ for (int j = 0; j < count; j++) {
+ cvalues[j] = (char) (stream.readUnsignedShort());
+ }
+ obj = cvalues;
+ break;
+
+ case TIFFTag.TIFF_LONG:
+ case TIFFTag.TIFF_IFD_POINTER:
+ long[] lvalues = new long[count];
+ for (int j = 0; j < count; j++) {
+ lvalues[j] = stream.readUnsignedInt();
+ }
+ obj = lvalues;
+ break;
+
+ case TIFFTag.TIFF_RATIONAL:
+ long[][] llvalues = new long[count][2];
+ for (int j = 0; j < count; j++) {
+ llvalues[j][0] = stream.readUnsignedInt();
+ llvalues[j][1] = stream.readUnsignedInt();
+ }
+ obj = llvalues;
+ break;
+
+ case TIFFTag.TIFF_SSHORT:
+ short[] svalues = new short[count];
+ for (int j = 0; j < count; j++) {
+ svalues[j] = stream.readShort();
+ }
+ obj = svalues;
+ break;
+
+ case TIFFTag.TIFF_SLONG:
+ int[] ivalues = new int[count];
+ for (int j = 0; j < count; j++) {
+ ivalues[j] = stream.readInt();
+ }
+ obj = ivalues;
+ break;
+
+ case TIFFTag.TIFF_SRATIONAL:
+ int[][] iivalues = new int[count][2];
+ for (int j = 0; j < count; j++) {
+ iivalues[j][0] = stream.readInt();
+ iivalues[j][1] = stream.readInt();
+ }
+ obj = iivalues;
+ break;
+
+ case TIFFTag.TIFF_FLOAT:
+ float[] fvalues = new float[count];
+ for (int j = 0; j < count; j++) {
+ fvalues[j] = stream.readFloat();
+ }
+ obj = fvalues;
+ break;
+
+ case TIFFTag.TIFF_DOUBLE:
+ double[] dvalues = new double[count];
+ for (int j = 0; j < count; j++) {
+ dvalues[j] = stream.readDouble();
+ }
+ obj = dvalues;
+ break;
+
+ default:
+ obj = null;
+ break;
+ }
+
+ data[0] = obj;
+
+ return count;
+ }
+
+ //
+ // Class to represent an IFD entry where the actual content is at an offset
+ // in the stream somewhere outside the IFD itself. This occurs when the
+ // value cannot be contained within four bytes. Seeking is required to read
+ // such field values.
+ //
+ private static class TIFFIFDEntry {
+ public final TIFFTag tag;
+ public final int type;
+ public final int count;
+ public final long offset;
+
+ TIFFIFDEntry(TIFFTag tag, int type, int count, long offset) {
+ this.tag = tag;
+ this.type = type;
+ this.count = count;
+ this.offset = offset;
+ }
+ }
+
+ //
+ // Verify that data pointed to outside of the IFD itself are within the
+ // stream. To be called after all fields have been read and populated.
+ //
+ private void checkFieldOffsets(long streamLength) throws IIOException {
+ if (streamLength < 0) {
+ return;
+ }
+
+ // StripOffsets
+ List<TIFFField> offsets = new ArrayList<>();
+ TIFFField f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
+ int count = 0;
+ if (f != null) {
+ count = f.getCount();
+ offsets.add(f);
+ }
+
+ // TileOffsets
+ f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
+ if (f != null) {
+ int sz = offsets.size();
+ int newCount = f.getCount();
+ if (sz > 0 && newCount != count) {
+ throw new IIOException
+ ("StripOffsets count != TileOffsets count");
+ }
+
+ if (sz == 0) {
+ count = newCount;
+ }
+ offsets.add(f);
+ }
+
+ if (offsets.size() > 0) {
+ // StripByteCounts
+ List<TIFFField> byteCounts = new ArrayList<>();
+ f = getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
+ if (f != null) {
+ if (f.getCount() != count) {
+ throw new IIOException
+ ("StripByteCounts count != number of offsets");
+ }
+ byteCounts.add(f);
+ }
+
+ // TileByteCounts
+ f = getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS);
+ if (f != null) {
+ if (f.getCount() != count) {
+ throw new IIOException
+ ("TileByteCounts count != number of offsets");
+ }
+ byteCounts.add(f);
+ }
+
+ if (byteCounts.size() > 0) {
+ for (TIFFField offset : offsets) {
+ for (TIFFField byteCount : byteCounts) {
+ for (int i = 0; i < count; i++) {
+ long dataOffset = offset.getAsLong(i);
+ long dataByteCount = byteCount.getAsLong(i);
+ if (dataOffset + dataByteCount > streamLength) {
+ throw new IIOException
+ ("Data segment out of stream");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // JPEGInterchangeFormat and JPEGInterchangeFormatLength
+ TIFFField jpegOffset =
+ getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
+ if (jpegOffset != null) {
+ TIFFField jpegLength =
+ getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
+ if (jpegLength != null) {
+ if (jpegOffset.getAsLong(0) + jpegLength.getAsLong(0)
+ > streamLength) {
+ throw new IIOException
+ ("JPEGInterchangeFormat data out of stream");
+ }
+ }
+ }
+
+ // JPEGQTables - one 64-byte table for each offset.
+ f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES);
+ if (f != null) {
+ long[] tableOffsets = f.getAsLongs();
+ for (long off : tableOffsets) {
+ if (off + 64 > streamLength) {
+ throw new IIOException("JPEGQTables data out of stream");
+ }
+ }
+ }
+
+ // JPEGDCTables
+ f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_DC_TABLES);
+ if (f != null) {
+ long[] tableOffsets = f.getAsLongs();
+ for (long off : tableOffsets) {
+ if (off + 16 > streamLength) {
+ throw new IIOException("JPEGDCTables data out of stream");
+ }
+ }
+ }
+
+ // JPEGACTables
+ f = getTIFFField(BaselineTIFFTagSet.TAG_JPEG_AC_TABLES);
+ if (f != null) {
+ long[] tableOffsets = f.getAsLongs();
+ for (long off : tableOffsets) {
+ if (off + 16 > streamLength) {
+ throw new IIOException("JPEGACTables data out of stream");
+ }
+ }
+ }
+ }
+
+ // Stream position initially at beginning, left at end
+ // if ignoreUnknownFields is true, do not load fields for which
+ // a tag cannot be found in an allowed TagSet.
+ public void initialize(ImageInputStream stream, boolean isPrimaryIFD,
+ boolean ignoreUnknownFields) throws IOException {
+
+ removeTIFFFields();
+
+ long streamLength = stream.length();
+ boolean haveStreamLength = streamLength != -1;
+
+ List<TIFFTagSet> tagSetList = getTagSetList();
+
+ List<Object> entries = new ArrayList<>();
+ Object[] entryData = new Object[1]; // allocate once for later reuse.
+
+ // Read the IFD entries, loading the field values which are no more than
+ // four bytes long, and storing the 4-byte offsets for the others.
+ int numEntries = stream.readUnsignedShort();
+ for (int i = 0; i < numEntries; i++) {
+ // Read tag number, value type, and value count.
+ int tagNumber = stream.readUnsignedShort();
+ int type = stream.readUnsignedShort();
+ int count = (int)stream.readUnsignedInt();
+
+ // Get the associated TIFFTag.
+ TIFFTag tag = getTag(tagNumber, tagSetList);
+
+ // Ignore unknown fields.
+ if((tag == null && ignoreUnknownFields)
+ || (tag != null && !tag.isDataTypeOK(type))) {
+ // Skip the value/offset so as to leave the stream
+ // position at the start of the next IFD entry.
+ stream.skipBytes(4);
+
+ // Continue with the next IFD entry.
+ continue;
+ }
+
+ if (tag == null) {
+ tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, tagNumber,
+ 1 << type, count);
+ } else {
+ int expectedCount = tag.getCount();
+ if (expectedCount > 0) {
+ // If the tag count is positive then the tag defines a
+ // specific, fixed count that the field must match.
+ if (count != expectedCount) {
+ throw new IIOException("Unexpected count "
+ + count + " for " + tag.getName() + " field");
+ }
+ } else if (type == TIFFTag.TIFF_ASCII) {
+ // Clamp the size of ASCII fields of unspecified length
+ // to a maximum value.
+ int asciiSize = TIFFTag.getSizeOfType(TIFFTag.TIFF_ASCII);
+ if (count*asciiSize > MAX_ASCII_SIZE) {
+ count = (int)(MAX_ASCII_SIZE/asciiSize);
+ }
+ }
+ }
+
+ int size = count*TIFFTag.getSizeOfType(type);
+ if (size > 4 || tag.isIFDPointer()) {
+ // The IFD entry value is a pointer to the actual field value.
+ long offset = stream.readUnsignedInt();
+
+ // Check whether the the field value is within the stream.
+ if (haveStreamLength && offset + size > streamLength) {
+ throw new IIOException("Field data is past end-of-stream");
+ }
+
+ // Add a TIFFIFDEntry as a placeholder. This avoids a mark,
+ // seek to the data, and a reset.
+ entries.add(new TIFFIFDEntry(tag, type, count, offset));
+ } else {
+ // The IFD entry value is the actual field value of no more than
+ // four bytes.
+ Object obj = null;
+ try {
+ // Read the field value and update the count.
+ count = readFieldValue(stream, type, count, entryData);
+ obj = entryData[0];
+ } catch (EOFException eofe) {
+ // The TIFF 6.0 fields have tag numbers less than or equal
+ // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright).
+ // If there is an error reading a baseline tag, then re-throw
+ // the exception and fail; otherwise continue with the next
+ // field.
+ if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) {
+ throw eofe;
+ }
+ }
+
+ // If the field value is smaller than four bytes then skip
+ // the remaining, unused bytes.
+ if (size < 4) {
+ stream.skipBytes(4 - size);
+ }
+
+ // Add the populated TIFFField to the list of entries.
+ entries.add(new TIFFField(tag, type, count, obj));
+ }
+ }
+
+ // After reading the IFD entries the stream is positioned at an unsigned
+ // four byte integer containing either the offset of the next IFD or
+ // zero if this is the last IFD.
+ long nextIFDOffset = stream.getStreamPosition();
+
+ Object[] fieldData = new Object[1];
+ for (Object entry : entries) {
+ if (entry instanceof TIFFField) {
+ // Add the populated field directly.
+ addTIFFField((TIFFField)entry);
+ } else {
+ TIFFIFDEntry e = (TIFFIFDEntry)entry;
+ TIFFTag tag = e.tag;
+ int tagNumber = tag.getNumber();
+ int type = e.type;
+ int count = e.count;
+
+ stream.seek(e.offset);
+
+ if (tag.isIFDPointer()) {
+ List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1);
+ tagSets.add(tag.getTagSet());
+ TIFFIFD subIFD = new TIFFIFD(tagSets);
+
+ subIFD.initialize(stream, false, ignoreUnknownFields);
+ TIFFField f = new TIFFField(tag, type, e.offset, subIFD);
+ addTIFFField(f);
+ } else {
+ if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS
+ || tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS
+ || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) {
+ this.stripOrTileByteCountsPosition
+ = stream.getStreamPosition();
+ } else if (tagNumber == BaselineTIFFTagSet.TAG_STRIP_OFFSETS
+ || tagNumber == BaselineTIFFTagSet.TAG_TILE_OFFSETS
+ || tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) {
+ this.stripOrTileOffsetsPosition
+ = stream.getStreamPosition();
+ }
+
+ Object obj = null;
+ try {
+ count = readFieldValue(stream, type, count, fieldData);
+ obj = fieldData[0];
+ } catch (EOFException eofe) {
+ // The TIFF 6.0 fields have tag numbers less than or equal
+ // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright).
+ // If there is an error reading a baseline tag, then re-throw
+ // the exception and fail; otherwise continue with the next
+ // field.
+ if (BaselineTIFFTagSet.getInstance().getTag(tagNumber) == null) {
+ throw eofe;
+ }
+ }
+
+ if (obj == null) {
+ continue;
+ }
+
+ TIFFField f = new TIFFField(tag, type, count, obj);
+ addTIFFField(f);
+ }
+ }
+ }
+
+ if(isPrimaryIFD && haveStreamLength) {
+ checkFieldOffsets(streamLength);
+ }
+
+ stream.seek(nextIFDOffset);
+ this.lastPosition = stream.getStreamPosition();
+ }
+
+ public void writeToStream(ImageOutputStream stream)
+ throws IOException {
+
+ int numFields = getNumTIFFFields();
+ stream.writeShort(numFields);
+
+ long nextSpace = stream.getStreamPosition() + 12*numFields + 4;
+
+ Iterator<TIFFField> iter = iterator();
+ while (iter.hasNext()) {
+ TIFFField f = iter.next();
+
+ TIFFTag tag = f.getTag();
+
+ int type = f.getType();
+ int count = f.getCount();
+
+ // Deal with unknown tags
+ if (type == 0) {
+ type = TIFFTag.TIFF_UNDEFINED;
+ }
+ int size = count*TIFFTag.getSizeOfType(type);
+
+ if (type == TIFFTag.TIFF_ASCII) {
+ int chars = 0;
+ for (int i = 0; i < count; i++) {
+ chars += f.getAsString(i).length() + 1;
+ }
+ count = chars;
+ size = count;
+ }
+
+ int tagNumber = f.getTagNumber();
+ stream.writeShort(tagNumber);
+ stream.writeShort(type);
+ stream.writeInt(count);
+
+ // Write a dummy value to fill space
+ stream.writeInt(0);
+ stream.mark(); // Mark beginning of next field
+ stream.skipBytes(-4);
+
+ long pos;
+
+ if (size > 4 || tag.isIFDPointer()) {
+ // Ensure IFD or value is written on a word boundary
+ nextSpace = (nextSpace + 3) & ~0x3;
+
+ stream.writeInt((int)nextSpace);
+ stream.seek(nextSpace);
+ pos = nextSpace;
+
+ if (tag.isIFDPointer() && f.hasDirectory()) {
+ TIFFIFD subIFD = (TIFFIFD)f.getDirectory();
+ subIFD.writeToStream(stream);
+ nextSpace = subIFD.lastPosition;
+ } else {
+ writeTIFFFieldToStream(f, stream);
+ nextSpace = stream.getStreamPosition();
+ }
+ } else {
+ pos = stream.getStreamPosition();
+ writeTIFFFieldToStream(f, stream);
+ }
+
+ // If we are writing the data for the
+ // StripByteCounts, TileByteCounts, StripOffsets,
+ // TileOffsets, JPEGInterchangeFormat, or
+ // JPEGInterchangeFormatLength fields, record the current stream
+ // position for backpatching
+ if (tagNumber ==
+ BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS ||
+ tagNumber == BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS ||
+ tagNumber == BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) {
+ this.stripOrTileByteCountsPosition = pos;
+ } else if (tagNumber ==
+ BaselineTIFFTagSet.TAG_STRIP_OFFSETS ||
+ tagNumber ==
+ BaselineTIFFTagSet.TAG_TILE_OFFSETS ||
+ tagNumber ==
+ BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) {
+ this.stripOrTileOffsetsPosition = pos;
+ }
+
+ stream.reset(); // Go to marked position of next field
+ }
+
+ this.lastPosition = nextSpace;
+ }
+
+ public long getStripOrTileByteCountsPosition() {
+ return stripOrTileByteCountsPosition;
+ }
+
+ public long getStripOrTileOffsetsPosition() {
+ return stripOrTileOffsetsPosition;
+ }
+
+ public long getLastPosition() {
+ return lastPosition;
+ }
+
+ void setPositions(long stripOrTileOffsetsPosition,
+ long stripOrTileByteCountsPosition,
+ long lastPosition) {
+ this.stripOrTileOffsetsPosition = stripOrTileOffsetsPosition;
+ this.stripOrTileByteCountsPosition = stripOrTileByteCountsPosition;
+ this.lastPosition = lastPosition;
+ }
+
+ /**
+ * Returns a <code>TIFFIFD</code> wherein all fields from the
+ * <code>BaselineTIFFTagSet</code> are copied by value and all other
+ * fields copied by reference.
+ */
+ public TIFFIFD getShallowClone() {
+ // Get the baseline TagSet.
+ TIFFTagSet baselineTagSet = BaselineTIFFTagSet.getInstance();
+
+ // If the baseline TagSet is not included just return.
+ List<TIFFTagSet> tagSetList = getTagSetList();
+ if(!tagSetList.contains(baselineTagSet)) {
+ return this;
+ }
+
+ // Create a new object.
+ TIFFIFD shallowClone = new TIFFIFD(tagSetList, getParentTag());
+
+ // Get the tag numbers in the baseline set.
+ Set<Integer> baselineTagNumbers = baselineTagSet.getTagNumbers();
+
+ // Iterate over the fields in this IFD.
+ Iterator<TIFFField> fields = iterator();
+ while(fields.hasNext()) {
+ // Get the next field.
+ TIFFField field = fields.next();
+
+ // Get its tag number.
+ Integer tagNumber = Integer.valueOf(field.getTagNumber());
+
+ // Branch based on membership in baseline set.
+ TIFFField fieldClone;
+ if(baselineTagNumbers.contains(tagNumber)) {
+ // Copy by value.
+ Object fieldData = field.getData();
+
+ int fieldType = field.getType();
+
+ try {
+ switch (fieldType) {
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_SBYTE:
+ case TIFFTag.TIFF_UNDEFINED:
+ fieldData = ((byte[])fieldData).clone();
+ break;
+ case TIFFTag.TIFF_ASCII:
+ fieldData = ((String[])fieldData).clone();
+ break;
+ case TIFFTag.TIFF_SHORT:
+ fieldData = ((char[])fieldData).clone();
+ break;
+ case TIFFTag.TIFF_LONG:
+ case TIFFTag.TIFF_IFD_POINTER:
+ fieldData = ((long[])fieldData).clone();
+ break;
+ case TIFFTag.TIFF_RATIONAL:
+ fieldData = ((long[][])fieldData).clone();
+ break;
+ case TIFFTag.TIFF_SSHORT:
+ fieldData = ((short[])fieldData).clone();
+ break;
+ case TIFFTag.TIFF_SLONG:
+ fieldData = ((int[])fieldData).clone();
+ break;
+ case TIFFTag.TIFF_SRATIONAL:
+ fieldData = ((int[][])fieldData).clone();
+ break;
+ case TIFFTag.TIFF_FLOAT:
+ fieldData = ((float[])fieldData).clone();
+ break;
+ case TIFFTag.TIFF_DOUBLE:
+ fieldData = ((double[])fieldData).clone();
+ break;
+ default:
+ // Shouldn't happen but do nothing ...
+ }
+ } catch(Exception e) {
+ // Ignore it and copy by reference ...
+ }
+
+ fieldClone = new TIFFField(field.getTag(), fieldType,
+ field.getCount(), fieldData);
+ } else {
+ // Copy by reference.
+ fieldClone = field;
+ }
+
+ // Add the field to the clone.
+ shallowClone.addTIFFField(fieldClone);
+ }
+
+ // Set positions.
+ shallowClone.setPositions(stripOrTileOffsetsPosition,
+ stripOrTileByteCountsPosition,
+ lastPosition);
+
+ return shallowClone;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageMetadata.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,1630 @@
+/*
+ * 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.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOInvalidTreeException;
+import javax.imageio.metadata.IIOMetadataFormatImpl;
+import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.stream.ImageInputStream;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.plugins.tiff.ExifParentTIFFTagSet;
+import javax.imageio.plugins.tiff.TIFFField;
+import javax.imageio.plugins.tiff.TIFFTag;
+import javax.imageio.plugins.tiff.TIFFTagSet;
+
+public class TIFFImageMetadata extends IIOMetadata {
+
+ // package scope
+
+ public static final String NATIVE_METADATA_FORMAT_NAME =
+ "javax_imageio_tiff_image_1.0";
+
+ public static final String NATIVE_METADATA_FORMAT_CLASS_NAME =
+ "javax.imageio.plugins.tiff.TIFFImageMetadataFormat";
+
+ private List<TIFFTagSet> tagSets;
+
+ TIFFIFD rootIFD;
+
+ public TIFFImageMetadata(List<TIFFTagSet> tagSets) {
+ super(true,
+ NATIVE_METADATA_FORMAT_NAME,
+ NATIVE_METADATA_FORMAT_CLASS_NAME,
+ null, null);
+
+ this.tagSets = tagSets;
+ this.rootIFD = new TIFFIFD(tagSets);
+ }
+
+ public TIFFImageMetadata(TIFFIFD ifd) {
+ super(true,
+ NATIVE_METADATA_FORMAT_NAME,
+ NATIVE_METADATA_FORMAT_CLASS_NAME,
+ null, null);
+ this.tagSets = ifd.getTagSetList();
+ this.rootIFD = ifd;
+ }
+
+ public void initializeFromStream(ImageInputStream stream,
+ boolean ignoreUnknownFields)
+ throws IOException {
+ rootIFD.initialize(stream, true, ignoreUnknownFields);
+ }
+
+ public void addShortOrLongField(int tagNumber, int value) {
+ TIFFField field = new TIFFField(rootIFD.getTag(tagNumber), value);
+ rootIFD.addTIFFField(field);
+ }
+
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ private Node getIFDAsTree(TIFFIFD ifd,
+ String parentTagName, int parentTagNumber) {
+ IIOMetadataNode IFDRoot = new IIOMetadataNode("TIFFIFD");
+ if (parentTagNumber != 0) {
+ IFDRoot.setAttribute("parentTagNumber",
+ Integer.toString(parentTagNumber));
+ }
+ if (parentTagName != null) {
+ IFDRoot.setAttribute("parentTagName", parentTagName);
+ }
+
+ List<TIFFTagSet> tagSets = ifd.getTagSetList();
+ if (tagSets.size() > 0) {
+ Iterator<TIFFTagSet> iter = tagSets.iterator();
+ StringBuilder tagSetNames = new StringBuilder();
+ while (iter.hasNext()) {
+ TIFFTagSet tagSet = iter.next();
+ tagSetNames.append(tagSet.getClass().getName());
+ if (iter.hasNext()) {
+ tagSetNames.append(",");
+ }
+ }
+
+ IFDRoot.setAttribute("tagSets", tagSetNames.toString());
+ }
+
+ Iterator<TIFFField> iter = ifd.iterator();
+ while (iter.hasNext()) {
+ TIFFField f = iter.next();
+ int tagNumber = f.getTagNumber();
+ TIFFTag tag = TIFFIFD.getTag(tagNumber, tagSets);
+
+ Node node = null;
+ if (tag == null) {
+ node = f.getAsNativeNode();
+ } else if (tag.isIFDPointer() && f.hasDirectory()) {
+ TIFFIFD subIFD = (TIFFIFD)f.getDirectory();
+
+ // Recurse
+ node = getIFDAsTree(subIFD, tag.getName(), tag.getNumber());
+ } else {
+ node = f.getAsNativeNode();
+ }
+
+ if (node != null) {
+ IFDRoot.appendChild(node);
+ }
+ }
+
+ return IFDRoot;
+ }
+
+ public Node getAsTree(String formatName) {
+ if (formatName.equals(nativeMetadataFormatName)) {
+ return getNativeTree();
+ } else if (formatName.equals
+ (IIOMetadataFormatImpl.standardMetadataFormatName)) {
+ return getStandardTree();
+ } else {
+ throw new IllegalArgumentException("Not a recognized format!");
+ }
+ }
+
+ private Node getNativeTree() {
+ IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName);
+
+ Node IFDNode = getIFDAsTree(rootIFD, null, 0);
+ root.appendChild(IFDNode);
+
+ return root;
+ }
+
+ private static final String[] colorSpaceNames = {
+ "GRAY", // WhiteIsZero
+ "GRAY", // BlackIsZero
+ "RGB", // RGB
+ "RGB", // PaletteColor
+ "GRAY", // TransparencyMask
+ "CMYK", // CMYK
+ "YCbCr", // YCbCr
+ "Lab", // CIELab
+ "Lab", // ICCLab
+ };
+
+ public IIOMetadataNode getStandardChromaNode() {
+ IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ // Set the PhotometricInterpretation and the palette color flag.
+ int photometricInterpretation = -1;
+ boolean isPaletteColor = false;
+ f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ if (f != null) {
+ photometricInterpretation = f.getAsInt(0);
+
+ isPaletteColor =
+ photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR;
+ }
+
+ // Determine the number of channels.
+ int numChannels = -1;
+ if(isPaletteColor) {
+ numChannels = 3;
+ } else {
+ f = getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
+ if (f != null) {
+ numChannels = f.getAsInt(0);
+ } else { // f == null
+ f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ if(f != null) {
+ numChannels = f.getCount();
+ }
+ }
+ }
+
+ if(photometricInterpretation != -1) {
+ if (photometricInterpretation >= 0 &&
+ photometricInterpretation < colorSpaceNames.length) {
+ node = new IIOMetadataNode("ColorSpaceType");
+ String csName;
+ if(photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CMYK &&
+ numChannels == 3) {
+ csName = "CMY";
+ } else {
+ csName = colorSpaceNames[photometricInterpretation];
+ }
+ node.setAttribute("name", csName);
+ chroma_node.appendChild(node);
+ }
+
+ node = new IIOMetadataNode("BlackIsZero");
+ node.setAttribute("value",
+ (photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO)
+ ? "FALSE" : "TRUE");
+ chroma_node.appendChild(node);
+ }
+
+ if(numChannels != -1) {
+ node = new IIOMetadataNode("NumChannels");
+ node.setAttribute("value", Integer.toString(numChannels));
+ chroma_node.appendChild(node);
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_COLOR_MAP);
+ if (f != null) {
+ // NOTE: The presence of hasAlpha is vestigial: there is
+ // no way in TIFF to represent an alpha component in a palette
+ // color image. See bug 5086341.
+ boolean hasAlpha = false;
+
+ node = new IIOMetadataNode("Palette");
+ int len = f.getCount()/(hasAlpha ? 4 : 3);
+ for (int i = 0; i < len; i++) {
+ IIOMetadataNode entry =
+ new IIOMetadataNode("PaletteEntry");
+ entry.setAttribute("index", Integer.toString(i));
+
+ int r = (f.getAsInt(i)*255)/65535;
+ int g = (f.getAsInt(len + i)*255)/65535;
+ int b = (f.getAsInt(2*len + i)*255)/65535;
+
+ entry.setAttribute("red", Integer.toString(r));
+ entry.setAttribute("green", Integer.toString(g));
+ entry.setAttribute("blue", Integer.toString(b));
+ if (hasAlpha) {
+ int alpha = 0;
+ entry.setAttribute("alpha", Integer.toString(alpha));
+ }
+ node.appendChild(entry);
+ }
+ chroma_node.appendChild(node);
+ }
+
+ return chroma_node;
+ }
+
+ public IIOMetadataNode getStandardCompressionNode() {
+ IIOMetadataNode compression_node = new IIOMetadataNode("Compression");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
+ if (f != null) {
+ String compressionTypeName = null;
+ int compression = f.getAsInt(0);
+ boolean isLossless = true; // obligate initialization.
+ if(compression == BaselineTIFFTagSet.COMPRESSION_NONE) {
+ compressionTypeName = "None";
+ isLossless = true;
+ } else {
+ int[] compressionNumbers = TIFFImageWriter.compressionNumbers;
+ for(int i = 0; i < compressionNumbers.length; i++) {
+ if(compression == compressionNumbers[i]) {
+ compressionTypeName =
+ TIFFImageWriter.compressionTypes[i];
+ isLossless =
+ TIFFImageWriter.isCompressionLossless[i];
+ break;
+ }
+ }
+ }
+
+ if (compressionTypeName != null) {
+ node = new IIOMetadataNode("CompressionTypeName");
+ node.setAttribute("value", compressionTypeName);
+ compression_node.appendChild(node);
+
+ node = new IIOMetadataNode("Lossless");
+ node.setAttribute("value", isLossless ? "TRUE" : "FALSE");
+ compression_node.appendChild(node);
+ }
+ }
+
+ node = new IIOMetadataNode("NumProgressiveScans");
+ node.setAttribute("value", "1");
+ compression_node.appendChild(node);
+
+ return compression_node;
+ }
+
+ private String repeat(String s, int times) {
+ if (times == 1) {
+ return s;
+ }
+ StringBuffer sb = new StringBuffer((s.length() + 1)*times - 1);
+ sb.append(s);
+ for (int i = 1; i < times; i++) {
+ sb.append(" ");
+ sb.append(s);
+ }
+ return sb.toString();
+ }
+
+ public IIOMetadataNode getStandardDataNode() {
+ IIOMetadataNode data_node = new IIOMetadataNode("Data");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ boolean isPaletteColor = false;
+ f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ if (f != null) {
+ isPaletteColor =
+ f.getAsInt(0) ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR;
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION);
+ String planarConfiguration = "PixelInterleaved";
+ if (f != null &&
+ f.getAsInt(0) == BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) {
+ planarConfiguration = "PlaneInterleaved";
+ }
+
+ node = new IIOMetadataNode("PlanarConfiguration");
+ node.setAttribute("value", planarConfiguration);
+ data_node.appendChild(node);
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ if (f != null) {
+ int photometricInterpretation = f.getAsInt(0);
+ String sampleFormat = "UnsignedIntegral";
+
+ if (photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR) {
+ sampleFormat = "Index";
+ } else {
+ f = getTIFFField(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
+ if (f != null) {
+ int format = f.getAsInt(0);
+ if (format ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER) {
+ sampleFormat = "SignedIntegral";
+ } else if (format ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER) {
+ sampleFormat = "UnsignedIntegral";
+ } else if (format ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ sampleFormat = "Real";
+ } else {
+ sampleFormat = null; // don't know
+ }
+ }
+ }
+ if (sampleFormat != null) {
+ node = new IIOMetadataNode("SampleFormat");
+ node.setAttribute("value", sampleFormat);
+ data_node.appendChild(node);
+ }
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ int[] bitsPerSample = null;
+ if(f != null) {
+ bitsPerSample = f.getAsInts();
+ } else {
+ f = getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
+ int compression = f != null ?
+ f.getAsInt(0) : BaselineTIFFTagSet.COMPRESSION_NONE;
+ if(getTIFFField(ExifParentTIFFTagSet.TAG_EXIF_IFD_POINTER) !=
+ null ||
+ compression == BaselineTIFFTagSet.COMPRESSION_JPEG ||
+ compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG ||
+ getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT) !=
+ null) {
+ f = getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ if(f != null &&
+ (f.getAsInt(0) ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO ||
+ f.getAsInt(0) ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO)) {
+ bitsPerSample = new int[] {8};
+ } else {
+ bitsPerSample = new int[] {8, 8, 8};
+ }
+ } else {
+ bitsPerSample = new int[] {1};
+ }
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < bitsPerSample.length; i++) {
+ if (i > 0) {
+ sb.append(" ");
+ }
+ sb.append(Integer.toString(bitsPerSample[i]));
+ }
+ node = new IIOMetadataNode("BitsPerSample");
+ if(isPaletteColor) {
+ node.setAttribute("value", repeat(sb.toString(), 3));
+ } else {
+ node.setAttribute("value", sb.toString());
+ }
+ data_node.appendChild(node);
+
+ // SampleMSB
+ f = getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER);
+ int fillOrder = f != null ?
+ f.getAsInt(0) : BaselineTIFFTagSet.FILL_ORDER_LEFT_TO_RIGHT;
+ sb = new StringBuffer();
+ for (int i = 0; i < bitsPerSample.length; i++) {
+ if (i > 0) {
+ sb.append(" ");
+ }
+ int maxBitIndex = bitsPerSample[i] == 1 ?
+ 7 : bitsPerSample[i] - 1;
+ int msb =
+ fillOrder == BaselineTIFFTagSet.FILL_ORDER_LEFT_TO_RIGHT ?
+ maxBitIndex : 0;
+ sb.append(Integer.toString(msb));
+ }
+ node = new IIOMetadataNode("SampleMSB");
+ if(isPaletteColor) {
+ node.setAttribute("value", repeat(sb.toString(), 3));
+ } else {
+ node.setAttribute("value", sb.toString());
+ }
+ data_node.appendChild(node);
+
+ return data_node;
+ }
+
+ private static final String[] orientationNames = {
+ null,
+ "Normal",
+ "FlipH",
+ "Rotate180",
+ "FlipV",
+ "FlipHRotate90",
+ "Rotate270",
+ "FlipVRotate90",
+ "Rotate90",
+ };
+
+ public IIOMetadataNode getStandardDimensionNode() {
+ IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ long[] xres = null;
+ long[] yres = null;
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_X_RESOLUTION);
+ if (f != null) {
+ xres = f.getAsRational(0).clone();
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
+ if (f != null) {
+ yres = f.getAsRational(0).clone();
+ }
+
+ if (xres != null && yres != null) {
+ node = new IIOMetadataNode("PixelAspectRatio");
+
+ // Compute (1/xres)/(1/yres)
+ // (xres_denom/xres_num)/(yres_denom/yres_num) =
+ // (xres_denom/xres_num)*(yres_num/yres_denom) =
+ // (xres_denom*yres_num)/(xres_num*yres_denom)
+ float ratio = (float)((double)xres[1]*yres[0])/(xres[0]*yres[1]);
+ node.setAttribute("value", Float.toString(ratio));
+ dimension_node.appendChild(node);
+ }
+
+ if (xres != null || yres != null) {
+ // Get unit field.
+ f = getTIFFField(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT);
+
+ // Set resolution unit.
+ int resolutionUnit = f != null ?
+ f.getAsInt(0) : BaselineTIFFTagSet.RESOLUTION_UNIT_INCH;
+
+ // Have size if either centimeters or inches.
+ boolean gotPixelSize =
+ resolutionUnit != BaselineTIFFTagSet.RESOLUTION_UNIT_NONE;
+
+ // Convert pixels/inch to pixels/centimeter.
+ if (resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH) {
+ // Divide xres by 2.54
+ if (xres != null) {
+ xres[0] *= 100;
+ xres[1] *= 254;
+ }
+
+ // Divide yres by 2.54
+ if (yres != null) {
+ yres[0] *= 100;
+ yres[1] *= 254;
+ }
+ }
+
+ if (gotPixelSize) {
+ if (xres != null) {
+ float horizontalPixelSize = (float)(10.0*xres[1]/xres[0]);
+ node = new IIOMetadataNode("HorizontalPixelSize");
+ node.setAttribute("value",
+ Float.toString(horizontalPixelSize));
+ dimension_node.appendChild(node);
+ }
+
+ if (yres != null) {
+ float verticalPixelSize = (float)(10.0*yres[1]/yres[0]);
+ node = new IIOMetadataNode("VerticalPixelSize");
+ node.setAttribute("value",
+ Float.toString(verticalPixelSize));
+ dimension_node.appendChild(node);
+ }
+ }
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT);
+ int resolutionUnit = f != null ?
+ f.getAsInt(0) : BaselineTIFFTagSet.RESOLUTION_UNIT_INCH;
+ if(resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH ||
+ resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_CENTIMETER) {
+ f = getTIFFField(BaselineTIFFTagSet.TAG_X_POSITION);
+ if(f != null) {
+ long[] xpos = f.getAsRational(0);
+ float xPosition = (float)xpos[0]/(float)xpos[1];
+ // Convert to millimeters.
+ if(resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH) {
+ xPosition *= 254F;
+ } else {
+ xPosition *= 10F;
+ }
+ node = new IIOMetadataNode("HorizontalPosition");
+ node.setAttribute("value",
+ Float.toString(xPosition));
+ dimension_node.appendChild(node);
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_Y_POSITION);
+ if(f != null) {
+ long[] ypos = f.getAsRational(0);
+ float yPosition = (float)ypos[0]/(float)ypos[1];
+ // Convert to millimeters.
+ if(resolutionUnit == BaselineTIFFTagSet.RESOLUTION_UNIT_INCH) {
+ yPosition *= 254F;
+ } else {
+ yPosition *= 10F;
+ }
+ node = new IIOMetadataNode("VerticalPosition");
+ node.setAttribute("value",
+ Float.toString(yPosition));
+ dimension_node.appendChild(node);
+ }
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_ORIENTATION);
+ if (f != null) {
+ int o = f.getAsInt(0);
+ if (o >= 0 && o < orientationNames.length) {
+ node = new IIOMetadataNode("ImageOrientation");
+ node.setAttribute("value", orientationNames[o]);
+ dimension_node.appendChild(node);
+ }
+ }
+
+ return dimension_node;
+ }
+
+ public IIOMetadataNode getStandardDocumentNode() {
+ IIOMetadataNode document_node = new IIOMetadataNode("Document");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ node = new IIOMetadataNode("FormatVersion");
+ node.setAttribute("value", "6.0");
+ document_node.appendChild(node);
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_NEW_SUBFILE_TYPE);
+ if(f != null) {
+ int newSubFileType = f.getAsInt(0);
+ String value = null;
+ if((newSubFileType &
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_TRANSPARENCY) != 0) {
+ value = "TransparencyMask";
+ } else if((newSubFileType &
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_REDUCED_RESOLUTION) != 0) {
+ value = "ReducedResolution";
+ } else if((newSubFileType &
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_SINGLE_PAGE) != 0) {
+ value = "SinglePage";
+ }
+ if(value != null) {
+ node = new IIOMetadataNode("SubimageInterpretation");
+ node.setAttribute("value", value);
+ document_node.appendChild(node);
+ }
+ }
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_DATE_TIME);
+ if (f != null) {
+ String s = f.getAsString(0);
+
+ // DateTime should be formatted as "YYYY:MM:DD hh:mm:ss".
+ if(s.length() == 19) {
+ node = new IIOMetadataNode("ImageCreationTime");
+
+ // Files with incorrect DateTime format have been
+ // observed so anticipate an exception from substring()
+ // and only add the node if the format is presumably
+ // correct.
+ boolean appendNode;
+ try {
+ node.setAttribute("year", s.substring(0, 4));
+ node.setAttribute("month", s.substring(5, 7));
+ node.setAttribute("day", s.substring(8, 10));
+ node.setAttribute("hour", s.substring(11, 13));
+ node.setAttribute("minute", s.substring(14, 16));
+ node.setAttribute("second", s.substring(17, 19));
+ appendNode = true;
+ } catch(IndexOutOfBoundsException e) {
+ appendNode = false;
+ }
+
+ if(appendNode) {
+ document_node.appendChild(node);
+ }
+ }
+ }
+
+ return document_node;
+ }
+
+ public IIOMetadataNode getStandardTextNode() {
+ IIOMetadataNode text_node = null;
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ int[] textFieldTagNumbers = new int[] {
+ BaselineTIFFTagSet.TAG_DOCUMENT_NAME,
+ BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION,
+ BaselineTIFFTagSet.TAG_MAKE,
+ BaselineTIFFTagSet.TAG_MODEL,
+ BaselineTIFFTagSet.TAG_PAGE_NAME,
+ BaselineTIFFTagSet.TAG_SOFTWARE,
+ BaselineTIFFTagSet.TAG_ARTIST,
+ BaselineTIFFTagSet.TAG_HOST_COMPUTER,
+ BaselineTIFFTagSet.TAG_INK_NAMES,
+ BaselineTIFFTagSet.TAG_COPYRIGHT
+ };
+
+ for(int i = 0; i < textFieldTagNumbers.length; i++) {
+ f = getTIFFField(textFieldTagNumbers[i]);
+ if(f != null) {
+ String value = f.getAsString(0);
+ if(text_node == null) {
+ text_node = new IIOMetadataNode("Text");
+ }
+ node = new IIOMetadataNode("TextEntry");
+ node.setAttribute("keyword", f.getTag().getName());
+ node.setAttribute("value", value);
+ text_node.appendChild(node);
+ }
+ }
+
+ return text_node;
+ }
+
+ public IIOMetadataNode getStandardTransparencyNode() {
+ IIOMetadataNode transparency_node =
+ new IIOMetadataNode("Transparency");
+ IIOMetadataNode node = null; // scratch node
+
+ TIFFField f;
+
+ node = new IIOMetadataNode("Alpha");
+ String value = "none";
+
+ f = getTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES);
+ if(f != null) {
+ int[] extraSamples = f.getAsInts();
+ for(int i = 0; i < extraSamples.length; i++) {
+ if(extraSamples[i] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA) {
+ value = "premultiplied";
+ break;
+ } else if(extraSamples[i] ==
+ BaselineTIFFTagSet.EXTRA_SAMPLES_UNASSOCIATED_ALPHA) {
+ value = "nonpremultiplied";
+ break;
+ }
+ }
+ }
+
+ node.setAttribute("value", value);
+ transparency_node.appendChild(node);
+
+ return transparency_node;
+ }
+
+ // Shorthand for throwing an IIOInvalidTreeException
+ private static void fatal(Node node, String reason)
+ throws IIOInvalidTreeException {
+ throw new IIOInvalidTreeException(reason, node);
+ }
+
+ private int[] listToIntArray(String list) {
+ StringTokenizer st = new StringTokenizer(list, " ");
+ ArrayList<Integer> intList = new ArrayList<Integer>();
+ while (st.hasMoreTokens()) {
+ String nextInteger = st.nextToken();
+ Integer nextInt = Integer.valueOf(nextInteger);
+ intList.add(nextInt);
+ }
+
+ int[] intArray = new int[intList.size()];
+ for(int i = 0; i < intArray.length; i++) {
+ intArray[i] = intList.get(i);
+ }
+
+ return intArray;
+ }
+
+ private char[] listToCharArray(String list) {
+ StringTokenizer st = new StringTokenizer(list, " ");
+ ArrayList<Integer> intList = new ArrayList<Integer>();
+ while (st.hasMoreTokens()) {
+ String nextInteger = st.nextToken();
+ Integer nextInt = Integer.valueOf(nextInteger);
+ intList.add(nextInt);
+ }
+
+ char[] charArray = new char[intList.size()];
+ for(int i = 0; i < charArray.length; i++) {
+ charArray[i] = (char)(intList.get(i).intValue());
+ }
+
+ return charArray;
+ }
+
+ private void mergeStandardTree(Node root)
+ throws IIOInvalidTreeException {
+ TIFFField f;
+ TIFFTag tag;
+
+ Node node = root;
+ if (!node.getNodeName()
+ .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
+ fatal(node, "Root must be " +
+ IIOMetadataFormatImpl.standardMetadataFormatName);
+ }
+
+ // Obtain the sample format and set the palette flag if appropriate.
+ String sampleFormat = null;
+ Node dataNode = getChildNode(root, "Data");
+ boolean isPaletteColor = false;
+ if(dataNode != null) {
+ Node sampleFormatNode = getChildNode(dataNode, "SampleFormat");
+ if(sampleFormatNode != null) {
+ sampleFormat = getAttribute(sampleFormatNode, "value");
+ isPaletteColor = sampleFormat.equals("Index");
+ }
+ }
+
+ // If palette flag not set check for palette.
+ if(!isPaletteColor) {
+ Node chromaNode = getChildNode(root, "Chroma");
+ if(chromaNode != null &&
+ getChildNode(chromaNode, "Palette") != null) {
+ isPaletteColor = true;
+ }
+ }
+
+ node = node.getFirstChild();
+ while (node != null) {
+ String name = node.getNodeName();
+
+ if (name.equals("Chroma")) {
+ String colorSpaceType = null;
+ String blackIsZero = null;
+ boolean gotPalette = false;
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+ if (childName.equals("ColorSpaceType")) {
+ colorSpaceType = getAttribute(child, "name");
+ } else if (childName.equals("NumChannels")) {
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
+ int samplesPerPixel = isPaletteColor ?
+ 1 : Integer.parseInt(getAttribute(child, "value"));
+ f = new TIFFField(tag, samplesPerPixel);
+ rootIFD.addTIFFField(f);
+ } else if (childName.equals("BlackIsZero")) {
+ blackIsZero = getAttribute(child, "value");
+ } else if (childName.equals("Palette")) {
+ Node entry = child.getFirstChild();
+ HashMap<Integer,char[]> palette = new HashMap<>();
+ int maxIndex = -1;
+ while(entry != null) {
+ String entryName = entry.getNodeName();
+ if(entryName.equals("PaletteEntry")) {
+ String idx = getAttribute(entry, "index");
+ int id = Integer.parseInt(idx);
+ if(id > maxIndex) {
+ maxIndex = id;
+ }
+ char red =
+ (char)Integer.parseInt(getAttribute(entry,
+ "red"));
+ char green =
+ (char)Integer.parseInt(getAttribute(entry,
+ "green"));
+ char blue =
+ (char)Integer.parseInt(getAttribute(entry,
+ "blue"));
+ palette.put(Integer.valueOf(id),
+ new char[] {red, green, blue});
+
+ gotPalette = true;
+ }
+ entry = entry.getNextSibling();
+ }
+
+ if(gotPalette) {
+ int mapSize = maxIndex + 1;
+ int paletteLength = 3*mapSize;
+ char[] paletteEntries = new char[paletteLength];
+ Iterator<Map.Entry<Integer,char[]>> paletteIter
+ = palette.entrySet().iterator();
+ while(paletteIter.hasNext()) {
+ Map.Entry<Integer,char[]> paletteEntry
+ = paletteIter.next();
+ int index = paletteEntry.getKey();
+ char[] rgb = paletteEntry.getValue();
+ paletteEntries[index] =
+ (char)((rgb[0]*65535)/255);
+ paletteEntries[mapSize + index] =
+ (char)((rgb[1]*65535)/255);
+ paletteEntries[2*mapSize + index] =
+ (char)((rgb[2]*65535)/255);
+ }
+
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_COLOR_MAP);
+ f = new TIFFField(tag, TIFFTag.TIFF_SHORT,
+ paletteLength, paletteEntries);
+ rootIFD.addTIFFField(f);
+ }
+ }
+
+ child = child.getNextSibling();
+ }
+
+ int photometricInterpretation = -1;
+ if((colorSpaceType == null || colorSpaceType.equals("GRAY")) &&
+ blackIsZero != null &&
+ blackIsZero.equalsIgnoreCase("FALSE")) {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
+ } else if(colorSpaceType != null) {
+ if(colorSpaceType.equals("GRAY")) {
+ boolean isTransparency = false;
+ if(root instanceof IIOMetadataNode) {
+ IIOMetadataNode iioRoot = (IIOMetadataNode)root;
+ NodeList siNodeList =
+ iioRoot.getElementsByTagName("SubimageInterpretation");
+ if(siNodeList.getLength() == 1) {
+ Node siNode = siNodeList.item(0);
+ String value = getAttribute(siNode, "value");
+ if(value.equals("TransparencyMask")) {
+ isTransparency = true;
+ }
+ }
+ }
+ if(isTransparency) {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_TRANSPARENCY_MASK;
+ } else {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
+ }
+ } else if(colorSpaceType.equals("RGB")) {
+ photometricInterpretation =
+ gotPalette ?
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR :
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB;
+ } else if(colorSpaceType.equals("YCbCr")) {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR;
+ } else if(colorSpaceType.equals("CMYK")) {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CMYK;
+ } else if(colorSpaceType.equals("Lab")) {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB;
+ }
+ }
+
+ if(photometricInterpretation != -1) {
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ f = new TIFFField(tag, photometricInterpretation);
+ rootIFD.addTIFFField(f);
+ }
+ } else if (name.equals("Compression")) {
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+ if (childName.equals("CompressionTypeName")) {
+ int compression = -1;
+ String compressionTypeName =
+ getAttribute(child, "value");
+ if(compressionTypeName.equalsIgnoreCase("None")) {
+ compression =
+ BaselineTIFFTagSet.COMPRESSION_NONE;
+ } else {
+ String[] compressionNames =
+ TIFFImageWriter.compressionTypes;
+ for(int i = 0; i < compressionNames.length; i++) {
+ if(compressionNames[i].equalsIgnoreCase(compressionTypeName)) {
+ compression =
+ TIFFImageWriter.compressionNumbers[i];
+ break;
+ }
+ }
+ }
+
+ if(compression != -1) {
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_COMPRESSION);
+ f = new TIFFField(tag, compression);
+ rootIFD.addTIFFField(f);
+
+ // Lossless is irrelevant.
+ }
+ }
+
+ child = child.getNextSibling();
+ }
+ } else if (name.equals("Data")) {
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+
+ if (childName.equals("PlanarConfiguration")) {
+ String pc = getAttribute(child, "value");
+ int planarConfiguration = -1;
+ if(pc.equals("PixelInterleaved")) {
+ planarConfiguration =
+ BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
+ } else if(pc.equals("PlaneInterleaved")) {
+ planarConfiguration =
+ BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR;
+ }
+ if(planarConfiguration != -1) {
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION);
+ f = new TIFFField(tag, planarConfiguration);
+ rootIFD.addTIFFField(f);
+ }
+ } else if (childName.equals("BitsPerSample")) {
+ String bps = getAttribute(child, "value");
+ char[] bitsPerSample = listToCharArray(bps);
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ if(isPaletteColor) {
+ f = new TIFFField(tag, TIFFTag.TIFF_SHORT, 1,
+ new char[] {bitsPerSample[0]});
+ } else {
+ f = new TIFFField(tag, TIFFTag.TIFF_SHORT,
+ bitsPerSample.length,
+ bitsPerSample);
+ }
+ rootIFD.addTIFFField(f);
+ } else if (childName.equals("SampleMSB")) {
+ // Add FillOrder only if lsb-to-msb (right to left)
+ // for all bands, i.e., SampleMSB is zero for all
+ // channels.
+ String sMSB = getAttribute(child, "value");
+ int[] sampleMSB = listToIntArray(sMSB);
+ boolean isRightToLeft = true;
+ for(int i = 0; i < sampleMSB.length; i++) {
+ if(sampleMSB[i] != 0) {
+ isRightToLeft = false;
+ break;
+ }
+ }
+ int fillOrder = isRightToLeft ?
+ BaselineTIFFTagSet.FILL_ORDER_RIGHT_TO_LEFT :
+ BaselineTIFFTagSet.FILL_ORDER_LEFT_TO_RIGHT;
+ tag =
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_FILL_ORDER);
+ f = new TIFFField(tag, fillOrder);
+ rootIFD.addTIFFField(f);
+ }
+
+ child = child.getNextSibling();
+ }
+ } else if (name.equals("Dimension")) {
+ float pixelAspectRatio = -1.0f;
+ boolean gotPixelAspectRatio = false;
+
+ float horizontalPixelSize = -1.0f;
+ boolean gotHorizontalPixelSize = false;
+
+ float verticalPixelSize = -1.0f;
+ boolean gotVerticalPixelSize = false;
+
+ boolean sizeIsAbsolute = false;
+
+ float horizontalPosition = -1.0f;
+ boolean gotHorizontalPosition = false;
+
+ float verticalPosition = -1.0f;
+ boolean gotVerticalPosition = false;
+
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+ if (childName.equals("PixelAspectRatio")) {
+ String par = getAttribute(child, "value");
+ pixelAspectRatio = Float.parseFloat(par);
+ gotPixelAspectRatio = true;
+ } else if (childName.equals("ImageOrientation")) {
+ String orientation = getAttribute(child, "value");
+ for (int i = 0; i < orientationNames.length; i++) {
+ if (orientation.equals(orientationNames[i])) {
+ char[] oData = new char[1];
+ oData[0] = (char)i;
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_ORIENTATION),
+ TIFFTag.TIFF_SHORT,
+ 1,
+ oData);
+
+ rootIFD.addTIFFField(f);
+ break;
+ }
+ }
+
+ } else if (childName.equals("HorizontalPixelSize")) {
+ String hps = getAttribute(child, "value");
+ horizontalPixelSize = Float.parseFloat(hps);
+ gotHorizontalPixelSize = true;
+ } else if (childName.equals("VerticalPixelSize")) {
+ String vps = getAttribute(child, "value");
+ verticalPixelSize = Float.parseFloat(vps);
+ gotVerticalPixelSize = true;
+ } else if (childName.equals("HorizontalPosition")) {
+ String hp = getAttribute(child, "value");
+ horizontalPosition = Float.parseFloat(hp);
+ gotHorizontalPosition = true;
+ } else if (childName.equals("VerticalPosition")) {
+ String vp = getAttribute(child, "value");
+ verticalPosition = Float.parseFloat(vp);
+ gotVerticalPosition = true;
+ }
+
+ child = child.getNextSibling();
+ }
+
+ sizeIsAbsolute = gotHorizontalPixelSize ||
+ gotVerticalPixelSize;
+
+ // Fill in pixel size data from aspect ratio
+ if (gotPixelAspectRatio) {
+ if (gotHorizontalPixelSize && !gotVerticalPixelSize) {
+ verticalPixelSize =
+ horizontalPixelSize/pixelAspectRatio;
+ gotVerticalPixelSize = true;
+ } else if (gotVerticalPixelSize &&
+ !gotHorizontalPixelSize) {
+ horizontalPixelSize =
+ verticalPixelSize*pixelAspectRatio;
+ gotHorizontalPixelSize = true;
+ } else if (!gotHorizontalPixelSize &&
+ !gotVerticalPixelSize) {
+ horizontalPixelSize = pixelAspectRatio;
+ verticalPixelSize = 1.0f;
+ gotHorizontalPixelSize = true;
+ gotVerticalPixelSize = true;
+ }
+ }
+
+ // Compute pixels/centimeter
+ if (gotHorizontalPixelSize) {
+ float xResolution =
+ (sizeIsAbsolute ? 10.0f : 1.0f)/horizontalPixelSize;
+ long[][] hData = new long[1][2];
+ hData[0] = new long[2];
+ hData[0][0] = (long)(xResolution*10000.0f);
+ hData[0][1] = (long)10000;
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ hData);
+ rootIFD.addTIFFField(f);
+ }
+
+ if (gotVerticalPixelSize) {
+ float yResolution =
+ (sizeIsAbsolute ? 10.0f : 1.0f)/verticalPixelSize;
+ long[][] vData = new long[1][2];
+ vData[0] = new long[2];
+ vData[0][0] = (long)(yResolution*10000.0f);
+ vData[0][1] = (long)10000;
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ vData);
+ rootIFD.addTIFFField(f);
+ }
+
+ // Emit ResolutionUnit tag
+ char[] res = new char[1];
+ res[0] = (char)(sizeIsAbsolute ?
+ BaselineTIFFTagSet.RESOLUTION_UNIT_CENTIMETER :
+ BaselineTIFFTagSet.RESOLUTION_UNIT_NONE);
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
+ TIFFTag.TIFF_SHORT,
+ 1,
+ res);
+ rootIFD.addTIFFField(f);
+
+ // Position
+ if(sizeIsAbsolute) {
+ if(gotHorizontalPosition) {
+ // Convert from millimeters to centimeters via
+ // numerator multiplier = denominator/10.
+ long[][] hData = new long[1][2];
+ hData[0][0] = (long)(horizontalPosition*10000.0f);
+ hData[0][1] = (long)100000;
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_X_POSITION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ hData);
+ rootIFD.addTIFFField(f);
+ }
+
+ if(gotVerticalPosition) {
+ // Convert from millimeters to centimeters via
+ // numerator multiplier = denominator/10.
+ long[][] vData = new long[1][2];
+ vData[0][0] = (long)(verticalPosition*10000.0f);
+ vData[0][1] = (long)100000;
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_Y_POSITION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ vData);
+ rootIFD.addTIFFField(f);
+ }
+ }
+ } else if (name.equals("Document")) {
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+
+ if (childName.equals("SubimageInterpretation")) {
+ String si = getAttribute(child, "value");
+ int newSubFileType = -1;
+ if(si.equals("TransparencyMask")) {
+ newSubFileType =
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_TRANSPARENCY;
+ } else if(si.equals("ReducedResolution")) {
+ newSubFileType =
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_REDUCED_RESOLUTION;
+ } else if(si.equals("SinglePage")) {
+ newSubFileType =
+ BaselineTIFFTagSet.NEW_SUBFILE_TYPE_SINGLE_PAGE;
+ }
+ if(newSubFileType != -1) {
+ tag =
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_NEW_SUBFILE_TYPE);
+ f = new TIFFField(tag, newSubFileType);
+ rootIFD.addTIFFField(f);
+ }
+ }
+
+ if (childName.equals("ImageCreationTime")) {
+ String year = getAttribute(child, "year");
+ String month = getAttribute(child, "month");
+ String day = getAttribute(child, "day");
+ String hour = getAttribute(child, "hour");
+ String minute = getAttribute(child, "minute");
+ String second = getAttribute(child, "second");
+
+ StringBuffer sb = new StringBuffer();
+ sb.append(year);
+ sb.append(":");
+ if(month.length() == 1) {
+ sb.append("0");
+ }
+ sb.append(month);
+ sb.append(":");
+ if(day.length() == 1) {
+ sb.append("0");
+ }
+ sb.append(day);
+ sb.append(" ");
+ if(hour.length() == 1) {
+ sb.append("0");
+ }
+ sb.append(hour);
+ sb.append(":");
+ if(minute.length() == 1) {
+ sb.append("0");
+ }
+ sb.append(minute);
+ sb.append(":");
+ if(second.length() == 1) {
+ sb.append("0");
+ }
+ sb.append(second);
+
+ String[] dt = new String[1];
+ dt[0] = sb.toString();
+
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_DATE_TIME),
+ TIFFTag.TIFF_ASCII,
+ 1,
+ dt);
+ rootIFD.addTIFFField(f);
+ }
+
+ child = child.getNextSibling();
+ }
+ } else if (name.equals("Text")) {
+ Node child = node.getFirstChild();
+ String theAuthor = null;
+ String theDescription = null;
+ String theTitle = null;
+ while (child != null) {
+ String childName = child.getNodeName();
+ if(childName.equals("TextEntry")) {
+ int tagNumber = -1;
+ NamedNodeMap childAttrs = child.getAttributes();
+ Node keywordNode = childAttrs.getNamedItem("keyword");
+ if(keywordNode != null) {
+ String keyword = keywordNode.getNodeValue();
+ String value = getAttribute(child, "value");
+ if(!keyword.equals("") && !value.equals("")) {
+ if(keyword.equalsIgnoreCase("DocumentName")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_DOCUMENT_NAME;
+ } else if(keyword.equalsIgnoreCase("ImageDescription")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION;
+ } else if(keyword.equalsIgnoreCase("Make")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_MAKE;
+ } else if(keyword.equalsIgnoreCase("Model")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_MODEL;
+ } else if(keyword.equalsIgnoreCase("PageName")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_PAGE_NAME;
+ } else if(keyword.equalsIgnoreCase("Software")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_SOFTWARE;
+ } else if(keyword.equalsIgnoreCase("Artist")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_ARTIST;
+ } else if(keyword.equalsIgnoreCase("HostComputer")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_HOST_COMPUTER;
+ } else if(keyword.equalsIgnoreCase("InkNames")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_INK_NAMES;
+ } else if(keyword.equalsIgnoreCase("Copyright")) {
+ tagNumber =
+ BaselineTIFFTagSet.TAG_COPYRIGHT;
+ } else if(keyword.equalsIgnoreCase("author")) {
+ theAuthor = value;
+ } else if(keyword.equalsIgnoreCase("description")) {
+ theDescription = value;
+ } else if(keyword.equalsIgnoreCase("title")) {
+ theTitle = value;
+ }
+ if(tagNumber != -1) {
+ f = new TIFFField(rootIFD.getTag(tagNumber),
+ TIFFTag.TIFF_ASCII,
+ 1,
+ new String[] {value});
+ rootIFD.addTIFFField(f);
+ }
+ }
+ }
+ }
+ child = child.getNextSibling();
+ } // child != null
+ if(theAuthor != null &&
+ getTIFFField(BaselineTIFFTagSet.TAG_ARTIST) == null) {
+ f = new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_ARTIST),
+ TIFFTag.TIFF_ASCII,
+ 1,
+ new String[] {theAuthor});
+ rootIFD.addTIFFField(f);
+ }
+ if(theDescription != null &&
+ getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION) == null) {
+ f = new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_IMAGE_DESCRIPTION),
+ TIFFTag.TIFF_ASCII,
+ 1,
+ new String[] {theDescription});
+ rootIFD.addTIFFField(f);
+ }
+ if(theTitle != null &&
+ getTIFFField(BaselineTIFFTagSet.TAG_DOCUMENT_NAME) == null) {
+ f = new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_DOCUMENT_NAME),
+ TIFFTag.TIFF_ASCII,
+ 1,
+ new String[] {theTitle});
+ rootIFD.addTIFFField(f);
+ }
+ } else if (name.equals("Transparency")) {
+ Node child = node.getFirstChild();
+ while (child != null) {
+ String childName = child.getNodeName();
+
+ if (childName.equals("Alpha")) {
+ String alpha = getAttribute(child, "value");
+
+ f = null;
+ if (alpha.equals("premultiplied")) {
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES),
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA);
+ } else if (alpha.equals("nonpremultiplied")) {
+ f = new TIFFField(
+ rootIFD.getTag(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES),
+ BaselineTIFFTagSet.EXTRA_SAMPLES_UNASSOCIATED_ALPHA);
+ }
+ if (f != null) {
+ rootIFD.addTIFFField(f);
+ }
+ }
+
+ child = child.getNextSibling();
+ }
+ }
+
+ node = node.getNextSibling();
+ }
+
+ // Set SampleFormat.
+ if(sampleFormat != null) {
+ // Derive the value.
+ int sf = -1;
+ if(sampleFormat.equals("SignedIntegral")) {
+ sf = BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER;
+ } else if(sampleFormat.equals("UnsignedIntegral")) {
+ sf = BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER;
+ } else if(sampleFormat.equals("Real")) {
+ sf = BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT;
+ } else if(sampleFormat.equals("Index")) {
+ sf = BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER;
+ }
+
+ if(sf != -1) {
+ // Derive the count.
+ int count = 1;
+
+ // Try SamplesPerPixel first.
+ f = getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
+ if(f != null) {
+ count = f.getAsInt(0);
+ } else {
+ // Try BitsPerSample.
+ f = getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ if(f != null) {
+ count = f.getCount();
+ }
+ }
+
+ char[] sampleFormatArray = new char[count];
+ Arrays.fill(sampleFormatArray, (char)sf);
+
+ // Add SampleFormat.
+ tag = rootIFD.getTag(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
+ f = new TIFFField(tag, TIFFTag.TIFF_SHORT,
+ sampleFormatArray.length, sampleFormatArray);
+ rootIFD.addTIFFField(f);
+ }
+ }
+ }
+
+ private static String getAttribute(Node node, String attrName) {
+ NamedNodeMap attrs = node.getAttributes();
+ Node attr = attrs.getNamedItem(attrName);
+ return attr != null ? attr.getNodeValue() : null;
+ }
+
+ private Node getChildNode(Node node, String childName) {
+ Node childNode = null;
+ if(node.hasChildNodes()) {
+ NodeList childNodes = node.getChildNodes();
+ int length = childNodes.getLength();
+ for(int i = 0; i < length; i++) {
+ Node item = childNodes.item(i);
+ if(item.getNodeName().equals(childName)) {
+ childNode = item;
+ break;
+ }
+ }
+ }
+ return childNode;
+ }
+
+ public static TIFFIFD parseIFD(Node node) throws IIOInvalidTreeException {
+ if (!node.getNodeName().equals("TIFFIFD")) {
+ fatal(node, "Expected \"TIFFIFD\" node");
+ }
+
+ String tagSetNames = getAttribute(node, "tagSets");
+ List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(5);
+
+ if (tagSetNames != null) {
+ StringTokenizer st = new StringTokenizer(tagSetNames, ",");
+ while (st.hasMoreTokens()) {
+ String className = st.nextToken();
+
+ Object o = null;
+ try {
+ Class<?> setClass = Class.forName(className);
+ Method getInstanceMethod =
+ setClass.getMethod("getInstance", (Class[])null);
+ o = getInstanceMethod.invoke(null, (Object[])null);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ if (!(o instanceof TIFFTagSet)) {
+ fatal(node, "Specified tag set class \"" +
+ className +
+ "\" is not an instance of TIFFTagSet");
+ } else {
+ tagSets.add((TIFFTagSet)o);
+ }
+ }
+ }
+
+ TIFFIFD ifd = new TIFFIFD(tagSets);
+
+ node = node.getFirstChild();
+ while (node != null) {
+ String name = node.getNodeName();
+
+ TIFFField f = null;
+ if (name.equals("TIFFIFD")) {
+ TIFFIFD subIFD = parseIFD(node);
+ String parentTagName = getAttribute(node, "parentTagName");
+ String parentTagNumber = getAttribute(node, "parentTagNumber");
+ TIFFTag tag = null;
+ if(parentTagName != null) {
+ tag = TIFFIFD.getTag(parentTagName, tagSets);
+ } else if(parentTagNumber != null) {
+ int tagNumber = Integer.parseUnsignedInt(parentTagNumber);
+ tag = TIFFIFD.getTag(tagNumber, tagSets);
+ }
+
+ int type;
+ if (tag == null) {
+ type = TIFFTag.TIFF_LONG;
+ tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, 0, 1 << type);
+ } else {
+ if (tag.isDataTypeOK(TIFFTag.TIFF_IFD_POINTER)) {
+ type = TIFFTag.TIFF_IFD_POINTER;
+ } else if (tag.isDataTypeOK(TIFFTag.TIFF_LONG)) {
+ type = TIFFTag.TIFF_LONG;
+ } else {
+ for (type = TIFFTag.MAX_DATATYPE;
+ type >= TIFFTag.MIN_DATATYPE;
+ type--) {
+ if (tag.isDataTypeOK(type)) {
+ break;
+ }
+ }
+ }
+ }
+
+ f = new TIFFField(tag, type, 1L, subIFD);
+ } else if (name.equals("TIFFField")) {
+ int number = Integer.parseInt(getAttribute(node, "number"));
+
+ TIFFTagSet tagSet = null;
+ Iterator<TIFFTagSet> iter = tagSets.iterator();
+ while (iter.hasNext()) {
+ TIFFTagSet t = iter.next();
+ if (t.getTag(number) != null) {
+ tagSet = t;
+ break;
+ }
+ }
+
+ f = TIFFField.createFromMetadataNode(tagSet, node);
+ } else {
+ fatal(node,
+ "Expected either \"TIFFIFD\" or \"TIFFField\" node, got "
+ + name);
+ }
+
+ ifd.addTIFFField(f);
+ node = node.getNextSibling();
+ }
+
+ return ifd;
+ }
+
+ private void mergeNativeTree(Node root) throws IIOInvalidTreeException {
+ Node node = root;
+ if (!node.getNodeName().equals(nativeMetadataFormatName)) {
+ fatal(node, "Root must be " + nativeMetadataFormatName);
+ }
+
+ node = node.getFirstChild();
+ if (node == null || !node.getNodeName().equals("TIFFIFD")) {
+ fatal(root, "Root must have \"TIFFIFD\" child");
+ }
+ TIFFIFD ifd = parseIFD(node);
+
+ List<TIFFTagSet> rootIFDTagSets = rootIFD.getTagSetList();
+ Iterator<TIFFTagSet> tagSetIter = ifd.getTagSetList().iterator();
+ while(tagSetIter.hasNext()) {
+ Object o = tagSetIter.next();
+ if(o instanceof TIFFTagSet && !rootIFDTagSets.contains(o)) {
+ rootIFD.addTagSet((TIFFTagSet)o);
+ }
+ }
+
+ Iterator<TIFFField> ifdIter = ifd.iterator();
+ while(ifdIter.hasNext()) {
+ TIFFField field = ifdIter.next();
+ rootIFD.addTIFFField(field);
+ }
+ }
+
+ public void mergeTree(String formatName, Node root)
+ throws IIOInvalidTreeException{
+ if (formatName.equals(nativeMetadataFormatName)) {
+ if (root == null) {
+ throw new NullPointerException("root == null!");
+ }
+ mergeNativeTree(root);
+ } else if (formatName.equals
+ (IIOMetadataFormatImpl.standardMetadataFormatName)) {
+ if (root == null) {
+ throw new NullPointerException("root == null!");
+ }
+ mergeStandardTree(root);
+ } else {
+ throw new IllegalArgumentException("Not a recognized format!");
+ }
+ }
+
+ public void reset() {
+ rootIFD = new TIFFIFD(tagSets);
+ }
+
+ public TIFFIFD getRootIFD() {
+ return rootIFD;
+ }
+
+ public TIFFField getTIFFField(int tagNumber) {
+ return rootIFD.getTIFFField(tagNumber);
+ }
+
+ public void removeTIFFField(int tagNumber) {
+ rootIFD.removeTIFFField(tagNumber);
+ }
+
+ /**
+ * Returns a <code>TIFFImageMetadata</code> wherein all fields in the
+ * root IFD from the <code>BaselineTIFFTagSet</code> are copied by value
+ * and all other fields copied by reference.
+ */
+ public TIFFImageMetadata getShallowClone() {
+ return new TIFFImageMetadata(rootIFD.getShallowClone());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageMetadataFormat.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,151 @@
+/*
+ * 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.ImageTypeSpecifier;
+import javax.imageio.metadata.IIOMetadataFormat;
+
+public class TIFFImageMetadataFormat extends TIFFMetadataFormat {
+
+ private static TIFFImageMetadataFormat theInstance = null;
+
+ static {
+ }
+
+ public boolean canNodeAppear(String elementName,
+ ImageTypeSpecifier imageType) {
+ return false;
+ }
+
+ private TIFFImageMetadataFormat() {
+ this.resourceBaseName =
+ "javax.imageio.plugins.tiff.TIFFImageMetadataFormatResources";
+ this.rootName = TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME;
+
+ TIFFElementInfo einfo;
+ TIFFAttrInfo ainfo;
+ String[] empty = new String[0];
+ String[] childNames;
+ String[] attrNames;
+
+ childNames = new String[] { "TIFFIFD" };
+ einfo = new TIFFElementInfo(childNames, empty, CHILD_POLICY_SEQUENCE);
+
+ elementInfoMap.put(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME,
+ einfo);
+
+ childNames = new String[] { "TIFFField", "TIFFIFD" };
+ attrNames =
+ new String[] { "tagSets", "parentTagNumber", "parentTagName" };
+ einfo = new TIFFElementInfo(childNames, attrNames, CHILD_POLICY_SEQUENCE);
+ elementInfoMap.put("TIFFIFD", einfo);
+
+ ainfo = new TIFFAttrInfo();
+ ainfo.dataType = DATATYPE_STRING;
+ ainfo.isRequired = true;
+ attrInfoMap.put("TIFFIFD/tagSets", ainfo);
+
+ ainfo = new TIFFAttrInfo();
+ ainfo.dataType = DATATYPE_INTEGER;
+ ainfo.isRequired = false;
+ attrInfoMap.put("TIFFIFD/parentTagNumber", ainfo);
+
+ ainfo = new TIFFAttrInfo();
+ ainfo.dataType = DATATYPE_STRING;
+ ainfo.isRequired = false;
+ attrInfoMap.put("TIFFIFD/parentTagName", ainfo);
+
+ String[] types = {
+ "TIFFByte",
+ "TIFFAscii",
+ "TIFFShort",
+ "TIFFSShort",
+ "TIFFLong",
+ "TIFFSLong",
+ "TIFFRational",
+ "TIFFSRational",
+ "TIFFFloat",
+ "TIFFDouble",
+ "TIFFUndefined"
+ };
+
+ attrNames = new String[] { "value", "description" };
+ String[] attrNamesValueOnly = new String[] { "value" };
+ TIFFAttrInfo ainfoValue = new TIFFAttrInfo();
+ TIFFAttrInfo ainfoDescription = new TIFFAttrInfo();
+
+ for (int i = 0; i < types.length; i++) {
+ if (!types[i].equals("TIFFUndefined")) {
+ childNames = new String[1];
+ childNames[0] = types[i];
+ einfo =
+ new TIFFElementInfo(childNames, empty, CHILD_POLICY_SEQUENCE);
+ elementInfoMap.put(types[i] + "s", einfo);
+ }
+
+ boolean hasDescription =
+ !types[i].equals("TIFFUndefined") &&
+ !types[i].equals("TIFFAscii") &&
+ !types[i].equals("TIFFRational") &&
+ !types[i].equals("TIFFSRational") &&
+ !types[i].equals("TIFFFloat") &&
+ !types[i].equals("TIFFDouble");
+
+ String[] anames = hasDescription ? attrNames : attrNamesValueOnly;
+ einfo = new TIFFElementInfo(empty, anames, CHILD_POLICY_EMPTY);
+ elementInfoMap.put(types[i], einfo);
+
+ attrInfoMap.put(types[i] + "/value", ainfoValue);
+ if (hasDescription) {
+ attrInfoMap.put(types[i] + "/description", ainfoDescription);
+ }
+ }
+
+ childNames = new String[2*types.length - 1];
+ for (int i = 0; i < types.length; i++) {
+ childNames[2*i] = types[i];
+ if (!types[i].equals("TIFFUndefined")) {
+ childNames[2*i + 1] = types[i] + "s";
+ }
+ }
+ attrNames = new String[] { "number", "name" };
+ einfo = new TIFFElementInfo(childNames, attrNames, CHILD_POLICY_CHOICE);
+ elementInfoMap.put("TIFFField", einfo);
+
+ ainfo = new TIFFAttrInfo();
+ ainfo.isRequired = true;
+ attrInfoMap.put("TIFFField/number", ainfo);
+
+ ainfo = new TIFFAttrInfo();
+ attrInfoMap.put("TIFFField/name", ainfo);
+ }
+
+ public static synchronized IIOMetadataFormat getInstance() {
+ if (theInstance == null) {
+ theInstance = new TIFFImageMetadataFormat();
+ }
+ return theInstance;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageMetadataFormatResources.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,102 @@
+/*
+ * 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.util.ListResourceBundle;
+
+public class TIFFImageMetadataFormatResources extends ListResourceBundle {
+
+ private static final Object[][] contents = {
+ { "TIFFIFD", "An IFD (directory) containing fields" },
+ { "TIFFIFD/parentTagNumber",
+ "The tag number of the field pointing to this IFD" },
+ { "TIFFIFD/parentTagName",
+ "A mnemonic name for the field pointing to this IFD, if known" },
+ { "TIFFField", "A field containing data" },
+ { "TIFFField/number", "The tag number asociated with the field" },
+ { "TIFFField/name",
+ "A mnemonic name associated with the field, if known" },
+
+ { "TIFFUndefined", "Uninterpreted byte data" },
+ { "TIFFUndefined/value", "A list of comma-separated byte values" },
+
+ { "TIFFBytes", "A sequence of TIFFByte nodes" },
+ { "TIFFByte", "An integral value between 0 and 255" },
+ { "TIFFByte/value", "The value" },
+ { "TIFFByte/description", "A description, if available" },
+
+ { "TIFFAsciis", "A sequence of TIFFAscii nodes" },
+ { "TIFFAscii", "A String value" },
+ { "TIFFAscii/value", "The value" },
+
+ { "TIFFShorts", "A sequence of TIFFShort nodes" },
+ { "TIFFShort", "An integral value between 0 and 65535" },
+ { "TIFFShort/value", "The value" },
+ { "TIFFShort/description", "A description, if available" },
+
+ { "TIFFSShorts", "A sequence of TIFFSShort nodes" },
+ { "TIFFSShort", "An integral value between -32768 and 32767" },
+ { "TIFFSShort/value", "The value" },
+ { "TIFFSShort/description", "A description, if available" },
+
+ { "TIFFLongs", "A sequence of TIFFLong nodes" },
+ { "TIFFLong", "An integral value between 0 and 4294967295" },
+ { "TIFFLong/value", "The value" },
+ { "TIFFLong/description", "A description, if available" },
+
+ { "TIFFSLongs", "A sequence of TIFFSLong nodes" },
+ { "TIFFSLong", "An integral value between -2147483648 and 2147483647" },
+ { "TIFFSLong/value", "The value" },
+ { "TIFFSLong/description", "A description, if available" },
+
+ { "TIFFRationals", "A sequence of TIFFRational nodes" },
+ { "TIFFRational",
+ "A rational value consisting of an unsigned numerator and denominator" },
+ { "TIFFRational/value",
+ "The numerator and denominator, separated by a slash" },
+
+ { "TIFFSRationals", "A sequence of TIFFSRational nodes" },
+ { "TIFFSRational",
+ "A rational value consisting of a signed numerator and denominator" },
+ { "TIFFSRational/value",
+ "The numerator and denominator, separated by a slash" },
+
+ { "TIFFFloats", "A sequence of TIFFFloat nodes" },
+ { "TIFFFloat", "A single-precision floating-point value" },
+ { "TIFFFloat/value", "The value" },
+
+ { "TIFFDoubles", "A sequence of TIFFDouble nodes" },
+ { "TIFFDouble", "A double-precision floating-point value" },
+ { "TIFFDouble/value", "The value" },
+
+ };
+
+ public TIFFImageMetadataFormatResources() {
+ }
+
+ public Object[][] getContents() {
+ return contents.clone();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageReader.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,1329 @@
+/*
+ * 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.Rectangle;
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.SampleModel;
+import java.io.IOException;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import javax.imageio.IIOException;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageReadParam;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.ImageInputStream;
+import org.w3c.dom.Node;
+import com.sun.imageio.plugins.common.ImageUtil;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.plugins.tiff.TIFFField;
+import javax.imageio.plugins.tiff.TIFFImageReadParam;
+import javax.imageio.plugins.tiff.TIFFTagSet;
+
+public class TIFFImageReader extends ImageReader {
+
+ // A somewhat arbitrary upper bound on SamplesPerPixel. Hyperspectral
+ // images as of this writing appear to be under 300 bands so this should
+ // account for those cases should they arise.
+ private static final int SAMPLES_PER_PIXEL_MAX = 1024;
+
+ // In baseline TIFF the largest data types are 64-bit long and double.
+ private static final int BITS_PER_SAMPLE_MAX = 64;
+
+ // The current ImageInputStream source.
+ private ImageInputStream stream = null;
+
+ // True if the file header has been read.
+ private boolean gotHeader = false;
+
+ private ImageReadParam imageReadParam = getDefaultReadParam();
+
+ // Stream metadata, or null.
+ private TIFFStreamMetadata streamMetadata = null;
+
+ // The current image index.
+ private int currIndex = -1;
+
+ // Metadata for image at 'currIndex', or null.
+ private TIFFImageMetadata imageMetadata = null;
+
+ // A <code>List</code> of <code>Long</code>s indicating the stream
+ // positions of the start of the IFD for each image. Entries
+ // are added as needed.
+ private List<Long> imageStartPosition = new ArrayList<Long>();
+
+ // The number of images in the stream, if known, otherwise -1.
+ private int numImages = -1;
+
+ // The ImageTypeSpecifiers of the images in the stream.
+ // Contains a map of Integers to Lists.
+ private HashMap<Integer, List<ImageTypeSpecifier>> imageTypeMap
+ = new HashMap<Integer, List<ImageTypeSpecifier>>();
+
+ private BufferedImage theImage = null;
+
+ private int width = -1;
+ private int height = -1;
+ private int numBands = -1;
+ private int tileOrStripWidth = -1, tileOrStripHeight = -1;
+
+ private int planarConfiguration = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
+
+ private int compression;
+ private int photometricInterpretation;
+ private int samplesPerPixel;
+ private int[] sampleFormat;
+ private int[] bitsPerSample;
+ private int[] extraSamples;
+ private char[] colorMap;
+
+ private int sourceXOffset;
+ private int sourceYOffset;
+ private int srcXSubsampling;
+ private int srcYSubsampling;
+
+ private int dstWidth;
+ private int dstHeight;
+ private int dstMinX;
+ private int dstMinY;
+ private int dstXOffset;
+ private int dstYOffset;
+
+ private int tilesAcross;
+ private int tilesDown;
+
+ private int pixelsRead;
+ private int pixelsToRead;
+
+ public TIFFImageReader(ImageReaderSpi originatingProvider) {
+ super(originatingProvider);
+ }
+
+ public void setInput(Object input,
+ boolean seekForwardOnly,
+ boolean ignoreMetadata) {
+ super.setInput(input, seekForwardOnly, ignoreMetadata);
+
+ // Clear all local values based on the previous stream contents.
+ resetLocal();
+
+ if (input != null) {
+ if (!(input instanceof ImageInputStream)) {
+ throw new IllegalArgumentException("input not an ImageInputStream!");
+ }
+ this.stream = (ImageInputStream) input;
+ } else {
+ this.stream = null;
+ }
+ }
+
+ // Do not seek to the beginning of the stream so as to allow users to
+ // point us at an IFD within some other file format
+ private void readHeader() throws IIOException {
+ if (gotHeader) {
+ return;
+ }
+ if (stream == null) {
+ throw new IllegalStateException("Input not set!");
+ }
+
+ // Create an object to store the stream metadata
+ this.streamMetadata = new TIFFStreamMetadata();
+
+ try {
+ int byteOrder = stream.readUnsignedShort();
+ if (byteOrder == 0x4d4d) {
+ streamMetadata.byteOrder = ByteOrder.BIG_ENDIAN;
+ stream.setByteOrder(ByteOrder.BIG_ENDIAN);
+ } else if (byteOrder == 0x4949) {
+ streamMetadata.byteOrder = ByteOrder.LITTLE_ENDIAN;
+ stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+ } else {
+ processWarningOccurred(
+ "Bad byte order in header, assuming little-endian");
+ streamMetadata.byteOrder = ByteOrder.LITTLE_ENDIAN;
+ stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+ }
+
+ int magic = stream.readUnsignedShort();
+ if (magic != 42) {
+ processWarningOccurred(
+ "Bad magic number in header, continuing");
+ }
+
+ // Seek to start of first IFD
+ long offset = stream.readUnsignedInt();
+ imageStartPosition.add(Long.valueOf(offset));
+ stream.seek(offset);
+ } catch (IOException e) {
+ throw new IIOException("I/O error reading header!", e);
+ }
+
+ gotHeader = true;
+ }
+
+ private int locateImage(int imageIndex) throws IIOException {
+ readHeader();
+
+ try {
+ // Find closest known index
+ int index = Math.min(imageIndex, imageStartPosition.size() - 1);
+
+ // Seek to that position
+ Long l = imageStartPosition.get(index);
+ stream.seek(l.longValue());
+
+ // Skip IFDs until at desired index or last image found
+ while (index < imageIndex) {
+ int count = stream.readUnsignedShort();
+ stream.skipBytes(12 * count);
+
+ long offset = stream.readUnsignedInt();
+ if (offset == 0) {
+ return index;
+ }
+
+ imageStartPosition.add(Long.valueOf(offset));
+ stream.seek(offset);
+ ++index;
+ }
+ } catch (IOException e) {
+ throw new IIOException("Couldn't seek!", e);
+ }
+
+ if (currIndex != imageIndex) {
+ imageMetadata = null;
+ }
+ currIndex = imageIndex;
+ return imageIndex;
+ }
+
+ public int getNumImages(boolean allowSearch) throws IOException {
+ if (stream == null) {
+ throw new IllegalStateException("Input not set!");
+ }
+ if (seekForwardOnly && allowSearch) {
+ throw new IllegalStateException("seekForwardOnly and allowSearch can't both be true!");
+ }
+
+ if (numImages > 0) {
+ return numImages;
+ }
+ if (allowSearch) {
+ this.numImages = locateImage(Integer.MAX_VALUE) + 1;
+ }
+ return numImages;
+ }
+
+ public IIOMetadata getStreamMetadata() throws IIOException {
+ readHeader();
+ return streamMetadata;
+ }
+
+ // Throw an IndexOutOfBoundsException if index < minIndex,
+ // and bump minIndex if required.
+ private void checkIndex(int imageIndex) {
+ if (imageIndex < minIndex) {
+ throw new IndexOutOfBoundsException("imageIndex < minIndex!");
+ }
+ if (seekForwardOnly) {
+ minIndex = imageIndex;
+ }
+ }
+
+ // Verify that imageIndex is in bounds, find the image IFD, read the
+ // image metadata, initialize instance variables from the metadata.
+ private void seekToImage(int imageIndex) throws IIOException {
+ checkIndex(imageIndex);
+
+ int index = locateImage(imageIndex);
+ if (index != imageIndex) {
+ throw new IndexOutOfBoundsException("imageIndex out of bounds!");
+ }
+
+ readMetadata();
+
+ initializeFromMetadata();
+ }
+
+ // Stream must be positioned at start of IFD for 'currIndex'
+ private void readMetadata() throws IIOException {
+ if (stream == null) {
+ throw new IllegalStateException("Input not set!");
+ }
+
+ if (imageMetadata != null) {
+ return;
+ }
+ try {
+ // Create an object to store the image metadata
+ List<TIFFTagSet> tagSets;
+ if (imageReadParam instanceof TIFFImageReadParam) {
+ tagSets
+ = ((TIFFImageReadParam) imageReadParam).getAllowedTagSets();
+ } else {
+ tagSets = new ArrayList<TIFFTagSet>(1);
+ tagSets.add(BaselineTIFFTagSet.getInstance());
+ }
+
+ this.imageMetadata = new TIFFImageMetadata(tagSets);
+ imageMetadata.initializeFromStream(stream, ignoreMetadata);
+ } catch (IIOException iioe) {
+ throw iioe;
+ } catch (IOException ioe) {
+ throw new IIOException("I/O error reading image metadata!", ioe);
+ }
+ }
+
+ private int getWidth() {
+ return this.width;
+ }
+
+ private int getHeight() {
+ return this.height;
+ }
+
+ // Returns tile width if image is tiled, else image width
+ private int getTileOrStripWidth() {
+ TIFFField f
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
+ return (f == null) ? getWidth() : f.getAsInt(0);
+ }
+
+ // Returns tile height if image is tiled, else strip height
+ private int getTileOrStripHeight() {
+ TIFFField f
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_LENGTH);
+ if (f != null) {
+ return f.getAsInt(0);
+ }
+
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
+ // Default for ROWS_PER_STRIP is 2^32 - 1, i.e., infinity
+ int h = (f == null) ? -1 : f.getAsInt(0);
+ return (h == -1) ? getHeight() : h;
+ }
+
+ private int getPlanarConfiguration() {
+ TIFFField f
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION);
+ if (f != null) {
+ int planarConfigurationValue = f.getAsInt(0);
+ if (planarConfigurationValue
+ == BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) {
+ // Some writers (e.g. Kofax standard Multi-Page TIFF
+ // Storage Filter v2.01.000; cf. bug 4929147) do not
+ // correctly set the value of this field. Attempt to
+ // ascertain whether the value is correctly Planar.
+ if (getCompression()
+ == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG
+ && imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT)
+ != null) {
+ // JPEG interchange format cannot have
+ // PlanarConfiguration value Chunky so reset.
+ processWarningOccurred("PlanarConfiguration \"Planar\" value inconsistent with JPEGInterchangeFormat; resetting to \"Chunky\".");
+ planarConfigurationValue
+ = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
+ } else {
+ TIFFField offsetField
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
+ if (offsetField == null) {
+ // Tiles
+ offsetField
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
+ int tw = getTileOrStripWidth();
+ int th = getTileOrStripHeight();
+ int tAcross = (getWidth() + tw - 1) / tw;
+ int tDown = (getHeight() + th - 1) / th;
+ int tilesPerImage = tAcross * tDown;
+ long[] offsetArray = offsetField.getAsLongs();
+ if (offsetArray != null
+ && offsetArray.length == tilesPerImage) {
+ // Length of offsets array is
+ // TilesPerImage for Chunky and
+ // SamplesPerPixel*TilesPerImage for Planar.
+ processWarningOccurred("PlanarConfiguration \"Planar\" value inconsistent with TileOffsets field value count; resetting to \"Chunky\".");
+ planarConfigurationValue
+ = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
+ }
+ } else {
+ // Strips
+ int rowsPerStrip = getTileOrStripHeight();
+ int stripsPerImage
+ = (getHeight() + rowsPerStrip - 1) / rowsPerStrip;
+ long[] offsetArray = offsetField.getAsLongs();
+ if (offsetArray != null
+ && offsetArray.length == stripsPerImage) {
+ // Length of offsets array is
+ // StripsPerImage for Chunky and
+ // SamplesPerPixel*StripsPerImage for Planar.
+ processWarningOccurred("PlanarConfiguration \"Planar\" value inconsistent with StripOffsets field value count; resetting to \"Chunky\".");
+ planarConfigurationValue
+ = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
+ }
+ }
+ }
+ }
+ return planarConfigurationValue;
+ }
+
+ return BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
+ }
+
+ private long getTileOrStripOffset(int tileIndex) throws IIOException {
+ TIFFField f
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
+ if (f == null) {
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
+ }
+ if (f == null) {
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
+ }
+
+ if (f == null) {
+ throw new IIOException("Missing required strip or tile offsets field.");
+ }
+
+ return f.getAsLong(tileIndex);
+ }
+
+ private long getTileOrStripByteCount(int tileIndex) throws IOException {
+ TIFFField f
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS);
+ if (f == null) {
+ f
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
+ }
+ if (f == null) {
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
+ }
+
+ long tileOrStripByteCount;
+ if (f != null) {
+ tileOrStripByteCount = f.getAsLong(tileIndex);
+ } else {
+ processWarningOccurred("TIFF directory contains neither StripByteCounts nor TileByteCounts field: attempting to calculate from strip or tile width and height.");
+
+ // Initialize to number of bytes per strip or tile assuming
+ // no compression.
+ int bitsPerPixel = bitsPerSample[0];
+ for (int i = 1; i < samplesPerPixel; i++) {
+ bitsPerPixel += bitsPerSample[i];
+ }
+ int bytesPerRow = (getTileOrStripWidth() * bitsPerPixel + 7) / 8;
+ tileOrStripByteCount = bytesPerRow * getTileOrStripHeight();
+
+ // Clamp to end of stream if possible.
+ long streamLength = stream.length();
+ if (streamLength != -1) {
+ tileOrStripByteCount
+ = Math.min(tileOrStripByteCount,
+ streamLength - getTileOrStripOffset(tileIndex));
+ } else {
+ processWarningOccurred("Stream length is unknown: cannot clamp estimated strip or tile byte count to EOF.");
+ }
+ }
+
+ return tileOrStripByteCount;
+ }
+
+ private int getCompression() {
+ TIFFField f
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
+ if (f == null) {
+ return BaselineTIFFTagSet.COMPRESSION_NONE;
+ } else {
+ return f.getAsInt(0);
+ }
+ }
+
+ public int getWidth(int imageIndex) throws IOException {
+ seekToImage(imageIndex);
+ return getWidth();
+ }
+
+ public int getHeight(int imageIndex) throws IOException {
+ seekToImage(imageIndex);
+ return getHeight();
+ }
+
+ /**
+ * Initializes these instance variables from the image metadata:
+ * <pre>
+ * compression
+ * width
+ * height
+ * samplesPerPixel
+ * numBands
+ * colorMap
+ * photometricInterpretation
+ * sampleFormat
+ * bitsPerSample
+ * extraSamples
+ * tileOrStripWidth
+ * tileOrStripHeight
+ * </pre>
+ */
+ private void initializeFromMetadata() throws IIOException {
+ TIFFField f;
+
+ // Compression
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
+ if (f == null) {
+ processWarningOccurred("Compression field is missing; assuming no compression");
+ compression = BaselineTIFFTagSet.COMPRESSION_NONE;
+ } else {
+ compression = f.getAsInt(0);
+ }
+
+ // Whether key dimensional information is absent.
+ boolean isMissingDimension = false;
+
+ // ImageWidth -> width
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
+ if (f != null) {
+ this.width = f.getAsInt(0);
+ } else {
+ processWarningOccurred("ImageWidth field is missing.");
+ isMissingDimension = true;
+ }
+
+ // ImageLength -> height
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH);
+ if (f != null) {
+ this.height = f.getAsInt(0);
+ } else {
+ processWarningOccurred("ImageLength field is missing.");
+ isMissingDimension = true;
+ }
+
+ // SamplesPerPixel
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
+ if (f != null) {
+ samplesPerPixel = f.getAsInt(0);
+ } else {
+ samplesPerPixel = 1;
+ isMissingDimension = true;
+ }
+
+ // If any dimension is missing and there is a JPEG stream available
+ // get the information from it.
+ int defaultBitDepth = 1;
+ if (isMissingDimension
+ && (f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT)) != null) {
+ Iterator<ImageReader> iter = ImageIO.getImageReadersByFormatName("JPEG");
+ if (iter != null && iter.hasNext()) {
+ ImageReader jreader = iter.next();
+ try {
+ stream.mark();
+ stream.seek(f.getAsLong(0));
+ jreader.setInput(stream);
+ if (imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH) == null) {
+ this.width = jreader.getWidth(0);
+ }
+ if (imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH) == null) {
+ this.height = jreader.getHeight(0);
+ }
+ ImageTypeSpecifier imageType = jreader.getRawImageType(0);
+ if (imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL) == null) {
+ this.samplesPerPixel =
+ imageType != null ?
+ imageType.getSampleModel().getNumBands() : 3;
+ }
+ stream.reset();
+ defaultBitDepth =
+ imageType != null ?
+ imageType.getColorModel().getComponentSize(0) : 8;
+ } catch (IOException e) {
+ // Ignore it and proceed: an error will occur later.
+ }
+ jreader.dispose();
+ }
+ }
+
+ if (samplesPerPixel < 1) {
+ throw new IIOException("Samples per pixel < 1!");
+ } else if (samplesPerPixel > SAMPLES_PER_PIXEL_MAX) {
+ throw new IIOException
+ ("Samples per pixel (" + samplesPerPixel
+ + ") greater than allowed maximum ("
+ + SAMPLES_PER_PIXEL_MAX + ")");
+ }
+
+ // SamplesPerPixel -> numBands
+ numBands = samplesPerPixel;
+
+ // ColorMap
+ this.colorMap = null;
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COLOR_MAP);
+ if (f != null) {
+ // Grab color map
+ colorMap = f.getAsChars();
+ }
+
+ // PhotometricInterpretation
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ if (f == null) {
+ if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_RLE
+ || compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_4
+ || compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_6) {
+ processWarningOccurred("PhotometricInterpretation field is missing; "
+ + "assuming WhiteIsZero");
+ photometricInterpretation
+ = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
+ } else if (this.colorMap != null) {
+ photometricInterpretation
+ = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR;
+ } else if (samplesPerPixel == 3 || samplesPerPixel == 4) {
+ photometricInterpretation
+ = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB;
+ } else {
+ processWarningOccurred("PhotometricInterpretation field is missing; "
+ + "assuming BlackIsZero");
+ photometricInterpretation
+ = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
+ }
+ } else {
+ photometricInterpretation = f.getAsInt(0);
+ }
+
+ // SampleFormat
+ boolean replicateFirst = false;
+ int first = -1;
+
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
+ sampleFormat = new int[samplesPerPixel];
+ replicateFirst = false;
+ if (f == null) {
+ replicateFirst = true;
+ first = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED;
+ } else if (f.getCount() != samplesPerPixel) {
+ replicateFirst = true;
+ first = f.getAsInt(0);
+ }
+
+ for (int i = 0; i < samplesPerPixel; i++) {
+ sampleFormat[i] = replicateFirst ? first : f.getAsInt(i);
+ if (sampleFormat[i]
+ != BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER
+ && sampleFormat[i]
+ != BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER
+ && sampleFormat[i]
+ != BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT
+ && sampleFormat[i]
+ != BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED) {
+ processWarningOccurred(
+ "Illegal value for SAMPLE_FORMAT, assuming SAMPLE_FORMAT_UNDEFINED");
+ sampleFormat[i] = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED;
+ }
+ }
+
+ // BitsPerSample
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ this.bitsPerSample = new int[samplesPerPixel];
+ replicateFirst = false;
+ if (f == null) {
+ replicateFirst = true;
+ first = defaultBitDepth;
+ } else if (f.getCount() != samplesPerPixel) {
+ replicateFirst = true;
+ first = f.getAsInt(0);
+ }
+
+ for (int i = 0; i < samplesPerPixel; i++) {
+ // Replicate initial value if not enough values provided
+ bitsPerSample[i] = replicateFirst ? first : f.getAsInt(i);
+ if (bitsPerSample[i] > BITS_PER_SAMPLE_MAX) {
+ throw new IIOException
+ ("Bits per sample (" + bitsPerSample[i]
+ + ") greater than allowed maximum ("
+ + BITS_PER_SAMPLE_MAX + ")");
+ }
+ }
+
+ // ExtraSamples
+ this.extraSamples = null;
+ f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES);
+ if (f != null) {
+ extraSamples = f.getAsInts();
+ }
+ }
+
+ public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IIOException {
+ List<ImageTypeSpecifier> l; // List of ImageTypeSpecifiers
+
+ Integer imageIndexInteger = Integer.valueOf(imageIndex);
+ if (imageTypeMap.containsKey(imageIndexInteger)) {
+ // Return the cached ITS List.
+ l = imageTypeMap.get(imageIndexInteger);
+ } else {
+ // Create a new ITS List.
+ l = new ArrayList<ImageTypeSpecifier>(1);
+
+ // Create the ITS and cache if for later use so that this method
+ // always returns an Iterator containing the same ITS objects.
+ seekToImage(imageIndex);
+ ImageTypeSpecifier itsRaw
+ = TIFFDecompressor.getRawImageTypeSpecifier(photometricInterpretation,
+ compression,
+ samplesPerPixel,
+ bitsPerSample,
+ sampleFormat,
+ extraSamples,
+ colorMap);
+
+ // Check for an ICCProfile field.
+ TIFFField iccProfileField
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_ICC_PROFILE);
+
+ // If an ICCProfile field is present change the ImageTypeSpecifier
+ // to use it if the data layout is component type.
+ if (iccProfileField != null
+ && itsRaw.getColorModel() instanceof ComponentColorModel) {
+ // Create a ColorSpace from the profile.
+ byte[] iccProfileValue = iccProfileField.getAsBytes();
+ ICC_Profile iccProfile
+ = ICC_Profile.getInstance(iccProfileValue);
+ ICC_ColorSpace iccColorSpace
+ = new ICC_ColorSpace(iccProfile);
+
+ // Get the raw sample and color information.
+ ColorModel cmRaw = itsRaw.getColorModel();
+ ColorSpace csRaw = cmRaw.getColorSpace();
+ SampleModel smRaw = itsRaw.getSampleModel();
+
+ // Get the number of samples per pixel and the number
+ // of color components.
+ int numBands = smRaw.getNumBands();
+ int numComponents = iccColorSpace.getNumComponents();
+
+ // Replace the ColorModel with the ICC ColorModel if the
+ // numbers of samples and color components are amenable.
+ if (numBands == numComponents
+ || numBands == numComponents + 1) {
+ // Set alpha flags.
+ boolean hasAlpha = numComponents != numBands;
+ boolean isAlphaPre
+ = hasAlpha && cmRaw.isAlphaPremultiplied();
+
+ // Create a ColorModel of the same class and with
+ // the same transfer type.
+ ColorModel iccColorModel
+ = new ComponentColorModel(iccColorSpace,
+ cmRaw.getComponentSize(),
+ hasAlpha,
+ isAlphaPre,
+ cmRaw.getTransparency(),
+ cmRaw.getTransferType());
+
+ // Prepend the ICC profile-based ITS to the List. The
+ // ColorModel and SampleModel are guaranteed to be
+ // compatible as the old and new ColorModels are both
+ // ComponentColorModels with the same transfer type
+ // and the same number of components.
+ l.add(new ImageTypeSpecifier(iccColorModel, smRaw));
+
+ // Append the raw ITS to the List if and only if its
+ // ColorSpace has the same type and number of components
+ // as the ICC ColorSpace.
+ if (csRaw.getType() == iccColorSpace.getType()
+ && csRaw.getNumComponents()
+ == iccColorSpace.getNumComponents()) {
+ l.add(itsRaw);
+ }
+ } else { // ICCProfile not compatible with SampleModel.
+ // Append the raw ITS to the List.
+ l.add(itsRaw);
+ }
+ } else { // No ICCProfile field or raw ColorModel not component.
+ // Append the raw ITS to the List.
+ l.add(itsRaw);
+ }
+
+ // Cache the ITS List.
+ imageTypeMap.put(imageIndexInteger, l);
+ }
+
+ return l.iterator();
+ }
+
+ public IIOMetadata getImageMetadata(int imageIndex) throws IIOException {
+ seekToImage(imageIndex);
+ TIFFImageMetadata im
+ = new TIFFImageMetadata(imageMetadata.getRootIFD().getTagSetList());
+ Node root
+ = imageMetadata.getAsTree(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME);
+ im.setFromTree(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME, root);
+ return im;
+ }
+
+ public IIOMetadata getStreamMetadata(int imageIndex) throws IIOException {
+ readHeader();
+ TIFFStreamMetadata sm = new TIFFStreamMetadata();
+ Node root = sm.getAsTree(TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME);
+ sm.setFromTree(TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME, root);
+ return sm;
+ }
+
+ public boolean isRandomAccessEasy(int imageIndex) throws IOException {
+ if (currIndex != -1) {
+ seekToImage(currIndex);
+ return getCompression() == BaselineTIFFTagSet.COMPRESSION_NONE;
+ } else {
+ return false;
+ }
+ }
+
+ // Thumbnails
+ public boolean readSupportsThumbnails() {
+ return false;
+ }
+
+ public boolean hasThumbnails(int imageIndex) {
+ return false;
+ }
+
+ public int getNumThumbnails(int imageIndex) throws IOException {
+ return 0;
+ }
+
+ public ImageReadParam getDefaultReadParam() {
+ return new TIFFImageReadParam();
+ }
+
+ public boolean isImageTiled(int imageIndex) throws IOException {
+ seekToImage(imageIndex);
+
+ TIFFField f
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
+ return f != null;
+ }
+
+ public int getTileWidth(int imageIndex) throws IOException {
+ seekToImage(imageIndex);
+ return getTileOrStripWidth();
+ }
+
+ public int getTileHeight(int imageIndex) throws IOException {
+ seekToImage(imageIndex);
+ return getTileOrStripHeight();
+ }
+
+ public BufferedImage readTile(int imageIndex, int tileX, int tileY)
+ throws IOException {
+
+ int w = getWidth(imageIndex);
+ int h = getHeight(imageIndex);
+ int tw = getTileWidth(imageIndex);
+ int th = getTileHeight(imageIndex);
+
+ int x = tw * tileX;
+ int y = th * tileY;
+
+ if (tileX < 0 || tileY < 0 || x >= w || y >= h) {
+ throw new IllegalArgumentException("Tile indices are out of bounds!");
+ }
+
+ if (x + tw > w) {
+ tw = w - x;
+ }
+
+ if (y + th > h) {
+ th = h - y;
+ }
+
+ ImageReadParam param = getDefaultReadParam();
+ Rectangle tileRect = new Rectangle(x, y, tw, th);
+ param.setSourceRegion(tileRect);
+
+ return read(imageIndex, param);
+ }
+
+ public boolean canReadRaster() {
+ return false;
+ }
+
+ public Raster readRaster(int imageIndex, ImageReadParam param)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ private int[] sourceBands;
+ private int[] destinationBands;
+
+ private TIFFDecompressor decompressor;
+
+ // floor(num/den)
+ private static int ifloor(int num, int den) {
+ if (num < 0) {
+ num -= den - 1;
+ }
+ return num / den;
+ }
+
+ // ceil(num/den)
+ private static int iceil(int num, int den) {
+ if (num > 0) {
+ num += den - 1;
+ }
+ return num / den;
+ }
+
+ private void prepareRead(int imageIndex, ImageReadParam param)
+ throws IOException {
+ if (stream == null) {
+ throw new IllegalStateException("Input not set!");
+ }
+
+ // A null ImageReadParam means we use the default
+ if (param == null) {
+ param = getDefaultReadParam();
+ }
+
+ this.imageReadParam = param;
+
+ seekToImage(imageIndex);
+
+ this.tileOrStripWidth = getTileOrStripWidth();
+ this.tileOrStripHeight = getTileOrStripHeight();
+ this.planarConfiguration = getPlanarConfiguration();
+
+ this.sourceBands = param.getSourceBands();
+ if (sourceBands == null) {
+ sourceBands = new int[numBands];
+ for (int i = 0; i < numBands; i++) {
+ sourceBands[i] = i;
+ }
+ }
+
+ // Initialize the destination image
+ Iterator<ImageTypeSpecifier> imageTypes = getImageTypes(imageIndex);
+ ImageTypeSpecifier theImageType
+ = ImageUtil.getDestinationType(param, imageTypes);
+
+ int destNumBands = theImageType.getSampleModel().getNumBands();
+
+ this.destinationBands = param.getDestinationBands();
+ if (destinationBands == null) {
+ destinationBands = new int[destNumBands];
+ for (int i = 0; i < destNumBands; i++) {
+ destinationBands[i] = i;
+ }
+ }
+
+ if (sourceBands.length != destinationBands.length) {
+ throw new IllegalArgumentException(
+ "sourceBands.length != destinationBands.length");
+ }
+
+ for (int i = 0; i < sourceBands.length; i++) {
+ int sb = sourceBands[i];
+ if (sb < 0 || sb >= numBands) {
+ throw new IllegalArgumentException(
+ "Source band out of range!");
+ }
+ int db = destinationBands[i];
+ if (db < 0 || db >= destNumBands) {
+ throw new IllegalArgumentException(
+ "Destination band out of range!");
+ }
+ }
+ }
+
+ public RenderedImage readAsRenderedImage(int imageIndex,
+ ImageReadParam param)
+ throws IOException {
+ prepareRead(imageIndex, param);
+ return new TIFFRenderedImage(this, imageIndex, imageReadParam,
+ width, height);
+ }
+
+ private void decodeTile(int ti, int tj, int band) throws IOException {
+ // Compute the region covered by the strip or tile
+ Rectangle tileRect = new Rectangle(ti * tileOrStripWidth,
+ tj * tileOrStripHeight,
+ tileOrStripWidth,
+ tileOrStripHeight);
+
+ // Clip against the image bounds if the image is not tiled. If it
+ // is tiled, the tile may legally extend beyond the image bounds.
+ if (!isImageTiled(currIndex)) {
+ tileRect
+ = tileRect.intersection(new Rectangle(0, 0, width, height));
+ }
+
+ // Return if the intersection is empty.
+ if (tileRect.width <= 0 || tileRect.height <= 0) {
+ return;
+ }
+
+ int srcMinX = tileRect.x;
+ int srcMinY = tileRect.y;
+ int srcWidth = tileRect.width;
+ int srcHeight = tileRect.height;
+
+ // Determine dest region that can be derived from the
+ // source region
+ dstMinX = iceil(srcMinX - sourceXOffset, srcXSubsampling);
+ int dstMaxX = ifloor(srcMinX + srcWidth - 1 - sourceXOffset,
+ srcXSubsampling);
+
+ dstMinY = iceil(srcMinY - sourceYOffset, srcYSubsampling);
+ int dstMaxY = ifloor(srcMinY + srcHeight - 1 - sourceYOffset,
+ srcYSubsampling);
+
+ dstWidth = dstMaxX - dstMinX + 1;
+ dstHeight = dstMaxY - dstMinY + 1;
+
+ dstMinX += dstXOffset;
+ dstMinY += dstYOffset;
+
+ // Clip against image bounds
+ Rectangle dstRect = new Rectangle(dstMinX, dstMinY,
+ dstWidth, dstHeight);
+ dstRect
+ = dstRect.intersection(theImage.getRaster().getBounds());
+
+ dstMinX = dstRect.x;
+ dstMinY = dstRect.y;
+ dstWidth = dstRect.width;
+ dstHeight = dstRect.height;
+
+ if (dstWidth <= 0 || dstHeight <= 0) {
+ return;
+ }
+
+ // Backwards map dest region to source to determine
+ // active source region
+ int activeSrcMinX = (dstMinX - dstXOffset) * srcXSubsampling
+ + sourceXOffset;
+ int sxmax
+ = (dstMinX + dstWidth - 1 - dstXOffset) * srcXSubsampling
+ + sourceXOffset;
+ int activeSrcWidth = sxmax - activeSrcMinX + 1;
+
+ int activeSrcMinY = (dstMinY - dstYOffset) * srcYSubsampling
+ + sourceYOffset;
+ int symax
+ = (dstMinY + dstHeight - 1 - dstYOffset) * srcYSubsampling
+ + sourceYOffset;
+ int activeSrcHeight = symax - activeSrcMinY + 1;
+
+ decompressor.setSrcMinX(srcMinX);
+ decompressor.setSrcMinY(srcMinY);
+ decompressor.setSrcWidth(srcWidth);
+ decompressor.setSrcHeight(srcHeight);
+
+ decompressor.setDstMinX(dstMinX);
+ decompressor.setDstMinY(dstMinY);
+ decompressor.setDstWidth(dstWidth);
+ decompressor.setDstHeight(dstHeight);
+
+ decompressor.setActiveSrcMinX(activeSrcMinX);
+ decompressor.setActiveSrcMinY(activeSrcMinY);
+ decompressor.setActiveSrcWidth(activeSrcWidth);
+ decompressor.setActiveSrcHeight(activeSrcHeight);
+
+ int tileIndex = tj * tilesAcross + ti;
+
+ if (planarConfiguration
+ == BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) {
+ tileIndex += band * tilesAcross * tilesDown;
+ }
+
+ long offset = getTileOrStripOffset(tileIndex);
+ long byteCount = getTileOrStripByteCount(tileIndex);
+
+ decompressor.setStream(stream);
+ decompressor.setOffset(offset);
+ decompressor.setByteCount((int) byteCount);
+
+ decompressor.beginDecoding();
+
+ stream.mark();
+ decompressor.decode();
+ stream.reset();
+ }
+
+ private void reportProgress() {
+ // Report image progress/update to listeners after each tile
+ pixelsRead += dstWidth * dstHeight;
+ processImageProgress(100.0f * pixelsRead / pixelsToRead);
+ processImageUpdate(theImage,
+ dstMinX, dstMinY, dstWidth, dstHeight,
+ 1, 1,
+ destinationBands);
+ }
+
+ public BufferedImage read(int imageIndex, ImageReadParam param)
+ throws IOException {
+ prepareRead(imageIndex, param);
+ this.theImage = getDestination(param,
+ getImageTypes(imageIndex),
+ width, height);
+
+ srcXSubsampling = imageReadParam.getSourceXSubsampling();
+ srcYSubsampling = imageReadParam.getSourceYSubsampling();
+
+ Point p = imageReadParam.getDestinationOffset();
+ dstXOffset = p.x;
+ dstYOffset = p.y;
+
+ // This could probably be made more efficient...
+ Rectangle srcRegion = new Rectangle(0, 0, 0, 0);
+ Rectangle destRegion = new Rectangle(0, 0, 0, 0);
+
+ computeRegions(imageReadParam, width, height, theImage,
+ srcRegion, destRegion);
+
+ // Initial source pixel, taking source region and source
+ // subsamplimg offsets into account
+ sourceXOffset = srcRegion.x;
+ sourceYOffset = srcRegion.y;
+
+ pixelsToRead = destRegion.width * destRegion.height;
+ pixelsRead = 0;
+
+ processImageStarted(imageIndex);
+ processImageProgress(0.0f);
+
+ tilesAcross = (width + tileOrStripWidth - 1) / tileOrStripWidth;
+ tilesDown = (height + tileOrStripHeight - 1) / tileOrStripHeight;
+
+ int compression = getCompression();
+
+ // Set the decompressor
+ if (compression == BaselineTIFFTagSet.COMPRESSION_NONE) {
+ // Get the fillOrder field.
+ TIFFField fillOrderField
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER);
+
+ // Set the decompressor based on the fill order.
+ if (fillOrderField != null && fillOrderField.getAsInt(0) == 2) {
+ this.decompressor = new TIFFLSBDecompressor();
+ } else {
+ this.decompressor = new TIFFNullDecompressor();
+ }
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_CCITT_T_6) {
+ this.decompressor = new TIFFFaxDecompressor();
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_CCITT_T_4) {
+ this.decompressor = new TIFFFaxDecompressor();
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_CCITT_RLE) {
+ this.decompressor = new TIFFFaxDecompressor();
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_PACKBITS) {
+ this.decompressor = new TIFFPackBitsDecompressor();
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_LZW) {
+ TIFFField predictorField
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PREDICTOR);
+ int predictor = ((predictorField == null)
+ ? BaselineTIFFTagSet.PREDICTOR_NONE
+ : predictorField.getAsInt(0));
+ this.decompressor = new TIFFLZWDecompressor(predictor);
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_JPEG) {
+ this.decompressor = new TIFFJPEGDecompressor();
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_ZLIB
+ || compression
+ == BaselineTIFFTagSet.COMPRESSION_DEFLATE) {
+ TIFFField predictorField
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PREDICTOR);
+ int predictor = ((predictorField == null)
+ ? BaselineTIFFTagSet.PREDICTOR_NONE
+ : predictorField.getAsInt(0));
+ this.decompressor = new TIFFDeflateDecompressor(predictor);
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
+ TIFFField JPEGProcField
+ = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_PROC);
+ if (JPEGProcField == null) {
+ processWarningOccurred("JPEGProc field missing; assuming baseline sequential JPEG process.");
+ } else if (JPEGProcField.getAsInt(0)
+ != BaselineTIFFTagSet.JPEG_PROC_BASELINE) {
+ throw new IIOException("Old-style JPEG supported for baseline sequential JPEG process only!");
+ }
+ this.decompressor = new TIFFOldJPEGDecompressor();
+ //throw new IIOException("Old-style JPEG not supported!");
+ } else {
+ throw new IIOException("Unsupported compression type (tag value = "
+ + compression + ")!");
+ }
+
+ if (photometricInterpretation
+ == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR
+ && compression != BaselineTIFFTagSet.COMPRESSION_JPEG
+ && compression != BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
+ boolean convertYCbCrToRGB
+ = theImage.getColorModel().getColorSpace().getType()
+ == ColorSpace.TYPE_RGB;
+ TIFFDecompressor wrappedDecompressor
+ = this.decompressor instanceof TIFFNullDecompressor
+ ? null : this.decompressor;
+ this.decompressor
+ = new TIFFYCbCrDecompressor(wrappedDecompressor,
+ convertYCbCrToRGB);
+ }
+
+ TIFFColorConverter colorConverter = null;
+ if (photometricInterpretation
+ == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB
+ && theImage.getColorModel().getColorSpace().getType()
+ == ColorSpace.TYPE_RGB) {
+ colorConverter = new TIFFCIELabColorConverter();
+ } else if (photometricInterpretation
+ == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR
+ && !(this.decompressor instanceof TIFFYCbCrDecompressor)
+ && compression != BaselineTIFFTagSet.COMPRESSION_JPEG
+ && compression != BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
+ colorConverter = new TIFFYCbCrColorConverter(imageMetadata);
+ }
+
+ decompressor.setReader(this);
+ decompressor.setMetadata(imageMetadata);
+ decompressor.setImage(theImage);
+
+ decompressor.setPhotometricInterpretation(photometricInterpretation);
+ decompressor.setCompression(compression);
+ decompressor.setSamplesPerPixel(samplesPerPixel);
+ decompressor.setBitsPerSample(bitsPerSample);
+ decompressor.setSampleFormat(sampleFormat);
+ decompressor.setExtraSamples(extraSamples);
+ decompressor.setColorMap(colorMap);
+
+ decompressor.setColorConverter(colorConverter);
+
+ decompressor.setSourceXOffset(sourceXOffset);
+ decompressor.setSourceYOffset(sourceYOffset);
+ decompressor.setSubsampleX(srcXSubsampling);
+ decompressor.setSubsampleY(srcYSubsampling);
+
+ decompressor.setDstXOffset(dstXOffset);
+ decompressor.setDstYOffset(dstYOffset);
+
+ decompressor.setSourceBands(sourceBands);
+ decompressor.setDestinationBands(destinationBands);
+
+ // Compute bounds on the tile indices for this source region.
+ int minTileX
+ = TIFFImageWriter.XToTileX(srcRegion.x, 0, tileOrStripWidth);
+ int minTileY
+ = TIFFImageWriter.YToTileY(srcRegion.y, 0, tileOrStripHeight);
+ int maxTileX
+ = TIFFImageWriter.XToTileX(srcRegion.x + srcRegion.width - 1,
+ 0, tileOrStripWidth);
+ int maxTileY
+ = TIFFImageWriter.YToTileY(srcRegion.y + srcRegion.height - 1,
+ 0, tileOrStripHeight);
+
+ if (planarConfiguration
+ == BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) {
+
+ decompressor.setPlanar(true);
+
+ int[] sb = new int[1];
+ int[] db = new int[1];
+ for (int tj = minTileY; tj <= maxTileY; tj++) {
+ for (int ti = minTileX; ti <= maxTileX; ti++) {
+ for (int band = 0; band < numBands; band++) {
+ sb[0] = sourceBands[band];
+ decompressor.setSourceBands(sb);
+ db[0] = destinationBands[band];
+ decompressor.setDestinationBands(db);
+
+ decodeTile(ti, tj, band);
+ }
+
+ reportProgress();
+ }
+ }
+ } else {
+ for (int tj = minTileY; tj <= maxTileY; tj++) {
+ for (int ti = minTileX; ti <= maxTileX; ti++) {
+ decodeTile(ti, tj, -1);
+
+ reportProgress();
+ }
+ }
+ }
+
+ if (abortRequested()) {
+ processReadAborted();
+ } else {
+ processImageComplete();
+ }
+
+ return theImage;
+ }
+
+ public void reset() {
+ super.reset();
+ resetLocal();
+ }
+
+ protected void resetLocal() {
+ stream = null;
+ gotHeader = false;
+ imageReadParam = getDefaultReadParam();
+ streamMetadata = null;
+ currIndex = -1;
+ imageMetadata = null;
+ imageStartPosition = new ArrayList<Long>();
+ numImages = -1;
+ imageTypeMap = new HashMap<Integer, List<ImageTypeSpecifier>>();
+ width = -1;
+ height = -1;
+ numBands = -1;
+ tileOrStripWidth = -1;
+ tileOrStripHeight = -1;
+ planarConfiguration = BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY;
+ }
+
+ /**
+ * Package scope method to allow decompressors, for example, to emit warning
+ * messages.
+ */
+ void forwardWarningMessage(String warning) {
+ processWarningOccurred(warning);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageReaderSpi.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,91 @@
+/*
+ * 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.Locale;
+import javax.imageio.ImageReader;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.spi.ServiceRegistry;
+import javax.imageio.stream.ImageInputStream;
+
+public class TIFFImageReaderSpi extends ImageReaderSpi {
+
+ private boolean registered = false;
+
+ public TIFFImageReaderSpi() {
+ super("Oracle Corporation",
+ "1.0",
+ new String[] {"tif", "TIF", "tiff", "TIFF"},
+ new String[] {"tif", "tiff"},
+ new String[] {"image/tiff"},
+ "com.sun.imageio.plugins.tiff.TIFFImageReader",
+ new Class<?>[] { ImageInputStream.class },
+ new String[] {"com.sun.imageio.plugins.tiff.TIFFImageWriterSpi"},
+ false,
+ TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME,
+ "com.sun.imageio.plugins.tiff.TIFFStreamMetadataFormat",
+ null, null,
+ true,
+ TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME,
+ "com.sun.imageio.plugins.tiff.TIFFImageMetadataFormat",
+ null, null
+ );
+ }
+
+ public String getDescription(Locale locale) {
+ return "Standard TIFF image reader";
+ }
+
+ public boolean canDecodeInput(Object input) throws IOException {
+ if (!(input instanceof ImageInputStream)) {
+ return false;
+ }
+
+ ImageInputStream stream = (ImageInputStream)input;
+ byte[] b = new byte[4];
+ stream.mark();
+ stream.readFully(b);
+ stream.reset();
+
+ return ((b[0] == (byte)0x49 && b[1] == (byte)0x49 &&
+ b[2] == (byte)0x2a && b[3] == (byte)0x00) ||
+ (b[0] == (byte)0x4d && b[1] == (byte)0x4d &&
+ b[2] == (byte)0x00 && b[3] == (byte)0x2a));
+ }
+
+ public ImageReader createReaderInstance(Object extension) {
+ return new TIFFImageReader(this);
+ }
+
+ public void onRegistration(ServiceRegistry registry,
+ Class<?> category) {
+ if (registered) {
+ return;
+ }
+
+ registered = true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriteParam.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,155 @@
+/*
+ * 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.util.Locale;
+import javax.imageio.ImageWriteParam;
+
+/**
+ * A subclass of {@link ImageWriteParam ImageWriteParam}
+ * allowing control over the TIFF writing process. The set of innately
+ * supported compression types is listed in the following table:
+ *
+ * <table border=1>
+ * <caption><b>Supported Compression Types</b></caption>
+ * <tr><th>Compression Type</th> <th>Description</th> <th>Reference</th></tr>
+ * <tr>
+ * <td>CCITT RLE</td>
+ * <td>Modified Huffman compression</td>
+ * <td>TIFF 6.0 Specification, Section 10</td>
+ * </tr>
+ * <tr>
+ * <td>CCITT T.4</td>
+ * <td>CCITT T.4 bilevel encoding/Group 3 facsimile compression</td>
+ * <td>TIFF 6.0 Specification, Section 11</td>
+ * </tr>
+ * <tr>
+ * <td>CCITT T.6</td>
+ * <td>CCITT T.6 bilevel encoding/Group 4 facsimile compression</td>
+ * <td>TIFF 6.0 Specification, Section 11</td></tr>
+ * <tr>
+ * <td>LZW</td>
+ * <td>LZW compression</td>
+ * <td>TIFF 6.0 Specification, Section 13</td></tr>
+ * <tr>
+ * <td>JPEG</td>
+ * <td>"New" JPEG-in-TIFF compression</td>
+ * <td><a href="ftp://ftp.sgi.com/graphics/tiff/TTN2.draft.txt">TIFF
+ * Technical Note #2</a></td>
+ * </tr>
+ * <tr>
+ * <td>ZLib</td>
+ * <td>"Deflate/Inflate" compression (see note following this table)</td>
+ * <td><a href="http://partners.adobe.com/asn/developer/pdfs/tn/TIFFphotoshop.pdf">
+ * Adobe Photoshop® TIFF Technical Notes</a> (PDF)</td>
+ * </tr>
+ * <tr>
+ * <td>PackBits</td>
+ * <td>Byte-oriented, run length compression</td>
+ * <td>TIFF 6.0 Specification, Section 9</td>
+ * </tr>
+ * <tr>
+ * <td>Deflate</td>
+ * <td>"Zip-in-TIFF" compression (see note following this table)</td>
+ * <td><a href="http://www.isi.edu/in-notes/rfc1950.txt">
+ * ZLIB Compressed Data Format Specification</a>,
+ * <a href="http://www.isi.edu/in-notes/rfc1951.txt">
+ * DEFLATE Compressed Data Format Specification</a></td>
+ * </tr>
+ * <tr>
+ * <td>Exif JPEG</td>
+ * <td>Exif-specific JPEG compression (see note following this table)</td>
+ * <td><a href="http://www.exif.org/Exif2-2.PDF">Exif 2.2 Specification</a>
+ * (PDF), section 4.5.5, "Basic Structure of Thumbnail Data"</td>
+ * </table>
+ *
+ * <p>
+ * Old-style JPEG compression as described in section 22 of the TIFF 6.0
+ * Specification is <i>not</i> supported.
+ * </p>
+ *
+ * <p> 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.</p>
+ *
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * "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 <i>not</i> later be appended using the pixel
+ * replacement capability of the TIFF writer.
+ * </p>
+ *
+ * <p> 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 <tt>[1, 9]</tt> 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.</p>
+ *
+ * <p> The <code>canWriteTiles</code> and
+ * <code>canWriteCompressed</code> methods will return
+ * <code>true</code>; the <code>canOffsetTiles</code> and
+ * <code>canWriteProgressive</code> methods will return
+ * <code>false</code>.</p>
+ *
+ * <p> 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.</p>
+ */
+public class TIFFImageWriteParam extends ImageWriteParam {
+
+ /**
+ * Constructs a <code>TIFFImageWriteParam</code> instance
+ * for a given <code>Locale</code>.
+ *
+ * @param locale the <code>Locale</code> for which messages
+ * should be localized.
+ */
+ public TIFFImageWriteParam(Locale locale) {
+ super(locale);
+ this.canWriteCompressed = true;
+ this.canWriteTiles = true;
+ this.compressionTypes = TIFFImageWriter.TIFFCompressionTypes;
+ };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,3610 @@
+/*
+ * 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.Rectangle;
+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.ComponentSampleModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.IndexColorModel;
+import java.awt.image.RenderedImage;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.imageio.IIOException;
+import javax.imageio.IIOImage;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.metadata.IIOInvalidTreeException;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataFormatImpl;
+import javax.imageio.spi.ImageWriterSpi;
+import javax.imageio.stream.ImageOutputStream;
+import org.w3c.dom.Node;
+import com.sun.imageio.plugins.common.ImageUtil;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.plugins.tiff.ExifParentTIFFTagSet;
+import javax.imageio.plugins.tiff.ExifTIFFTagSet;
+import javax.imageio.plugins.tiff.TIFFField;
+import javax.imageio.plugins.tiff.TIFFTag;
+import javax.imageio.plugins.tiff.TIFFTagSet;
+import com.sun.imageio.plugins.common.SimpleRenderedImage;
+import com.sun.imageio.plugins.common.SingleTileRenderedImage;
+import java.nio.charset.StandardCharsets;
+
+public class TIFFImageWriter extends ImageWriter {
+
+ static final String EXIF_JPEG_COMPRESSION_TYPE = "Exif JPEG";
+
+ private static final int DEFAULT_BYTES_PER_STRIP = 8192;
+
+ /**
+ * Supported TIFF compression types.
+ */
+ static final String[] TIFFCompressionTypes = {
+ "CCITT RLE",
+ "CCITT T.4",
+ "CCITT T.6",
+ "LZW",
+ // "Old JPEG",
+ "JPEG",
+ "ZLib",
+ "PackBits",
+ "Deflate",
+ EXIF_JPEG_COMPRESSION_TYPE
+ };
+
+ //
+ // The lengths of the arrays 'compressionTypes',
+ // 'isCompressionLossless', and 'compressionNumbers'
+ // must be equal.
+ //
+
+ /**
+ * Known TIFF compression types.
+ */
+ static final String[] compressionTypes = {
+ "CCITT RLE",
+ "CCITT T.4",
+ "CCITT T.6",
+ "LZW",
+ "Old JPEG",
+ "JPEG",
+ "ZLib",
+ "PackBits",
+ "Deflate",
+ EXIF_JPEG_COMPRESSION_TYPE
+ };
+
+ /**
+ * Lossless flag for known compression types.
+ */
+ static final boolean[] isCompressionLossless = {
+ true, // RLE
+ true, // T.4
+ true, // T.6
+ true, // LZW
+ false, // Old JPEG
+ false, // JPEG
+ true, // ZLib
+ true, // PackBits
+ true, // DEFLATE
+ false // Exif JPEG
+ };
+
+ /**
+ * Compression tag values for known compression types.
+ */
+ static final int[] compressionNumbers = {
+ BaselineTIFFTagSet.COMPRESSION_CCITT_RLE,
+ BaselineTIFFTagSet.COMPRESSION_CCITT_T_4,
+ BaselineTIFFTagSet.COMPRESSION_CCITT_T_6,
+ BaselineTIFFTagSet.COMPRESSION_LZW,
+ BaselineTIFFTagSet.COMPRESSION_OLD_JPEG,
+ BaselineTIFFTagSet.COMPRESSION_JPEG,
+ BaselineTIFFTagSet.COMPRESSION_ZLIB,
+ BaselineTIFFTagSet.COMPRESSION_PACKBITS,
+ BaselineTIFFTagSet.COMPRESSION_DEFLATE,
+ BaselineTIFFTagSet.COMPRESSION_OLD_JPEG, // Exif JPEG
+ };
+
+ private ImageOutputStream stream;
+ private long headerPosition;
+ private RenderedImage image;
+ private ImageTypeSpecifier imageType;
+ private ByteOrder byteOrder;
+ private ImageWriteParam param;
+ private TIFFCompressor compressor;
+ private TIFFColorConverter colorConverter;
+
+ private TIFFStreamMetadata streamMetadata;
+ private TIFFImageMetadata imageMetadata;
+
+ private int sourceXOffset;
+ private int sourceYOffset;
+ private int sourceWidth;
+ private int sourceHeight;
+ private int[] sourceBands;
+ private int periodX;
+ private int periodY;
+
+ private int bitDepth; // bits per channel
+ private int numBands;
+ private int tileWidth;
+ private int tileLength;
+ private int tilesAcross;
+ private int tilesDown;
+
+ private int[] sampleSize = null; // Input sample size per band, in bits
+ private int scalingBitDepth = -1; // Output bit depth of the scaling tables
+ private boolean isRescaling = false; // Whether rescaling is needed.
+
+ private boolean isBilevel; // Whether image is bilevel
+ private boolean isImageSimple; // Whether image can be copied into directly
+ private boolean isInverted; // Whether photometric inversion is required
+
+ private boolean isTiled; // Whether the image is tiled (true) or stipped (false).
+
+ private int nativePhotometricInterpretation;
+ private int photometricInterpretation;
+
+ private char[] bitsPerSample; // Output sample size per band
+ private int sampleFormat =
+ BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED; // Output sample format
+
+ // Tables for 1, 2, 4, or 8 bit output
+ private byte[][] scale = null; // 8 bit table
+ private byte[] scale0 = null; // equivalent to scale[0]
+
+ // Tables for 16 bit output
+ private byte[][] scaleh = null; // High bytes of output
+ private byte[][] scalel = null; // Low bytes of output
+
+ private int compression;
+ private int predictor;
+
+ private int totalPixels;
+ private int pixelsDone;
+
+ private long nextIFDPointerPos;
+
+ // Next available space.
+ private long nextSpace = 0L;
+
+ // Whether a sequence is being written.
+ private boolean isWritingSequence = false;
+ private boolean isInsertingEmpty = false;
+ private boolean isWritingEmpty = false;
+
+ private int currentImage = 0;
+
+ /**
+ * 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.
+ *
+ * <p> If <code>tileWidth < 0</code>, the results of this method
+ * are undefined. If <code>tileWidth == 0</code>, an
+ * <code>ArithmeticException</code> will be thrown.
+ *
+ * @throws ArithmeticException If <code>tileWidth == 0</code>.
+ */
+ public static int XToTileX(int x, int tileGridXOffset, int tileWidth) {
+ x -= tileGridXOffset;
+ if (x < 0) {
+ x += 1 - tileWidth; // force round to -infinity (ceiling)
+ }
+ 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.
+ *
+ * <p> If <code>tileHeight < 0</code>, the results of this method
+ * are undefined. If <code>tileHeight == 0</code>, an
+ * <code>ArithmeticException</code> will be thrown.
+ *
+ * @throws ArithmeticException If <code>tileHeight == 0</code>.
+ */
+ public static int YToTileY(int y, int tileGridYOffset, int tileHeight) {
+ y -= tileGridYOffset;
+ if (y < 0) {
+ y += 1 - tileHeight; // force round to -infinity (ceiling)
+ }
+ return y/tileHeight;
+ }
+
+ public TIFFImageWriter(ImageWriterSpi originatingProvider) {
+ super(originatingProvider);
+ }
+
+ public ImageWriteParam getDefaultWriteParam() {
+ return new TIFFImageWriteParam(getLocale());
+ }
+
+ public void setOutput(Object output) {
+ super.setOutput(output);
+
+ if (output != null) {
+ if (!(output instanceof ImageOutputStream)) {
+ throw new IllegalArgumentException
+ ("output not an ImageOutputStream!");
+ }
+ this.stream = (ImageOutputStream)output;
+
+ //
+ // The output is expected to be positioned at a TIFF header
+ // or at some arbitrary location which may or may not be
+ // the EOF. In the former case the writer should be able
+ // either to overwrite the existing sequence or append to it.
+ //
+
+ // Set the position of the header and the next available space.
+ try {
+ headerPosition = this.stream.getStreamPosition();
+ try {
+ // Read byte order and magic number.
+ byte[] b = new byte[4];
+ stream.readFully(b);
+
+ // Check bytes for TIFF header.
+ if((b[0] == (byte)0x49 && b[1] == (byte)0x49 &&
+ b[2] == (byte)0x2a && b[3] == (byte)0x00) ||
+ (b[0] == (byte)0x4d && b[1] == (byte)0x4d &&
+ b[2] == (byte)0x00 && b[3] == (byte)0x2a)) {
+ // TIFF header.
+ this.nextSpace = stream.length();
+ } else {
+ // Neither TIFF header nor EOF: overwrite.
+ this.nextSpace = headerPosition;
+ }
+ } catch(IOException io) { // thrown by readFully()
+ // At EOF or not at a TIFF header.
+ this.nextSpace = headerPosition;
+ }
+ stream.seek(headerPosition);
+ } catch(IOException ioe) { // thrown by getStreamPosition()
+ // Assume it's at zero.
+ this.nextSpace = headerPosition = 0L;
+ }
+ } else {
+ this.stream = null;
+ }
+ }
+
+ public IIOMetadata
+ getDefaultStreamMetadata(ImageWriteParam param) {
+ return new TIFFStreamMetadata();
+ }
+
+ public IIOMetadata
+ getDefaultImageMetadata(ImageTypeSpecifier imageType,
+ ImageWriteParam param) {
+
+ List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1);
+ tagSets.add(BaselineTIFFTagSet.getInstance());
+ TIFFImageMetadata imageMetadata = new TIFFImageMetadata(tagSets);
+
+ if(imageType != null) {
+ TIFFImageMetadata im =
+ (TIFFImageMetadata)convertImageMetadata(imageMetadata,
+ imageType,
+ param);
+ if(im != null) {
+ imageMetadata = im;
+ }
+ }
+
+ return imageMetadata;
+ }
+
+ public IIOMetadata convertStreamMetadata(IIOMetadata inData,
+ ImageWriteParam param) {
+ // Check arguments.
+ if(inData == null) {
+ throw new NullPointerException("inData == null!");
+ }
+
+ // Note: param is irrelevant as it does not contain byte order.
+
+ TIFFStreamMetadata outData = null;
+ if(inData instanceof TIFFStreamMetadata) {
+ outData = new TIFFStreamMetadata();
+ outData.byteOrder = ((TIFFStreamMetadata)inData).byteOrder;
+ return outData;
+ } else if(Arrays.asList(inData.getMetadataFormatNames()).contains(
+ TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME)) {
+ outData = new TIFFStreamMetadata();
+ String format = TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME;
+ try {
+ outData.mergeTree(format, inData.getAsTree(format));
+ } catch(IIOInvalidTreeException e) {
+ return null;
+ }
+ }
+
+ return outData;
+ }
+
+ public IIOMetadata
+ convertImageMetadata(IIOMetadata inData,
+ ImageTypeSpecifier imageType,
+ ImageWriteParam param) {
+ // Check arguments.
+ if(inData == null) {
+ throw new NullPointerException("inData == null!");
+ }
+ if(imageType == null) {
+ throw new NullPointerException("imageType == null!");
+ }
+
+ TIFFImageMetadata outData = null;
+
+ // Obtain a TIFFImageMetadata object.
+ if(inData instanceof TIFFImageMetadata) {
+ // Create a new metadata object from a clone of the input IFD.
+ TIFFIFD inIFD = ((TIFFImageMetadata)inData).getRootIFD();
+ outData = new TIFFImageMetadata(inIFD.getShallowClone());
+ } else if(Arrays.asList(inData.getMetadataFormatNames()).contains(
+ TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME)) {
+ // Initialize from the native metadata form of the input tree.
+ try {
+ outData = convertNativeImageMetadata(inData);
+ } catch(IIOInvalidTreeException e) {
+ return null;
+ }
+ } else if(inData.isStandardMetadataFormatSupported()) {
+ // Initialize from the standard metadata form of the input tree.
+ try {
+ outData = convertStandardImageMetadata(inData);
+ } catch(IIOInvalidTreeException e) {
+ return null;
+ }
+ }
+
+ // Update the metadata per the image type and param.
+ if(outData != null) {
+ TIFFImageWriter bogusWriter =
+ new TIFFImageWriter(this.originatingProvider);
+ bogusWriter.imageMetadata = outData;
+ bogusWriter.param = param;
+ SampleModel sm = imageType.getSampleModel();
+ try {
+ bogusWriter.setupMetadata(imageType.getColorModel(), sm,
+ sm.getWidth(), sm.getHeight());
+ return bogusWriter.imageMetadata;
+ } catch(IIOException e) {
+ return null;
+ }
+ }
+
+ return outData;
+ }
+
+ /**
+ * Converts a standard <code>javax_imageio_1.0</code> tree to a
+ * <code>TIFFImageMetadata</code> object.
+ *
+ * @param inData The metadata object.
+ * @return a <code>TIFFImageMetadata</code> or <code>null</code> if
+ * the standard tree derived from the input object is <code>null</code>.
+ * @throws IllegalArgumentException if <code>inData</code> is
+ * <code>null</code>.
+ * @throws IllegalArgumentException if <code>inData</code> does not support
+ * the standard metadata format.
+ * @throws IIOInvalidTreeException if <code>inData</code> generates an
+ * invalid standard metadata tree.
+ */
+ private TIFFImageMetadata convertStandardImageMetadata(IIOMetadata inData)
+ throws IIOInvalidTreeException {
+
+ if(inData == null) {
+ throw new NullPointerException("inData == null!");
+ } else if(!inData.isStandardMetadataFormatSupported()) {
+ throw new IllegalArgumentException
+ ("inData does not support standard metadata format!");
+ }
+
+ TIFFImageMetadata outData = null;
+
+ String formatName = IIOMetadataFormatImpl.standardMetadataFormatName;
+ Node tree = inData.getAsTree(formatName);
+ if (tree != null) {
+ List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1);
+ tagSets.add(BaselineTIFFTagSet.getInstance());
+ outData = new TIFFImageMetadata(tagSets);
+ outData.setFromTree(formatName, tree);
+ }
+
+ return outData;
+ }
+
+ /**
+ * Converts a native
+ * <code>javax_imageio_tiff_image_1.0</code> tree to a
+ * <code>TIFFImageMetadata</code> object.
+ *
+ * @param inData The metadata object.
+ * @return a <code>TIFFImageMetadata</code> or <code>null</code> if
+ * the native tree derived from the input object is <code>null</code>.
+ * @throws IllegalArgumentException if <code>inData</code> is
+ * <code>null</code> or does not support the native metadata format.
+ * @throws IIOInvalidTreeException if <code>inData</code> generates an
+ * invalid native metadata tree.
+ */
+ private TIFFImageMetadata convertNativeImageMetadata(IIOMetadata inData)
+ throws IIOInvalidTreeException {
+
+ if(inData == null) {
+ throw new NullPointerException("inData == null!");
+ } else if(!Arrays.asList(inData.getMetadataFormatNames()).contains(
+ TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME)) {
+ throw new IllegalArgumentException
+ ("inData does not support native metadata format!");
+ }
+
+ TIFFImageMetadata outData = null;
+
+ String formatName = TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME;
+ Node tree = inData.getAsTree(formatName);
+ if (tree != null) {
+ List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1);
+ tagSets.add(BaselineTIFFTagSet.getInstance());
+ outData = new TIFFImageMetadata(tagSets);
+ outData.setFromTree(formatName, tree);
+ }
+
+ return outData;
+ }
+
+ /**
+ * Sets up the output metadata adding, removing, and overriding fields
+ * as needed. The destination image dimensions are provided as parameters
+ * because these might differ from those of the source due to subsampling.
+ *
+ * @param cm The <code>ColorModel</code> of the image being written.
+ * @param sm The <code>SampleModel</code> of the image being written.
+ * @param destWidth The width of the written image after subsampling.
+ * @param destHeight The height of the written image after subsampling.
+ */
+ void setupMetadata(ColorModel cm, SampleModel sm,
+ int destWidth, int destHeight)
+ throws IIOException {
+ // Get initial IFD from metadata
+
+ // Always emit these fields:
+ //
+ // Override values from metadata:
+ //
+ // planarConfiguration -> chunky (planar not supported on output)
+ //
+ // Override values from metadata with image-derived values:
+ //
+ // bitsPerSample (if not bilivel)
+ // colorMap (if palette color)
+ // photometricInterpretation (derive from image)
+ // imageLength
+ // imageWidth
+ //
+ // rowsPerStrip \ / tileLength
+ // stripOffsets | OR | tileOffsets
+ // stripByteCounts / | tileByteCounts
+ // \ tileWidth
+ //
+ //
+ // Override values from metadata with write param values:
+ //
+ // compression
+
+ // Use values from metadata if present for these fields,
+ // otherwise use defaults:
+ //
+ // resolutionUnit
+ // XResolution (take from metadata if present)
+ // YResolution
+ // rowsPerStrip
+ // sampleFormat
+
+ TIFFIFD rootIFD = imageMetadata.getRootIFD();
+
+ BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
+
+ // If PlanarConfiguration field present, set value to chunky.
+
+ TIFFField f =
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION);
+ if(f != null &&
+ f.getAsInt(0) != BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY) {
+ TIFFField planarConfigurationField =
+ new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION),
+ BaselineTIFFTagSet.PLANAR_CONFIGURATION_CHUNKY);
+ rootIFD.addTIFFField(planarConfigurationField);
+ }
+
+ char[] extraSamples = null;
+
+ this.photometricInterpretation = -1;
+ boolean forcePhotometricInterpretation = false;
+
+ f =
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+ if (f != null) {
+ photometricInterpretation = f.getAsInt(0);
+ if(photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR &&
+ !(cm instanceof IndexColorModel)) {
+ photometricInterpretation = -1;
+ } else {
+ forcePhotometricInterpretation = true;
+ }
+ }
+
+ int[] sampleSize = sm.getSampleSize();
+
+ int numBands = sm.getNumBands();
+ int numExtraSamples = 0;
+
+ // Check that numBands > 1 here because TIFF requires that
+ // SamplesPerPixel = numBands + numExtraSamples and numBands
+ // cannot be zero.
+ if (numBands > 1 && cm != null && cm.hasAlpha()) {
+ --numBands;
+ numExtraSamples = 1;
+ extraSamples = new char[1];
+ if (cm.isAlphaPremultiplied()) {
+ extraSamples[0] =
+ BaselineTIFFTagSet.EXTRA_SAMPLES_ASSOCIATED_ALPHA;
+ } else {
+ extraSamples[0] =
+ BaselineTIFFTagSet.EXTRA_SAMPLES_UNASSOCIATED_ALPHA;
+ }
+ }
+
+ if (numBands == 3) {
+ this.nativePhotometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB;
+ if (photometricInterpretation == -1) {
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB;
+ }
+ } else if (sm.getNumBands() == 1 && cm instanceof IndexColorModel) {
+ IndexColorModel icm = (IndexColorModel)cm;
+ int r0 = icm.getRed(0);
+ int r1 = icm.getRed(1);
+ if (icm.getMapSize() == 2 &&
+ (r0 == icm.getGreen(0)) && (r0 == icm.getBlue(0)) &&
+ (r1 == icm.getGreen(1)) && (r1 == icm.getBlue(1)) &&
+ (r0 == 0 || r0 == 255) &&
+ (r1 == 0 || r1 == 255) &&
+ (r0 != r1)) {
+ // Black/white image
+
+ if (r0 == 0) {
+ nativePhotometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
+ } else {
+ nativePhotometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
+ }
+
+
+ // If photometricInterpretation is already set to
+ // WhiteIsZero or BlackIsZero, leave it alone
+ if (photometricInterpretation !=
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO &&
+ photometricInterpretation !=
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO) {
+ photometricInterpretation =
+ r0 == 0 ?
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO :
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
+ }
+ } else {
+ nativePhotometricInterpretation =
+ photometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR;
+ }
+ } else {
+ if(cm != null) {
+ switch(cm.getColorSpace().getType()) {
+ case ColorSpace.TYPE_Lab:
+ nativePhotometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB;
+ break;
+ case ColorSpace.TYPE_YCbCr:
+ nativePhotometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR;
+ break;
+ case ColorSpace.TYPE_CMYK:
+ nativePhotometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CMYK;
+ break;
+ default:
+ nativePhotometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
+ }
+ } else {
+ nativePhotometricInterpretation =
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
+ }
+ if (photometricInterpretation == -1) {
+ photometricInterpretation = nativePhotometricInterpretation;
+ }
+ }
+
+ // Emit compression tag
+
+ int compressionMode = param.getCompressionMode();
+ switch(compressionMode) {
+ case ImageWriteParam.MODE_EXPLICIT:
+ {
+ String compressionType = param.getCompressionType();
+ if (compressionType == null) {
+ this.compression = BaselineTIFFTagSet.COMPRESSION_NONE;
+ } else {
+ // Determine corresponding compression tag value.
+ int len = compressionTypes.length;
+ for (int i = 0; i < len; i++) {
+ if (compressionType.equals(compressionTypes[i])) {
+ this.compression = compressionNumbers[i];
+ }
+ }
+ }
+ }
+ break;
+ case ImageWriteParam.MODE_COPY_FROM_METADATA:
+ {
+ TIFFField compField =
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
+ if(compField != null) {
+ this.compression = compField.getAsInt(0);
+ } else {
+ this.compression = BaselineTIFFTagSet.COMPRESSION_NONE;
+ }
+ }
+ break;
+ default:
+ this.compression = BaselineTIFFTagSet.COMPRESSION_NONE;
+ }
+
+ TIFFField predictorField =
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_PREDICTOR);
+ if (predictorField != null) {
+ this.predictor = predictorField.getAsInt(0);
+
+ // We only support Horizontal Predictor for a bitDepth of 8
+ if (sampleSize[0] != 8 ||
+ // Check the value of the tag for validity
+ (predictor != BaselineTIFFTagSet.PREDICTOR_NONE &&
+ predictor !=
+ BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING)) {
+ // Set to default
+ predictor = BaselineTIFFTagSet.PREDICTOR_NONE;
+
+ // Emit this changed predictor value to metadata
+ TIFFField newPredictorField =
+ new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_PREDICTOR),
+ predictor);
+ rootIFD.addTIFFField(newPredictorField);
+ }
+ }
+
+ TIFFField compressionField =
+ new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_COMPRESSION),
+ compression);
+ rootIFD.addTIFFField(compressionField);
+
+ // Set Exif flag. Note that there is no way to determine definitively
+ // when an uncompressed thumbnail is being written as the Exif IFD
+ // pointer field is optional for thumbnails.
+ boolean isExif = false;
+ if(numBands == 3 &&
+ sampleSize[0] == 8 && sampleSize[1] == 8 && sampleSize[2] == 8) {
+ // Three bands with 8 bits per sample.
+ if(rootIFD.getTIFFField(ExifParentTIFFTagSet.TAG_EXIF_IFD_POINTER)
+ != null) {
+ // Exif IFD pointer present.
+ if(compression == BaselineTIFFTagSet.COMPRESSION_NONE &&
+ (photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB ||
+ photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR)) {
+ // Uncompressed RGB or YCbCr.
+ isExif = true;
+ } else if(compression ==
+ BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
+ // Compressed.
+ isExif = true;
+ }
+ } else if(compressionMode == ImageWriteParam.MODE_EXPLICIT &&
+ EXIF_JPEG_COMPRESSION_TYPE.equals
+ (param.getCompressionType())) {
+ // Exif IFD pointer absent but Exif JPEG compression set.
+ isExif = true;
+ }
+ }
+
+ // Initialize JPEG interchange format flag which is used to
+ // indicate that the image is stored as a single JPEG stream.
+ // This flag is separated from the 'isExif' flag in case JPEG
+ // interchange format is eventually supported for non-Exif images.
+ boolean isJPEGInterchange =
+ isExif && compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG;
+
+ this.compressor = null;
+ if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_RLE) {
+ compressor = new TIFFRLECompressor();
+
+ if (!forcePhotometricInterpretation) {
+ photometricInterpretation
+ = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
+ }
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_CCITT_T_4) {
+ compressor = new TIFFT4Compressor();
+
+ if (!forcePhotometricInterpretation) {
+ photometricInterpretation
+ = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
+ }
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_CCITT_T_6) {
+ compressor = new TIFFT6Compressor();
+
+ if (!forcePhotometricInterpretation) {
+ photometricInterpretation
+ = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
+ }
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_LZW) {
+ compressor = new TIFFLZWCompressor(predictor);
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
+ if (isExif) {
+ compressor = new TIFFExifJPEGCompressor(param);
+ } else {
+ throw new IIOException("Old JPEG compression not supported!");
+ }
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_JPEG) {
+ if (numBands == 3 && sampleSize[0] == 8
+ && sampleSize[1] == 8 && sampleSize[2] == 8) {
+ photometricInterpretation
+ = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR;
+ } else if (numBands == 1 && sampleSize[0] == 8) {
+ photometricInterpretation
+ = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
+ } else {
+ throw new IIOException("JPEG compression supported for 1- and 3-band byte images only!");
+ }
+ compressor = new TIFFJPEGCompressor(param);
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_ZLIB) {
+ compressor = new TIFFZLibCompressor(param, predictor);
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_PACKBITS) {
+ compressor = new TIFFPackBitsCompressor();
+ } else if (compression
+ == BaselineTIFFTagSet.COMPRESSION_DEFLATE) {
+ compressor = new TIFFDeflateCompressor(param, predictor);
+ } else {
+ // Determine inverse fill setting.
+ f = rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER);
+ boolean inverseFill = (f != null && f.getAsInt(0) == 2);
+
+ if (inverseFill) {
+ compressor = new TIFFLSBCompressor();
+ } else {
+ compressor = new TIFFNullCompressor();
+ }
+ }
+
+
+ this.colorConverter = null;
+ if (cm != null
+ && cm.getColorSpace().getType() == ColorSpace.TYPE_RGB) {
+ //
+ // Perform color conversion only if image has RGB color space.
+ //
+ if (photometricInterpretation
+ == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR
+ && compression
+ != BaselineTIFFTagSet.COMPRESSION_JPEG) {
+ //
+ // Convert RGB to YCbCr only if compression type is not
+ // JPEG in which case this is handled implicitly by the
+ // compressor.
+ //
+ colorConverter = new TIFFYCbCrColorConverter(imageMetadata);
+ } else if (photometricInterpretation
+ == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB) {
+ colorConverter = new TIFFCIELabColorConverter();
+ }
+ }
+
+ //
+ // Cannot at this time do YCbCr subsampling so set the
+ // YCbCrSubsampling field value to [1, 1] and the YCbCrPositioning
+ // field value to "cosited".
+ //
+ if(photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR &&
+ compression !=
+ BaselineTIFFTagSet.COMPRESSION_JPEG) {
+ // Remove old subsampling and positioning fields.
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING);
+
+ // Add unity chrominance subsampling factors.
+ rootIFD.addTIFFField
+ (new TIFFField
+ (base.getTag(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING),
+ TIFFTag.TIFF_SHORT,
+ 2,
+ new char[] {(char)1, (char)1}));
+
+ // Add cosited positioning.
+ rootIFD.addTIFFField
+ (new TIFFField
+ (base.getTag(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING),
+ TIFFTag.TIFF_SHORT,
+ 1,
+ new char[] {
+ (char)BaselineTIFFTagSet.Y_CB_CR_POSITIONING_COSITED
+ }));
+ }
+
+ TIFFField photometricInterpretationField =
+ new TIFFField(
+ base.getTag(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION),
+ photometricInterpretation);
+ rootIFD.addTIFFField(photometricInterpretationField);
+
+ this.bitsPerSample = new char[numBands + numExtraSamples];
+ this.bitDepth = 0;
+ for (int i = 0; i < numBands; i++) {
+ this.bitDepth = Math.max(bitDepth, sampleSize[i]);
+ }
+ if (bitDepth == 3) {
+ bitDepth = 4;
+ } else if (bitDepth > 4 && bitDepth < 8) {
+ bitDepth = 8;
+ } else if (bitDepth > 8 && bitDepth < 16) {
+ bitDepth = 16;
+ } else if (bitDepth > 16 && bitDepth < 32) {
+ bitDepth = 32;
+ } else if (bitDepth > 32) {
+ bitDepth = 64;
+ }
+
+ for (int i = 0; i < bitsPerSample.length; i++) {
+ bitsPerSample[i] = (char)bitDepth;
+ }
+
+ // Emit BitsPerSample. If the image is bilevel, emit if and only
+ // if already in the metadata and correct (count and value == 1).
+ if (bitsPerSample.length != 1 || bitsPerSample[0] != 1) {
+ TIFFField bitsPerSampleField =
+ new TIFFField(
+ base.getTag(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE),
+ TIFFTag.TIFF_SHORT,
+ bitsPerSample.length,
+ bitsPerSample);
+ rootIFD.addTIFFField(bitsPerSampleField);
+ } else { // bitsPerSample.length == 1 && bitsPerSample[0] == 1
+ TIFFField bitsPerSampleField =
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ if(bitsPerSampleField != null) {
+ int[] bps = bitsPerSampleField.getAsInts();
+ if(bps == null || bps.length != 1 || bps[0] != 1) {
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ }
+ }
+ }
+
+ // Prepare SampleFormat field.
+ f = rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
+ if(f == null && (bitDepth == 16 || bitDepth == 32 || bitDepth == 64)) {
+ // Set up default content for 16-, 32-, and 64-bit cases.
+ char sampleFormatValue;
+ int dataType = sm.getDataType();
+ if(bitDepth == 16 && dataType == DataBuffer.TYPE_USHORT) {
+ sampleFormatValue =
+ (char)BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER;
+ } else if((bitDepth == 32 && dataType == DataBuffer.TYPE_FLOAT) ||
+ (bitDepth == 64 && dataType == DataBuffer.TYPE_DOUBLE)) {
+ sampleFormatValue =
+ (char)BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT;
+ } else {
+ sampleFormatValue =
+ BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER;
+ }
+ this.sampleFormat = (int)sampleFormatValue;
+ char[] sampleFormatArray = new char[bitsPerSample.length];
+ Arrays.fill(sampleFormatArray, sampleFormatValue);
+
+ // Update the metadata.
+ TIFFTag sampleFormatTag =
+ base.getTag(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
+
+ TIFFField sampleFormatField =
+ new TIFFField(sampleFormatTag, TIFFTag.TIFF_SHORT,
+ sampleFormatArray.length, sampleFormatArray);
+
+ rootIFD.addTIFFField(sampleFormatField);
+ } else if(f != null) {
+ // Get whatever was provided.
+ sampleFormat = f.getAsInt(0);
+ } else {
+ // Set default value for internal use only.
+ sampleFormat = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED;
+ }
+
+ if (extraSamples != null) {
+ TIFFField extraSamplesField =
+ new TIFFField(
+ base.getTag(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES),
+ TIFFTag.TIFF_SHORT,
+ extraSamples.length,
+ extraSamples);
+ rootIFD.addTIFFField(extraSamplesField);
+ } else {
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES);
+ }
+
+ TIFFField samplesPerPixelField =
+ new TIFFField(
+ base.getTag(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL),
+ bitsPerSample.length);
+ rootIFD.addTIFFField(samplesPerPixelField);
+
+ // Emit ColorMap if image is of palette color type
+ if (photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR &&
+ cm instanceof IndexColorModel) {
+ char[] colorMap = new char[3*(1 << bitsPerSample[0])];
+
+ IndexColorModel icm = (IndexColorModel)cm;
+
+ // mapSize is determined by BitsPerSample, not by incoming ICM.
+ int mapSize = 1 << bitsPerSample[0];
+ int indexBound = Math.min(mapSize, icm.getMapSize());
+ for (int i = 0; i < indexBound; i++) {
+ colorMap[i] = (char)((icm.getRed(i)*65535)/255);
+ colorMap[mapSize + i] = (char)((icm.getGreen(i)*65535)/255);
+ colorMap[2*mapSize + i] = (char)((icm.getBlue(i)*65535)/255);
+ }
+
+ TIFFField colorMapField =
+ new TIFFField(
+ base.getTag(BaselineTIFFTagSet.TAG_COLOR_MAP),
+ TIFFTag.TIFF_SHORT,
+ colorMap.length,
+ colorMap);
+ rootIFD.addTIFFField(colorMapField);
+ } else {
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_COLOR_MAP);
+ }
+
+ // Emit ICCProfile if there is no ICCProfile field already in the
+ // metadata and the ColorSpace is non-standard ICC.
+ if(cm != null &&
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_ICC_PROFILE) == null &&
+ ImageUtil.isNonStandardICCColorSpace(cm.getColorSpace())) {
+ ICC_ColorSpace iccColorSpace = (ICC_ColorSpace)cm.getColorSpace();
+ byte[] iccProfileData = iccColorSpace.getProfile().getData();
+ TIFFField iccProfileField =
+ new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_ICC_PROFILE),
+ TIFFTag.TIFF_UNDEFINED,
+ iccProfileData.length,
+ iccProfileData);
+ rootIFD.addTIFFField(iccProfileField);
+ }
+
+ // Always emit XResolution and YResolution.
+
+ TIFFField XResolutionField =
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_X_RESOLUTION);
+ TIFFField YResolutionField =
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
+
+ if(XResolutionField == null && YResolutionField == null) {
+ long[][] resRational = new long[1][2];
+ resRational[0] = new long[2];
+
+ TIFFField ResolutionUnitField =
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT);
+
+ // Don't force dimensionless if one of the other dimensional
+ // quantities is present.
+ if(ResolutionUnitField == null &&
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_X_POSITION) == null &&
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_Y_POSITION) == null) {
+ // Set resolution to unit and units to dimensionless.
+ resRational[0][0] = 1;
+ resRational[0][1] = 1;
+
+ ResolutionUnitField =
+ new TIFFField(rootIFD.getTag
+ (BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
+ BaselineTIFFTagSet.RESOLUTION_UNIT_NONE);
+ rootIFD.addTIFFField(ResolutionUnitField);
+ } else {
+ // Set resolution to a value which would make the maximum
+ // image dimension equal to 4 inches as arbitrarily stated
+ // in the description of ResolutionUnit in the TIFF 6.0
+ // specification. If the ResolutionUnit field specifies
+ // "none" then set the resolution to unity (1/1).
+ int resolutionUnit = ResolutionUnitField != null ?
+ ResolutionUnitField.getAsInt(0) :
+ BaselineTIFFTagSet.RESOLUTION_UNIT_INCH;
+ int maxDimension = Math.max(destWidth, destHeight);
+ switch(resolutionUnit) {
+ case BaselineTIFFTagSet.RESOLUTION_UNIT_INCH:
+ resRational[0][0] = maxDimension;
+ resRational[0][1] = 4;
+ break;
+ case BaselineTIFFTagSet.RESOLUTION_UNIT_CENTIMETER:
+ resRational[0][0] = 100L*maxDimension; // divide out 100
+ resRational[0][1] = 4*254; // 2.54 cm/inch * 100
+ break;
+ default:
+ resRational[0][0] = 1;
+ resRational[0][1] = 1;
+ }
+ }
+
+ XResolutionField =
+ new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ resRational);
+ rootIFD.addTIFFField(XResolutionField);
+
+ YResolutionField =
+ new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ resRational);
+ rootIFD.addTIFFField(YResolutionField);
+ } else if(XResolutionField == null && YResolutionField != null) {
+ // Set XResolution to YResolution.
+ long[] yResolution =
+ YResolutionField.getAsRational(0).clone();
+ XResolutionField =
+ new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ yResolution);
+ rootIFD.addTIFFField(XResolutionField);
+ } else if(XResolutionField != null && YResolutionField == null) {
+ // Set YResolution to XResolution.
+ long[] xResolution =
+ XResolutionField.getAsRational(0).clone();
+ YResolutionField =
+ new TIFFField(rootIFD.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION),
+ TIFFTag.TIFF_RATIONAL,
+ 1,
+ xResolution);
+ rootIFD.addTIFFField(YResolutionField);
+ }
+
+ // Set mandatory fields, overriding metadata passed in
+
+ int width = destWidth;
+ TIFFField imageWidthField =
+ new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_IMAGE_WIDTH),
+ width);
+ rootIFD.addTIFFField(imageWidthField);
+
+ int height = destHeight;
+ TIFFField imageLengthField =
+ new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_IMAGE_LENGTH),
+ height);
+ rootIFD.addTIFFField(imageLengthField);
+
+ // Determine rowsPerStrip
+
+ int rowsPerStrip;
+
+ TIFFField rowsPerStripField =
+ rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
+ if (rowsPerStripField != null) {
+ rowsPerStrip = rowsPerStripField.getAsInt(0);
+ if(rowsPerStrip < 0) {
+ rowsPerStrip = height;
+ }
+ } else {
+ int bitsPerPixel = bitDepth*(numBands + numExtraSamples);
+ int bytesPerRow = (bitsPerPixel*width + 7)/8;
+ rowsPerStrip =
+ Math.max(Math.max(DEFAULT_BYTES_PER_STRIP/bytesPerRow, 1), 8);
+ }
+ rowsPerStrip = Math.min(rowsPerStrip, height);
+
+ // Tiling flag.
+ boolean useTiling = false;
+
+ // Analyze tiling parameters
+ int tilingMode = param.getTilingMode();
+ if (tilingMode == ImageWriteParam.MODE_DISABLED ||
+ tilingMode == ImageWriteParam.MODE_DEFAULT) {
+ this.tileWidth = width;
+ this.tileLength = rowsPerStrip;
+ useTiling = false;
+ } else if (tilingMode == ImageWriteParam.MODE_EXPLICIT) {
+ tileWidth = param.getTileWidth();
+ tileLength = param.getTileHeight();
+ useTiling = true;
+ } else if (tilingMode == ImageWriteParam.MODE_COPY_FROM_METADATA) {
+ f = rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
+ if (f == null) {
+ tileWidth = width;
+ useTiling = false;
+ } else {
+ tileWidth = f.getAsInt(0);
+ useTiling = true;
+ }
+
+ f = rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_TILE_LENGTH);
+ if (f == null) {
+ tileLength = rowsPerStrip;
+ } else {
+ tileLength = f.getAsInt(0);
+ useTiling = true;
+ }
+ } else {
+ throw new IIOException("Illegal value of tilingMode!");
+ }
+
+ if(compression == BaselineTIFFTagSet.COMPRESSION_JPEG) {
+ // Reset tile size per TTN2 spec for JPEG compression.
+ int subX;
+ int subY;
+ if(numBands == 1) {
+ subX = subY = 1;
+ } else {
+ subX = subY = TIFFJPEGCompressor.CHROMA_SUBSAMPLING;
+ }
+ if(useTiling) {
+ int MCUMultipleX = 8*subX;
+ int MCUMultipleY = 8*subY;
+ tileWidth =
+ Math.max(MCUMultipleX*((tileWidth +
+ MCUMultipleX/2)/MCUMultipleX),
+ MCUMultipleX);
+ tileLength =
+ Math.max(MCUMultipleY*((tileLength +
+ MCUMultipleY/2)/MCUMultipleY),
+ MCUMultipleY);
+ } else if(rowsPerStrip < height) {
+ int MCUMultiple = 8*Math.max(subX, subY);
+ rowsPerStrip = tileLength =
+ Math.max(MCUMultiple*((tileLength +
+ MCUMultiple/2)/MCUMultiple),
+ MCUMultiple);
+ }
+
+ // The written image may be unreadable if these fields are present.
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
+
+ // Also remove fields related to the old JPEG encoding scheme
+ // which may be misleading when the compression type is JPEG.
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_PROC);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_RESTART_INTERVAL);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_LOSSLESS_PREDICTORS);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_POINT_TRANSFORMS);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_DC_TABLES);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_AC_TABLES);
+ } else if(isJPEGInterchange) {
+ // Force tile size to equal image size.
+ tileWidth = width;
+ tileLength = height;
+ } else if(useTiling) {
+ // Round tile size to multiple of 16 per TIFF 6.0 specification
+ // (see pages 67-68 of version 6.0.1 from Adobe).
+ int tileWidthRemainder = tileWidth % 16;
+ if(tileWidthRemainder != 0) {
+ // Round to nearest multiple of 16 not less than 16.
+ tileWidth = Math.max(16*((tileWidth + 8)/16), 16);
+ processWarningOccurred(currentImage,
+ "Tile width rounded to multiple of 16.");
+ }
+
+ int tileLengthRemainder = tileLength % 16;
+ if(tileLengthRemainder != 0) {
+ // Round to nearest multiple of 16 not less than 16.
+ tileLength = Math.max(16*((tileLength + 8)/16), 16);
+ processWarningOccurred(currentImage,
+ "Tile height rounded to multiple of 16.");
+ }
+ }
+
+ this.tilesAcross = (width + tileWidth - 1)/tileWidth;
+ this.tilesDown = (height + tileLength - 1)/tileLength;
+
+ if (!useTiling) {
+ this.isTiled = false;
+
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_TILE_WIDTH);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_TILE_LENGTH);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS);
+
+ rowsPerStripField =
+ new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP),
+ rowsPerStrip);
+ rootIFD.addTIFFField(rowsPerStripField);
+
+ TIFFField stripOffsetsField =
+ new TIFFField(
+ base.getTag(BaselineTIFFTagSet.TAG_STRIP_OFFSETS),
+ TIFFTag.TIFF_LONG,
+ tilesDown);
+ rootIFD.addTIFFField(stripOffsetsField);
+
+ TIFFField stripByteCountsField =
+ new TIFFField(
+ base.getTag(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS),
+ TIFFTag.TIFF_LONG,
+ tilesDown);
+ rootIFD.addTIFFField(stripByteCountsField);
+ } else {
+ this.isTiled = true;
+
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
+
+ TIFFField tileWidthField =
+ new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_TILE_WIDTH),
+ tileWidth);
+ rootIFD.addTIFFField(tileWidthField);
+
+ TIFFField tileLengthField =
+ new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_TILE_LENGTH),
+ tileLength);
+ rootIFD.addTIFFField(tileLengthField);
+
+ TIFFField tileOffsetsField =
+ new TIFFField(
+ base.getTag(BaselineTIFFTagSet.TAG_TILE_OFFSETS),
+ TIFFTag.TIFF_LONG,
+ tilesDown*tilesAcross);
+ rootIFD.addTIFFField(tileOffsetsField);
+
+ TIFFField tileByteCountsField =
+ new TIFFField(
+ base.getTag(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS),
+ TIFFTag.TIFF_LONG,
+ tilesDown*tilesAcross);
+ rootIFD.addTIFFField(tileByteCountsField);
+ }
+
+ if(isExif) {
+ //
+ // Ensure presence of mandatory fields and absence of prohibited
+ // fields and those that duplicate information in JPEG marker
+ // segments per tables 14-18 of the Exif 2.2 specification.
+ //
+
+ // If an empty image is being written or inserted then infer
+ // that the primary IFD is being set up.
+ boolean isPrimaryIFD = isEncodingEmpty();
+
+ // Handle TIFF fields in order of increasing tag number.
+ if(compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
+ // ImageWidth
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
+
+ // ImageLength
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH);
+
+ // BitsPerSample
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ // Compression
+ if(isPrimaryIFD) {
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_COMPRESSION);
+ }
+
+ // PhotometricInterpretation
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
+
+ // StripOffsets
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
+
+ // SamplesPerPixel
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
+
+ // RowsPerStrip
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_ROWS_PER_STRIP);
+
+ // StripByteCounts
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
+ // XResolution and YResolution are handled above for all TIFFs.
+
+ // PlanarConfiguration
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_PLANAR_CONFIGURATION);
+
+ // ResolutionUnit
+ if(rootIFD.getTIFFField
+ (BaselineTIFFTagSet.TAG_RESOLUTION_UNIT) == null) {
+ f = new TIFFField(base.getTag
+ (BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
+ BaselineTIFFTagSet.RESOLUTION_UNIT_INCH);
+ rootIFD.addTIFFField(f);
+ }
+
+ if(isPrimaryIFD) {
+ // JPEGInterchangeFormat
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
+
+ // JPEGInterchangeFormatLength
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
+
+ // YCbCrSubsampling
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
+
+ // YCbCrPositioning
+ if(rootIFD.getTIFFField
+ (BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING) == null) {
+ f = new TIFFField
+ (base.getTag
+ (BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING),
+ TIFFTag.TIFF_SHORT,
+ 1,
+ new char[] {
+ (char)BaselineTIFFTagSet.Y_CB_CR_POSITIONING_CENTERED
+ });
+ rootIFD.addTIFFField(f);
+ }
+ } else { // Thumbnail IFD
+ // JPEGInterchangeFormat
+ f = new TIFFField
+ (base.getTag
+ (BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT),
+ TIFFTag.TIFF_LONG,
+ 1);
+ rootIFD.addTIFFField(f);
+
+ // JPEGInterchangeFormatLength
+ f = new TIFFField
+ (base.getTag
+ (BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH),
+ TIFFTag.TIFF_LONG,
+ 1);
+ rootIFD.addTIFFField(f);
+
+ // YCbCrSubsampling
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
+ }
+ } else { // Uncompressed
+ // ImageWidth through PlanarConfiguration are set above.
+ // XResolution and YResolution are handled above for all TIFFs.
+
+ // ResolutionUnit
+ if(rootIFD.getTIFFField
+ (BaselineTIFFTagSet.TAG_RESOLUTION_UNIT) == null) {
+ f = new TIFFField(base.getTag
+ (BaselineTIFFTagSet.TAG_RESOLUTION_UNIT),
+ BaselineTIFFTagSet.RESOLUTION_UNIT_INCH);
+ rootIFD.addTIFFField(f);
+ }
+
+
+ // JPEGInterchangeFormat
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
+
+ // JPEGInterchangeFormatLength
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
+
+ if(photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB) {
+ // YCbCrCoefficients
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_Y_CB_CR_COEFFICIENTS);
+
+ // YCbCrSubsampling
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
+
+ // YCbCrPositioning
+ rootIFD.removeTIFFField
+ (BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING);
+ }
+ }
+
+ // Get Exif tags.
+ TIFFTagSet exifTags = ExifTIFFTagSet.getInstance();
+
+ // Retrieve or create the Exif IFD.
+ TIFFIFD exifIFD = null;
+ f = rootIFD.getTIFFField
+ (ExifParentTIFFTagSet.TAG_EXIF_IFD_POINTER);
+ if(f != null && f.hasDirectory()) {
+ // Retrieve the Exif IFD.
+ exifIFD = (TIFFIFD)f.getDirectory();
+ } else if(isPrimaryIFD) {
+ // Create the Exif IFD.
+ List<TIFFTagSet> exifTagSets = new ArrayList<TIFFTagSet>(1);
+ exifTagSets.add(exifTags);
+ exifIFD = new TIFFIFD(exifTagSets);
+
+ // Add it to the root IFD.
+ TIFFTagSet tagSet = ExifParentTIFFTagSet.getInstance();
+ TIFFTag exifIFDTag =
+ tagSet.getTag(ExifParentTIFFTagSet.TAG_EXIF_IFD_POINTER);
+ rootIFD.addTIFFField(new TIFFField(exifIFDTag,
+ TIFFTag.TIFF_LONG,
+ 1L,
+ exifIFD));
+ }
+
+ if(exifIFD != null) {
+ // Handle Exif private fields in order of increasing
+ // tag number.
+
+ // ExifVersion
+ if(exifIFD.getTIFFField
+ (ExifTIFFTagSet.TAG_EXIF_VERSION) == null) {
+ f = new TIFFField
+ (exifTags.getTag(ExifTIFFTagSet.TAG_EXIF_VERSION),
+ TIFFTag.TIFF_UNDEFINED,
+ 4,
+ ExifTIFFTagSet.EXIF_VERSION_2_2.getBytes(StandardCharsets.US_ASCII));
+ exifIFD.addTIFFField(f);
+ }
+
+ if(compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
+ // ComponentsConfiguration
+ if(exifIFD.getTIFFField
+ (ExifTIFFTagSet.TAG_COMPONENTS_CONFIGURATION) == null) {
+ f = new TIFFField
+ (exifTags.getTag
+ (ExifTIFFTagSet.TAG_COMPONENTS_CONFIGURATION),
+ TIFFTag.TIFF_UNDEFINED,
+ 4,
+ new byte[] {
+ (byte)ExifTIFFTagSet.COMPONENTS_CONFIGURATION_Y,
+ (byte)ExifTIFFTagSet.COMPONENTS_CONFIGURATION_CB,
+ (byte)ExifTIFFTagSet.COMPONENTS_CONFIGURATION_CR,
+ (byte)0
+ });
+ exifIFD.addTIFFField(f);
+ }
+ } else {
+ // ComponentsConfiguration
+ exifIFD.removeTIFFField
+ (ExifTIFFTagSet.TAG_COMPONENTS_CONFIGURATION);
+
+ // CompressedBitsPerPixel
+ exifIFD.removeTIFFField
+ (ExifTIFFTagSet.TAG_COMPRESSED_BITS_PER_PIXEL);
+ }
+
+ // FlashpixVersion
+ if(exifIFD.getTIFFField
+ (ExifTIFFTagSet.TAG_FLASHPIX_VERSION) == null) {
+ f = new TIFFField
+ (exifTags.getTag(ExifTIFFTagSet.TAG_FLASHPIX_VERSION),
+ TIFFTag.TIFF_UNDEFINED,
+ 4,
+ new byte[] {(byte)'0', (byte)'1', (byte)'0', (byte)'0'});
+ exifIFD.addTIFFField(f);
+ }
+
+ // ColorSpace
+ if(exifIFD.getTIFFField
+ (ExifTIFFTagSet.TAG_COLOR_SPACE) == null) {
+ f = new TIFFField
+ (exifTags.getTag(ExifTIFFTagSet.TAG_COLOR_SPACE),
+ TIFFTag.TIFF_SHORT,
+ 1,
+ new char[] {
+ (char)ExifTIFFTagSet.COLOR_SPACE_SRGB
+ });
+ exifIFD.addTIFFField(f);
+ }
+
+ if(compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
+ // PixelXDimension
+ if(exifIFD.getTIFFField
+ (ExifTIFFTagSet.TAG_PIXEL_X_DIMENSION) == null) {
+ f = new TIFFField
+ (exifTags.getTag(ExifTIFFTagSet.TAG_PIXEL_X_DIMENSION),
+ width);
+ exifIFD.addTIFFField(f);
+ }
+
+ // PixelYDimension
+ if(exifIFD.getTIFFField
+ (ExifTIFFTagSet.TAG_PIXEL_Y_DIMENSION) == null) {
+ f = new TIFFField
+ (exifTags.getTag(ExifTIFFTagSet.TAG_PIXEL_Y_DIMENSION),
+ height);
+ exifIFD.addTIFFField(f);
+ }
+ } else {
+ exifIFD.removeTIFFField
+ (ExifTIFFTagSet.TAG_INTEROPERABILITY_IFD_POINTER);
+ }
+ }
+
+ } // if(isExif)
+ }
+
+ ImageTypeSpecifier getImageType() {
+ return imageType;
+ }
+
+ /**
+ @param tileRect The area to be written which might be outside the image.
+ */
+ private int writeTile(Rectangle tileRect, TIFFCompressor compressor)
+ throws IOException {
+ // Determine the rectangle which will actually be written
+ // and set the padding flag. Padding will occur only when the
+ // image is written as a tiled TIFF and the tile bounds are not
+ // contained within the image bounds.
+ Rectangle activeRect;
+ boolean isPadded;
+ Rectangle imageBounds =
+ new Rectangle(image.getMinX(), image.getMinY(),
+ image.getWidth(), image.getHeight());
+ if(!isTiled) {
+ // Stripped
+ activeRect = tileRect.intersection(imageBounds);
+ tileRect = activeRect;
+ isPadded = false;
+ } else if(imageBounds.contains(tileRect)) {
+ // Tiled, tile within image bounds
+ activeRect = tileRect;
+ isPadded = false;
+ } else {
+ // Tiled, tile not within image bounds
+ activeRect = imageBounds.intersection(tileRect);
+ isPadded = true;
+ }
+
+ // Return early if empty intersection.
+ if(activeRect.isEmpty()) {
+ return 0;
+ }
+
+ int minX = tileRect.x;
+ int minY = tileRect.y;
+ int width = tileRect.width;
+ int height = tileRect.height;
+
+ if(isImageSimple) {
+
+ SampleModel sm = image.getSampleModel();
+
+ // Read only data from the active rectangle.
+ Raster raster = image.getData(activeRect);
+
+ // If padding is required, create a larger Raster and fill
+ // it from the active rectangle.
+ if(isPadded) {
+ WritableRaster wr =
+ raster.createCompatibleWritableRaster(minX, minY,
+ width, height);
+ wr.setRect(raster);
+ raster = wr;
+ }
+
+ if(isBilevel) {
+ byte[] buf = ImageUtil.getPackedBinaryData(raster,
+ tileRect);
+
+ if(isInverted) {
+ DataBuffer dbb = raster.getDataBuffer();
+ if(dbb instanceof DataBufferByte &&
+ buf == ((DataBufferByte)dbb).getData()) {
+ byte[] bbuf = new byte[buf.length];
+ int len = buf.length;
+ for(int i = 0; i < len; i++) {
+ bbuf[i] = (byte)(buf[i] ^ 0xff);
+ }
+ buf = bbuf;
+ } else {
+ int len = buf.length;
+ for(int i = 0; i < len; i++) {
+ buf[i] ^= 0xff;
+ }
+ }
+ }
+
+ return compressor.encode(buf, 0,
+ width, height, sampleSize,
+ (tileRect.width + 7)/8);
+ } else if(bitDepth == 8 &&
+ sm.getDataType() == DataBuffer.TYPE_BYTE) {
+ ComponentSampleModel csm =
+ (ComponentSampleModel)raster.getSampleModel();
+
+ byte[] buf =
+ ((DataBufferByte)raster.getDataBuffer()).getData();
+
+ int off =
+ csm.getOffset(minX -
+ raster.getSampleModelTranslateX(),
+ minY -
+ raster.getSampleModelTranslateY());
+
+ return compressor.encode(buf, off,
+ width, height, sampleSize,
+ csm.getScanlineStride());
+ }
+ }
+
+ // Set offsets and skips based on source subsampling factors
+ int xOffset = minX;
+ int xSkip = periodX;
+ int yOffset = minY;
+ int ySkip = periodY;
+
+ // Early exit if no data for this pass
+ int hpixels = (width + xSkip - 1)/xSkip;
+ int vpixels = (height + ySkip - 1)/ySkip;
+ if (hpixels == 0 || vpixels == 0) {
+ return 0;
+ }
+
+ // Convert X offset and skip from pixels to samples
+ xOffset *= numBands;
+ xSkip *= numBands;
+
+ // Initialize sizes
+ int samplesPerByte = 8/bitDepth;
+ int numSamples = width*numBands;
+ int bytesPerRow = hpixels*numBands;
+
+ // Update number of bytes per row.
+ if (bitDepth < 8) {
+ bytesPerRow = (bytesPerRow + samplesPerByte - 1)/samplesPerByte;
+ } else if (bitDepth == 16) {
+ bytesPerRow *= 2;
+ } else if (bitDepth == 32) {
+ bytesPerRow *= 4;
+ } else if (bitDepth == 64) {
+ bytesPerRow *= 8;
+ }
+
+ // Create row buffers
+ int[] samples = null;
+ float[] fsamples = null;
+ double[] dsamples = null;
+ if(sampleFormat == BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ if (bitDepth == 32) {
+ fsamples = new float[numSamples];
+ } else {
+ dsamples = new double[numSamples];
+ }
+ } else {
+ samples = new int[numSamples];
+ }
+
+ // Create tile buffer
+ byte[] currTile = new byte[bytesPerRow*vpixels];
+
+ // Sub-optimal case: shy of "isImageSimple" only by virtue of
+ // not being contiguous.
+ if(!isInverted && // no inversion
+ !isRescaling && // no value rescaling
+ sourceBands == null && // no subbanding
+ periodX == 1 && periodY == 1 && // no subsampling
+ colorConverter == null) {
+
+ SampleModel sm = image.getSampleModel();
+
+ if(sm instanceof ComponentSampleModel && // component
+ bitDepth == 8 && // 8 bits/sample
+ sm.getDataType() == DataBuffer.TYPE_BYTE) { // byte type
+
+ // Read only data from the active rectangle.
+ Raster raster = image.getData(activeRect);
+
+ // If padding is required, create a larger Raster and fill
+ // it from the active rectangle.
+ if(isPadded) {
+ WritableRaster wr =
+ raster.createCompatibleWritableRaster(minX, minY,
+ width, height);
+ wr.setRect(raster);
+ raster = wr;
+ }
+
+ // Get SampleModel info.
+ ComponentSampleModel csm =
+ (ComponentSampleModel)raster.getSampleModel();
+ int[] bankIndices = csm.getBankIndices();
+ byte[][] bankData =
+ ((DataBufferByte)raster.getDataBuffer()).getBankData();
+ int lineStride = csm.getScanlineStride();
+ int pixelStride = csm.getPixelStride();
+
+ // Copy the data into a contiguous pixel interleaved buffer.
+ for(int k = 0; k < numBands; k++) {
+ byte[] bandData = bankData[bankIndices[k]];
+ int lineOffset =
+ csm.getOffset(raster.getMinX() -
+ raster.getSampleModelTranslateX(),
+ raster.getMinY() -
+ raster.getSampleModelTranslateY(), k);
+ int idx = k;
+ for(int j = 0; j < vpixels; j++) {
+ int offset = lineOffset;
+ for(int i = 0; i < hpixels; i++) {
+ currTile[idx] = bandData[offset];
+ idx += numBands;
+ offset += pixelStride;
+ }
+ lineOffset += lineStride;
+ }
+ }
+
+ // Compressor and return.
+ return compressor.encode(currTile, 0,
+ width, height, sampleSize,
+ width*numBands);
+ }
+ }
+
+ int tcount = 0;
+
+ // Save active rectangle variables.
+ int activeMinX = activeRect.x;
+ int activeMinY = activeRect.y;
+ int activeMaxY = activeMinY + activeRect.height - 1;
+ int activeWidth = activeRect.width;
+
+ // Set a SampleModel for use in padding.
+ SampleModel rowSampleModel = null;
+ if(isPadded) {
+ rowSampleModel =
+ image.getSampleModel().createCompatibleSampleModel(width, 1);
+ }
+
+ for (int row = yOffset; row < yOffset + height; row += ySkip) {
+ Raster ras = null;
+ if(isPadded) {
+ // Create a raster for the entire row.
+ WritableRaster wr =
+ Raster.createWritableRaster(rowSampleModel,
+ new Point(minX, row));
+
+ // Populate the raster from the active sub-row, if any.
+ if(row >= activeMinY && row <= activeMaxY) {
+ Rectangle rect =
+ new Rectangle(activeMinX, row, activeWidth, 1);
+ ras = image.getData(rect);
+ wr.setRect(ras);
+ }
+
+ // Update the raster variable.
+ ras = wr;
+ } else {
+ Rectangle rect = new Rectangle(minX, row, width, 1);
+ ras = image.getData(rect);
+ }
+ if (sourceBands != null) {
+ ras = ras.createChild(minX, row, width, 1, minX, row,
+ sourceBands);
+ }
+
+ if(sampleFormat ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ if (fsamples != null) {
+ ras.getPixels(minX, row, width, 1, fsamples);
+ } else {
+ ras.getPixels(minX, row, width, 1, dsamples);
+ }
+ } else {
+ ras.getPixels(minX, row, width, 1, samples);
+
+ if ((nativePhotometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO &&
+ photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO) ||
+ (nativePhotometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO &&
+ photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO)) {
+ int bitMask = (1 << bitDepth) - 1;
+ for (int s = 0; s < numSamples; s++) {
+ samples[s] ^= bitMask;
+ }
+ }
+ }
+
+ if (colorConverter != null) {
+ int idx = 0;
+ float[] result = new float[3];
+
+ if(sampleFormat ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ if (bitDepth == 32) {
+ for (int i = 0; i < width; i++) {
+ float r = fsamples[idx];
+ float g = fsamples[idx + 1];
+ float b = fsamples[idx + 2];
+
+ colorConverter.fromRGB(r, g, b, result);
+
+ fsamples[idx] = result[0];
+ fsamples[idx + 1] = result[1];
+ fsamples[idx + 2] = result[2];
+
+ idx += 3;
+ }
+ } else {
+ for (int i = 0; i < width; i++) {
+ // Note: Possible loss of precision.
+ float r = (float)dsamples[idx];
+ float g = (float)dsamples[idx + 1];
+ float b = (float)dsamples[idx + 2];
+
+ colorConverter.fromRGB(r, g, b, result);
+
+ dsamples[idx] = result[0];
+ dsamples[idx + 1] = result[1];
+ dsamples[idx + 2] = result[2];
+
+ idx += 3;
+ }
+ }
+ } else {
+ for (int i = 0; i < width; i++) {
+ float r = (float)samples[idx];
+ float g = (float)samples[idx + 1];
+ float b = (float)samples[idx + 2];
+
+ colorConverter.fromRGB(r, g, b, result);
+
+ samples[idx] = (int)(result[0]);
+ samples[idx + 1] = (int)(result[1]);
+ samples[idx + 2] = (int)(result[2]);
+
+ idx += 3;
+ }
+ }
+ }
+
+ int tmp = 0;
+ int pos = 0;
+
+ switch (bitDepth) {
+ case 1: case 2: case 4:
+ // Image can only have a single band
+
+ if(isRescaling) {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ byte val = scale0[samples[s]];
+ tmp = (tmp << bitDepth) | val;
+
+ if (++pos == samplesPerByte) {
+ currTile[tcount++] = (byte)tmp;
+ tmp = 0;
+ pos = 0;
+ }
+ }
+ } else {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ byte val = (byte)samples[s];
+ tmp = (tmp << bitDepth) | val;
+
+ if (++pos == samplesPerByte) {
+ currTile[tcount++] = (byte)tmp;
+ tmp = 0;
+ pos = 0;
+ }
+ }
+ }
+
+ // Left shift the last byte
+ if (pos != 0) {
+ tmp <<= ((8/bitDepth) - pos)*bitDepth;
+ currTile[tcount++] = (byte)tmp;
+ }
+ break;
+
+ case 8:
+ if (numBands == 1) {
+ if(isRescaling) {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ currTile[tcount++] = scale0[samples[s]];
+ }
+ } else {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ currTile[tcount++] = (byte)samples[s];
+ }
+ }
+ } else {
+ if(isRescaling) {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ currTile[tcount++] = scale[b][samples[s + b]];
+ }
+ }
+ } else {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ currTile[tcount++] = (byte)samples[s + b];
+ }
+ }
+ }
+ }
+ break;
+
+ case 16:
+ if(isRescaling) {
+ if(stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ int sample = samples[s + b];
+ currTile[tcount++] = scaleh[b][sample];
+ currTile[tcount++] = scalel[b][sample];
+ }
+ }
+ } else { // ByteOrder.LITLE_ENDIAN
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ int sample = samples[s + b];
+ currTile[tcount++] = scalel[b][sample];
+ currTile[tcount++] = scaleh[b][sample];
+ }
+ }
+ }
+ } else {
+ if(stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ int sample = samples[s + b];
+ currTile[tcount++] =
+ (byte)((sample >>> 8) & 0xff);
+ currTile[tcount++] =
+ (byte)(sample & 0xff);
+ }
+ }
+ } else { // ByteOrder.LITLE_ENDIAN
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ int sample = samples[s + b];
+ currTile[tcount++] =
+ (byte)(sample & 0xff);
+ currTile[tcount++] =
+ (byte)((sample >>> 8) & 0xff);
+ }
+ }
+ }
+ }
+ break;
+
+ case 32:
+ if(sampleFormat ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ if(stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ float fsample = fsamples[s + b];
+ int isample = Float.floatToIntBits(fsample);
+ currTile[tcount++] =
+ (byte)((isample & 0xff000000) >> 24);
+ currTile[tcount++] =
+ (byte)((isample & 0x00ff0000) >> 16);
+ currTile[tcount++] =
+ (byte)((isample & 0x0000ff00) >> 8);
+ currTile[tcount++] =
+ (byte)(isample & 0x000000ff);
+ }
+ }
+ } else { // ByteOrder.LITLE_ENDIAN
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ float fsample = fsamples[s + b];
+ int isample = Float.floatToIntBits(fsample);
+ currTile[tcount++] =
+ (byte)(isample & 0x000000ff);
+ currTile[tcount++] =
+ (byte)((isample & 0x0000ff00) >> 8);
+ currTile[tcount++] =
+ (byte)((isample & 0x00ff0000) >> 16);
+ currTile[tcount++] =
+ (byte)((isample & 0xff000000) >> 24);
+ }
+ }
+ }
+ } else {
+ if(isRescaling) {
+ long[] maxIn = new long[numBands];
+ long[] halfIn = new long[numBands];
+ long maxOut = (1L << (long)bitDepth) - 1L;
+
+ for (int b = 0; b < numBands; b++) {
+ maxIn[b] = ((1L << (long)sampleSize[b]) - 1L);
+ halfIn[b] = maxIn[b]/2;
+ }
+
+ if(stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ long sampleOut =
+ (samples[s + b]*maxOut + halfIn[b])/
+ maxIn[b];
+ currTile[tcount++] =
+ (byte)((sampleOut & 0xff000000) >> 24);
+ currTile[tcount++] =
+ (byte)((sampleOut & 0x00ff0000) >> 16);
+ currTile[tcount++] =
+ (byte)((sampleOut & 0x0000ff00) >> 8);
+ currTile[tcount++] =
+ (byte)(sampleOut & 0x000000ff);
+ }
+ }
+ } else { // ByteOrder.LITLE_ENDIAN
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ long sampleOut =
+ (samples[s + b]*maxOut + halfIn[b])/
+ maxIn[b];
+ currTile[tcount++] =
+ (byte)(sampleOut & 0x000000ff);
+ currTile[tcount++] =
+ (byte)((sampleOut & 0x0000ff00) >> 8);
+ currTile[tcount++] =
+ (byte)((sampleOut & 0x00ff0000) >> 16);
+ currTile[tcount++] =
+ (byte)((sampleOut & 0xff000000) >> 24);
+ }
+ }
+ }
+ } else {
+ if(stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ int isample = samples[s + b];
+ currTile[tcount++] =
+ (byte)((isample & 0xff000000) >> 24);
+ currTile[tcount++] =
+ (byte)((isample & 0x00ff0000) >> 16);
+ currTile[tcount++] =
+ (byte)((isample & 0x0000ff00) >> 8);
+ currTile[tcount++] =
+ (byte)(isample & 0x000000ff);
+ }
+ }
+ } else { // ByteOrder.LITLE_ENDIAN
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ int isample = samples[s + b];
+ currTile[tcount++] =
+ (byte)(isample & 0x000000ff);
+ currTile[tcount++] =
+ (byte)((isample & 0x0000ff00) >> 8);
+ currTile[tcount++] =
+ (byte)((isample & 0x00ff0000) >> 16);
+ currTile[tcount++] =
+ (byte)((isample & 0xff000000) >> 24);
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case 64:
+ if(sampleFormat ==
+ BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT) {
+ if(stream.getByteOrder() == ByteOrder.BIG_ENDIAN) {
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ double dsample = dsamples[s + b];
+ long lsample = Double.doubleToLongBits(dsample);
+ currTile[tcount++] =
+ (byte)((lsample & 0xff00000000000000L) >> 56);
+ currTile[tcount++] =
+ (byte)((lsample & 0x00ff000000000000L) >> 48);
+ currTile[tcount++] =
+ (byte)((lsample & 0x0000ff0000000000L) >> 40);
+ currTile[tcount++] =
+ (byte)((lsample & 0x000000ff00000000L) >> 32);
+ currTile[tcount++] =
+ (byte)((lsample & 0x00000000ff000000L) >> 24);
+ currTile[tcount++] =
+ (byte)((lsample & 0x0000000000ff0000L) >> 16);
+ currTile[tcount++] =
+ (byte)((lsample & 0x000000000000ff00L) >> 8);
+ currTile[tcount++] =
+ (byte)(lsample & 0x00000000000000ffL);
+ }
+ }
+ } else { // ByteOrder.LITLE_ENDIAN
+ for (int s = 0; s < numSamples; s += xSkip) {
+ for (int b = 0; b < numBands; b++) {
+ double dsample = dsamples[s + b];
+ long lsample = Double.doubleToLongBits(dsample);
+ currTile[tcount++] =
+ (byte)(lsample & 0x00000000000000ffL);
+ currTile[tcount++] =
+ (byte)((lsample & 0x000000000000ff00L) >> 8);
+ currTile[tcount++] =
+ (byte)((lsample & 0x0000000000ff0000L) >> 16);
+ currTile[tcount++] =
+ (byte)((lsample & 0x00000000ff000000L) >> 24);
+ currTile[tcount++] =
+ (byte)((lsample & 0x000000ff00000000L) >> 32);
+ currTile[tcount++] =
+ (byte)((lsample & 0x0000ff0000000000L) >> 40);
+ currTile[tcount++] =
+ (byte)((lsample & 0x00ff000000000000L) >> 48);
+ currTile[tcount++] =
+ (byte)((lsample & 0xff00000000000000L) >> 56);
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ int[] bitsPerSample = new int[numBands];
+ for (int i = 0; i < bitsPerSample.length; i++) {
+ bitsPerSample[i] = bitDepth;
+ }
+
+ int byteCount = compressor.encode(currTile, 0,
+ hpixels, vpixels,
+ bitsPerSample,
+ bytesPerRow);
+ return byteCount;
+ }
+
+ // Check two int arrays for value equality, always returns false
+ // if either array is null
+ private boolean equals(int[] s0, int[] s1) {
+ if (s0 == null || s1 == null) {
+ return false;
+ }
+ if (s0.length != s1.length) {
+ return false;
+ }
+ for (int i = 0; i < s0.length; i++) {
+ if (s0[i] != s1[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Initialize the scale/scale0 or scaleh/scalel arrays to
+ // hold the results of scaling an input value to the desired
+ // output bit depth
+ private void initializeScaleTables(int[] sampleSize) {
+ // Save the sample size in the instance variable.
+
+ // If the existing tables are still valid, just return.
+ if (bitDepth == scalingBitDepth &&
+ equals(sampleSize, this.sampleSize)) {
+ return;
+ }
+
+ // Reset scaling variables.
+ isRescaling = false;
+ scalingBitDepth = -1;
+ scale = scalel = scaleh = null;
+ scale0 = null;
+
+ // Set global sample size to parameter.
+ this.sampleSize = sampleSize;
+
+ // Check whether rescaling is called for.
+ if(bitDepth <= 16) {
+ for(int b = 0; b < numBands; b++) {
+ if(sampleSize[b] != bitDepth) {
+ isRescaling = true;
+ break;
+ }
+ }
+ }
+
+ // If not rescaling then return after saving the sample size.
+ if(!isRescaling) {
+ return;
+ }
+
+ // Compute new tables
+ this.scalingBitDepth = bitDepth;
+ int maxOutSample = (1 << bitDepth) - 1;
+ if (bitDepth <= 8) {
+ scale = new byte[numBands][];
+ for (int b = 0; b < numBands; b++) {
+ int maxInSample = (1 << sampleSize[b]) - 1;
+ int halfMaxInSample = maxInSample/2;
+ scale[b] = new byte[maxInSample + 1];
+ for (int s = 0; s <= maxInSample; s++) {
+ scale[b][s] =
+ (byte)((s*maxOutSample + halfMaxInSample)/maxInSample);
+ }
+ }
+ scale0 = scale[0];
+ scaleh = scalel = null;
+ } else if(bitDepth <= 16) {
+ // Divide scaling table into high and low bytes
+ scaleh = new byte[numBands][];
+ scalel = new byte[numBands][];
+
+ for (int b = 0; b < numBands; b++) {
+ int maxInSample = (1 << sampleSize[b]) - 1;
+ int halfMaxInSample = maxInSample/2;
+ scaleh[b] = new byte[maxInSample + 1];
+ scalel[b] = new byte[maxInSample + 1];
+ for (int s = 0; s <= maxInSample; s++) {
+ int val = (s*maxOutSample + halfMaxInSample)/maxInSample;
+ scaleh[b][s] = (byte)(val >> 8);
+ scalel[b][s] = (byte)(val & 0xff);
+ }
+ }
+ scale = null;
+ scale0 = null;
+ }
+ }
+
+ public void write(IIOMetadata sm,
+ IIOImage iioimage,
+ ImageWriteParam p) throws IOException {
+ write(sm, iioimage, p, true, true);
+ }
+
+ private void writeHeader() throws IOException {
+ if (streamMetadata != null) {
+ this.byteOrder = streamMetadata.byteOrder;
+ } else {
+ this.byteOrder = ByteOrder.BIG_ENDIAN;
+ }
+
+ stream.setByteOrder(byteOrder);
+ if (byteOrder == ByteOrder.BIG_ENDIAN) {
+ stream.writeShort(0x4d4d);
+ } else {
+ stream.writeShort(0x4949);
+ }
+
+ stream.writeShort(42); // Magic number
+ stream.writeInt(0); // Offset of first IFD (0 == none)
+
+ nextSpace = stream.getStreamPosition();
+ headerPosition = nextSpace - 8;
+ }
+
+ private void write(IIOMetadata sm,
+ IIOImage iioimage,
+ ImageWriteParam p,
+ boolean writeHeader,
+ boolean writeData) throws IOException {
+ if (stream == null) {
+ throw new IllegalStateException("output == null!");
+ }
+ if (iioimage == null) {
+ throw new NullPointerException("image == null!");
+ }
+ if(iioimage.hasRaster() && !canWriteRasters()) {
+ throw new UnsupportedOperationException
+ ("TIFF ImageWriter cannot write Rasters!");
+ }
+
+ this.image = iioimage.getRenderedImage();
+ SampleModel sampleModel = image.getSampleModel();
+
+ this.sourceXOffset = image.getMinX();
+ this.sourceYOffset = image.getMinY();
+ this.sourceWidth = image.getWidth();
+ this.sourceHeight = image.getHeight();
+
+ Rectangle imageBounds = new Rectangle(sourceXOffset,
+ sourceYOffset,
+ sourceWidth,
+ sourceHeight);
+
+ ColorModel colorModel = null;
+ if (p == null) {
+ this.param = getDefaultWriteParam();
+ this.sourceBands = null;
+ this.periodX = 1;
+ this.periodY = 1;
+ this.numBands = sampleModel.getNumBands();
+ colorModel = image.getColorModel();
+ } else {
+ this.param = p;
+
+ // Get source region and subsampling factors
+ Rectangle sourceRegion = param.getSourceRegion();
+ if (sourceRegion != null) {
+ // Clip to actual image bounds
+ sourceRegion = sourceRegion.intersection(imageBounds);
+
+ sourceXOffset = sourceRegion.x;
+ sourceYOffset = sourceRegion.y;
+ sourceWidth = sourceRegion.width;
+ sourceHeight = sourceRegion.height;
+ }
+
+ // Adjust for subsampling offsets
+ int gridX = param.getSubsamplingXOffset();
+ int gridY = param.getSubsamplingYOffset();
+ this.sourceXOffset += gridX;
+ this.sourceYOffset += gridY;
+ this.sourceWidth -= gridX;
+ this.sourceHeight -= gridY;
+
+ // Get subsampling factors
+ this.periodX = param.getSourceXSubsampling();
+ this.periodY = param.getSourceYSubsampling();
+
+ int[] sBands = param.getSourceBands();
+ if (sBands != null) {
+ sourceBands = sBands;
+ this.numBands = sourceBands.length;
+ } else {
+ this.numBands = sampleModel.getNumBands();
+ }
+
+ ImageTypeSpecifier destType = p.getDestinationType();
+ if(destType != null) {
+ ColorModel cm = destType.getColorModel();
+ if(cm.getNumComponents() == numBands) {
+ colorModel = cm;
+ }
+ }
+
+ if(colorModel == null) {
+ colorModel = image.getColorModel();
+ }
+ }
+
+ this.imageType = new ImageTypeSpecifier(colorModel, sampleModel);
+
+ ImageUtil.canEncodeImage(this, this.imageType);
+
+ // Compute output dimensions
+ int destWidth = (sourceWidth + periodX - 1)/periodX;
+ int destHeight = (sourceHeight + periodY - 1)/periodY;
+ if (destWidth <= 0 || destHeight <= 0) {
+ throw new IllegalArgumentException("Empty source region!");
+ }
+
+ clearAbortRequest();
+ processImageStarted(0);
+
+ // Optionally write the header.
+ if (writeHeader) {
+ // Clear previous stream metadata.
+ this.streamMetadata = null;
+
+ // Try to convert non-null input stream metadata.
+ if (sm != null) {
+ this.streamMetadata =
+ (TIFFStreamMetadata)convertStreamMetadata(sm, param);
+ }
+
+ // Set to default if not converted.
+ if(this.streamMetadata == null) {
+ this.streamMetadata =
+ (TIFFStreamMetadata)getDefaultStreamMetadata(param);
+ }
+
+ // Write the header.
+ writeHeader();
+
+ // Seek to the position of the IFD pointer in the header.
+ stream.seek(headerPosition + 4);
+
+ // Ensure IFD is written on a word boundary
+ nextSpace = (nextSpace + 3) & ~0x3;
+
+ // Write the pointer to the first IFD after the header.
+ stream.writeInt((int)nextSpace);
+ }
+
+ // Write out the IFD and any sub IFDs, followed by a zero
+
+ // Clear previous image metadata.
+ this.imageMetadata = null;
+
+ // Initialize the metadata object.
+ IIOMetadata im = iioimage.getMetadata();
+ if(im != null) {
+ if (im instanceof TIFFImageMetadata) {
+ // Clone the one passed in.
+ this.imageMetadata = ((TIFFImageMetadata)im).getShallowClone();
+ } else if(Arrays.asList(im.getMetadataFormatNames()).contains(
+ TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME)) {
+ this.imageMetadata = convertNativeImageMetadata(im);
+ } else if(im.isStandardMetadataFormatSupported()) {
+ // Convert standard metadata.
+ this.imageMetadata = convertStandardImageMetadata(im);
+ }
+ if (this.imageMetadata == null) {
+ processWarningOccurred(currentImage,
+ "Could not initialize image metadata");
+ }
+ }
+
+ // Use default metadata if still null.
+ if(this.imageMetadata == null) {
+ this.imageMetadata =
+ (TIFFImageMetadata)getDefaultImageMetadata(this.imageType,
+ this.param);
+ }
+
+ // Set or overwrite mandatory fields in the root IFD
+ setupMetadata(colorModel, sampleModel, destWidth, destHeight);
+
+ // Set compressor fields.
+ compressor.setWriter(this);
+ // Metadata needs to be set on the compressor before the IFD is
+ // written as the compressor could modify the metadata.
+ compressor.setMetadata(imageMetadata);
+ compressor.setStream(stream);
+
+ // Initialize scaling tables for this image
+ sampleSize = sampleModel.getSampleSize();
+ initializeScaleTables(sampleModel.getSampleSize());
+
+ // Determine whether bilevel.
+ this.isBilevel = ImageUtil.isBinary(this.image.getSampleModel());
+
+ // Check for photometric inversion.
+ this.isInverted =
+ (nativePhotometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO &&
+ photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO) ||
+ (nativePhotometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO &&
+ photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO);
+
+ // Analyze image data suitability for direct copy.
+ this.isImageSimple =
+ (isBilevel ||
+ (!isInverted && ImageUtil.imageIsContiguous(this.image))) &&
+ !isRescaling && // no value rescaling
+ sourceBands == null && // no subbanding
+ periodX == 1 && periodY == 1 && // no subsampling
+ colorConverter == null;
+
+ TIFFIFD rootIFD = imageMetadata.getRootIFD();
+
+ rootIFD.writeToStream(stream);
+
+ this.nextIFDPointerPos = stream.getStreamPosition();
+ stream.writeInt(0);
+
+ // Seek to end of IFD data
+ long lastIFDPosition = rootIFD.getLastPosition();
+ stream.seek(lastIFDPosition);
+ if(lastIFDPosition > this.nextSpace) {
+ this.nextSpace = lastIFDPosition;
+ }
+
+ // If not writing the image data, i.e., if writing or inserting an
+ // empty image, return.
+ if(!writeData) {
+ return;
+ }
+
+ // Get positions of fields within the IFD to update as we write
+ // each strip or tile
+ long stripOrTileByteCountsPosition =
+ rootIFD.getStripOrTileByteCountsPosition();
+ long stripOrTileOffsetsPosition =
+ rootIFD.getStripOrTileOffsetsPosition();
+
+ // Compute total number of pixels for progress notification
+ this.totalPixels = tileWidth*tileLength*tilesDown*tilesAcross;
+ this.pixelsDone = 0;
+
+ // Write the image, a strip or tile at a time
+ for (int tj = 0; tj < tilesDown; tj++) {
+ for (int ti = 0; ti < tilesAcross; ti++) {
+ long pos = stream.getStreamPosition();
+
+ // Write the (possibly compressed) tile data
+
+ Rectangle tileRect =
+ new Rectangle(sourceXOffset + ti*tileWidth*periodX,
+ sourceYOffset + tj*tileLength*periodY,
+ tileWidth*periodX,
+ tileLength*periodY);
+
+ try {
+ int byteCount = writeTile(tileRect, compressor);
+
+ if(pos + byteCount > nextSpace) {
+ nextSpace = pos + byteCount;
+ }
+
+ pixelsDone += tileRect.width*tileRect.height;
+ processImageProgress(100.0F*pixelsDone/totalPixels);
+
+ // Fill in the offset and byte count for the file
+ stream.mark();
+ stream.seek(stripOrTileOffsetsPosition);
+ stream.writeInt((int)pos);
+ stripOrTileOffsetsPosition += 4;
+
+ stream.seek(stripOrTileByteCountsPosition);
+ stream.writeInt(byteCount);
+ stripOrTileByteCountsPosition += 4;
+ stream.reset();
+ } catch (IOException e) {
+ throw new IIOException("I/O error writing TIFF file!", e);
+ }
+
+ if (abortRequested()) {
+ processWriteAborted();
+ return;
+ }
+ }
+ }
+
+ processImageComplete();
+ currentImage++;
+ }
+
+ public boolean canWriteSequence() {
+ return true;
+ }
+
+ public void prepareWriteSequence(IIOMetadata streamMetadata)
+ throws IOException {
+ if (getOutput() == null) {
+ throw new IllegalStateException("getOutput() == null!");
+ }
+
+ // Set up stream metadata.
+ if (streamMetadata != null) {
+ streamMetadata = convertStreamMetadata(streamMetadata, null);
+ }
+ if(streamMetadata == null) {
+ streamMetadata = getDefaultStreamMetadata(null);
+ }
+ this.streamMetadata = (TIFFStreamMetadata)streamMetadata;
+
+ // Write the header.
+ writeHeader();
+
+ // Set the sequence flag.
+ this.isWritingSequence = true;
+ }
+
+ public void writeToSequence(IIOImage image, ImageWriteParam param)
+ throws IOException {
+ // Check sequence flag.
+ if(!this.isWritingSequence) {
+ throw new IllegalStateException
+ ("prepareWriteSequence() has not been called!");
+ }
+
+ // Append image.
+ writeInsert(-1, image, param);
+ }
+
+ public void endWriteSequence() throws IOException {
+ // Check output.
+ if (getOutput() == null) {
+ throw new IllegalStateException("getOutput() == null!");
+ }
+
+ // Check sequence flag.
+ if(!isWritingSequence) {
+ throw new IllegalStateException
+ ("prepareWriteSequence() has not been called!");
+ }
+
+ // Unset sequence flag.
+ this.isWritingSequence = false;
+
+ // Position the stream at the end, not at the next IFD pointer position.
+ long streamLength = this.stream.length();
+ if (streamLength != -1) {
+ stream.seek(streamLength);
+ }
+ }
+
+ public boolean canInsertImage(int imageIndex) throws IOException {
+ if (getOutput() == null) {
+ throw new IllegalStateException("getOutput() == null!");
+ }
+
+ // Mark position as locateIFD() will seek to IFD at imageIndex.
+ stream.mark();
+
+ // locateIFD() will throw an IndexOutOfBoundsException if
+ // imageIndex is < -1 or is too big thereby satisfying the spec.
+ long[] ifdpos = new long[1];
+ long[] ifd = new long[1];
+ locateIFD(imageIndex, ifdpos, ifd);
+
+ // Reset to position before locateIFD().
+ stream.reset();
+
+ return true;
+ }
+
+ // Locate start of IFD for image.
+ // Throws IIOException if not at a TIFF header and
+ // IndexOutOfBoundsException if imageIndex is < -1 or is too big.
+ private void locateIFD(int imageIndex, long[] ifdpos, long[] ifd)
+ throws IOException {
+
+ if(imageIndex < -1) {
+ throw new IndexOutOfBoundsException("imageIndex < -1!");
+ }
+
+ long startPos = stream.getStreamPosition();
+
+ stream.seek(headerPosition);
+ int byteOrder = stream.readUnsignedShort();
+ if (byteOrder == 0x4d4d) {
+ stream.setByteOrder(ByteOrder.BIG_ENDIAN);
+ } else if (byteOrder == 0x4949) {
+ stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+ } else {
+ stream.seek(startPos);
+ throw new IIOException("Illegal byte order");
+ }
+ if (stream.readUnsignedShort() != 42) {
+ stream.seek(startPos);
+ throw new IIOException("Illegal magic number");
+ }
+
+ ifdpos[0] = stream.getStreamPosition();
+ ifd[0] = stream.readUnsignedInt();
+ if (ifd[0] == 0) {
+ // imageIndex has to be >= -1 due to check above.
+ if(imageIndex > 0) {
+ stream.seek(startPos);
+ throw new IndexOutOfBoundsException
+ ("imageIndex is greater than the largest available index!");
+ }
+ return;
+ }
+ stream.seek(ifd[0]);
+
+ for (int i = 0; imageIndex == -1 || i < imageIndex; i++) {
+ int numFields;
+ try {
+ numFields = stream.readShort();
+ } catch (EOFException eof) {
+ stream.seek(startPos);
+ ifd[0] = 0;
+ return;
+ }
+
+ stream.skipBytes(12*numFields);
+
+ ifdpos[0] = stream.getStreamPosition();
+ ifd[0] = stream.readUnsignedInt();
+ if (ifd[0] == 0) {
+ if (imageIndex != -1 && i < imageIndex - 1) {
+ stream.seek(startPos);
+ throw new IndexOutOfBoundsException(
+ "imageIndex is greater than the largest available index!");
+ }
+ break;
+ }
+ stream.seek(ifd[0]);
+ }
+ }
+
+ public void writeInsert(int imageIndex,
+ IIOImage image,
+ ImageWriteParam param) throws IOException {
+ int currentImageCached = currentImage;
+ try {
+ insert(imageIndex, image, param, true);
+ } catch (Exception e) {
+ throw e;
+ } finally {
+ currentImage = currentImageCached;
+ }
+ }
+
+ private void insert(int imageIndex,
+ IIOImage image,
+ ImageWriteParam param,
+ boolean writeData) throws IOException {
+ if (stream == null) {
+ throw new IllegalStateException("Output not set!");
+ }
+ if (image == null) {
+ throw new NullPointerException("image == null!");
+ }
+
+ // Locate the position of the old IFD (ifd) and the location
+ // of the pointer to that position (ifdpos).
+ long[] ifdpos = new long[1];
+ long[] ifd = new long[1];
+
+ // locateIFD() will throw an IndexOutOfBoundsException if
+ // imageIndex is < -1 or is too big thereby satisfying the spec.
+ locateIFD(imageIndex, ifdpos, ifd);
+
+ // Seek to the position containing the pointer to the old IFD.
+ stream.seek(ifdpos[0]);
+
+ // Update next space pointer in anticipation of next write.
+ if(ifdpos[0] + 4 > nextSpace) {
+ nextSpace = ifdpos[0] + 4;
+ }
+
+ // Ensure IFD is written on a word boundary
+ nextSpace = (nextSpace + 3) & ~0x3;
+
+ // Update the value to point to the next available space.
+ stream.writeInt((int)nextSpace);
+
+ // Seek to the next available space.
+ stream.seek(nextSpace);
+
+ // Write the image (IFD and data).
+ write(null, image, param, false, writeData);
+
+ // Seek to the position containing the pointer in the new IFD.
+ stream.seek(nextIFDPointerPos);
+
+ // Update the new IFD to point to the old IFD.
+ stream.writeInt((int)ifd[0]);
+ // Don't need to update nextSpace here as already done in write().
+ }
+
+ // ----- BEGIN insert/writeEmpty methods -----
+
+ private boolean isEncodingEmpty() {
+ return isInsertingEmpty || isWritingEmpty;
+ }
+
+ public boolean canInsertEmpty(int imageIndex) throws IOException {
+ return canInsertImage(imageIndex);
+ }
+
+ public boolean canWriteEmpty() throws IOException {
+ if (getOutput() == null) {
+ throw new IllegalStateException("getOutput() == null!");
+ }
+ return true;
+ }
+
+ // Check state and parameters for writing or inserting empty images.
+ private void checkParamsEmpty(ImageTypeSpecifier imageType,
+ int width,
+ int height,
+ List<? extends BufferedImage> thumbnails) {
+ if (getOutput() == null) {
+ throw new IllegalStateException("getOutput() == null!");
+ }
+
+ if(imageType == null) {
+ throw new NullPointerException("imageType == null!");
+ }
+
+ if(width < 1 || height < 1) {
+ throw new IllegalArgumentException("width < 1 || height < 1!");
+ }
+
+ if(thumbnails != null) {
+ int numThumbs = thumbnails.size();
+ for(int i = 0; i < numThumbs; i++) {
+ Object thumb = thumbnails.get(i);
+ if(thumb == null || !(thumb instanceof BufferedImage)) {
+ throw new IllegalArgumentException
+ ("thumbnails contains null references or objects other than BufferedImages!");
+ }
+ }
+ }
+
+ if(this.isInsertingEmpty) {
+ throw new IllegalStateException
+ ("Previous call to prepareInsertEmpty() without corresponding call to endInsertEmpty()!");
+ }
+
+ if(this.isWritingEmpty) {
+ throw new IllegalStateException
+ ("Previous call to prepareWriteEmpty() without corresponding call to endWriteEmpty()!");
+ }
+ }
+
+ public void prepareInsertEmpty(int imageIndex,
+ ImageTypeSpecifier imageType,
+ int width,
+ int height,
+ IIOMetadata imageMetadata,
+ List<? extends BufferedImage> thumbnails,
+ ImageWriteParam param) throws IOException {
+ checkParamsEmpty(imageType, width, height, thumbnails);
+
+ this.isInsertingEmpty = true;
+
+ SampleModel emptySM = imageType.getSampleModel();
+ RenderedImage emptyImage =
+ new EmptyImage(0, 0, width, height,
+ 0, 0, emptySM.getWidth(), emptySM.getHeight(),
+ emptySM, imageType.getColorModel());
+
+ insert(imageIndex, new IIOImage(emptyImage, null, imageMetadata),
+ param, false);
+ }
+
+ public void prepareWriteEmpty(IIOMetadata streamMetadata,
+ ImageTypeSpecifier imageType,
+ int width,
+ int height,
+ IIOMetadata imageMetadata,
+ List<? extends BufferedImage> thumbnails,
+ ImageWriteParam param) throws IOException {
+ checkParamsEmpty(imageType, width, height, thumbnails);
+
+ this.isWritingEmpty = true;
+
+ SampleModel emptySM = imageType.getSampleModel();
+ RenderedImage emptyImage =
+ new EmptyImage(0, 0, width, height,
+ 0, 0, emptySM.getWidth(), emptySM.getHeight(),
+ emptySM, imageType.getColorModel());
+
+ write(streamMetadata, new IIOImage(emptyImage, null, imageMetadata),
+ param, true, false);
+ }
+
+ public void endInsertEmpty() throws IOException {
+ if (getOutput() == null) {
+ throw new IllegalStateException("getOutput() == null!");
+ }
+
+ if(!this.isInsertingEmpty) {
+ throw new IllegalStateException
+ ("No previous call to prepareInsertEmpty()!");
+ }
+
+ if(this.isWritingEmpty) {
+ throw new IllegalStateException
+ ("Previous call to prepareWriteEmpty() without corresponding call to endWriteEmpty()!");
+ }
+
+ if (inReplacePixelsNest) {
+ throw new IllegalStateException
+ ("In nested call to prepareReplacePixels!");
+ }
+
+ this.isInsertingEmpty = false;
+ }
+
+ public void endWriteEmpty() throws IOException {
+ if (getOutput() == null) {
+ throw new IllegalStateException("getOutput() == null!");
+ }
+
+ if(!this.isWritingEmpty) {
+ throw new IllegalStateException
+ ("No previous call to prepareWriteEmpty()!");
+ }
+
+ if(this.isInsertingEmpty) {
+ throw new IllegalStateException
+ ("Previous call to prepareInsertEmpty() without corresponding call to endInsertEmpty()!");
+ }
+
+ if (inReplacePixelsNest) {
+ throw new IllegalStateException
+ ("In nested call to prepareReplacePixels!");
+ }
+
+ this.isWritingEmpty = false;
+ }
+
+ // ----- END insert/writeEmpty methods -----
+
+ // ----- BEGIN replacePixels methods -----
+
+ private TIFFIFD readIFD(int imageIndex) throws IOException {
+ if (stream == null) {
+ throw new IllegalStateException("Output not set!");
+ }
+ if (imageIndex < 0) {
+ throw new IndexOutOfBoundsException("imageIndex < 0!");
+ }
+
+ stream.mark();
+ long[] ifdpos = new long[1];
+ long[] ifd = new long[1];
+ locateIFD(imageIndex, ifdpos, ifd);
+ if (ifd[0] == 0) {
+ stream.reset();
+ throw new IndexOutOfBoundsException
+ ("imageIndex out of bounds!");
+ }
+
+ List<TIFFTagSet> tagSets = new ArrayList<TIFFTagSet>(1);
+ tagSets.add(BaselineTIFFTagSet.getInstance());
+ TIFFIFD rootIFD = new TIFFIFD(tagSets);
+ rootIFD.initialize(stream, true, true);
+ stream.reset();
+
+ return rootIFD;
+ }
+
+ public boolean canReplacePixels(int imageIndex) throws IOException {
+ if (getOutput() == null) {
+ throw new IllegalStateException("getOutput() == null!");
+ }
+
+ TIFFIFD rootIFD = readIFD(imageIndex);
+ TIFFField f = rootIFD.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
+ int compression = f.getAsInt(0);
+
+ return compression == BaselineTIFFTagSet.COMPRESSION_NONE;
+ }
+
+ private Object replacePixelsLock = new Object();
+
+ private int replacePixelsIndex = -1;
+ private TIFFImageMetadata replacePixelsMetadata = null;
+ private long[] replacePixelsTileOffsets = null;
+ private long[] replacePixelsByteCounts = null;
+ private long replacePixelsOffsetsPosition = 0L;
+ private long replacePixelsByteCountsPosition = 0L;
+ private Rectangle replacePixelsRegion = null;
+ private boolean inReplacePixelsNest = false;
+
+ private TIFFImageReader reader = null;
+
+ public void prepareReplacePixels(int imageIndex,
+ Rectangle region) throws IOException {
+ synchronized(replacePixelsLock) {
+ // Check state and parameters vis-a-vis ImageWriter specification.
+ if (stream == null) {
+ throw new IllegalStateException("Output not set!");
+ }
+ if (region == null) {
+ throw new NullPointerException("region == null!");
+ }
+ if (region.getWidth() < 1) {
+ throw new IllegalArgumentException("region.getWidth() < 1!");
+ }
+ if (region.getHeight() < 1) {
+ throw new IllegalArgumentException("region.getHeight() < 1!");
+ }
+ if (inReplacePixelsNest) {
+ throw new IllegalStateException
+ ("In nested call to prepareReplacePixels!");
+ }
+
+ // Read the IFD for the pixel replacement index.
+ TIFFIFD replacePixelsIFD = readIFD(imageIndex);
+
+ // Ensure that compression is "none".
+ TIFFField f =
+ replacePixelsIFD.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
+ int compression = f.getAsInt(0);
+ if (compression != BaselineTIFFTagSet.COMPRESSION_NONE) {
+ throw new UnsupportedOperationException
+ ("canReplacePixels(imageIndex) == false!");
+ }
+
+ // Get the image dimensions.
+ f =
+ replacePixelsIFD.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
+ if(f == null) {
+ throw new IIOException("Cannot read ImageWidth field.");
+ }
+ int w = f.getAsInt(0);
+
+ f =
+ replacePixelsIFD.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH);
+ if(f == null) {
+ throw new IIOException("Cannot read ImageHeight field.");
+ }
+ int h = f.getAsInt(0);
+
+ // Create image bounds.
+ Rectangle bounds = new Rectangle(0, 0, w, h);
+
+ // Intersect region with bounds.
+ region = region.intersection(bounds);
+
+ // Check for empty intersection.
+ if(region.isEmpty()) {
+ throw new IIOException("Region does not intersect image bounds");
+ }
+
+ // Save the region.
+ replacePixelsRegion = region;
+
+ // Get the tile offsets.
+ f = replacePixelsIFD.getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
+ if(f == null) {
+ f = replacePixelsIFD.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
+ }
+ replacePixelsTileOffsets = f.getAsLongs();
+
+ // Get the byte counts.
+ f = replacePixelsIFD.getTIFFField(BaselineTIFFTagSet.TAG_TILE_BYTE_COUNTS);
+ if(f == null) {
+ f = replacePixelsIFD.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_BYTE_COUNTS);
+ }
+ replacePixelsByteCounts = f.getAsLongs();
+
+ replacePixelsOffsetsPosition =
+ replacePixelsIFD.getStripOrTileOffsetsPosition();
+ replacePixelsByteCountsPosition =
+ replacePixelsIFD.getStripOrTileByteCountsPosition();
+
+ // Get the image metadata.
+ replacePixelsMetadata = new TIFFImageMetadata(replacePixelsIFD);
+
+ // Save the image index.
+ replacePixelsIndex = imageIndex;
+
+ // Set the pixel replacement flag.
+ inReplacePixelsNest = true;
+ }
+ }
+
+ private Raster subsample(Raster raster, int[] sourceBands,
+ int subOriginX, int subOriginY,
+ int subPeriodX, int subPeriodY,
+ int dstOffsetX, int dstOffsetY,
+ Rectangle target) {
+
+ int x = raster.getMinX();
+ int y = raster.getMinY();
+ int w = raster.getWidth();
+ int h = raster.getHeight();
+ int b = raster.getSampleModel().getNumBands();
+ int t = raster.getSampleModel().getDataType();
+
+ int outMinX = XToTileX(x, subOriginX, subPeriodX) + dstOffsetX;
+ int outMinY = YToTileY(y, subOriginY, subPeriodY) + dstOffsetY;
+ int outMaxX = XToTileX(x + w - 1, subOriginX, subPeriodX) + dstOffsetX;
+ int outMaxY = YToTileY(y + h - 1, subOriginY, subPeriodY) + dstOffsetY;
+ int outWidth = outMaxX - outMinX + 1;
+ int outHeight = outMaxY - outMinY + 1;
+
+ if(outWidth <= 0 || outHeight <= 0) return null;
+
+ int inMinX = (outMinX - dstOffsetX)*subPeriodX + subOriginX;
+ int inMaxX = (outMaxX - dstOffsetX)*subPeriodX + subOriginX;
+ int inWidth = inMaxX - inMinX + 1;
+ int inMinY = (outMinY - dstOffsetY)*subPeriodY + subOriginY;
+ int inMaxY = (outMaxY - dstOffsetY)*subPeriodY + subOriginY;
+ int inHeight = inMaxY - inMinY + 1;
+
+ WritableRaster wr =
+ raster.createCompatibleWritableRaster(outMinX, outMinY,
+ outWidth, outHeight);
+
+ int jMax = inMinY + inHeight;
+
+ if(t == DataBuffer.TYPE_FLOAT) {
+ float[] fsamples = new float[inWidth];
+ float[] fsubsamples = new float[outWidth];
+
+ for(int k = 0; k < b; k++) {
+ int outY = outMinY;
+ for(int j = inMinY; j < jMax; j += subPeriodY) {
+ raster.getSamples(inMinX, j, inWidth, 1, k, fsamples);
+ int s = 0;
+ for(int i = 0; i < inWidth; i += subPeriodX) {
+ fsubsamples[s++] = fsamples[i];
+ }
+ wr.setSamples(outMinX, outY++, outWidth, 1, k,
+ fsubsamples);
+ }
+ }
+ } else if (t == DataBuffer.TYPE_DOUBLE) {
+ double[] dsamples = new double[inWidth];
+ double[] dsubsamples = new double[outWidth];
+
+ for(int k = 0; k < b; k++) {
+ int outY = outMinY;
+ for(int j = inMinY; j < jMax; j += subPeriodY) {
+ raster.getSamples(inMinX, j, inWidth, 1, k, dsamples);
+ int s = 0;
+ for(int i = 0; i < inWidth; i += subPeriodX) {
+ dsubsamples[s++] = dsamples[i];
+ }
+ wr.setSamples(outMinX, outY++, outWidth, 1, k,
+ dsubsamples);
+ }
+ }
+ } else {
+ int[] samples = new int[inWidth];
+ int[] subsamples = new int[outWidth];
+
+ for(int k = 0; k < b; k++) {
+ int outY = outMinY;
+ for(int j = inMinY; j < jMax; j += subPeriodY) {
+ raster.getSamples(inMinX, j, inWidth, 1, k, samples);
+ int s = 0;
+ for(int i = 0; i < inWidth; i += subPeriodX) {
+ subsamples[s++] = samples[i];
+ }
+ wr.setSamples(outMinX, outY++, outWidth, 1, k,
+ subsamples);
+ }
+ }
+ }
+
+ return wr.createChild(outMinX, outMinY,
+ target.width, target.height,
+ target.x, target.y,
+ sourceBands);
+ }
+
+ public void replacePixels(RenderedImage image, ImageWriteParam param)
+ throws IOException {
+
+ synchronized(replacePixelsLock) {
+ // Check state and parameters vis-a-vis ImageWriter specification.
+ if (stream == null) {
+ throw new IllegalStateException("stream == null!");
+ }
+
+ if (image == null) {
+ throw new NullPointerException("image == null!");
+ }
+
+ if (!inReplacePixelsNest) {
+ throw new IllegalStateException
+ ("No previous call to prepareReplacePixels!");
+ }
+
+ // Subsampling values.
+ int stepX = 1, stepY = 1, gridX = 0, gridY = 0;
+
+ // Initialize the ImageWriteParam.
+ if (param == null) {
+ // Use the default.
+ param = getDefaultWriteParam();
+ } else {
+ // Make a copy of the ImageWriteParam.
+ ImageWriteParam paramCopy = getDefaultWriteParam();
+
+ // Force uncompressed.
+ paramCopy.setCompressionMode(ImageWriteParam.MODE_DISABLED);
+
+ // Force tiling to remain as in the already written image.
+ paramCopy.setTilingMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
+
+ // Retain source and destination region and band settings.
+ paramCopy.setDestinationOffset(param.getDestinationOffset());
+ paramCopy.setSourceBands(param.getSourceBands());
+ paramCopy.setSourceRegion(param.getSourceRegion());
+
+ // Save original subsampling values for subsampling the
+ // replacement data - not the data re-read from the image.
+ stepX = param.getSourceXSubsampling();
+ stepY = param.getSourceYSubsampling();
+ gridX = param.getSubsamplingXOffset();
+ gridY = param.getSubsamplingYOffset();
+
+ // Replace the param.
+ param = paramCopy;
+ }
+
+ // Check band count and bit depth compatibility.
+ TIFFField f =
+ replacePixelsMetadata.getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
+ if(f == null) {
+ throw new IIOException
+ ("Cannot read destination BitsPerSample");
+ }
+ int[] dstBitsPerSample = f.getAsInts();
+ int[] srcBitsPerSample = image.getSampleModel().getSampleSize();
+ int[] sourceBands = param.getSourceBands();
+ if(sourceBands != null) {
+ if(sourceBands.length != dstBitsPerSample.length) {
+ throw new IIOException
+ ("Source and destination have different SamplesPerPixel");
+ }
+ for(int i = 0; i < sourceBands.length; i++) {
+ if(dstBitsPerSample[i] !=
+ srcBitsPerSample[sourceBands[i]]) {
+ throw new IIOException
+ ("Source and destination have different BitsPerSample");
+ }
+ }
+ } else {
+ int srcNumBands = image.getSampleModel().getNumBands();
+ if(srcNumBands != dstBitsPerSample.length) {
+ throw new IIOException
+ ("Source and destination have different SamplesPerPixel");
+ }
+ for(int i = 0; i < srcNumBands; i++) {
+ if(dstBitsPerSample[i] != srcBitsPerSample[i]) {
+ throw new IIOException
+ ("Source and destination have different BitsPerSample");
+ }
+ }
+ }
+
+ // Get the source image bounds.
+ Rectangle srcImageBounds =
+ new Rectangle(image.getMinX(), image.getMinY(),
+ image.getWidth(), image.getHeight());
+
+ // Initialize the source rect.
+ Rectangle srcRect = param.getSourceRegion();
+ if(srcRect == null) {
+ srcRect = srcImageBounds;
+ }
+
+ // Set subsampling grid parameters.
+ int subPeriodX = stepX;
+ int subPeriodY = stepY;
+ int subOriginX = gridX + srcRect.x;
+ int subOriginY = gridY + srcRect.y;
+
+ // Intersect with the source bounds.
+ if(!srcRect.equals(srcImageBounds)) {
+ srcRect = srcRect.intersection(srcImageBounds);
+ if(srcRect.isEmpty()) {
+ throw new IllegalArgumentException
+ ("Source region does not intersect source image!");
+ }
+ }
+
+ // Get the destination offset.
+ Point dstOffset = param.getDestinationOffset();
+
+ // Forward map source rectangle to determine destination width.
+ int dMinX = XToTileX(srcRect.x, subOriginX, subPeriodX) +
+ dstOffset.x;
+ int dMinY = YToTileY(srcRect.y, subOriginY, subPeriodY) +
+ dstOffset.y;
+ int dMaxX = XToTileX(srcRect.x + srcRect.width,
+ subOriginX, subPeriodX) + dstOffset.x;
+ int dMaxY = YToTileY(srcRect.y + srcRect.height,
+ subOriginY, subPeriodY) + dstOffset.y;
+
+ // Initialize the destination rectangle.
+ Rectangle dstRect =
+ new Rectangle(dstOffset.x, dstOffset.y,
+ dMaxX - dMinX, dMaxY - dMinY);
+
+ // Intersect with the replacement region.
+ dstRect = dstRect.intersection(replacePixelsRegion);
+ if(dstRect.isEmpty()) {
+ throw new IllegalArgumentException
+ ("Forward mapped source region does not intersect destination region!");
+ }
+
+ // Backward map to the active source region.
+ int activeSrcMinX = (dstRect.x - dstOffset.x)*subPeriodX +
+ subOriginX;
+ int sxmax =
+ (dstRect.x + dstRect.width - 1 - dstOffset.x)*subPeriodX +
+ subOriginX;
+ int activeSrcWidth = sxmax - activeSrcMinX + 1;
+
+ int activeSrcMinY = (dstRect.y - dstOffset.y)*subPeriodY +
+ subOriginY;
+ int symax =
+ (dstRect.y + dstRect.height - 1 - dstOffset.y)*subPeriodY +
+ subOriginY;
+ int activeSrcHeight = symax - activeSrcMinY + 1;
+ Rectangle activeSrcRect =
+ new Rectangle(activeSrcMinX, activeSrcMinY,
+ activeSrcWidth, activeSrcHeight);
+ if(activeSrcRect.intersection(srcImageBounds).isEmpty()) {
+ throw new IllegalArgumentException
+ ("Backward mapped destination region does not intersect source image!");
+ }
+
+ if(reader == null) {
+ reader = new TIFFImageReader(new TIFFImageReaderSpi());
+ } else {
+ reader.reset();
+ }
+
+ stream.mark();
+
+ try {
+ stream.seek(headerPosition);
+ reader.setInput(stream);
+
+ this.imageMetadata = replacePixelsMetadata;
+ this.param = param;
+ SampleModel sm = image.getSampleModel();
+ ColorModel cm = image.getColorModel();
+ this.numBands = sm.getNumBands();
+ this.imageType = new ImageTypeSpecifier(image);
+ this.periodX = param.getSourceXSubsampling();
+ this.periodY = param.getSourceYSubsampling();
+ this.sourceBands = null;
+ int[] sBands = param.getSourceBands();
+ if (sBands != null) {
+ this.sourceBands = sBands;
+ this.numBands = sourceBands.length;
+ }
+ setupMetadata(cm, sm,
+ reader.getWidth(replacePixelsIndex),
+ reader.getHeight(replacePixelsIndex));
+ int[] scaleSampleSize = sm.getSampleSize();
+ initializeScaleTables(scaleSampleSize);
+
+ // Determine whether bilevel.
+ this.isBilevel = ImageUtil.isBinary(image.getSampleModel());
+
+ // Check for photometric inversion.
+ this.isInverted =
+ (nativePhotometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO &&
+ photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO) ||
+ (nativePhotometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO &&
+ photometricInterpretation ==
+ BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO);
+
+ // Analyze image data suitability for direct copy.
+ this.isImageSimple =
+ (isBilevel ||
+ (!isInverted && ImageUtil.imageIsContiguous(image))) &&
+ !isRescaling && // no value rescaling
+ sourceBands == null && // no subbanding
+ periodX == 1 && periodY == 1 && // no subsampling
+ colorConverter == null;
+
+ int minTileX = XToTileX(dstRect.x, 0, tileWidth);
+ int minTileY = YToTileY(dstRect.y, 0, tileLength);
+ int maxTileX = XToTileX(dstRect.x + dstRect.width - 1,
+ 0, tileWidth);
+ int maxTileY = YToTileY(dstRect.y + dstRect.height - 1,
+ 0, tileLength);
+
+ TIFFCompressor encoder = new TIFFNullCompressor();
+ encoder.setWriter(this);
+ encoder.setStream(stream);
+ encoder.setMetadata(this.imageMetadata);
+
+ Rectangle tileRect = new Rectangle();
+ for(int ty = minTileY; ty <= maxTileY; ty++) {
+ for(int tx = minTileX; tx <= maxTileX; tx++) {
+ int tileIndex = ty*tilesAcross + tx;
+ boolean isEmpty =
+ replacePixelsByteCounts[tileIndex] == 0L;
+ WritableRaster raster;
+ if(isEmpty) {
+ SampleModel tileSM =
+ sm.createCompatibleSampleModel(tileWidth,
+ tileLength);
+ raster = Raster.createWritableRaster(tileSM, null);
+ } else {
+ BufferedImage tileImage =
+ reader.readTile(replacePixelsIndex, tx, ty);
+ raster = tileImage.getRaster();
+ }
+
+ tileRect.setLocation(tx*tileWidth,
+ ty*tileLength);
+ tileRect.setSize(raster.getWidth(),
+ raster.getHeight());
+ raster =
+ raster.createWritableTranslatedChild(tileRect.x,
+ tileRect.y);
+
+ Rectangle replacementRect =
+ tileRect.intersection(dstRect);
+
+ int srcMinX =
+ (replacementRect.x - dstOffset.x)*subPeriodX +
+ subOriginX;
+ int srcXmax =
+ (replacementRect.x + replacementRect.width - 1 -
+ dstOffset.x)*subPeriodX + subOriginX;
+ int srcWidth = srcXmax - srcMinX + 1;
+
+ int srcMinY =
+ (replacementRect.y - dstOffset.y)*subPeriodY +
+ subOriginY;
+ int srcYMax =
+ (replacementRect.y + replacementRect.height - 1 -
+ dstOffset.y)*subPeriodY + subOriginY;
+ int srcHeight = srcYMax - srcMinY + 1;
+ Rectangle srcTileRect =
+ new Rectangle(srcMinX, srcMinY,
+ srcWidth, srcHeight);
+
+ Raster replacementData = image.getData(srcTileRect);
+ if(subPeriodX == 1 && subPeriodY == 1 &&
+ subOriginX == 0 && subOriginY == 0) {
+ replacementData =
+ replacementData.createChild(srcTileRect.x,
+ srcTileRect.y,
+ srcTileRect.width,
+ srcTileRect.height,
+ replacementRect.x,
+ replacementRect.y,
+ sourceBands);
+ } else {
+ replacementData = subsample(replacementData,
+ sourceBands,
+ subOriginX,
+ subOriginY,
+ subPeriodX,
+ subPeriodY,
+ dstOffset.x,
+ dstOffset.y,
+ replacementRect);
+ if(replacementData == null) {
+ continue;
+ }
+ }
+
+ raster.setRect(replacementData);
+
+ if(isEmpty) {
+ stream.seek(nextSpace);
+ } else {
+ stream.seek(replacePixelsTileOffsets[tileIndex]);
+ }
+
+ this.image = new SingleTileRenderedImage(raster, cm);
+
+ int numBytes = writeTile(tileRect, encoder);
+
+ if(isEmpty) {
+ // Update Strip/TileOffsets and
+ // Strip/TileByteCounts fields.
+ stream.mark();
+ stream.seek(replacePixelsOffsetsPosition +
+ 4*tileIndex);
+ stream.writeInt((int)nextSpace);
+ stream.seek(replacePixelsByteCountsPosition +
+ 4*tileIndex);
+ stream.writeInt(numBytes);
+ stream.reset();
+
+ // Increment location of next available space.
+ nextSpace += numBytes;
+ }
+ }
+ }
+
+ } catch(IOException e) {
+ throw e;
+ } finally {
+ stream.reset();
+ }
+ }
+ }
+
+ public void replacePixels(Raster raster, ImageWriteParam param)
+ throws IOException {
+ if (raster == null) {
+ throw new NullPointerException("raster == null!");
+ }
+
+ replacePixels(new SingleTileRenderedImage(raster,
+ image.getColorModel()),
+ param);
+ }
+
+ public void endReplacePixels() throws IOException {
+ synchronized(replacePixelsLock) {
+ if(!this.inReplacePixelsNest) {
+ throw new IllegalStateException
+ ("No previous call to prepareReplacePixels()!");
+ }
+ replacePixelsIndex = -1;
+ replacePixelsMetadata = null;
+ replacePixelsTileOffsets = null;
+ replacePixelsByteCounts = null;
+ replacePixelsOffsetsPosition = 0L;
+ replacePixelsByteCountsPosition = 0L;
+ replacePixelsRegion = null;
+ inReplacePixelsNest = false;
+ }
+ }
+
+ // ----- END replacePixels methods -----
+
+ public void reset() {
+ super.reset();
+
+ stream = null;
+ image = null;
+ imageType = null;
+ byteOrder = null;
+ param = null;
+ compressor = null;
+ colorConverter = null;
+ streamMetadata = null;
+ imageMetadata = null;
+
+ isWritingSequence = false;
+ isWritingEmpty = false;
+ isInsertingEmpty = false;
+
+ replacePixelsIndex = -1;
+ replacePixelsMetadata = null;
+ replacePixelsTileOffsets = null;
+ replacePixelsByteCounts = null;
+ replacePixelsOffsetsPosition = 0L;
+ replacePixelsByteCountsPosition = 0L;
+ replacePixelsRegion = null;
+ inReplacePixelsNest = false;
+ }
+}
+
+class EmptyImage extends SimpleRenderedImage {
+ EmptyImage(int minX, int minY, int width, int height,
+ int tileGridXOffset, int tileGridYOffset,
+ int tileWidth, int tileHeight,
+ SampleModel sampleModel, ColorModel colorModel) {
+ this.minX = minX;
+ this.minY = minY;
+ this.width = width;
+ this.height = height;
+ this.tileGridXOffset = tileGridXOffset;
+ this.tileGridYOffset = tileGridYOffset;
+ this.tileWidth = tileWidth;
+ this.tileHeight = tileHeight;
+ this.sampleModel = sampleModel;
+ this.colorModel = colorModel;
+ }
+
+ public Raster getTile(int tileX, int tileY) {
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriterSpi.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,78 @@
+/*
+ * 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.util.Locale;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.ImageWriter;
+import javax.imageio.spi.ImageWriterSpi;
+import javax.imageio.spi.ServiceRegistry;
+import javax.imageio.stream.ImageOutputStream;
+
+public class TIFFImageWriterSpi extends ImageWriterSpi {
+
+ private boolean registered = false;
+
+ public TIFFImageWriterSpi() {
+ super("Oracle Corporation",
+ "1.0",
+ new String[] {"tif", "TIF", "tiff", "TIFF"},
+ new String[] {"tif", "tiff"},
+ new String[] {"image/tiff"},
+ "com.sun.imageio.plugins.tiff.TIFFImageWriter",
+ new Class<?>[] {ImageOutputStream.class},
+ new String[] {"com.sun.imageio.plugins.tiff.TIFFImageReaderSpi"},
+ false,
+ TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME,
+ "com.sun.imageio.plugins.tiff.TIFFStreamMetadataFormat",
+ null, null,
+ false,
+ TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME,
+ "com.sun.imageio.plugins.tiff.TIFFImageMetadataFormat",
+ null, null
+ );
+ }
+
+ public boolean canEncodeImage(ImageTypeSpecifier type) {
+ return true;
+ }
+
+ public String getDescription(Locale locale) {
+ return "Standard TIFF image writer";
+ }
+
+ public ImageWriter createWriterInstance(Object extension) {
+ return new TIFFImageWriter(this);
+ }
+
+ public void onRegistration(ServiceRegistry registry,
+ Class<?> category) {
+ if (registered) {
+ return;
+ }
+
+ registered = true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFJPEGCompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,263 @@
+/*
+ * 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.plugins.tiff.TIFFField;
+import javax.imageio.plugins.tiff.TIFFTag;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Iterator;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.spi.IIORegistry;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.spi.ServiceRegistry;
+import javax.imageio.stream.MemoryCacheImageInputStream;
+import javax.imageio.stream.MemoryCacheImageOutputStream;
+
+/**
+ * Compressor for encoding compression type 7, TTN2/Adobe JPEG-in-TIFF.
+ */
+public class TIFFJPEGCompressor extends TIFFBaseJPEGCompressor {
+
+ // Subsampling factor for chroma bands (Cb Cr).
+ static final int CHROMA_SUBSAMPLING = 2;
+
+ /**
+ * A filter which identifies the ImageReaderSpi of a JPEG reader
+ * which supports JPEG native stream metadata.
+ */
+ private static class JPEGSPIFilter implements ServiceRegistry.Filter {
+ JPEGSPIFilter() {}
+
+ public boolean filter(Object provider) {
+ ImageReaderSpi readerSPI = (ImageReaderSpi)provider;
+
+ if(readerSPI != null) {
+ String streamMetadataName =
+ readerSPI.getNativeStreamMetadataFormatName();
+ if(streamMetadataName != null) {
+ return streamMetadataName.equals(STREAM_METADATA_NAME);
+ } else {
+ return false;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Retrieves a JPEG reader which supports native JPEG stream metadata.
+ */
+ private static ImageReader getJPEGTablesReader() {
+ ImageReader jpegReader = null;
+
+ try {
+ IIORegistry registry = IIORegistry.getDefaultInstance();
+ Class<?> imageReaderClass =
+ Class.forName("javax.imageio.spi.ImageReaderSpi");
+ Iterator<?> readerSPIs =
+ registry.getServiceProviders(imageReaderClass,
+ new JPEGSPIFilter(),
+ true);
+ if(readerSPIs.hasNext()) {
+ ImageReaderSpi jpegReaderSPI =
+ (ImageReaderSpi)readerSPIs.next();
+ jpegReader = jpegReaderSPI.createReaderInstance();
+ }
+ } catch(Exception e) {
+ // Ignore it ...
+ }
+
+ return jpegReader;
+ }
+
+ public TIFFJPEGCompressor(ImageWriteParam param) {
+ super("JPEG", BaselineTIFFTagSet.COMPRESSION_JPEG, false, param);
+ }
+
+ /**
+ * Sets the value of the <code>metadata</code> field.
+ *
+ * <p>The implementation in this class also adds the TIFF fields
+ * JPEGTables, YCbCrSubSampling, YCbCrPositioning, and
+ * ReferenceBlackWhite superseding any prior settings of those
+ * fields.</p>
+ *
+ * @param metadata the <code>IIOMetadata</code> object for the
+ * image being written.
+ *
+ * @see #getMetadata()
+ */
+ public void setMetadata(IIOMetadata metadata) {
+ super.setMetadata(metadata);
+
+ if (metadata instanceof TIFFImageMetadata) {
+ TIFFImageMetadata tim = (TIFFImageMetadata)metadata;
+ TIFFIFD rootIFD = tim.getRootIFD();
+ BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
+
+ TIFFField f =
+ tim.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
+ int numBands = f.getAsInt(0);
+
+ if(numBands == 1) {
+ // Remove YCbCr fields not relevant for grayscale.
+
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING);
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE);
+ } else { // numBands == 3
+ // Replace YCbCr fields.
+
+ // YCbCrSubSampling
+ TIFFField YCbCrSubSamplingField = new TIFFField
+ (base.getTag(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING),
+ TIFFTag.TIFF_SHORT, 2,
+ new char[] {CHROMA_SUBSAMPLING, CHROMA_SUBSAMPLING});
+ rootIFD.addTIFFField(YCbCrSubSamplingField);
+
+ // YCbCrPositioning
+ TIFFField YCbCrPositioningField = new TIFFField
+ (base.getTag(BaselineTIFFTagSet.TAG_Y_CB_CR_POSITIONING),
+ TIFFTag.TIFF_SHORT, 1,
+ new char[]
+ {BaselineTIFFTagSet.Y_CB_CR_POSITIONING_CENTERED});
+ rootIFD.addTIFFField(YCbCrPositioningField);
+
+ // ReferenceBlackWhite
+ TIFFField referenceBlackWhiteField = new TIFFField
+ (base.getTag(BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE),
+ TIFFTag.TIFF_RATIONAL, 6,
+ new long[][] { // no headroon/footroom
+ {0, 1}, {255, 1},
+ {128, 1}, {255, 1},
+ {128, 1}, {255, 1}
+ });
+ rootIFD.addTIFFField(referenceBlackWhiteField);
+ }
+
+ // JPEGTables field is written if and only if one is
+ // already present in the metadata. If one is present
+ // and has either zero length or does not represent a
+ // valid tables-only stream, then a JPEGTables field
+ // will be written initialized to the standard tables-
+ // only stream written by the JPEG writer.
+
+ // Retrieve the JPEGTables field.
+ TIFFField JPEGTablesField =
+ tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_TABLES);
+
+ // Initialize JPEG writer to one supporting abbreviated streams.
+ if(JPEGTablesField != null) {
+ // Intialize the JPEG writer to one that supports stream
+ // metadata, i.e., abbreviated streams, and may or may not
+ // support image metadata.
+ initJPEGWriter(true, false);
+ }
+
+ // Write JPEGTables field if a writer supporting abbreviated
+ // streams was available.
+ if(JPEGTablesField != null && JPEGWriter != null) {
+ // Set the abbreviated stream flag.
+ this.writeAbbreviatedStream = true;
+
+ //Branch based on field value count.
+ if(JPEGTablesField.getCount() > 0) {
+ // Derive the stream metadata from the field.
+
+ // Get the field values.
+ byte[] tables = JPEGTablesField.getAsBytes();
+
+ // Create an input stream for the tables.
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(tables);
+ MemoryCacheImageInputStream iis =
+ new MemoryCacheImageInputStream(bais);
+
+ // Read the tables stream using the JPEG reader.
+ ImageReader jpegReader = getJPEGTablesReader();
+ jpegReader.setInput(iis);
+
+ // Initialize the stream metadata object.
+ try {
+ JPEGStreamMetadata = jpegReader.getStreamMetadata();
+ } catch(Exception e) {
+ // Fall back to default tables.
+ JPEGStreamMetadata = null;
+ } finally {
+ jpegReader.reset();
+ }
+ }
+
+ if(JPEGStreamMetadata == null) {
+ // Derive the field from default stream metadata.
+
+ // Get default stream metadata.
+ JPEGStreamMetadata =
+ JPEGWriter.getDefaultStreamMetadata(JPEGParam);
+
+ // Create an output stream for the tables.
+ ByteArrayOutputStream tableByteStream =
+ new ByteArrayOutputStream();
+ MemoryCacheImageOutputStream tableStream =
+ new MemoryCacheImageOutputStream(tableByteStream);
+
+ // Write a tables-only stream.
+ JPEGWriter.setOutput(tableStream);
+ try {
+ JPEGWriter.prepareWriteSequence(JPEGStreamMetadata);
+ tableStream.flush();
+ JPEGWriter.endWriteSequence();
+
+ // Get the tables-only stream content.
+ byte[] tables = tableByteStream.toByteArray();
+
+ // Add the JPEGTables field.
+ JPEGTablesField = new TIFFField
+ (base.getTag(BaselineTIFFTagSet.TAG_JPEG_TABLES),
+ TIFFTag.TIFF_UNDEFINED,
+ tables.length,
+ tables);
+ rootIFD.addTIFFField(JPEGTablesField);
+ } catch(Exception e) {
+ // Do not write JPEGTables field.
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_TABLES);
+ this.writeAbbreviatedStream = false;
+ }
+ }
+ } else { // Do not write JPEGTables field.
+ // Remove any field present.
+ rootIFD.removeTIFFField(BaselineTIFFTagSet.TAG_JPEG_TABLES);
+
+ // Initialize the writer preferring codecLib.
+ initJPEGWriter(false, false);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFJPEGDecompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,146 @@
+/*
+ * 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.io.ByteArrayInputStream;
+import java.util.Iterator;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageReadParam;
+import javax.imageio.stream.MemoryCacheImageInputStream;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.plugins.tiff.TIFFField;
+
+public class TIFFJPEGDecompressor extends TIFFDecompressor {
+ // Start of Image
+ protected static final int SOI = 0xD8;
+
+ // End of Image
+ protected static final int EOI = 0xD9;
+
+ protected ImageReader JPEGReader = null;
+ protected ImageReadParam JPEGParam;
+
+ protected boolean hasJPEGTables = false;
+ protected byte[] tables = null;
+
+ private byte[] data = new byte[0];
+
+ public TIFFJPEGDecompressor() {}
+
+ public void beginDecoding() {
+ // Initialize the JPEG reader if needed.
+ if(this.JPEGReader == null) {
+ // Get all JPEG readers.
+ Iterator<ImageReader> iter = ImageIO.getImageReadersByFormatName("jpeg");
+
+ if(!iter.hasNext()) {
+ throw new IllegalStateException("No JPEG readers found!");
+ }
+
+ // Initialize reader to the first one.
+ this.JPEGReader = iter.next();
+
+ this.JPEGParam = JPEGReader.getDefaultReadParam();
+ }
+
+ // Get the JPEGTables field.
+ TIFFImageMetadata tmetadata = (TIFFImageMetadata)metadata;
+ TIFFField f =
+ tmetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_TABLES);
+
+ if (f != null) {
+ this.hasJPEGTables = true;
+ this.tables = f.getAsBytes();
+ } else {
+ this.hasJPEGTables = false;
+ }
+ }
+
+ public void decodeRaw(byte[] b,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException {
+ // Seek to the data position for this segment.
+ stream.seek(offset);
+
+ // Set the stream variable depending on presence of JPEGTables.
+ ImageInputStream is;
+ if(this.hasJPEGTables) {
+ // The current strip or tile is an abbreviated JPEG stream.
+
+ // Reallocate memory if there is not enough already.
+ int dataLength = tables.length + byteCount;
+ if(data.length < dataLength) {
+ data = new byte[dataLength];
+ }
+
+ // Copy the tables ignoring any EOI and subsequent bytes.
+ int dataOffset = tables.length;
+ for(int i = tables.length - 2; i > 0; i--) {
+ if((tables[i] & 0xff) == 0xff &&
+ (tables[i+1] & 0xff) == EOI) {
+ dataOffset = i;
+ break;
+ }
+ }
+ System.arraycopy(tables, 0, data, 0, dataOffset);
+
+ // Check for SOI and skip it if present.
+ byte byte1 = (byte)stream.read();
+ byte byte2 = (byte)stream.read();
+ if(!((byte1 & 0xff) == 0xff && (byte2 & 0xff) == SOI)) {
+ data[dataOffset++] = byte1;
+ data[dataOffset++] = byte2;
+ }
+
+ // Read remaining data.
+ stream.readFully(data, dataOffset, byteCount - 2);
+
+ // Create ImageInputStream.
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ is = new MemoryCacheImageInputStream(bais);
+ } else {
+ // The current strip or tile is a complete JPEG stream.
+ is = stream;
+ }
+
+ // Set the stream on the reader.
+ JPEGReader.setInput(is, false, true);
+
+ // Set the destination to the raw image ignoring the parameters.
+ JPEGParam.setDestination(rawImage);
+
+ // Read the strip or tile.
+ JPEGReader.read(0, JPEGParam);
+ }
+
+ protected void finalize() throws Throwable {
+ super.finalize();
+ JPEGReader.dispose();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFLSBCompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,60 @@
+/*
+ * 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 java.io.IOException;
+
+/**
+ * Uncompressed data with LSB-to-MSB fill order.
+ */
+public class TIFFLSBCompressor extends TIFFCompressor {
+
+ public TIFFLSBCompressor() {
+ super("", BaselineTIFFTagSet.COMPRESSION_NONE, true);
+ }
+
+ public int encode(byte[] b, int off,
+ int width, int height,
+ int[] bitsPerSample,
+ int scanlineStride) throws IOException {
+ int bitsPerPixel = 0;
+ for (int i = 0; i < bitsPerSample.length; i++) {
+ bitsPerPixel += bitsPerSample[i];
+ }
+ int bytesPerRow = (bitsPerPixel*width + 7)/8;
+ byte[] compData = new byte[bytesPerRow];
+ byte[] flipTable = TIFFFaxDecompressor.flipTable;
+ for (int row = 0; row < height; row++) {
+ System.arraycopy(b, off, compData, 0, bytesPerRow);
+ for(int j = 0; j < bytesPerRow; j++) {
+ compData[j] = flipTable[compData[j]&0xff];
+ }
+ stream.write(compData, 0, bytesPerRow);
+ off += scanlineStride;
+ }
+ return height*bytesPerRow;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFLSBDecompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+public class TIFFLSBDecompressor extends TIFFDecompressor {
+
+ /**
+ * Table for flipping bytes from LSB-to-MSB to MSB-to-LSB.
+ */
+ private static final byte[] flipTable = TIFFFaxDecompressor.flipTable;
+
+ public TIFFLSBDecompressor() {}
+
+ public void decodeRaw(byte[] b,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException {
+ stream.seek(offset);
+
+ int bytesPerRow = (srcWidth*bitsPerPixel + 7)/8;
+ if(bytesPerRow == scanlineStride) {
+ int numBytes = bytesPerRow*srcHeight;
+ stream.readFully(b, dstOffset, numBytes);
+ int xMax = dstOffset + numBytes;
+ for (int x = dstOffset; x < xMax; x++) {
+ b[x] = flipTable[b[x]&0xff];
+ }
+ } else {
+ for (int y = 0; y < srcHeight; y++) {
+ stream.readFully(b, dstOffset, bytesPerRow);
+ int xMax = dstOffset + bytesPerRow;
+ for (int x = dstOffset; x < xMax; x++) {
+ b[x] = flipTable[b[x]&0xff];
+ }
+ dstOffset += scanlineStride;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFLZWCompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,94 @@
+/*
+ * 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 com.sun.imageio.plugins.common.LZWCompressor;
+import java.io.IOException;
+import javax.imageio.stream.ImageOutputStream;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+
+/**
+ * LZW Compressor.
+ */
+public class TIFFLZWCompressor extends TIFFCompressor {
+
+ private final int predictor;
+
+ public TIFFLZWCompressor(int predictorValue) {
+ super("LZW", BaselineTIFFTagSet.COMPRESSION_LZW, true);
+ this.predictor = predictorValue;
+ }
+
+ public void setStream(ImageOutputStream stream) {
+ super.setStream(stream);
+ }
+
+ public int encode(byte[] b, int off,
+ int width, int height,
+ int[] bitsPerSample,
+ int scanlineStride) throws IOException {
+
+ LZWCompressor lzwCompressor = new LZWCompressor(stream, 8, true);
+
+ int samplesPerPixel = bitsPerSample.length;
+ int bitsPerPixel = 0;
+ for (int i = 0; i < samplesPerPixel; i++) {
+ bitsPerPixel += bitsPerSample[i];
+ }
+ int bytesPerRow = (bitsPerPixel*width + 7)/8;
+
+ long initialStreamPosition = stream.getStreamPosition();
+
+ boolean usePredictor =
+ predictor == BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING;
+
+ if(bytesPerRow == scanlineStride && !usePredictor) {
+ lzwCompressor.compress(b, off, bytesPerRow*height);
+ } else {
+ byte[] rowBuf = usePredictor ? new byte[bytesPerRow] : null;
+ for(int i = 0; i < height; i++) {
+ if(usePredictor) {
+ // Cannot modify b[] in place as it might be a data
+ // array from the image being written so make a copy.
+ System.arraycopy(b, off, rowBuf, 0, bytesPerRow);
+ for(int j = bytesPerRow - 1; j >= samplesPerPixel; j--) {
+ rowBuf[j] -= rowBuf[j - samplesPerPixel];
+ }
+ lzwCompressor.compress(rowBuf, 0, bytesPerRow);
+ } else {
+ lzwCompressor.compress(b, off, bytesPerRow);
+ }
+ off += scanlineStride;
+ }
+ }
+
+ lzwCompressor.flush();
+
+ int bytesWritten =
+ (int)(stream.getStreamPosition() - initialStreamPosition);
+
+ return bytesWritten;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFLZWDecompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,285 @@
+/*
+ * 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.IIOException;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+
+class TIFFLZWDecompressor extends TIFFDecompressor {
+
+ private static final int andTable[] = {
+ 511,
+ 1023,
+ 2047,
+ 4095
+ };
+
+ private int predictor;
+
+ private byte[] srcData;
+ private byte[] dstData;
+
+ private int srcIndex;
+ private int dstIndex;
+
+ private byte stringTable[][];
+ private int tableIndex, bitsToGet = 9;
+
+ private int nextData = 0;
+ private int nextBits = 0;
+
+ public TIFFLZWDecompressor(int predictor) throws IIOException {
+ super();
+
+ if (predictor != BaselineTIFFTagSet.PREDICTOR_NONE &&
+ predictor !=
+ BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
+ throw new IIOException("Illegal value for Predictor in " +
+ "TIFF file");
+ }
+
+ this.predictor = predictor;
+ }
+
+ public 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");
+ }
+ }
+ }
+
+ stream.seek(offset);
+
+ byte[] sdata = new byte[byteCount];
+ stream.readFully(sdata);
+
+ 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;
+ }
+
+ int numBytesDecoded = decode(sdata, 0, buf, bufOffset);
+
+ if(bytesPerRow != scanlineStride) {
+ int off = 0;
+ for (int y = 0; y < srcHeight; y++) {
+ System.arraycopy(buf, off, b, dstOffset, bytesPerRow);
+ off += bytesPerRow;
+ dstOffset += scanlineStride;
+ }
+ }
+ }
+
+ public int decode(byte[] sdata, int srcOffset,
+ byte[] ddata, int dstOffset)
+ throws IOException {
+ if (sdata[0] == (byte)0x00 && sdata[1] == (byte)0x01) {
+ throw new IIOException
+ ("TIFF 5.0-style LZW compression is not supported!");
+ }
+
+ this.srcData = sdata;
+ this.dstData = ddata;
+
+ this.srcIndex = srcOffset;
+ this.dstIndex = dstOffset;
+
+ this.nextData = 0;
+ this.nextBits = 0;
+
+ initializeStringTable();
+
+ int code, oldCode = 0;
+ byte[] string;
+
+ while ((code = getNextCode()) != 257) {
+ if (code == 256) {
+ initializeStringTable();
+ code = getNextCode();
+ if (code == 257) {
+ break;
+ }
+
+ writeString(stringTable[code]);
+ oldCode = code;
+ } else {
+ if (code < tableIndex) {
+ string = stringTable[code];
+
+ writeString(string);
+ addStringToTable(stringTable[oldCode], string[0]);
+ oldCode = code;
+ } else {
+ string = stringTable[oldCode];
+ string = composeString(string, string[0]);
+ writeString(string);
+ addStringToTable(string);
+ oldCode = code;
+ }
+ }
+ }
+
+ if (predictor ==
+ BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) {
+
+ for (int j = 0; j < srcHeight; j++) {
+
+ int count = dstOffset + samplesPerPixel * (j * srcWidth + 1);
+
+ for (int i = samplesPerPixel; i < srcWidth * samplesPerPixel; i++) {
+
+ dstData[count] += dstData[count - samplesPerPixel];
+ count++;
+ }
+ }
+ }
+
+ return dstIndex - dstOffset;
+ }
+
+ /**
+ * Initialize the string table.
+ */
+ public void initializeStringTable() {
+ stringTable = new byte[4096][];
+
+ for (int i = 0; i < 256; i++) {
+ stringTable[i] = new byte[1];
+ stringTable[i][0] = (byte)i;
+ }
+
+ tableIndex = 258;
+ bitsToGet = 9;
+ }
+
+ /**
+ * Write out the string just uncompressed.
+ */
+ public void writeString(byte string[]) {
+ if(dstIndex < dstData.length) {
+ int maxIndex = Math.min(string.length,
+ dstData.length - dstIndex);
+
+ for (int i=0; i < maxIndex; i++) {
+ dstData[dstIndex++] = string[i];
+ }
+ }
+ }
+
+ /**
+ * Add a new string to the string table.
+ */
+ public void addStringToTable(byte oldString[], byte newString) {
+ int length = oldString.length;
+ byte string[] = new byte[length + 1];
+ System.arraycopy(oldString, 0, string, 0, length);
+ string[length] = newString;
+
+ // Add this new String to the table
+ stringTable[tableIndex++] = string;
+
+ if (tableIndex == 511) {
+ bitsToGet = 10;
+ } else if (tableIndex == 1023) {
+ bitsToGet = 11;
+ } else if (tableIndex == 2047) {
+ bitsToGet = 12;
+ }
+ }
+
+ /**
+ * Add a new string to the string table.
+ */
+ public void addStringToTable(byte string[]) {
+ // Add this new String to the table
+ stringTable[tableIndex++] = string;
+
+ if (tableIndex == 511) {
+ bitsToGet = 10;
+ } else if (tableIndex == 1023) {
+ bitsToGet = 11;
+ } else if (tableIndex == 2047) {
+ bitsToGet = 12;
+ }
+ }
+
+ /**
+ * Append <code>newString</code> to the end of <code>oldString</code>.
+ */
+ public byte[] composeString(byte oldString[], byte newString) {
+ int length = oldString.length;
+ byte string[] = new byte[length + 1];
+ System.arraycopy(oldString, 0, string, 0, length);
+ string[length] = newString;
+
+ return string;
+ }
+
+ // Returns the next 9, 10, 11 or 12 bits
+ public int getNextCode() {
+ // Attempt to get the next code. The exception is caught to make
+ // this robust to cases wherein the EndOfInformation code has been
+ // omitted from a strip. Examples of such cases have been observed
+ // in practice.
+
+ try {
+ nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
+ nextBits += 8;
+
+ if (nextBits < bitsToGet) {
+ nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
+ nextBits += 8;
+ }
+
+ int code =
+ (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet - 9];
+ nextBits -= bitsToGet;
+
+ return code;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Strip not terminated as expected: return EndOfInformation code.
+ return 257;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFLZWUtil.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,228 @@
+/*
+ * 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.IIOException;
+
+class TIFFLZWUtil {
+ public TIFFLZWUtil() {
+ }
+
+ byte[] srcData;
+ int srcIndex;
+
+ byte[] dstData;
+ int dstIndex = 0;
+
+ byte stringTable[][];
+ int tableIndex, bitsToGet = 9;
+
+ int nextData = 0;
+ int nextBits = 0;
+
+ private static final int andTable[] = {
+ 511,
+ 1023,
+ 2047,
+ 4095
+ };
+
+ public byte[] decode(byte[] data, int predictor, int samplesPerPixel,
+ int width, int height) throws IOException {
+ if (data[0] == (byte)0x00 && data[1] == (byte)0x01) {
+ throw new IIOException("TIFF 5.0-style LZW compression is not supported!");
+ }
+
+ this.srcData = data;
+ this.srcIndex = 0;
+ this.nextData = 0;
+ this.nextBits = 0;
+
+ this.dstData = new byte[8192];
+ this.dstIndex = 0;
+
+ initializeStringTable();
+
+ int code, oldCode = 0;
+ byte[] string;
+
+ while ((code = getNextCode()) != 257) {
+ if (code == 256) {
+ initializeStringTable();
+ code = getNextCode();
+ if (code == 257) {
+ break;
+ }
+
+ writeString(stringTable[code]);
+ oldCode = code;
+ } else {
+ if (code < tableIndex) {
+ string = stringTable[code];
+
+ writeString(string);
+ addStringToTable(stringTable[oldCode], string[0]);
+ oldCode = code;
+ } else {
+ string = stringTable[oldCode];
+ string = composeString(string, string[0]);
+ writeString(string);
+ addStringToTable(string);
+ oldCode = code;
+ }
+ }
+ }
+
+ if (predictor == 2) {
+
+ int count;
+ for (int j = 0; j < height; j++) {
+
+ count = samplesPerPixel * (j * width + 1);
+
+ for (int i = samplesPerPixel; i < width * samplesPerPixel; i++) {
+
+ dstData[count] += dstData[count - samplesPerPixel];
+ count++;
+ }
+ }
+ }
+
+ byte[] newDstData = new byte[dstIndex];
+ System.arraycopy(dstData, 0, newDstData, 0, dstIndex);
+ return newDstData;
+ }
+
+ /**
+ * Initialize the string table.
+ */
+ public void initializeStringTable() {
+ stringTable = new byte[4096][];
+
+ for (int i = 0; i < 256; i++) {
+ stringTable[i] = new byte[1];
+ stringTable[i][0] = (byte)i;
+ }
+
+ tableIndex = 258;
+ bitsToGet = 9;
+ }
+
+ private void ensureCapacity(int bytesToAdd) {
+ if (dstIndex + bytesToAdd > dstData.length) {
+ byte[] newDstData = new byte[Math.max((int)(dstData.length*1.2f),
+ dstIndex + bytesToAdd)];
+ System.arraycopy(dstData, 0, newDstData, 0, dstData.length);
+ dstData = newDstData;
+ }
+ }
+
+ /**
+ * Write out the string just uncompressed.
+ */
+ public void writeString(byte string[]) {
+ ensureCapacity(string.length);
+ for (int i = 0; i < string.length; i++) {
+ dstData[dstIndex++] = string[i];
+ }
+ }
+
+ /**
+ * Add a new string to the string table.
+ */
+ public void addStringToTable(byte oldString[], byte newString) {
+ int length = oldString.length;
+ byte string[] = new byte[length + 1];
+ System.arraycopy(oldString, 0, string, 0, length);
+ string[length] = newString;
+
+ // Add this new String to the table
+ stringTable[tableIndex++] = string;
+
+ if (tableIndex == 511) {
+ bitsToGet = 10;
+ } else if (tableIndex == 1023) {
+ bitsToGet = 11;
+ } else if (tableIndex == 2047) {
+ bitsToGet = 12;
+ }
+ }
+
+ /**
+ * Add a new string to the string table.
+ */
+ public void addStringToTable(byte string[]) {
+ // Add this new String to the table
+ stringTable[tableIndex++] = string;
+
+ if (tableIndex == 511) {
+ bitsToGet = 10;
+ } else if (tableIndex == 1023) {
+ bitsToGet = 11;
+ } else if (tableIndex == 2047) {
+ bitsToGet = 12;
+ }
+ }
+
+ /**
+ * Append <code>newString</code> to the end of <code>oldString</code>.
+ */
+ public byte[] composeString(byte oldString[], byte newString) {
+ int length = oldString.length;
+ byte string[] = new byte[length + 1];
+ System.arraycopy(oldString, 0, string, 0, length);
+ string[length] = newString;
+
+ return string;
+ }
+
+ // Returns the next 9, 10, 11 or 12 bits
+ public int getNextCode() {
+ // Attempt to get the next code. The exception is caught to make
+ // this robust to cases wherein the EndOfInformation code has been
+ // omitted from a strip. Examples of such cases have been observed
+ // in practice.
+
+ try {
+ nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
+ nextBits += 8;
+
+ if (nextBits < bitsToGet) {
+ nextData = (nextData << 8) | (srcData[srcIndex++] & 0xff);
+ nextBits += 8;
+ }
+
+ int code =
+ (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet - 9];
+ nextBits -= bitsToGet;
+
+ return code;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Strip not terminated as expected: return EndOfInformation code.
+ return 257;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFMetadataFormat.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,241 @@
+/*
+ * 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.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import javax.imageio.metadata.IIOMetadataFormat;
+
+public abstract class TIFFMetadataFormat implements IIOMetadataFormat {
+
+ protected Map<String,TIFFElementInfo> elementInfoMap = new HashMap<String,TIFFElementInfo>();
+ protected Map<String,TIFFAttrInfo> attrInfoMap = new HashMap<String,TIFFAttrInfo>();
+
+ protected String resourceBaseName;
+ protected String rootName;
+
+ public String getRootName() {
+ return rootName;
+ }
+
+ private String getResource(String key, Locale locale) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+ try {
+ ResourceBundle bundle =
+ ResourceBundle.getBundle(resourceBaseName, locale);
+ return bundle.getString(key);
+ } catch (MissingResourceException e) {
+ return null;
+ }
+ }
+
+ private TIFFElementInfo getElementInfo(String elementName) {
+ if (elementName == null) {
+ throw new NullPointerException("elementName == null!");
+ }
+ TIFFElementInfo info =
+ elementInfoMap.get(elementName);
+ if (info == null) {
+ throw new IllegalArgumentException("No such element: " +
+ elementName);
+ }
+ return info;
+ }
+
+ private TIFFAttrInfo getAttrInfo(String elementName, String attrName) {
+ if (elementName == null) {
+ throw new NullPointerException("elementName == null!");
+ }
+ if (attrName == null) {
+ throw new NullPointerException("attrName == null!");
+ }
+ String key = elementName + "/" + attrName;
+ TIFFAttrInfo info = attrInfoMap.get(key);
+ if (info == null) {
+ throw new IllegalArgumentException("No such attribute: " + key);
+ }
+ return info;
+ }
+
+ public int getElementMinChildren(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ return info.minChildren;
+ }
+
+ public int getElementMaxChildren(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ return info.maxChildren;
+ }
+
+ public String getElementDescription(String elementName, Locale locale) {
+ if (!elementInfoMap.containsKey(elementName)) {
+ throw new IllegalArgumentException("No such element: " +
+ elementName);
+ }
+ return getResource(elementName, locale);
+ }
+
+ public int getChildPolicy(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ return info.childPolicy;
+ }
+
+ public String[] getChildNames(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ return info.childNames;
+ }
+
+ public String[] getAttributeNames(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ return info.attributeNames;
+ }
+
+ public int getAttributeValueType(String elementName, String attrName) {
+ TIFFAttrInfo info = getAttrInfo(elementName, attrName);
+ return info.valueType;
+ }
+
+ public int getAttributeDataType(String elementName, String attrName) {
+ TIFFAttrInfo info = getAttrInfo(elementName, attrName);
+ return info.dataType;
+ }
+
+ public boolean isAttributeRequired(String elementName, String attrName) {
+ TIFFAttrInfo info = getAttrInfo(elementName, attrName);
+ return info.isRequired;
+ }
+
+ public String getAttributeDefaultValue(String elementName,
+ String attrName) {
+ return null;
+ }
+
+ public String[] getAttributeEnumerations(String elementName,
+ String attrName) {
+ throw new IllegalArgumentException("The attribute is not an enumeration.");
+ }
+
+ public String getAttributeMinValue(String elementName, String attrName) {
+ throw new IllegalArgumentException("The attribute is not a range.");
+ }
+
+ public String getAttributeMaxValue(String elementName, String attrName) {
+ throw new IllegalArgumentException("The attribute is not a range.");
+ }
+
+ public int getAttributeListMinLength(String elementName, String attrName) {
+ TIFFAttrInfo info = getAttrInfo(elementName, attrName);
+ return info.listMinLength;
+ }
+
+ public int getAttributeListMaxLength(String elementName, String attrName) {
+ TIFFAttrInfo info = getAttrInfo(elementName, attrName);
+ return info.listMaxLength;
+ }
+
+ public String getAttributeDescription(String elementName, String attrName,
+ Locale locale) {
+ String key = elementName + "/" + attrName;
+ if (!attrInfoMap.containsKey(key)) {
+ throw new IllegalArgumentException("No such attribute: " + key);
+ }
+ return getResource(key, locale);
+ }
+
+ public int getObjectValueType(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ return info.objectValueType;
+ }
+
+ public Class<?> getObjectClass(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ if (info.objectValueType == VALUE_NONE) {
+ throw new IllegalArgumentException(
+ "Element cannot contain an object value: " + elementName);
+ }
+ return info.objectClass;
+ }
+
+ public Object getObjectDefaultValue(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ if (info.objectValueType == VALUE_NONE) {
+ throw new IllegalArgumentException(
+ "Element cannot contain an object value: " + elementName);
+ }
+ return info.objectDefaultValue;
+ }
+
+ public Object[] getObjectEnumerations(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ if (info.objectValueType == VALUE_NONE) {
+ throw new IllegalArgumentException(
+ "Element cannot contain an object value: " + elementName);
+ }
+ return info.objectEnumerations;
+ }
+
+ public Comparable<Object> getObjectMinValue(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ if (info.objectValueType == VALUE_NONE) {
+ throw new IllegalArgumentException(
+ "Element cannot contain an object value: " + elementName);
+ }
+ return info.objectMinValue;
+ }
+
+ public Comparable<Object> getObjectMaxValue(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ if (info.objectValueType == VALUE_NONE) {
+ throw new IllegalArgumentException(
+ "Element cannot contain an object value: " + elementName);
+ }
+ return info.objectMaxValue;
+ }
+
+ public int getObjectArrayMinLength(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ if (info.objectValueType == VALUE_NONE) {
+ throw new IllegalArgumentException(
+ "Element cannot contain an object value: " + elementName);
+ }
+ return info.objectArrayMinLength;
+ }
+
+ public int getObjectArrayMaxLength(String elementName) {
+ TIFFElementInfo info = getElementInfo(elementName);
+ if (info.objectValueType == VALUE_NONE) {
+ throw new IllegalArgumentException(
+ "Element cannot contain an object value: " + elementName);
+ }
+ return info.objectArrayMaxLength;
+ }
+
+ public TIFFMetadataFormat() {}
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFNullCompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,59 @@
+/*
+ * 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 java.io.IOException;
+
+public class TIFFNullCompressor extends TIFFCompressor {
+
+ public TIFFNullCompressor() {
+ super("", BaselineTIFFTagSet.COMPRESSION_NONE, true);
+ }
+
+ public int encode(byte[] b, int off,
+ int width, int height,
+ int[] bitsPerSample,
+ int scanlineStride) throws IOException {
+ int bitsPerPixel = 0;
+ for (int i = 0; i < bitsPerSample.length; i++) {
+ bitsPerPixel += bitsPerSample[i];
+ }
+
+ int bytesPerRow = (bitsPerPixel*width + 7)/8;
+ int numBytes = height*bytesPerRow;
+
+ if(bytesPerRow == scanlineStride) {
+ stream.write(b, off, numBytes);
+ } else {
+ for (int row = 0; row < height; row++) {
+ stream.write(b, off, bytesPerRow);
+ off += scanlineStride;
+ }
+ }
+
+ return numBytes;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFNullDecompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,173 @@
+/*
+ * 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.EOFException;
+import java.io.IOException;
+
+public class TIFFNullDecompressor extends TIFFDecompressor {
+
+ /**
+ * Whether to read the active source region only.
+ */
+ private boolean isReadActiveOnly = false;
+
+ /** The original value of <code>srcMinX</code>. */
+ private int originalSrcMinX;
+
+ /** The original value of <code>srcMinY</code>. */
+ private int originalSrcMinY;
+
+ /** The original value of <code>srcWidth</code>. */
+ private int originalSrcWidth;
+
+ /** The original value of <code>srcHeight</code>. */
+ private int originalSrcHeight;
+
+ public TIFFNullDecompressor() {}
+
+ //
+ // This approach to reading the active region is a not the best
+ // as the original values of the entire source region are stored,
+ // overwritten, and then restored. It would probably be better to
+ // revise TIFFDecompressor such that this were not necessary, i.e.,
+ // change beginDecoding() and decode() to use the active region values
+ // when random access is easy and the entire region values otherwise.
+ //
+ public void beginDecoding() {
+ // Determine number of bits per pixel.
+ int bitsPerPixel = 0;
+ for(int i = 0; i < bitsPerSample.length; i++) {
+ bitsPerPixel += bitsPerSample[i];
+ }
+
+ // Can read active region only if row starts on a byte boundary.
+ if((activeSrcMinX != srcMinX || activeSrcMinY != srcMinY ||
+ activeSrcWidth != srcWidth || activeSrcHeight != srcHeight) &&
+ ((activeSrcMinX - srcMinX)*bitsPerPixel) % 8 == 0) {
+ // Set flag.
+ isReadActiveOnly = true;
+
+ // Cache original region.
+ originalSrcMinX = srcMinX;
+ originalSrcMinY = srcMinY;
+ originalSrcWidth = srcWidth;
+ originalSrcHeight = srcHeight;
+
+ // Replace region with active region.
+ srcMinX = activeSrcMinX;
+ srcMinY = activeSrcMinY;
+ srcWidth = activeSrcWidth;
+ srcHeight = activeSrcHeight;
+ } else {
+ // Clear flag.
+ isReadActiveOnly = false;
+ }
+
+ super.beginDecoding();
+ }
+
+ public void decode() throws IOException {
+ super.decode();
+
+ // Reset state.
+ if(isReadActiveOnly) {
+ // Restore original source region values.
+ srcMinX = originalSrcMinX;
+ srcMinY = originalSrcMinY;
+ srcWidth = originalSrcWidth;
+ srcHeight = originalSrcHeight;
+
+ // Unset flag.
+ isReadActiveOnly = false;
+ }
+ }
+
+ public void decodeRaw(byte[] b,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException {
+ if(isReadActiveOnly) {
+ // Read the active source region only.
+
+ int activeBytesPerRow = (activeSrcWidth*bitsPerPixel + 7)/8;
+ int totalBytesPerRow = (originalSrcWidth*bitsPerPixel + 7)/8;
+ int bytesToSkipPerRow = totalBytesPerRow - activeBytesPerRow;
+
+ //
+ // Seek to the start of the active region:
+ //
+ // active offset = original offset +
+ // number of bytes to start of first active row +
+ // number of bytes to first active pixel within row
+ //
+ // Since the condition for reading from the active region only is
+ //
+ // ((activeSrcMinX - srcMinX)*bitsPerPixel) % 8 == 0
+ //
+ // the bit offset to the first active pixel within the first
+ // active row is a multiple of 8.
+ //
+ stream.seek(offset +
+ (activeSrcMinY - originalSrcMinY)*totalBytesPerRow +
+ ((activeSrcMinX - originalSrcMinX)*bitsPerPixel)/8);
+
+ int lastRow = activeSrcHeight - 1;
+ for (int y = 0; y < activeSrcHeight; y++) {
+ int bytesRead = stream.read(b, dstOffset, activeBytesPerRow);
+ if (bytesRead < 0) {
+ throw new EOFException();
+ } else if (bytesRead != activeBytesPerRow) {
+ break;
+ }
+ dstOffset += scanlineStride;
+
+ // Skip unneeded bytes (row suffix + row prefix).
+ if(y != lastRow) {
+ stream.skipBytes(bytesToSkipPerRow);
+ }
+ }
+ } else {
+ // Read the entire source region.
+ stream.seek(offset);
+ int bytesPerRow = (srcWidth*bitsPerPixel + 7)/8;
+ if(bytesPerRow == scanlineStride) {
+ if (stream.read(b, dstOffset, bytesPerRow*srcHeight) < 0) {
+ throw new EOFException();
+ }
+ } else {
+ for (int y = 0; y < srcHeight; y++) {
+ int bytesRead = stream.read(b, dstOffset, bytesPerRow);
+ if (bytesRead < 0) {
+ throw new EOFException();
+ } else if (bytesRead != bytesPerRow) {
+ break;
+ }
+ dstOffset += scanlineStride;
+ }
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFOldJPEGDecompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,617 @@
+/*
+ * 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.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import javax.imageio.IIOException;
+import javax.imageio.stream.MemoryCacheImageInputStream;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.plugins.tiff.TIFFField;
+
+/**
+ * <code>TIFFDecompressor</code> for "Old JPEG" compression.
+ */
+public class TIFFOldJPEGDecompressor extends TIFFJPEGDecompressor {
+
+ // Define Huffman Tables
+ private static final int DHT = 0xC4;
+
+ // Define Quantisation Tables
+ private static final int DQT = 0xDB;
+
+ // Define Restart Interval
+ private static final int DRI = 0xDD;
+
+ // Baseline DCT
+ private static final int SOF0 = 0xC0;
+
+ // Start of Scan
+ private static final int SOS = 0xDA;
+
+ // End of Image
+ // private static final int EOI = 0xD9; // now defined in superclass
+
+ // Whether the decompressor has been initialized.
+ private boolean isInitialized = false;
+
+ //
+ // Instance variables set by the initialize() method.
+ //
+ // Offset to a complete, contiguous JPEG stream.
+ private Long JPEGStreamOffset = null;
+ // Offset to the SOF marker.
+ private int SOFPosition = -1;
+ // Value of the SOS marker.
+ private byte[] SOSMarker = null;
+
+ // Horizontal chroma subsampling factor.
+ private int subsamplingX = 2;
+
+ // Vertical chroma subsampling factor.
+ private int subsamplingY = 2;
+
+ public TIFFOldJPEGDecompressor() {}
+
+ //
+ // Intialize instance variables according to an analysis of the
+ // TIFF field content. See bug 4929147 for test image information.
+ //
+ // Case 1: Image contains a single strip or tile and the offset to
+ // that strip or tile points to an SOI marker.
+ //
+ // Example:
+ // "Visionshape Inc. Compression Software, version 2.5"
+ // ColorTiffWithBarcode.tif
+ // Color2.tif (pages 2-5 (indexes 1-4)
+ // color3.tif (pages 2-5 (indexes 1-4)
+ //
+ // "Kofax standard Multi-Page TIFF Storage Filter v2.01.000"
+ // 01.tif (pages 1 and 3(indexes 0 and 2))
+ //
+ // Instance variables set: JPEGStreamOffset
+ //
+ // Case 2: Image contains a single strip or tile and a
+ // JPEGInterchangeFormat field is present but the
+ // JPEGInterchangeFormatLength is erroneously missing.
+ //
+ // Example:
+ // "Kofax standard Multi-Page TIFF Storage Filter v2.01.000"
+ // 01.tif (pages 1 and 3(indexes 0 and 2))
+ // (but this example also satisfies case 1)
+ //
+ // Instance variables set: JPEGStreamOffset
+ //
+ // Case 3: Image contains a single strip or tile, the
+ // JPEGInterchangeFormat and JPEGInterchangeFormatLength
+ // fields are both present, the value of JPEGInterchangeFormat
+ // is less than the offset to the strip or tile, and the sum
+ // of the values of JPEGInterchangeFormat and
+ // JPEGInterchangeFormatLength is greater than the offset to
+ // the strip or tile.
+ //
+ // Instance variables set: JPEGStreamOffset
+ //
+ // Example:
+ // "HP IL v1.1"
+ // smallliz.tif from libtiff test data.
+ //
+ // Instance variables set: JPEGStreamOffset
+ //
+ // Cases 4-5 apply if none of cases 1-3 applies or the image has multiple
+ // strips or tiles.
+ //
+ // Case 4: JPEGInterchangeFormat and JPEGInterchangeFormatLength are
+ // present, the value of JPEGInterchangeFormatLength is at least 2,
+ // and the sum of the values of these two fields is at most the
+ // value of the offset to the first strip or tile.
+ //
+ // Instance variables set: tables, SOFPosition, SOSMarker
+ //
+ // Example:
+ // "Oi/GFS, writer v00.06.00P, (c) Wang Labs, Inc. 1990, 1991"
+ // 03.tif (pages 1 and 3(indexes 0 and 2))
+ //
+ // "Oi/GFS, writer v00.06.02"
+ // Color2.tif (page 1 (index 0))
+ // color3.tif (page 1 (index 0))
+ //
+ // Case 5: If none of the foregoing cases apply. For this case the
+ // JPEGQTables, JPEGACTables, and JPEGDCTables must be valid.
+ //
+ // Instance variables set: tables, SOFPosition, SOSMarker
+ //
+ // Example:
+ // "NeXT"
+ // zackthecat.tif from libtiff test data.
+ //
+ private synchronized void initialize() throws IOException {
+ if(isInitialized) {
+ return;
+ }
+
+ // Get the TIFF metadata object.
+ TIFFImageMetadata tim = (TIFFImageMetadata)metadata;
+
+ // Get the JPEGInterchangeFormat field.
+ TIFFField JPEGInterchangeFormatField =
+ tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT);
+
+ // Get the tile or strip offsets.
+ TIFFField segmentOffsetField =
+ tim.getTIFFField(BaselineTIFFTagSet.TAG_TILE_OFFSETS);
+ if(segmentOffsetField == null) {
+ segmentOffsetField =
+ tim.getTIFFField(BaselineTIFFTagSet.TAG_STRIP_OFFSETS);
+ if(segmentOffsetField == null) {
+ segmentOffsetField = JPEGInterchangeFormatField;
+ }
+ }
+ long[] segmentOffsets = segmentOffsetField.getAsLongs();
+
+ // Determine whether the image has more than one strip or tile.
+ boolean isTiled = segmentOffsets.length > 1;
+
+ if(!isTiled) {
+ //
+ // If the image has only a single strip or tile and it looks
+ // as if a complete JPEG stream is present then set the value
+ // of JPEGStreamOffset to the offset of the JPEG stream;
+ // otherwise leave JPEGStreamOffset set to null.
+ //
+
+ stream.seek(offset);
+ stream.mark();
+ if(stream.read() == 0xff && stream.read() == SOI) {
+ // Tile or strip offset points to SOI.
+ JPEGStreamOffset = Long.valueOf(offset);
+
+ // Set initialization flag and return.
+ ((TIFFImageReader)reader).forwardWarningMessage("SOI marker detected at start of strip or tile.");
+ isInitialized = true;
+ return;
+ }
+ stream.reset();
+
+ if(JPEGInterchangeFormatField != null) {
+ // Get the value of JPEGInterchangeFormat.
+ long jpegInterchangeOffset =
+ JPEGInterchangeFormatField.getAsLong(0);
+
+ // Get the JPEGInterchangeFormatLength field.
+ TIFFField JPEGInterchangeFormatLengthField =
+ tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
+
+ if(JPEGInterchangeFormatLengthField == null) {
+ // JPEGInterchangeFormat stream is of indeterminate
+ // length so use it as a complete JPEG stream.
+ JPEGStreamOffset = Long.valueOf(jpegInterchangeOffset);
+
+ // Set initialization flag and return.
+ ((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormatLength field is missing");
+ isInitialized = true;
+ return;
+ } else {
+ // Get the JPEGInterchangeFormatLength field's value.
+ long jpegInterchangeLength =
+ JPEGInterchangeFormatLengthField.getAsLong(0);
+
+ if(jpegInterchangeOffset < segmentOffsets[0] &&
+ (jpegInterchangeOffset + jpegInterchangeLength) >
+ segmentOffsets[0]) {
+ // JPEGInterchangeFormat points to a position
+ // below the segment start position and ends at
+ // a position after the segment start position.
+ JPEGStreamOffset = Long.valueOf(jpegInterchangeOffset);
+
+ // Set initialization flag and return.
+ isInitialized = true;
+ return;
+ }
+ }
+ }
+ }
+
+ // Get the subsampling factors.
+ TIFFField YCbCrSubsamplingField =
+ tim.getTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
+ if(YCbCrSubsamplingField != null) {
+ subsamplingX = YCbCrSubsamplingField.getAsChars()[0];
+ subsamplingY = YCbCrSubsamplingField.getAsChars()[1];
+ }
+
+ //
+ // Initialize the 'tables' instance variable either for later
+ // use in prepending to individual abbreviated strips or tiles.
+ //
+ if(JPEGInterchangeFormatField != null) {
+ // Get the value of JPEGInterchangeFormat.
+ long jpegInterchangeOffset =
+ JPEGInterchangeFormatField.getAsLong(0);
+
+ // Get the JPEGInterchangeFormatLength field.
+ TIFFField JPEGInterchangeFormatLengthField =
+ tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
+
+ if(JPEGInterchangeFormatLengthField != null) {
+ // Get the JPEGInterchangeFormatLength field's value.
+ long jpegInterchangeLength =
+ JPEGInterchangeFormatLengthField.getAsLong(0);
+
+ if(jpegInterchangeLength >= 2 &&
+ jpegInterchangeOffset + jpegInterchangeLength <=
+ segmentOffsets[0]) {
+ // Determine the length excluding any terminal EOI marker
+ // and allocate table memory.
+ stream.mark();
+ stream.seek(jpegInterchangeOffset+jpegInterchangeLength-2);
+ if(stream.read() == 0xff && stream.read() == EOI) {
+ this.tables = new byte[(int)(jpegInterchangeLength-2)];
+ } else {
+ this.tables = new byte[(int)jpegInterchangeLength];
+ }
+ stream.reset();
+
+ // Read the tables.
+ stream.mark();
+ stream.seek(jpegInterchangeOffset);
+ stream.readFully(tables);
+ stream.reset();
+
+ ((TIFFImageReader)reader).forwardWarningMessage("Incorrect JPEG interchange format: using JPEGInterchangeFormat offset to derive tables.");
+ } else {
+ ((TIFFImageReader)reader).forwardWarningMessage("JPEGInterchangeFormat+JPEGInterchangeFormatLength > offset to first strip or tile.");
+ }
+ }
+ }
+
+ if(this.tables == null) {
+ //
+ // Create tables-only stream in tables[] consisting of
+ // SOI+DQTs+DHTs
+ //
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ // Save stream length;
+ long streamLength = stream.length();
+
+ // SOI
+ baos.write(0xff);
+ baos.write(SOI);
+
+ // Quantization Tables
+ TIFFField f =
+ tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_Q_TABLES);
+ if(f == null) {
+ throw new IIOException("JPEGQTables field missing!");
+ }
+ long[] off = f.getAsLongs();
+
+ for(int i = 0; i < off.length; i++) {
+ baos.write(0xff); // Marker ID
+ baos.write(DQT);
+
+ char markerLength = (char)67;
+ baos.write((markerLength >>> 8) & 0xff); // Length
+ baos.write(markerLength & 0xff);
+
+ baos.write(i); // Table ID and precision
+
+ byte[] qtable = new byte[64];
+ if(streamLength != -1 && off[i] > streamLength) {
+ throw new IIOException("JPEGQTables offset for index "+
+ i+" is not in the stream!");
+ }
+ stream.seek(off[i]);
+ stream.readFully(qtable);
+
+ baos.write(qtable); // Table data
+ }
+
+ // Huffman Tables (k == 0 ? DC : AC).
+ for(int k = 0; k < 2; k++) {
+ int tableTagNumber = k == 0 ?
+ BaselineTIFFTagSet.TAG_JPEG_DC_TABLES :
+ BaselineTIFFTagSet.TAG_JPEG_AC_TABLES;
+ f = tim.getTIFFField(tableTagNumber);
+ String fieldName =
+ tableTagNumber ==
+ BaselineTIFFTagSet.TAG_JPEG_DC_TABLES ?
+ "JPEGDCTables" : "JPEGACTables";
+
+ if(f == null) {
+ throw new IIOException(fieldName+" field missing!");
+ }
+ off = f.getAsLongs();
+
+ for(int i = 0; i < off.length; i++) {
+ baos.write(0xff); // Marker ID
+ baos.write(DHT);
+
+ byte[] blengths = new byte[16];
+ if(streamLength != -1 && off[i] > streamLength) {
+ throw new IIOException(fieldName+" offset for index "+
+ i+" is not in the stream!");
+ }
+ stream.seek(off[i]);
+ stream.readFully(blengths);
+ int numCodes = 0;
+ for(int j = 0; j < 16; j++) {
+ numCodes += blengths[j]&0xff;
+ }
+
+ char markerLength = (char)(19 + numCodes);
+
+ baos.write((markerLength >>> 8) & 0xff); // Length
+ baos.write(markerLength & 0xff);
+
+ baos.write(i | (k << 4)); // Table ID and type
+
+ baos.write(blengths); // Number of codes
+
+ byte[] bcodes = new byte[numCodes];
+ stream.readFully(bcodes);
+ baos.write(bcodes); // Codes
+ }
+ }
+
+ // SOF0
+ baos.write((byte)0xff); // Marker identifier
+ baos.write((byte)SOF0);
+ short sval = (short)(8 + 3*samplesPerPixel); // Length
+ baos.write((byte)((sval >>> 8) & 0xff));
+ baos.write((byte)(sval & 0xff));
+ baos.write((byte)8); // Data precision
+ sval = (short)srcHeight; // Tile/strip height
+ baos.write((byte)((sval >>> 8) & 0xff));
+ baos.write((byte)(sval & 0xff));
+ sval = (short)srcWidth; // Tile/strip width
+ baos.write((byte)((sval >>> 8) & 0xff));
+ baos.write((byte)(sval & 0xff));
+ baos.write((byte)samplesPerPixel); // Number of components
+ if(samplesPerPixel == 1) {
+ baos.write((byte)1); // Component ID
+ baos.write((byte)0x11); // Subsampling factor
+ baos.write((byte)0); // Quantization table ID
+ } else { // 3
+ for(int i = 0; i < 3; i++) {
+ baos.write((byte)(i + 1)); // Component ID
+ baos.write((i != 0) ?
+ (byte)0x11 :
+ (byte)(((subsamplingX & 0x0f) << 4) |
+ (subsamplingY & 0x0f)));
+
+ baos.write((byte)i); // Quantization table ID
+ }
+ };
+
+
+ // DRI (optional).
+ f = tim.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_RESTART_INTERVAL);
+ if(f != null) {
+ char restartInterval = f.getAsChars()[0];
+
+ if(restartInterval != 0) {
+ baos.write((byte)0xff); // Marker identifier
+ baos.write((byte)DRI);
+
+ sval = 4;
+ baos.write((byte)((sval >>> 8) & 0xff)); // Length
+ baos.write((byte)(sval & 0xff));
+
+ // RestartInterval
+ baos.write((byte)((restartInterval >>> 8) & 0xff));
+ baos.write((byte)(restartInterval & 0xff));
+ }
+ }
+
+ tables = baos.toByteArray();
+ }
+
+ //
+ // Check for presence of SOF marker and save its position.
+ //
+ int idx = 0;
+ int idxMax = tables.length - 1;
+ while(idx < idxMax) {
+ if((tables[idx]&0xff) == 0xff &&
+ (tables[idx+1]&0xff) == SOF0) {
+ SOFPosition = idx;
+ break;
+ }
+ idx++;
+ }
+
+ //
+ // If no SOF marker, add one.
+ //
+ if(SOFPosition == -1) {
+ byte[] tmpTables =
+ new byte[tables.length + 10 + 3*samplesPerPixel];
+ System.arraycopy(tables, 0, tmpTables, 0, tables.length);
+ int tmpOffset = tables.length;
+ SOFPosition = tables.length;
+ tables = tmpTables;
+
+ tables[tmpOffset++] = (byte)0xff; // Marker identifier
+ tables[tmpOffset++] = (byte)SOF0;
+ short sval = (short)(8 + 3*samplesPerPixel); // Length
+ tables[tmpOffset++] = (byte)((sval >>> 8) & 0xff);
+ tables[tmpOffset++] = (byte)(sval & 0xff);
+ tables[tmpOffset++] = (byte)8; // Data precision
+ sval = (short)srcHeight; // Tile/strip height
+ tables[tmpOffset++] = (byte)((sval >>> 8) & 0xff);
+ tables[tmpOffset++] = (byte)(sval & 0xff);
+ sval = (short)srcWidth; // Tile/strip width
+ tables[tmpOffset++] = (byte)((sval >>> 8) & 0xff);
+ tables[tmpOffset++] = (byte)(sval & 0xff);
+ tables[tmpOffset++] = (byte)samplesPerPixel; // Number of components
+ if(samplesPerPixel == 1) {
+ tables[tmpOffset++] = (byte)1; // Component ID
+ tables[tmpOffset++] = (byte)0x11; // Subsampling factor
+ tables[tmpOffset++] = (byte)0; // Quantization table ID
+ } else { // 3
+ for(int i = 0; i < 3; i++) {
+ tables[tmpOffset++] = (byte)(i + 1); // Component ID
+ tables[tmpOffset++] = (i != 0) ?
+ (byte)0x11 :
+ (byte)(((subsamplingX & 0x0f) << 4) |
+ (subsamplingY & 0x0f));
+
+ tables[tmpOffset++] = (byte)i; // Quantization table ID
+ }
+ };
+ }
+
+ //
+ // Initialize SOSMarker.
+ //
+ stream.mark();
+ stream.seek(segmentOffsets[0]);
+ if(stream.read() == 0xff && stream.read() == SOS) {
+ //
+ // If the first segment starts with an SOS marker save it.
+ //
+ int SOSLength = (stream.read()<<8)|stream.read();
+ SOSMarker = new byte[SOSLength+2];
+ SOSMarker[0] = (byte)0xff;
+ SOSMarker[1] = (byte)SOS;
+ SOSMarker[2] = (byte)((SOSLength & 0xff00) >> 8);
+ SOSMarker[3] = (byte)(SOSLength & 0xff);
+ stream.readFully(SOSMarker, 4, SOSLength - 2);
+ } else {
+ //
+ // Manufacture an SOS marker.
+ //
+ SOSMarker = new byte[2 + 6 + 2*samplesPerPixel];
+ int SOSMarkerIndex = 0;
+ SOSMarker[SOSMarkerIndex++] = (byte)0xff; // Marker identifier
+ SOSMarker[SOSMarkerIndex++] = (byte)SOS;
+ short sval = (short)(6 + 2*samplesPerPixel); // Length
+ SOSMarker[SOSMarkerIndex++] = (byte)((sval >>> 8) & 0xff);
+ SOSMarker[SOSMarkerIndex++] = (byte)(sval & 0xff);
+ // Number of components in scan
+ SOSMarker[SOSMarkerIndex++] = (byte)samplesPerPixel;
+ if(samplesPerPixel == 1) {
+ SOSMarker[SOSMarkerIndex++] = (byte)1; // Component ID
+ SOSMarker[SOSMarkerIndex++] = (byte)0; // Huffman table ID
+ } else { // 3
+ for(int i = 0; i < 3; i++) {
+ SOSMarker[SOSMarkerIndex++] =
+ (byte)(i + 1); // Component ID
+ SOSMarker[SOSMarkerIndex++] =
+ (byte)((i << 4) | i); // Huffman table IDs
+ }
+ };
+ SOSMarker[SOSMarkerIndex++] = (byte)0;
+ SOSMarker[SOSMarkerIndex++] = (byte)0x3f;
+ SOSMarker[SOSMarkerIndex++] = (byte)0;
+ }
+ stream.reset();
+
+ // Set initialization flag.
+ isInitialized = true;
+ }
+
+ //
+ // The strategy for reading cases 1-3 is to treat the data as a complete
+ // JPEG interchange stream located at JPEGStreamOffset.
+ //
+ // The strategy for cases 4-5 is to concatenate a tables stream created
+ // in initialize() with the entropy coded data in each strip or tile.
+ //
+ public void decodeRaw(byte[] b,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException {
+
+ initialize();
+
+ TIFFImageMetadata tim = (TIFFImageMetadata)metadata;
+
+ if(JPEGStreamOffset != null) {
+ stream.seek(JPEGStreamOffset.longValue());
+ JPEGReader.setInput(stream, false, true);
+ } else {
+ // Determine buffer length and allocate.
+ int tableLength = tables.length;
+ int bufLength =
+ tableLength + SOSMarker.length + byteCount + 2; // 2 for EOI.
+ byte[] buf = new byte[bufLength];
+ System.arraycopy(tables, 0, buf, 0, tableLength);
+ int bufOffset = tableLength;
+
+ // Update the SOF dimensions.
+ short sval = (short)srcHeight; // Tile/strip height
+ buf[SOFPosition + 5] = (byte)((sval >>> 8) & 0xff);
+ buf[SOFPosition + 6] = (byte)(sval & 0xff);
+ sval = (short)srcWidth; // Tile/strip width
+ buf[SOFPosition + 7] = (byte)((sval >>> 8) & 0xff);
+ buf[SOFPosition + 8] = (byte)(sval & 0xff);
+
+ // Seek to data.
+ stream.seek(offset);
+
+ // Add SOS marker if data segment does not start with one.
+ byte[] twoBytes = new byte[2];
+ stream.readFully(twoBytes);
+ if(!((twoBytes[0]&0xff) == 0xff && (twoBytes[1]&0xff) == SOS)) {
+ // Segment does not start with SOS marker;
+ // use the pre-calculated SOS marker.
+ System.arraycopy(SOSMarker, 0, buf, bufOffset,
+ SOSMarker.length);
+ bufOffset += SOSMarker.length;
+ }
+
+ // Copy the segment data into the buffer.
+ buf[bufOffset++] = twoBytes[0];
+ buf[bufOffset++] = twoBytes[1];
+ stream.readFully(buf, bufOffset, byteCount - 2);
+ bufOffset += byteCount - 2;
+
+ // EOI.
+ buf[bufOffset++] = (byte)0xff; // Marker identifier
+ buf[bufOffset++] = (byte)EOI;
+
+ ByteArrayInputStream bais =
+ new ByteArrayInputStream(buf, 0, bufOffset);
+ ImageInputStream is = new MemoryCacheImageInputStream(bais);
+
+ JPEGReader.setInput(is, true, true);
+ }
+
+ // Read real image
+ JPEGParam.setDestination(rawImage);
+ JPEGReader.read(0, JPEGParam);
+ }
+
+ protected void finalize() throws Throwable {
+ super.finalize();
+ JPEGReader.dispose();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFPackBitsCompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,112 @@
+/*
+ * 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 java.io.IOException;
+
+public class TIFFPackBitsCompressor extends TIFFCompressor {
+
+ public TIFFPackBitsCompressor() {
+ super("PackBits", BaselineTIFFTagSet.COMPRESSION_PACKBITS, true);
+ }
+
+ /**
+ * Performs PackBits compression for a single buffer of data.
+ * This should be called for each row of each tile. The returned
+ * value is the offset into the output buffer after compression.
+ */
+ private static int packBits(byte[] input, int inOffset, int inCount,
+ byte[] output, int outOffset) {
+ int inMax = inOffset + inCount - 1;
+ int inMaxMinus1 = inMax - 1;
+
+ while(inOffset <= inMax) {
+ int run = 1;
+ byte replicate = input[inOffset];
+ while(run < 127 && inOffset < inMax &&
+ input[inOffset] == input[inOffset+1]) {
+ run++;
+ inOffset++;
+ }
+ if(run > 1) {
+ inOffset++;
+ output[outOffset++] = (byte)(-(run - 1));
+ output[outOffset++] = replicate;
+ }
+
+ run = 0;
+ int saveOffset = outOffset;
+ while(run < 128 &&
+ ((inOffset < inMax &&
+ input[inOffset] != input[inOffset+1]) ||
+ (inOffset < inMaxMinus1 &&
+ input[inOffset] != input[inOffset+2]))) {
+ run++;
+ output[++outOffset] = input[inOffset++];
+ }
+ if(run > 0) {
+ output[saveOffset] = (byte)(run - 1);
+ outOffset++;
+ }
+
+ if(inOffset == inMax) {
+ if(run > 0 && run < 128) {
+ output[saveOffset]++;
+ output[outOffset++] = input[inOffset++];
+ } else {
+ output[outOffset++] = (byte)0;
+ output[outOffset++] = input[inOffset++];
+ }
+ }
+ }
+
+ return outOffset;
+ }
+
+ public int encode(byte[] b, int off,
+ int width, int height,
+ int[] bitsPerSample,
+ int scanlineStride) throws IOException {
+ int bitsPerPixel = 0;
+ for (int i = 0; i < bitsPerSample.length; i++) {
+ bitsPerPixel += bitsPerSample[i];
+ }
+ int bytesPerRow = (bitsPerPixel*width + 7)/8;
+ int bufSize = (bytesPerRow + (bytesPerRow + 127)/128);
+ byte[] compData = new byte[bufSize];
+
+ int bytesWritten = 0;
+
+ for(int i = 0; i < height; i++) {
+ int bytes = packBits(b, off, scanlineStride, compData, 0);
+ off += scanlineStride;
+ bytesWritten += bytes;
+ stream.write(compData, 0, bytes);
+ }
+
+ return bytesWritten;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFPackBitsDecompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+public class TIFFPackBitsDecompressor extends TIFFDecompressor {
+
+ public TIFFPackBitsDecompressor() {
+ }
+
+ public int decode(byte[] srcData, int srcOffset,
+ byte[] dstData, int dstOffset)
+ throws IOException {
+
+ int srcIndex = srcOffset;
+ int dstIndex = dstOffset;
+
+ int dstArraySize = dstData.length;
+ int srcArraySize = srcData.length;
+ try {
+ while (dstIndex < dstArraySize && srcIndex < srcArraySize) {
+ byte b = srcData[srcIndex++];
+
+ if (b >= 0 && b <= 127) {
+ // Literal run packet
+
+ for (int i = 0; i < b + 1; i++) {
+ dstData[dstIndex++] = srcData[srcIndex++];
+ }
+ } else if (b <= -1 && b >= -127) {
+ // 2-byte encoded run packet
+ byte repeat = srcData[srcIndex++];
+ for (int i = 0; i < (-b + 1); i++) {
+ dstData[dstIndex++] = repeat;
+ }
+ } else {
+ // No-op packet, do nothing
+ ++srcIndex;
+ }
+ }
+ } catch(ArrayIndexOutOfBoundsException e) {
+ if(reader instanceof TIFFImageReader) {
+ ((TIFFImageReader)reader).forwardWarningMessage
+ ("ArrayIndexOutOfBoundsException ignored in TIFFPackBitsDecompressor.decode()");
+ }
+ }
+
+ return dstIndex - dstOffset;
+ }
+
+ public void decodeRaw(byte[] b,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException {
+ stream.seek(offset);
+
+ 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;
+ }
+
+ decode(srcData, 0, buf, bufOffset);
+
+ if(bytesPerRow != scanlineStride) {
+ int off = 0;
+ for (int y = 0; y < srcHeight; y++) {
+ System.arraycopy(buf, off, b, dstOffset, bytesPerRow);
+ off += bytesPerRow;
+ dstOffset += scanlineStride;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFPackBitsUtil.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+public class TIFFPackBitsUtil {
+
+ byte[] dstData = new byte[8192];
+ int dstIndex = 0;
+
+ public TIFFPackBitsUtil() {
+ }
+
+ private void ensureCapacity(int bytesToAdd) {
+ if (dstIndex + bytesToAdd > dstData.length) {
+ byte[] newDstData = new byte[Math.max((int)(dstData.length*1.2f),
+ dstIndex + bytesToAdd)];
+ System.arraycopy(dstData, 0, newDstData, 0, dstData.length);
+ dstData = newDstData;
+ }
+ }
+
+ public byte[] decode(byte[] srcData) throws IOException {
+ int inIndex = 0;
+ while (inIndex < srcData.length) {
+ byte b = srcData[inIndex++];
+
+ if (b >= 0 && b <= 127) {
+ // Literal run packet
+
+ ensureCapacity(b + 1);
+ for (int i = 0; i < b + 1; i++) {
+ dstData[dstIndex++] = srcData[inIndex++];
+ }
+ } else if (b <= -1 && b >= -127) {
+ // 2-byte encoded run packet
+ byte repeat = srcData[inIndex++];
+ ensureCapacity(-b + 1);
+ for (int i = 0; i < (-b + 1); i++) {
+ dstData[dstIndex++] = repeat;
+ }
+ } else {
+ // No-op packet, do nothing
+ ++inIndex;
+ }
+ }
+
+ byte[] newDstData = new byte[dstIndex];
+ System.arraycopy(dstData, 0, newDstData, 0, dstIndex);
+ return newDstData;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFRLECompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,117 @@
+/*
+ * 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 java.io.IOException;
+import javax.imageio.IIOException;
+
+/**
+ *
+ */
+public class TIFFRLECompressor extends TIFFFaxCompressor {
+
+ public TIFFRLECompressor() {
+ super("CCITT RLE", BaselineTIFFTagSet.COMPRESSION_CCITT_RLE, true);
+ }
+
+ /**
+ * Encode a row of data using Modified Huffman Compression also known as
+ * CCITT RLE (Run Lenth Encoding).
+ *
+ * @param data The row of data to compress.
+ * @param rowOffset Starting index in <code>data</code>.
+ * @param colOffset Bit offset within first <code>data[rowOffset]</code>.
+ * @param rowLength Number of bits in the row.
+ * @param compData The compressed data.
+ *
+ * @return The number of bytes saved in the compressed data array.
+ */
+ public int encodeRLE(byte[] data,
+ int rowOffset,
+ int colOffset,
+ int rowLength,
+ byte[] compData) {
+ //
+ // Initialize bit buffer machinery.
+ //
+ initBitBuf();
+
+ //
+ // Run-length encode line.
+ //
+ int outIndex =
+ encode1D(data, rowOffset, colOffset, rowLength, compData, 0);
+
+ //
+ // Flush pending bits
+ //
+ while (ndex > 0) {
+ compData[outIndex++] = (byte)(bits >>> 24);
+ bits <<= 8;
+ ndex -= 8;
+ }
+
+ //
+ // Flip the bytes if inverse fill was requested.
+ //
+ if (inverseFill) {
+ byte[] flipTable = TIFFFaxDecompressor.flipTable;
+ for(int i = 0; i < outIndex; i++) {
+ compData[i] = flipTable[compData[i] & 0xff];
+ }
+ }
+
+ return outIndex;
+ }
+
+ public int encode(byte[] b, int off,
+ int width, int height,
+ int[] bitsPerSample,
+ int scanlineStride) throws IOException {
+ if (bitsPerSample.length != 1 || bitsPerSample[0] != 1) {
+ throw new IIOException(
+ "Bits per sample must be 1 for RLE compression!");
+ }
+
+ // In the worst case, 2 bits of input will result in 9 bits of output,
+ // plus 2 extra bits if the row starts with black.
+ int maxBits = 9*((width + 1)/2) + 2;
+ byte[] compData = new byte[(maxBits + 7)/8];
+
+ int bytes = 0;
+ int rowOffset = off;
+
+ for (int i = 0; i < height; i++) {
+ int rowBytes = encodeRLE(b, rowOffset, 0, width, compData);
+ stream.write(compData, 0, rowBytes);
+
+ rowOffset += scanlineStride;
+ bytes += rowBytes;
+ }
+
+ return bytes;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFRenderedImage.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,248 @@
+/*
+ * 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.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+import javax.imageio.ImageReadParam;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.plugins.tiff.TIFFImageReadParam;
+import javax.imageio.plugins.tiff.TIFFTagSet;
+
+public class TIFFRenderedImage implements RenderedImage {
+
+ private TIFFImageReader reader;
+ private int imageIndex;
+ private ImageReadParam tileParam;
+
+ private int subsampleX;
+ private int subsampleY;
+
+ private boolean isSubsampling;
+
+ private int width;
+ private int height;
+ private int tileWidth;
+ private int tileHeight;
+
+ private ImageTypeSpecifier its;
+
+ public TIFFRenderedImage(TIFFImageReader reader,
+ int imageIndex,
+ ImageReadParam readParam,
+ int width, int height) throws IOException {
+ this.reader = reader;
+ this.imageIndex = imageIndex;
+ this.tileParam = cloneImageReadParam(readParam, false);
+
+ this.subsampleX = tileParam.getSourceXSubsampling();
+ this.subsampleY = tileParam.getSourceYSubsampling();
+
+ this.isSubsampling = this.subsampleX != 1 || this.subsampleY != 1;
+
+ this.width = width/subsampleX;
+ this.height = height/subsampleY;
+
+ // If subsampling is being used, we may not match the
+ // true tile grid exactly, but everything should still work
+ this.tileWidth = reader.getTileWidth(imageIndex)/subsampleX;
+ this.tileHeight = reader.getTileHeight(imageIndex)/subsampleY;
+
+ Iterator<ImageTypeSpecifier> iter = reader.getImageTypes(imageIndex);
+ this.its = iter.next();
+ tileParam.setDestinationType(its);
+ }
+
+ /**
+ * Creates a copy of <code>param</code>. The source subsampling and
+ * and bands settings and the destination bands and offset settings
+ * are copied. If <code>param</code> is a <code>TIFFImageReadParam</code>
+ * then the <code>TIFFDecompressor</code> and
+ * <code>TIFFColorConverter</code> settings are also copied; otherwise
+ * they are explicitly set to <code>null</code>.
+ *
+ * @param param the parameters to be copied.
+ * @param copyTagSets whether the <code>TIFFTagSet</code> settings
+ * should be copied if set.
+ * @return copied parameters.
+ */
+ private ImageReadParam cloneImageReadParam(ImageReadParam param,
+ boolean copyTagSets) {
+ // Create a new TIFFImageReadParam.
+ TIFFImageReadParam newParam = new TIFFImageReadParam();
+
+ // Copy the basic settings.
+ newParam.setSourceSubsampling(param.getSourceXSubsampling(),
+ param.getSourceYSubsampling(),
+ param.getSubsamplingXOffset(),
+ param.getSubsamplingYOffset());
+ newParam.setSourceBands(param.getSourceBands());
+ newParam.setDestinationBands(param.getDestinationBands());
+ newParam.setDestinationOffset(param.getDestinationOffset());
+
+ if (param instanceof TIFFImageReadParam && copyTagSets) {
+ // Copy the settings from the input parameter.
+ TIFFImageReadParam tparam = (TIFFImageReadParam) param;
+
+ List<TIFFTagSet> tagSets = tparam.getAllowedTagSets();
+ if (tagSets != null) {
+ Iterator<TIFFTagSet> tagSetIter = tagSets.iterator();
+ if (tagSetIter != null) {
+ while (tagSetIter.hasNext()) {
+ TIFFTagSet tagSet = tagSetIter.next();
+ newParam.addAllowedTagSet(tagSet);
+ }
+ }
+ }
+ }
+
+ return newParam;
+ }
+
+ public Vector<RenderedImage> getSources() {
+ return null;
+ }
+
+ public Object getProperty(String name) {
+ return java.awt.Image.UndefinedProperty;
+ }
+
+ public String[] getPropertyNames() {
+ return null;
+ }
+
+ public ColorModel getColorModel() {
+ return its.getColorModel();
+ }
+
+ public SampleModel getSampleModel() {
+ return its.getSampleModel();
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public int getMinX() {
+ return 0;
+ }
+
+ public int getMinY() {
+ return 0;
+ }
+
+ public int getNumXTiles() {
+ return (width + tileWidth - 1)/tileWidth;
+ }
+
+ public int getNumYTiles() {
+ return (height + tileHeight - 1)/tileHeight;
+ }
+
+ public int getMinTileX() {
+ return 0;
+ }
+
+ public int getMinTileY() {
+ return 0;
+ }
+
+ public int getTileWidth() {
+ return tileWidth;
+ }
+
+ public int getTileHeight() {
+ return tileHeight;
+ }
+
+ public int getTileGridXOffset() {
+ return 0;
+ }
+
+ public int getTileGridYOffset() {
+ return 0;
+ }
+
+ public Raster getTile(int tileX, int tileY) {
+ Rectangle tileRect = new Rectangle(tileX*tileWidth,
+ tileY*tileHeight,
+ tileWidth,
+ tileHeight);
+ return getData(tileRect);
+ }
+
+ public Raster getData() {
+ return read(new Rectangle(0, 0, getWidth(), getHeight()));
+ }
+
+ public Raster getData(Rectangle rect) {
+ return read(rect);
+ }
+
+ // This method needs to be synchronized as it updates the instance
+ // variable 'tileParam'.
+ public synchronized WritableRaster read(Rectangle rect) {
+ tileParam.setSourceRegion(isSubsampling ?
+ new Rectangle(subsampleX*rect.x,
+ subsampleY*rect.y,
+ subsampleX*rect.width,
+ subsampleY*rect.height) :
+ rect);
+
+ try {
+ BufferedImage bi = reader.read(imageIndex, tileParam);
+ WritableRaster ras = bi.getRaster();
+ return ras.createWritableChild(0, 0,
+ ras.getWidth(), ras.getHeight(),
+ rect.x, rect.y,
+ null);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public WritableRaster copyData(WritableRaster raster) {
+ if (raster == null) {
+ return read(new Rectangle(0, 0, getWidth(), getHeight()));
+ } else {
+ Raster src = read(raster.getBounds());
+ raster.setRect(src);
+ return raster;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFStreamMetadata.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,121 @@
+/*
+ * 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.nio.ByteOrder;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.metadata.IIOInvalidTreeException;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+public class TIFFStreamMetadata extends IIOMetadata {
+
+ // package scope
+ static final String NATIVE_METADATA_FORMAT_NAME =
+ "javax_imageio_tiff_stream_1.0";
+
+ static final String NATIVE_METADATA_FORMAT_CLASS_NAME =
+ "javax.imageio.plugins.tiff.TIFFStreamMetadataFormat";
+
+ private static final String bigEndianString =
+ ByteOrder.BIG_ENDIAN.toString();
+ private static final String littleEndianString =
+ ByteOrder.LITTLE_ENDIAN.toString();
+
+ public ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
+
+ public TIFFStreamMetadata() {
+ super(false,
+ NATIVE_METADATA_FORMAT_NAME,
+ NATIVE_METADATA_FORMAT_CLASS_NAME,
+ null, null);
+ }
+
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ // Shorthand for throwing an IIOInvalidTreeException
+ private static void fatal(Node node, String reason)
+ throws IIOInvalidTreeException {
+ throw new IIOInvalidTreeException(reason, node);
+ }
+
+ public Node getAsTree(String formatName) {
+ IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName);
+
+ IIOMetadataNode byteOrderNode = new IIOMetadataNode("ByteOrder");
+ byteOrderNode.setAttribute("value", byteOrder.toString());
+
+ root.appendChild(byteOrderNode);
+ return root;
+ }
+
+// public void setFromTree(String formatName, Node root) {
+// }
+
+ private void mergeNativeTree(Node root) throws IIOInvalidTreeException {
+ Node node = root;
+ if (!node.getNodeName().equals(nativeMetadataFormatName)) {
+ fatal(node, "Root must be " + nativeMetadataFormatName);
+ }
+
+ node = node.getFirstChild();
+ if (node == null || !node.getNodeName().equals("ByteOrder")) {
+ fatal(node, "Root must have \"ByteOrder\" child");
+ }
+
+ NamedNodeMap attrs = node.getAttributes();
+ String order = attrs.getNamedItem("value").getNodeValue();
+
+ if (order == null) {
+ fatal(node, "ByteOrder node must have a \"value\" attribute");
+ }
+ if (order.equals(bigEndianString)) {
+ this.byteOrder = ByteOrder.BIG_ENDIAN;
+ } else if (order.equals(littleEndianString)) {
+ this.byteOrder = ByteOrder.LITTLE_ENDIAN;
+ } else {
+ fatal(node, "Incorrect value for ByteOrder \"value\" attribute");
+ }
+ }
+
+ public void mergeTree(String formatName, Node root)
+ throws IIOInvalidTreeException {
+ if (formatName.equals(nativeMetadataFormatName)) {
+ if (root == null) {
+ throw new NullPointerException("root == null!");
+ }
+ mergeNativeTree(root);
+ } else {
+ throw new IllegalArgumentException("Not a recognized format!");
+ }
+ }
+
+ public void reset() {
+ this.byteOrder = ByteOrder.BIG_ENDIAN;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFStreamMetadataFormat.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,73 @@
+/*
+ * 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.ImageTypeSpecifier;
+import javax.imageio.metadata.IIOMetadataFormat;
+
+public class TIFFStreamMetadataFormat extends TIFFMetadataFormat {
+
+ private static TIFFStreamMetadataFormat theInstance = null;
+
+ public boolean canNodeAppear(String elementName,
+ ImageTypeSpecifier imageType) {
+ return false;
+ }
+
+ private TIFFStreamMetadataFormat() {
+ this.resourceBaseName =
+ "javax.imageio.plugins.tiff.TIFFStreamMetadataFormatResources";
+ this.rootName = TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME;
+
+ TIFFElementInfo einfo;
+ TIFFAttrInfo ainfo;
+ String[] empty = new String[0];
+ String[] childNames;
+ String[] attrNames;
+
+ childNames = new String[] { "ByteOrder" };
+ einfo = new TIFFElementInfo(childNames, empty, CHILD_POLICY_ALL);
+
+ elementInfoMap.put(TIFFStreamMetadata.NATIVE_METADATA_FORMAT_NAME,
+ einfo);
+
+ childNames = empty;
+ attrNames = new String[] { "value" };
+ einfo = new TIFFElementInfo(childNames, attrNames, CHILD_POLICY_EMPTY);
+ elementInfoMap.put("ByteOrder", einfo);
+
+ ainfo = new TIFFAttrInfo();
+ ainfo.dataType = DATATYPE_STRING;
+ ainfo.isRequired = true;
+ attrInfoMap.put("ByteOrder/value", ainfo);
+ }
+
+ public static synchronized IIOMetadataFormat getInstance() {
+ if (theInstance == null) {
+ theInstance = new TIFFStreamMetadataFormat();
+ }
+ return theInstance;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFStreamMetadataFormatResources.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,42 @@
+/*
+ * 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.util.ListResourceBundle;
+
+public class TIFFStreamMetadataFormatResources extends ListResourceBundle {
+
+ private static final Object[][] contents = {
+ { "ByteOrder", "The stream byte order" },
+ { "ByteOrder/value", "One of \"BIG_ENDIAN\" or \"LITTLE_ENDIAN\"" }
+ };
+
+ public TIFFStreamMetadataFormatResources() {
+ }
+
+ public Object[][] getContents() {
+ return contents.clone();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFT4Compressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,254 @@
+/*
+ * 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.plugins.tiff.TIFFField;
+import javax.imageio.plugins.tiff.TIFFTag;
+import java.io.IOException;
+import javax.imageio.IIOException;
+import javax.imageio.metadata.IIOMetadata;
+
+public class TIFFT4Compressor extends TIFFFaxCompressor {
+
+ private boolean is1DMode = false;
+ private boolean isEOLAligned = false;
+
+ public TIFFT4Compressor() {
+ super("CCITT T.4", BaselineTIFFTagSet.COMPRESSION_CCITT_T_4, true);
+ }
+
+ /**
+ * Sets the value of the <code>metadata</code> field.
+ *
+ * <p> The implementation in this class also sets local options
+ * from the T4_OPTIONS field if it exists, and if it doesn't, adds
+ * it with default values.</p>
+ *
+ * @param metadata the <code>IIOMetadata</code> object for the
+ * image being written.
+ *
+ * @see #getMetadata()
+ */
+ public void setMetadata(IIOMetadata metadata) {
+ super.setMetadata(metadata);
+
+ if (metadata instanceof TIFFImageMetadata) {
+ TIFFImageMetadata tim = (TIFFImageMetadata)metadata;
+ TIFFField f = tim.getTIFFField(BaselineTIFFTagSet.TAG_T4_OPTIONS);
+ if (f != null) {
+ int options = f.getAsInt(0);
+ is1DMode = (options & 0x1) == 0;
+ isEOLAligned = (options & 0x4) == 0x4;
+ } else {
+ long[] oarray = new long[1];
+ oarray[0] = (isEOLAligned ? 0x4 : 0x0) |
+ (is1DMode ? 0x0 : 0x1);
+
+ BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
+ TIFFField T4Options =
+ new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_T4_OPTIONS),
+ TIFFTag.TIFF_LONG,
+ 1,
+ oarray);
+ tim.rootIFD.addTIFFField(T4Options);
+ }
+ }
+ }
+
+ /**
+ * Encode a buffer of data using CCITT T.4 Compression also known as
+ * Group 3 facsimile compression.
+ *
+ * @param is1DMode Whether to perform one-dimensional encoding.
+ * @param isEOLAligned Whether EOL bit sequences should be padded.
+ * @param data The row of data to compress.
+ * @param lineStride Byte step between the same sample in different rows.
+ * @param colOffset Bit offset within first <code>data[rowOffset]</code>.
+ * @param width Number of bits in the row.
+ * @param height Number of rows in the buffer.
+ * @param compData The compressed data.
+ *
+ * @return The number of bytes saved in the compressed data array.
+ */
+ public int encodeT4(boolean is1DMode,
+ boolean isEOLAligned,
+ byte[] data,
+ int lineStride,
+ int colOffset,
+ int width,
+ int height,
+ byte[] compData)
+ {
+ //
+ // ao, a1, a2 are bit indices in the current line
+ // b1 and b2 are bit indices in the reference line (line above)
+ // color is the current color (WHITE or BLACK)
+ //
+ byte[] refData = data;
+ int lineAddr = 0;
+ int outIndex = 0;
+
+ initBitBuf();
+
+ int KParameter = 2;
+ for(int numRows = 0; numRows < height; numRows++) {
+ if(is1DMode || (numRows % KParameter) == 0) { // 1D encoding
+ // Write EOL+1
+ outIndex += addEOL(is1DMode, isEOLAligned, true,
+ compData, outIndex);
+
+ // Encode row
+ outIndex += encode1D(data, lineAddr, colOffset, width,
+ compData, outIndex);
+ } else { // 2D encoding.
+ // Write EOL+0
+ outIndex += addEOL(is1DMode, isEOLAligned, false,
+ compData, outIndex);
+
+ // Set reference to previous line
+ int refAddr = lineAddr - lineStride;
+
+ // Encode row
+ int a0 = colOffset;
+ int last = a0 + width;
+
+ int testbit =
+ ((data[lineAddr + (a0>>>3)]&0xff) >>>
+ (7-(a0 & 0x7))) & 0x1;
+ int a1 = testbit != 0 ?
+ a0 : nextState(data, lineAddr, a0, last);
+
+ testbit = ((refData[refAddr + (a0>>>3)]&0xff) >>>
+ (7-(a0 & 0x7))) & 0x1;
+ int b1 = testbit != 0 ?
+ a0 : nextState(refData, refAddr, a0, last);
+
+ // The current color is set to WHITE at line start
+ int color = WHITE;
+
+ while(true) {
+ int b2 = nextState(refData, refAddr, b1, last);
+ if(b2 < a1) { // pass mode
+ outIndex += add2DBits(compData, outIndex, pass, 0);
+ a0 = b2;
+ } else {
+ int tmp = b1 - a1 + 3;
+ if((tmp <= 6) && (tmp >= 0)) { // vertical mode
+ outIndex +=
+ add2DBits(compData, outIndex, vert, tmp);
+ a0 = a1;
+ } else { // horizontal mode
+ int a2 = nextState(data, lineAddr, a1, last);
+ outIndex +=
+ add2DBits(compData, outIndex, horz, 0);
+ outIndex +=
+ add1DBits(compData, outIndex, a1-a0, color);
+ outIndex +=
+ add1DBits(compData, outIndex, a2-a1, color^1);
+ a0 = a2;
+ }
+ }
+ if(a0 >= last) {
+ break;
+ }
+ color = ((data[lineAddr + (a0>>>3)]&0xff) >>>
+ (7-(a0 & 0x7))) & 0x1;
+ a1 = nextState(data, lineAddr, a0, last);
+ b1 = nextState(refData, refAddr, a0, last);
+ testbit = ((refData[refAddr + (b1>>>3)]&0xff) >>>
+ (7-(b1 & 0x7))) & 0x1;
+ if(testbit == color) {
+ b1 = nextState(refData, refAddr, b1, last);
+ }
+ }
+ }
+
+ // Skip to next line.
+ lineAddr += lineStride;
+ }
+
+ for(int i = 0; i < 6; i++) {
+ outIndex += addEOL(is1DMode, isEOLAligned, true,
+ compData, outIndex);
+ }
+
+ //
+ // flush all pending bits
+ //
+ while(ndex > 0) {
+ compData[outIndex++] = (byte)(bits >>> 24);
+ bits <<= 8;
+ ndex -= 8;
+ }
+
+ // Flip the bytes if inverse fill was requested.
+ if(inverseFill) {
+ for(int i = 0; i < outIndex; i++) {
+ compData[i] = TIFFFaxDecompressor.flipTable[compData[i]&0xff];
+ }
+ }
+
+ return outIndex;
+ }
+
+ public int encode(byte[] b, int off,
+ int width, int height,
+ int[] bitsPerSample,
+ int scanlineStride) throws IOException {
+ if (bitsPerSample.length != 1 || bitsPerSample[0] != 1) {
+ throw new IIOException(
+ "Bits per sample must be 1 for T4 compression!");
+ }
+
+ // This initial buffer size is based on an alternating 1-0
+ // pattern generating the most bits when converted to code
+ // words: 9 bits out for each pair of bits in. So the number
+ // of bit pairs is determined, multiplied by 9, converted to
+ // bytes, and a ceil() is taken to account for fill bits at the
+ // end of each line. The "2" addend accounts for the case
+ // of the pattern beginning with black. The buffer is intended
+ // to hold only a single row.
+
+ int maxBits = 9*((width + 1)/2) + 2;
+ int bufSize = (maxBits + 7)/8;
+
+ // Calculate the maximum row as the G3-1D size plus the EOL,
+ // multiply this by the number of rows in the tile, and add
+ // 6 EOLs for the RTC (return to control).
+ bufSize = height*(bufSize + 2) + 12;
+
+ byte[] compData = new byte[bufSize];
+
+ int bytes = encodeT4(is1DMode,
+ isEOLAligned,
+ b, scanlineStride, 8*off,
+ width, height,
+ compData);
+
+ stream.write(compData, 0, bytes);
+ return bytes;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFT6Compressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,185 @@
+/*
+ * 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.plugins.tiff.TIFFField;
+import javax.imageio.plugins.tiff.TIFFTag;
+import java.io.IOException;
+import javax.imageio.IIOException;
+
+public class TIFFT6Compressor extends TIFFFaxCompressor {
+
+ public TIFFT6Compressor() {
+ super("CCITT T.6", BaselineTIFFTagSet.COMPRESSION_CCITT_T_6, true);
+ }
+
+ /**
+ * Encode a buffer of data using CCITT T.6 Compression also known as
+ * Group 4 facsimile compression.
+ *
+ * @param data The row of data to compress.
+ * @param lineStride Byte step between the same sample in different rows.
+ * @param colOffset Bit offset within first <code>data[rowOffset]</code>.
+ * @param width Number of bits in the row.
+ * @param height Number of rows in the buffer.
+ * @param compData The compressed data.
+ *
+ * @return The number of bytes saved in the compressed data array.
+ */
+ public synchronized int encodeT6(byte[] data,
+ int lineStride,
+ int colOffset,
+ int width,
+ int height,
+ byte[] compData)
+ {
+ //
+ // ao, a1, a2 are bit indices in the current line
+ // b1 and b2 are bit indices in the reference line (line above)
+ // color is the current color (WHITE or BLACK)
+ //
+ byte[] refData = null;
+ int refAddr = 0;
+ int lineAddr = 0;
+ int outIndex = 0;
+
+ initBitBuf();
+
+ //
+ // Iterate over all lines
+ //
+ while(height-- != 0) {
+ int a0 = colOffset;
+ int last = a0 + width;
+
+ int testbit =
+ ((data[lineAddr + (a0>>>3)]&0xff) >>>
+ (7-(a0 & 0x7))) & 0x1;
+ int a1 = testbit != 0 ?
+ a0 : nextState(data, lineAddr, a0, last);
+
+ testbit = refData == null ?
+ 0: ((refData[refAddr + (a0>>>3)]&0xff) >>>
+ (7-(a0 & 0x7))) & 0x1;
+ int b1 = testbit != 0 ?
+ a0 : nextState(refData, refAddr, a0, last);
+
+ //
+ // The current color is set to WHITE at line start
+ //
+ int color = WHITE;
+
+ while(true) {
+ int b2 = nextState(refData, refAddr, b1, last);
+ if(b2 < a1) { // pass mode
+ outIndex += add2DBits(compData, outIndex, pass, 0);
+ a0 = b2;
+ } else {
+ int tmp = b1 - a1 + 3;
+ if((tmp <= 6) && (tmp >= 0)) { // vertical mode
+ outIndex += add2DBits(compData, outIndex, vert, tmp);
+ a0 = a1;
+ } else { // horizontal mode
+ int a2 = nextState(data, lineAddr, a1, last);
+ outIndex += add2DBits(compData, outIndex, horz, 0);
+ outIndex += add1DBits(compData, outIndex, a1-a0, color);
+ outIndex += add1DBits(compData, outIndex, a2-a1, color^1);
+ a0 = a2;
+ }
+ }
+ if(a0 >= last) {
+ break;
+ }
+ color = ((data[lineAddr + (a0>>>3)]&0xff) >>>
+ (7-(a0 & 0x7))) & 0x1;
+ a1 = nextState(data, lineAddr, a0, last);
+ b1 = nextState(refData, refAddr, a0, last);
+ testbit = refData == null ?
+ 0: ((refData[refAddr + (b1>>>3)]&0xff) >>>
+ (7-(b1 & 0x7))) & 0x1;
+ if(testbit == color) {
+ b1 = nextState(refData, refAddr, b1, last);
+ }
+ }
+
+ refData = data;
+ refAddr = lineAddr;
+ lineAddr += lineStride;
+
+ } // End while(height--)
+
+ //
+ // append eofb
+ //
+ outIndex += addEOFB(compData, outIndex);
+
+ // Flip the bytes if inverse fill was requested.
+ if(inverseFill) {
+ for(int i = 0; i < outIndex; i++) {
+ compData[i] = TIFFFaxDecompressor.flipTable[compData[i]&0xff];
+ }
+ }
+
+ return outIndex;
+ }
+
+ public int encode(byte[] b, int off,
+ int width, int height,
+ int[] bitsPerSample,
+ int scanlineStride) throws IOException {
+ if (bitsPerSample.length != 1 || bitsPerSample[0] != 1) {
+ throw new IIOException(
+ "Bits per sample must be 1 for T6 compression!");
+ }
+
+
+ if (metadata instanceof TIFFImageMetadata) {
+ TIFFImageMetadata tim = (TIFFImageMetadata)metadata;
+
+ long[] options = new long[1];
+ options[0] = 0;
+
+ BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
+ TIFFField T6Options =
+ new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_T6_OPTIONS),
+ TIFFTag.TIFF_LONG,
+ 1,
+ options);
+ tim.rootIFD.addTIFFField(T6Options);
+ }
+
+ // See comment in TIFFT4Compressor
+ int maxBits = 9*((width + 1)/2) + 2;
+ int bufSize = (maxBits + 7)/8;
+ bufSize = height*(bufSize + 2) + 12;
+
+ byte[] compData = new byte[bufSize];
+ int bytes = encodeT6(b, scanlineStride, 8*off, width, height,
+ compData);
+ stream.write(compData, 0, bytes);
+ return bytes;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFYCbCrColorConverter.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,112 @@
+/*
+ * 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.plugins.tiff.TIFFField;
+
+public class TIFFYCbCrColorConverter extends TIFFColorConverter {
+
+ private static final float CODING_RANGE_Y = 255.0f;
+ private static final float CODING_RANGE_CB_CR = 127.0f;
+
+ private float lumaRed = 0.299f;
+ private float lumaGreen = 0.587f;
+ private float lumaBlue = 0.114f;
+
+ private float referenceBlackY = 0.0f;
+ private float referenceWhiteY = 255.0f;
+
+ private float referenceBlackCb = 128.0f;
+ private float referenceWhiteCb = 255.0f;
+
+ private float referenceBlackCr = 128.0f;
+ private float referenceWhiteCr = 255.0f;
+
+ public TIFFYCbCrColorConverter(TIFFImageMetadata metadata) {
+ TIFFImageMetadata tmetadata = metadata;
+
+ TIFFField f =
+ tmetadata.getTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_COEFFICIENTS);
+ if (f != null && f.getCount() == 3) {
+ this.lumaRed = f.getAsFloat(0);
+ this.lumaGreen = f.getAsFloat(1);
+ this.lumaBlue = f.getAsFloat(2);
+ }
+
+ f =
+ tmetadata.getTIFFField(BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE);
+ if (f != null && f.getCount() == 6) {
+ this.referenceBlackY = f.getAsFloat(0);
+ this.referenceWhiteY = f.getAsFloat(1);
+ this.referenceBlackCb = f.getAsFloat(2);
+ this.referenceWhiteCb = f.getAsFloat(3);
+ this.referenceBlackCr = f.getAsFloat(4);
+ this.referenceWhiteCr = f.getAsFloat(5);
+ }
+ }
+
+ /*
+ The full range component value is converted from the code by:
+
+ FullRangeValue = (code - ReferenceBlack) * CodingRange
+ / (ReferenceWhite - ReferenceBlack);
+
+ The code is converted from the full-range component value by:
+
+ code = (FullRangeValue * (ReferenceWhite - ReferenceBlack)
+ / CodingRange) + ReferenceBlack;
+
+ */
+ public void fromRGB(float r, float g, float b, float[] result) {
+ // Convert RGB to full-range YCbCr.
+ float Y = (lumaRed*r + lumaGreen*g + lumaBlue*b);
+ float Cb = (b - Y)/(2 - 2*lumaBlue);
+ float Cr = (r - Y)/(2 - 2*lumaRed);
+
+ // Convert full-range YCbCr to code.
+ result[0] = Y*(referenceWhiteY - referenceBlackY)/CODING_RANGE_Y +
+ referenceBlackY;
+ result[1] = Cb*(referenceWhiteCb - referenceBlackCb)/CODING_RANGE_CB_CR +
+ referenceBlackCb;
+ result[2] = Cr*(referenceWhiteCr - referenceBlackCr)/CODING_RANGE_CB_CR +
+ referenceBlackCr;
+ }
+
+ public void toRGB(float x0, float x1, float x2, float[] rgb) {
+ // Convert YCbCr code to full-range YCbCr.
+ float Y = (x0 - referenceBlackY)*CODING_RANGE_Y/
+ (referenceWhiteY - referenceBlackY);
+ float Cb = (x1 - referenceBlackCb)*CODING_RANGE_CB_CR/
+ (referenceWhiteCb - referenceBlackCb);
+ float Cr = (x2 - referenceBlackCr)*CODING_RANGE_CB_CR/
+ (referenceWhiteCr - referenceBlackCr);
+
+ // Convert YCbCr to RGB.
+ rgb[0] = Cr*(2 - 2*lumaRed) + Y;
+ rgb[2] = Cb*(2 - 2*lumaBlue) + Y;
+ rgb[1] = (Y - lumaBlue*rgb[2] - lumaRed*rgb[0])/lumaGreen;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFYCbCrDecompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,538 @@
+/*
+ * 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.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import javax.imageio.ImageReader;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.stream.MemoryCacheImageInputStream;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.plugins.tiff.TIFFField;
+
+public class TIFFYCbCrDecompressor extends TIFFDecompressor {
+ // Store constants in S15.16 format
+ private static final int FRAC_BITS = 16;
+ private static final float FRAC_SCALE = (float)(1 << FRAC_BITS);
+
+ private float lumaRed = 0.299f;
+ private float lumaGreen = 0.587f;
+ private float lumaBlue = 0.114f;
+
+ private float referenceBlackY = 0.0f;
+ private float referenceWhiteY = 255.0f;
+
+ private float referenceBlackCb = 128.0f;
+ private float referenceWhiteCb = 255.0f;
+
+ private float referenceBlackCr = 128.0f;
+ private float referenceWhiteCr = 255.0f;
+
+ private float codingRangeY = 255.0f;
+
+ private int[] iYTab = new int[256];
+ private int[] iCbTab = new int[256];
+ private int[] iCrTab = new int[256];
+
+ private int[] iGYTab = new int[256];
+ private int[] iGCbTab = new int[256];
+ private int[] iGCrTab = new int[256];
+
+ private int chromaSubsampleH = 2;
+ private int chromaSubsampleV = 2;
+
+ private boolean colorConvert;
+
+ private TIFFDecompressor decompressor;
+
+ private BufferedImage tmpImage;
+
+ //
+ // If 'decompressor' is not null then it reads the data from the
+ // actual stream first and passes the result on to YCrCr decompression
+ // and possibly color conversion.
+ //
+
+ public TIFFYCbCrDecompressor(TIFFDecompressor decompressor,
+ boolean colorConvert) {
+ this.decompressor = decompressor;
+ this.colorConvert = colorConvert;
+ }
+
+ private void warning(String message) {
+ if(this.reader instanceof TIFFImageReader) {
+ ((TIFFImageReader)reader).forwardWarningMessage(message);
+ }
+ }
+
+ //
+ // "Chained" decompressor methods.
+ //
+
+ public void setReader(ImageReader reader) {
+ if(decompressor != null) {
+ decompressor.setReader(reader);
+ }
+ super.setReader(reader);
+ }
+
+ public void setMetadata(IIOMetadata metadata) {
+ if(decompressor != null) {
+ decompressor.setMetadata(metadata);
+ }
+ super.setMetadata(metadata);
+ }
+
+ public void setPhotometricInterpretation(int photometricInterpretation) {
+ if(decompressor != null) {
+ decompressor.setPhotometricInterpretation(photometricInterpretation);
+ }
+ super.setPhotometricInterpretation(photometricInterpretation);
+ }
+
+ public void setCompression(int compression) {
+ if(decompressor != null) {
+ decompressor.setCompression(compression);
+ }
+ super.setCompression(compression);
+ }
+
+ public void setPlanar(boolean planar) {
+ if(decompressor != null) {
+ decompressor.setPlanar(planar);
+ }
+ super.setPlanar(planar);
+ }
+
+ public void setSamplesPerPixel(int samplesPerPixel) {
+ if(decompressor != null) {
+ decompressor.setSamplesPerPixel(samplesPerPixel);
+ }
+ super.setSamplesPerPixel(samplesPerPixel);
+ }
+
+ public void setBitsPerSample(int[] bitsPerSample) {
+ if(decompressor != null) {
+ decompressor.setBitsPerSample(bitsPerSample);
+ }
+ super.setBitsPerSample(bitsPerSample);
+ }
+
+ public void setSampleFormat(int[] sampleFormat) {
+ if(decompressor != null) {
+ decompressor.setSampleFormat(sampleFormat);
+ }
+ super.setSampleFormat(sampleFormat);
+ }
+
+ public void setExtraSamples(int[] extraSamples) {
+ if(decompressor != null) {
+ decompressor.setExtraSamples(extraSamples);
+ }
+ super.setExtraSamples(extraSamples);
+ }
+
+ public void setColorMap(char[] colorMap) {
+ if(decompressor != null) {
+ decompressor.setColorMap(colorMap);
+ }
+ super.setColorMap(colorMap);
+ }
+
+ public void setStream(ImageInputStream stream) {
+ if(decompressor != null) {
+ decompressor.setStream(stream);
+ } else {
+ super.setStream(stream);
+ }
+ }
+
+ public void setOffset(long offset) {
+ if(decompressor != null) {
+ decompressor.setOffset(offset);
+ }
+ super.setOffset(offset);
+ }
+
+ public void setByteCount(int byteCount) {
+ if(decompressor != null) {
+ decompressor.setByteCount(byteCount);
+ }
+ super.setByteCount(byteCount);
+ }
+
+ public void setSrcMinX(int srcMinX) {
+ if(decompressor != null) {
+ decompressor.setSrcMinX(srcMinX);
+ }
+ super.setSrcMinX(srcMinX);
+ }
+
+ public void setSrcMinY(int srcMinY) {
+ if(decompressor != null) {
+ decompressor.setSrcMinY(srcMinY);
+ }
+ super.setSrcMinY(srcMinY);
+ }
+
+ public void setSrcWidth(int srcWidth) {
+ if(decompressor != null) {
+ decompressor.setSrcWidth(srcWidth);
+ }
+ super.setSrcWidth(srcWidth);
+ }
+
+ public void setSrcHeight(int srcHeight) {
+ if(decompressor != null) {
+ decompressor.setSrcHeight(srcHeight);
+ }
+ super.setSrcHeight(srcHeight);
+ }
+
+ public void setSourceXOffset(int sourceXOffset) {
+ if(decompressor != null) {
+ decompressor.setSourceXOffset(sourceXOffset);
+ }
+ super.setSourceXOffset(sourceXOffset);
+ }
+
+ public void setDstXOffset(int dstXOffset) {
+ if(decompressor != null) {
+ decompressor.setDstXOffset(dstXOffset);
+ }
+ super.setDstXOffset(dstXOffset);
+ }
+
+ public void setSourceYOffset(int sourceYOffset) {
+ if(decompressor != null) {
+ decompressor.setSourceYOffset(sourceYOffset);
+ }
+ super.setSourceYOffset(sourceYOffset);
+ }
+
+ public void setDstYOffset(int dstYOffset) {
+ if(decompressor != null) {
+ decompressor.setDstYOffset(dstYOffset);
+ }
+ super.setDstYOffset(dstYOffset);
+ }
+
+ /* Should not need to override these mutators as subsampling
+ should not be done by the wrapped decompressor.
+ public void setSubsampleX(int subsampleX) {
+ if(decompressor != null) {
+ decompressor.setSubsampleX(subsampleX);
+ }
+ super.setSubsampleX(subsampleX);
+ }
+
+ public void setSubsampleY(int subsampleY) {
+ if(decompressor != null) {
+ decompressor.setSubsampleY(subsampleY);
+ }
+ super.setSubsampleY(subsampleY);
+ }
+ */
+
+ public void setSourceBands(int[] sourceBands) {
+ if(decompressor != null) {
+ decompressor.setSourceBands(sourceBands);
+ }
+ super.setSourceBands(sourceBands);
+ }
+
+ public void setDestinationBands(int[] destinationBands) {
+ if(decompressor != null) {
+ decompressor.setDestinationBands(destinationBands);
+ }
+ super.setDestinationBands(destinationBands);
+ }
+
+ public void setImage(BufferedImage image) {
+ if(decompressor != null) {
+ ColorModel cm = image.getColorModel();
+ tmpImage =
+ new BufferedImage(cm,
+ image.getRaster().createCompatibleWritableRaster(1, 1),
+ cm.isAlphaPremultiplied(),
+ null);
+ decompressor.setImage(tmpImage);
+ }
+ super.setImage(image);
+ }
+
+ public void setDstMinX(int dstMinX) {
+ if(decompressor != null) {
+ decompressor.setDstMinX(dstMinX);
+ }
+ super.setDstMinX(dstMinX);
+ }
+
+ public void setDstMinY(int dstMinY) {
+ if(decompressor != null) {
+ decompressor.setDstMinY(dstMinY);
+ }
+ super.setDstMinY(dstMinY);
+ }
+
+ public void setDstWidth(int dstWidth) {
+ if(decompressor != null) {
+ decompressor.setDstWidth(dstWidth);
+ }
+ super.setDstWidth(dstWidth);
+ }
+
+ public void setDstHeight(int dstHeight) {
+ if(decompressor != null) {
+ decompressor.setDstHeight(dstHeight);
+ }
+ super.setDstHeight(dstHeight);
+ }
+
+ public void setActiveSrcMinX(int activeSrcMinX) {
+ if(decompressor != null) {
+ decompressor.setActiveSrcMinX(activeSrcMinX);
+ }
+ super.setActiveSrcMinX(activeSrcMinX);
+ }
+
+ public void setActiveSrcMinY(int activeSrcMinY) {
+ if(decompressor != null) {
+ decompressor.setActiveSrcMinY(activeSrcMinY);
+ }
+ super.setActiveSrcMinY(activeSrcMinY);
+ }
+
+ public void setActiveSrcWidth(int activeSrcWidth) {
+ if(decompressor != null) {
+ decompressor.setActiveSrcWidth(activeSrcWidth);
+ }
+ super.setActiveSrcWidth(activeSrcWidth);
+ }
+
+ public void setActiveSrcHeight(int activeSrcHeight) {
+ if(decompressor != null) {
+ decompressor.setActiveSrcHeight(activeSrcHeight);
+ }
+ super.setActiveSrcHeight(activeSrcHeight);
+ }
+
+ private byte clamp(int f) {
+ if (f < 0) {
+ return (byte)0;
+ } else if (f > 255*65536) {
+ return (byte)255;
+ } else {
+ return (byte)(f >> 16);
+ }
+ }
+
+ public void beginDecoding() {
+ if(decompressor != null) {
+ decompressor.beginDecoding();
+ }
+
+ TIFFImageMetadata tmetadata = (TIFFImageMetadata)metadata;
+ TIFFField f;
+
+ f = tmetadata.getTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_SUBSAMPLING);
+ if (f != null) {
+ if (f.getCount() == 2) {
+ this.chromaSubsampleH = f.getAsInt(0);
+ this.chromaSubsampleV = f.getAsInt(1);
+
+ if (chromaSubsampleH != 1 && chromaSubsampleH != 2 &&
+ chromaSubsampleH != 4) {
+ warning("Y_CB_CR_SUBSAMPLING[0] has illegal value " +
+ chromaSubsampleH +
+ " (should be 1, 2, or 4), setting to 1");
+ chromaSubsampleH = 1;
+ }
+
+ if (chromaSubsampleV != 1 && chromaSubsampleV != 2 &&
+ chromaSubsampleV != 4) {
+ warning("Y_CB_CR_SUBSAMPLING[1] has illegal value " +
+ chromaSubsampleV +
+ " (should be 1, 2, or 4), setting to 1");
+ chromaSubsampleV = 1;
+ }
+ } else {
+ warning("Y_CB_CR_SUBSAMPLING count != 2, " +
+ "assuming no subsampling");
+ }
+ }
+
+ f =
+ tmetadata.getTIFFField(BaselineTIFFTagSet.TAG_Y_CB_CR_COEFFICIENTS);
+ if (f != null) {
+ if (f.getCount() == 3) {
+ this.lumaRed = f.getAsFloat(0);
+ this.lumaGreen = f.getAsFloat(1);
+ this.lumaBlue = f.getAsFloat(2);
+ } else {
+ warning("Y_CB_CR_COEFFICIENTS count != 3, " +
+ "assuming default values for CCIR 601-1");
+ }
+ }
+
+ f =
+ tmetadata.getTIFFField(BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE);
+ if (f != null) {
+ if (f.getCount() == 6) {
+ this.referenceBlackY = f.getAsFloat(0);
+ this.referenceWhiteY = f.getAsFloat(1);
+ this.referenceBlackCb = f.getAsFloat(2);
+ this.referenceWhiteCb = f.getAsFloat(3);
+ this.referenceBlackCr = f.getAsFloat(4);
+ this.referenceWhiteCr = f.getAsFloat(5);
+ } else {
+ warning("REFERENCE_BLACK_WHITE count != 6, ignoring it");
+ }
+ } else {
+ warning("REFERENCE_BLACK_WHITE not found, assuming 0-255/128-255/128-255");
+ }
+
+ this.colorConvert = true;
+
+ float BCb = (2.0f - 2.0f*lumaBlue);
+ float RCr = (2.0f - 2.0f*lumaRed);
+
+ float GY = (1.0f - lumaBlue - lumaRed)/lumaGreen;
+ float GCb = 2.0f*lumaBlue*(lumaBlue - 1.0f)/lumaGreen;
+ float GCr = 2.0f*lumaRed*(lumaRed - 1.0f)/lumaGreen;
+
+ for (int i = 0; i < 256; i++) {
+ float fY = (i - referenceBlackY)*codingRangeY/
+ (referenceWhiteY - referenceBlackY);
+ float fCb = (i - referenceBlackCb)*127.0f/
+ (referenceWhiteCb - referenceBlackCb);
+ float fCr = (i - referenceBlackCr)*127.0f/
+ (referenceWhiteCr - referenceBlackCr);
+
+ iYTab[i] = (int)(fY*FRAC_SCALE);
+ iCbTab[i] = (int)(fCb*BCb*FRAC_SCALE);
+ iCrTab[i] = (int)(fCr*RCr*FRAC_SCALE);
+
+ iGYTab[i] = (int)(fY*GY*FRAC_SCALE);
+ iGCbTab[i] = (int)(fCb*GCb*FRAC_SCALE);
+ iGCrTab[i] = (int)(fCr*GCr*FRAC_SCALE);
+ }
+ }
+
+ public void decodeRaw(byte[] buf,
+ int dstOffset,
+ int bitsPerPixel,
+ int scanlineStride) throws IOException {
+ int elementsPerPacket = chromaSubsampleH*chromaSubsampleV + 2;
+ byte[] packet = new byte[elementsPerPacket];
+
+ if(decompressor != null) {
+ int bytesPerRow = 3*srcWidth;
+ byte[] tmpBuf = new byte[bytesPerRow*srcHeight];
+ decompressor.decodeRaw(tmpBuf, dstOffset, bitsPerPixel,
+ bytesPerRow);
+ ByteArrayInputStream byteStream =
+ new ByteArrayInputStream(tmpBuf);
+ stream = new MemoryCacheImageInputStream(byteStream);
+ } else {
+ stream.seek(offset);
+ }
+
+ for (int y = srcMinY; y < srcMinY + srcHeight; y += chromaSubsampleV) {
+ // Decode chromaSubsampleV rows
+ for (int x = srcMinX; x < srcMinX + srcWidth;
+ x += chromaSubsampleH) {
+ try {
+ stream.readFully(packet);
+ } catch (EOFException e) {
+ return;
+ }
+
+ byte Cb = packet[elementsPerPacket - 2];
+ byte Cr = packet[elementsPerPacket - 1];
+
+ int iCb = 0, iCr = 0, iGCb = 0, iGCr = 0;
+
+ if (colorConvert) {
+ int Cbp = Cb & 0xff;
+ int Crp = Cr & 0xff;
+
+ iCb = iCbTab[Cbp];
+ iCr = iCrTab[Crp];
+
+ iGCb = iGCbTab[Cbp];
+ iGCr = iGCrTab[Crp];
+ }
+
+ int yIndex = 0;
+ for (int v = 0; v < chromaSubsampleV; v++) {
+ int idx = dstOffset + 3*(x - srcMinX) +
+ scanlineStride*(y - srcMinY + v);
+
+ // Check if we reached the last scanline
+ if (y + v >= srcMinY + srcHeight) {
+ break;
+ }
+
+ for (int h = 0; h < chromaSubsampleH; h++) {
+ if (x + h >= srcMinX + srcWidth) {
+ break;
+ }
+
+ byte Y = packet[yIndex++];
+
+ if (colorConvert) {
+ int Yp = Y & 0xff;
+ int iY = iYTab[Yp];
+ int iGY = iGYTab[Yp];
+
+ int iR = iY + iCr;
+ int iG = iGY + iGCb + iGCr;
+ int iB = iY + iCb;
+
+ byte r = clamp(iR);
+ byte g = clamp(iG);
+ byte b = clamp(iB);
+
+ buf[idx] = r;
+ buf[idx + 1] = g;
+ buf[idx + 2] = b;
+ } else {
+ buf[idx] = Y;
+ buf[idx + 1] = Cb;
+ buf[idx + 2] = Cr;
+ }
+
+ idx += 3;
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFZLibCompressor.java Mon Nov 23 12:26:12 2015 -0800
@@ -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.plugins.tiff.BaselineTIFFTagSet;
+import javax.imageio.ImageWriteParam;
+
+/**
+ * Compressor for ZLib compression.
+ */
+public class TIFFZLibCompressor extends TIFFDeflater {
+ public TIFFZLibCompressor(ImageWriteParam param, int predictor) {
+ super("ZLib", BaselineTIFFTagSet.COMPRESSION_ZLIB, param, predictor);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/metadata/doc-files/tiff_metadata.html Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,1200 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+Copyright (c) 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.
+-->
+
+<title>TIFF Metadata Format Specification and Usage Notes</title>
+</head>
+
+<body bgcolor="white">
+
+<center><h1>
+TIFF Metadata Format Specification and Usage Notes
+</h1></center>
+
+ <p>
+<a href="#Reading">Reading Images</a><br/>
+<font size="-1">
+<ul>
+<li><a href="#ColorConversionRead">Color Conversion</a></li>
+<li><a href="#ColorSpacesRead">Color Spaces</a></li>
+<li><a href="#ICCProfilesRead">ICC Profiles</a></li>
+<li><a href="#MetadataIssuesRead">Metadata Issues</a>
+<font size="-2">
+<ul>
+<li><a href="#MapNativeStandard">Native to Standard Metadata Mapping</a></li>
+</ul>
+</font>
+</li>
+<li><a href="#ExifRead">Reading Exif Images</a>
+<font size="-2">
+<ul>
+<li><a href="#ExifReadTIFF">Reading Uncompressed Exif Images</a></li>
+<li><a href="#ExifReadJPEG">Reading Compressed Exif Images</a></li>
+</ul>
+</font>
+</li>
+</ul>
+</font>
+<a href="#Writing">Writing Images</a><br/>
+<font size="-1">
+<ul>
+<li><a href="#Compression">Compression</a></li>
+<li><a href="#ColorConversionWrite">Color Conversion</a></li>
+<li><a href="#ICCProfilesWrite">ICC Profiles</a></li>
+<li><a href="#MetadataIssuesWrite">Metadata Issues</a></li>
+<font size="-2">
+<ul>
+<li><a href="#MapStandardNative">Standard to Native Metadata Mapping</a></li>
+</ul>
+</font>
+<li><a href="#ExifWrite">Writing Exif Images</a>
+<font size="-2">
+<ul>
+<li><a href="#ExifWriteTIFF">Writing Uncompressed Exif Images</a></li>
+<li><a href="#ExifWriteJPEG">Writing Compressed Exif Images</a></li>
+</ul>
+</font>
+</li>
+</ul>
+</font>
+<a href="#StreamMetadata">Native Stream Metadata Format</a><br/>
+<a href="#ImageMetadata">Native Image Metadata Format</a>
+</p>
+
+<h3><a name="Reading"/>Reading Images</h3>
+
+TIFF images are read by an <a href="../../ImageReader.html">ImageReader</a>
+which may be controlled by its public interface as well as via a supplied
+<a href="../../plugins/tiff/TIFFImageReadParam.html">TIFFImageReadParam</a>.
+
+<!-- <h4>Supported Image Types</h4> -->
+
+<!-- Table? -->
+
+<h4><a name="ColorConversionRead"/>Color Conversion</h4>
+
+<p>If the source image data
+have photometric type CIE L*a*b* or YCbCr, and the destination color space
+type is RGB, then the source image data will be automatically converted to
+RGB using an internal color converter.</p>
+
+<h4><a name="ColorSpacesRead"/>Color Spaces</h4>
+
+The raw color space assigned by default, i.e., in the absence of a
+user-supplied <a href="../../ImageTypeSpecifier.html">ImageTypeSpecifier</a>,
+will be the first among the following which applies:
+
+<ul>
+<li>A color space created from the <a href="#ICCProfilesRead">ICC Profile</a>
+metadata field if it is present and compatible with the image data
+layout.</li>
+<a name="nonICCProfile"><li>sRGB if the image is monochrome/bilevel
+(a two-level color map is created internally).</li>
+<li>sRGB if the image is palette-color.</li>
+<li>Linear RGB if the image has three samples per pixel, has photometric type
+CIE L*a*b*, or has photometric type YCbCr and is <i>not</i>
+JPEG-compressed.</li>
+<li>A <a href="#DefaultCMYK">default CMYK color space</a> if the image has
+photometric type CMYK and four samples per pixel.</li>
+<li>Grayscale if the image has one or two samples per pixel and uniformly
+1, 2, 4, 8, 16, or 32 bits per sample or is floating point.</li>
+<li>sRGB if the image has three or four samples per pixel and uniformly
+1, 2, 4, 8, 16, or 32 bits per sample or is floating point.</li>
+<li>A fabricated, <a href="#GenericCS">generic color space</a> if the image
+has more than four samples per pixel and the number of bits per sample for
+all bands is the same and is a multiple of 8.</li>
+<li>Grayscale if the image has one or two samples per pixel regardless of
+the number of bits per sample.</li>
+<li>sRGB if the image has three or four samples per pixel regardless of
+the number of bits per sample.</li>
+<li>A fabricated, <a href="#GenericCS">generic color space</a> if the image
+has more than four samples per pixel regardless of the number of bits per
+sample.</li>
+</ul>
+
+<p><a name="DefaultCMYK"/>The normalized color coordinate transformations
+used for the default CMYK color space are defined as follows:
+
+<ul>
+<li>CMYK to linear RGB
+<pre>
+R = (1 - K)*(1 - C)
+G = (1 - K)*(1 - M)
+B = (1 - K)*(1 - Y)
+</pre>
+</li>
+<li>Linear RGB to CMYK
+<pre>
+K = min{1 - R, 1 - G, 1 - B}
+if(K != 1) {
+ C = (1 - R - K)/(1 - K)
+ M = (1 - G - K)/(1 - K)
+ Y = (1 - B - K)/(1 - K)
+} else {
+ C = M = Y = 0
+}
+</pre>
+</li>
+</ul>
+</p>
+
+<p><a name="GenericCS"/>The generic color space used when no other color space
+can be inferred is provided merely to enable the data to be loaded. It is not
+intended to provide accurate conversions of any kind.</p>
+
+<p>If the data are known to be in a color space not correctly handled by the
+foregoing, then an <code>ImageTypeSpecifier</code> should be
+supplied to the reader and should be derived from a color space which is correct
+for the data in question.</p>
+
+<h4><a name="ICCProfilesRead"/>ICC Profiles</h4>
+
+If an ICC profile is contained in the image metadata
+(<a href="../../plugins/tiff/BaselineTIFFTagSet.html">
+BaselineTIFFTagSet</a>.TAG_ICC_PROFILE, tag number 34675),
+an attempt will be made to use it to create the color space
+of the loaded image. It will be used if the data layout is of component type
+and the number of samples per pixel equals or is one greater than the number
+of components described by the ICC profile. If the ICC profile is not used
+then the color space will be inferred in one of the subsequent steps described
+<a href="#nonICCProfile">above</a>.
+
+<p>If for some reason the embedded ICC profile is not used automatically, then
+it may be used manually by following this procedure:
+
+<ol>
+<li>Obtain the image metadata from
+<code>ImageReader.getImageMetadata</code></li>
+<li>Extract the ICC profile field and its value.</li>
+<li>Create an <a href="../../../../java/awt/color/ICC_ColorSpace.html">
+ICC_ColorSpace</a> from an
+<a href="../../../../java/awt/color/ICC_Profile.html">
+ICC_Profile</a> created from the ICC profile field data
+using <code>ICC_Profile.getInstance(byte[])</code>.</li>
+<li>Create an <code>ImageTypeSpecifier</code> from the new color
+space using one of its factory methods which accepts an
+<code>ICC_ColorSpace</code>.
+<li>Create a compatible <a href="../../ImageReadParam.html">ImageReadParam</a>
+and set the <code>ImageTypeSpecifier</code> using
+<code>ImageReadParam.setDestinationType</code>.</li>
+<li>Pass the parameter object to the appropriate <code>read</code> method.</li>
+</ol>
+</p>
+
+<p>If the inferred color space not based on the ICC Profile field is compatible
+with the ICC profile-based color space, then a second
+<code>ImageTypeSpecifier</code> derived from this inferred color
+space will be included in the
+<a href="../../../../java/util/Iterator.html">Iterator</a> returned by
+<code>ImageReader.getImageTypes</code>. 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.</p>
+
+<h4><a name="MetadataIssuesRead"/>Metadata Issues</h4>
+
+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 <code>ignoreMetadata</code> parameter of
+<code>ImageReader.setInput(Object,boolean,boolean)</code>. It is
+informed of which <a href="../../plugins/tiff/TIFFTag.html">TIFFTag</a>s to
+recognize or not to recognize via
+<code>TIFFImageReadParam.addAllowedTagSet(TIFFTagSet)</code>
+and
+<code>TIFFImageReadParam.removeAllowedTagSet(TIFFTagSet)</code>.
+If <code>ignoreMetadata</code> is <code>true</code>, then the reader will
+load into the native image metadata object only those fields which have a
+<code>TIFFTag</code> contained in the one of the allowed
+<code>TIFFTagSet</code>s.
+
+<p>Use of a <a href="../../plugins/tiff/TIFFDirectory.html">TIFFDirectory</a>
+object may simplify gaining access to metadata values. An instance of
+<code>TIFFDirectory</code> may be created from the <code>IIOMetadata</code>
+object returned by the TIFF reader using the
+<code>TIFFDirectory.createFromMetadata</code> method.</p>
+
+<h5><a name="MapNativeStandard"/>
+Mapping of TIFF Native Image Metadata to the Standard Metadata Format</h5>
+
+The derivation of standard metadata format
+<a href="standard_metadata.html">javax_imageio_1.0</a>
+elements from <a href="#ImageMetadata">TIFF native image metadata</a> is given
+in the following table.
+
+<p>
+<table border="1">
+<tr>
+<th>Standard Metadata Element</th>
+<th>Derivation from TIFF Fields</th>
+</tr>
+<tr>
+<td>/Chroma/ColorSpaceType@name</td>
+<td>PhotometricInterpretation: WhiteIsZero, BlackIsZero, TransparencyMask =
+"GRAY"; RGB, PaletteColor => "RGB"; CMYK => "CMYK";
+YCbCr => "YCbCr";
+CIELab, ICCLab => "Lab".</td>
+</tr>
+<tr>
+<td>/Chroma/NumChannels@value</td>
+<td>SamplesPerPixel</td>
+</tr>
+<tr>
+<td>/Chroma/BlackIsZero@value</td>
+<td>"TRUE" <=> PhotometricInterpretation => WhiteIsZero</td>
+</tr>
+<tr>
+<td>/Chroma/Palette</td>
+<td>ColorMap</td>
+</tr>
+<tr>
+<td>/Compression/CompressionTypeName@value</td>
+<td>Compression: Uncompressed => "none"; CCITT 1D => "CCITT
+RLE";
+Group 3 Fax => "CCITT T.4"; Group 4 Fax => "CCITT T.6";
+LZW => "LZW";
+JPEG => "Old JPEG"; New JPEG => "JPEG"; Zlib =>> "ZLib"; PackBits =>
+"PackBits";
+Deflate => "Deflate"; Exif JPEG => "JPEG".</td>
+</tr>
+<tr>
+<td>/Compression/Lossless@value</td>
+<td>Compression: JPEG or New JPEG => "FALSE"; otherwise "TRUE".</td>
+</tr>
+<tr>
+<td>/Data/PlanarConfiguration@value</td>
+<td>Chunky => "PixelInterleaved"; Planar => "PlaneInterleaved".</td>
+</tr>
+<tr>
+<td>/Data/SampleFormat@value</td>
+<td>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.
+</td>
+</tr>
+<tr>
+<td>/Data/BitsPerSample@value</td>
+<td>BitsPerSample as a space-separated list.</td>
+</tr>
+<tr>
+<td>/Data/SampleMSB@value</td>
+<td>FillOrder: left-to-right => space-separated list of BitsPerSample-1;
+right-to-left => space-separated list of 0s.</td>
+</tr>
+<tr>
+<td>/Dimension/PixelAspectRatio@value</td>
+<td>(1/XResolution)/(1/YResolution)</td>
+</tr>
+<tr>
+<td>/Dimension/ImageOrientation@value</td>
+<td>Orientation</td>
+</tr>
+<tr>
+<td>/Dimension/HorizontalPixelSize@value</td>
+<td>1/XResolution in millimeters if ResolutionUnit is not None.</td>
+</tr>
+<tr>
+<td>/Dimension/VerticalPixelSize@value</td>
+<td>1/YResolution in millimeters if ResolutionUnit is not None.</td>
+</tr>
+<tr>
+<td>/Dimension/HorizontalPosition@value</td>
+<td>XPosition in millimeters if ResolutionUnit is not None.</td>
+</tr>
+<tr>
+<td>/Dimension/VerticalPosition@value</td>
+<td>YPosition in millimeters if ResolutionUnit is not None.</td>
+</tr>
+<tr>
+<td>/Document/FormatVersion@value</td>
+<td>6.0</td>
+</tr>
+<tr>
+<td>/Document/SubimageInterpretation@value</td>
+<td>NewSubFileType: transparency => "TransparencyMask";
+reduced-resolution => "ReducedResolution";
+single page => "SinglePage".</td>
+</tr>
+<tr>
+<td>/Document/ImageCreationTime@value</td>
+<td>DateTime</td>
+</tr>
+<tr>
+<td>/Text/TextEntry</td>
+<td>DocumentName, ImageDescription, Make, Model, PageName, Software,
+Artist, HostComputer, InkNames, Copyright:
+/Text/TextEntry@keyword = field name,
+/Text/TextEntry@value = field value.<br>
+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.</td>
+</tr>
+<tr>
+<td>/Transparency/Alpha@value</td>
+<td>ExtraSamples: associated alpha => "premultiplied";
+unassociated alpha => "nonpremultiplied".</td>
+</tr>
+</table>
+</p>
+
+<h4><a name="ExifRead"/>Reading Exif Images</h4>
+
+The TIFF reader may be used to read an uncompressed Exif image or the
+contents of the <tt>APP1</tt> marker segment of a compressed Exif image.
+
+<h5><a name="ExifReadTIFF"/>Reading Uncompressed Exif Images</h5>
+
+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
+<code>ImageReader</code> methods may be used to read the image
+data and metadata:
+
+<pre><code>
+ ImageInputStream input;
+ ImageReader tiffReader;
+ ImageReadParam tiffReadParam;
+
+ tiffReader.setInput(input);
+
+ // Read primary image and IFD.
+ BufferedImage image = tiffReader.read(0, tiffReadParam);
+ IIOMetadata primaryIFD = tiffReader.getImageMetadata(0);
+
+ // Read thumbnail if present.
+ BufferedImage thumbnail = null;
+ if (tiffReader.getNumImages(true) > 1) {
+ thumbnail = tiffReader.read(1, tiffReadParam);
+ }
+</code></pre>
+
+Note that the Exif thumbnail is treated as a separate page in the TIFF
+stream and not as a thumbnail, i.e.,
+<code>tiffReader.hasThumbnails(0)</code> will return <code>false</code>.
+
+<h5><a name="ExifReadJPEG"/>Reading Compressed Exif Images</h5>
+
+A compressed Exif image is a 3-band ISO/IEC 10918-1 baseline DCT JPEG stream
+with an inserted <tt>APP1</tt> marker segment. The parameters of the marker
+segment after the length are the 6-byte sequence
+<code>{'E', 'x', 'i', 'f', 0x00, 0x00}</code></code>
+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.
+
+<p>The parameter content of the <tt>APP1</tt> marker segment may be obtained
+from the user object of the associated <code>Node</code> in a
+<tt>javax_imageio_jpeg_image_1.0</tt> 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 <tt>unknown</tt> and an attribute named <tt>MarkerTag</tt> with
+integral value <code>0xE1</code> (<code>String</code> value
+<code>"225"</code>). The user object of this node will be a byte array
+which starts with the six bytes <code>{'E', 'x', 'i', 'f', '0', '0'}</code>.
+The primary IFD and the thumbnail IFD and image may be
+read from the user object by the usual <code>ImageReader</code>
+methods:
+
+<pre><code>
+ 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);
+</code></pre>
+
+Note that <code>tiffReader.getNumImages(true)</code> returns the number of
+IFDs in the embedded TIFF stream including those corresponding to empty
+images. Calling <code>tiffReader.read(0, readParam)</code> 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.
+</p>
+
+<h3><a name="Writing"/>Writing Images</h3>
+
+TIFF images are written by a <a href="../../ImageWriter.html">ImageWriter</a> which may be
+controlled by its public interface as well as via a supplied
+<a href="../../ImageWriteParam.html">ImageWriteParam</a>. For an <code>ImageWriteParam</code> returned
+by the <code>getDefaultWriteParam()</code> method of the TIFF <code>ImageWriter</code>,
+the <code>canWriteTiles()</code> and <code>canWriteCompressed()</code> methods
+will return <code>true</code>; the <code>canOffsetTiles()</code> and
+<code>canWriteProgressive()</code> methods will return <code>false</code>.</p>
+
+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.
+
+<p> 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.</p>
+
+ <!-- <h4>Supported Image Types</h4> -->
+
+<!-- Table? -->
+
+<h4><a name="Compression"/>Compression</h4>
+
+The compression type may be set via the <code>setCompressionType()</code> method of
+the <code>ImageWriteParam</code> after setting the compression mode to
+<code>MODE_EXPLICIT</code>. The set of innately
+supported compression types is listed in the following table:
+
+<table border=1>
+<caption><b>Supported Compression Types</b></caption>
+<tr><th>Compression Type</th> <th>Description</th> <th>Reference</th></tr>
+<tr>
+<td>CCITT RLE</td>
+<td>Modified Huffman compression</td>
+<td>TIFF 6.0 Specification, Section 10</td>
+</tr>
+<tr>
+<td>CCITT T.4</td>
+<td>CCITT T.4 bilevel encoding/Group 3 facsimile compression</td>
+<td>TIFF 6.0 Specification, Section 11</td>
+</tr>
+<tr>
+<td>CCITT T.6</td>
+<td>CCITT T.6 bilevel encoding/Group 4 facsimile compression</td>
+<td>TIFF 6.0 Specification, Section 11</td></tr>
+<tr>
+<td>LZW</td>
+<td>LZW compression</td>
+<td>TIFF 6.0 Specification, Section 13</td></tr>
+<tr>
+<td>JPEG</td>
+<td>"New" JPEG-in-TIFF compression</td>
+<td><a href="ftp://ftp.sgi.com/graphics/tiff/TTN2.draft.txt">TIFF
+Technical Note #2</a></td>
+</tr>
+<tr>
+<td>ZLib</td>
+<td>"Deflate/Inflate" compression (see note following this table)</td>
+<td><a href="http://partners.adobe.com/asn/developer/pdfs/tn/TIFFphotoshop.pdf">
+Adobe Photoshop® TIFF Technical Notes</a> (PDF)</td>
+</tr>
+<tr>
+<td>PackBits</td>
+<td>Byte-oriented, run length compression</td>
+<td>TIFF 6.0 Specification, Section 9</td>
+</tr>
+<tr>
+<td>Deflate</td>
+<td>"Zip-in-TIFF" compression (see note following this table)</td>
+<td><a href="http://www.isi.edu/in-notes/rfc1950.txt">
+ZLIB Compressed Data Format Specification</a>,
+<a href="http://www.isi.edu/in-notes/rfc1951.txt">
+DEFLATE Compressed Data Format Specification</a></td>
+</tr>
+<tr>
+<td>Exif JPEG</td>
+<td>Exif-specific JPEG compression (see note following this table)</td>
+<td><a href="http://www.exif.org/Exif2-2.PDF">Exif 2.2 Specification</a>
+(PDF), section 4.5.5, "Basic Structure of Thumbnail Data"</td>
+</table>
+
+<p>
+Old-style JPEG compression as described in section 22 of the TIFF 6.0
+Specification is <i>not</i> supported.
+</p>
+
+<p> 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.</p>
+
+<p>
+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.
+</p>
+
+<p>
+"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 <i>not</i> later be appended using the pixel
+replacement capability of the TIFF writer.
+</p>
+
+<p> 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 <tt>[1, 9]</tt> 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.</p>
+
+<h4><a name="ColorConversionWrite"/>Color Conversion</h4>
+
+<p>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.</p>
+
+<h4><a name="ICCProfilesWrite"/>ICC Profiles</h4>
+
+An <tt>ICC Profile</tt> field will be written if either:
+<ul>
+<li>one is present in the native image metadata
+<a href="../IIOMetadata.html">IIOMetadata</a> instance supplied to the writer,
+or</li>
+<li>the <a href="../../../../java/awt/color/ColorSpace.html">ColorSpace</a>
+of the destination <code>ImageTypeSpecifier</code> is an instance of
+<code>ICC_ColorSpace</code> which is not one of the standard
+color spaces defined by the <tt>CS_*</tt> constants in the
+<code>ColorSpace</code> class. The destination type is set via
+<code>ImageWriteParam.setDestinationType(ImageTypeSpecifier)</code> and defaults
+to the <code>ImageTypeSpecifier</code> of the image being written.
+</li>
+</ul>
+
+<h4><a name="MetadataIssuesWrite"/>Metadata Issues</h4>
+
+Some behavior of the writer is affected by or may affect the contents of
+the image metadata which may be supplied by the user.
+
+<p>For bilevel images, the <tt>FillOrder</tt>, and <tt>T4Options</tt>
+fields affect the output data. The data will be filled right-to-left if
+<tt>FillOrder</tt> is present with a value of 2
+(<code>BaselineTIFFTagSet.FILL_ORDER_RIGHT_TO_LEFT</code>)
+and will be filled left-to-right otherwise. The value of <tt>T4Options</tt>
+specifies whether the data should be 1D- or 2D-encoded and whether EOL
+padding should be used.</p>
+
+<p>For all images the value of the <tt>RowsPerStrip</tt> 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.</p>
+
+<p>For all images the tile dimensions may be set using the <tt>TileWidth</tt>
+and <tt>TileLength</tt> field values if the tiling mode is
+<code>ImageWriteParam.MODE_COPY_FROM_METADATA</code>. If this mode
+is set but the fields are not, their respective default values are the image
+width and height.</p>
+
+<p>When using JPEG-in-TIFF compression, a <tt>JPEGTables</tt> field will be
+written to the IFD and abbreviated JPEG streams to each strip or tile if and
+only if a <tt>JPEGTables</tt> field is contained in the metadata object
+provided to the writer. If the contents of the <tt>JPEGTables</tt> 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 <tt>JPEGTables</tt> field is present in the metadata, then no
+<tt>JPEGTables</tt> field will be written to the output and each strip or
+tile will be written as a separate, self-contained JPEG stream.</p>
+
+<p>When using Deflate/ZLib or LZW compression, if the image has 8 bits per
+sample, a horizontal differencing predictor will be used if the
+<tt>Predictor</tt> field is present with a value of 2
+(<code>BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING</code>).
+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
+(<code>BaselineTIFFTagSet.PREDICTOR_NONE</code>).
+</p>
+
+<p>Some fields may be added or modified:
+
+<ul>
+<li><tt>PhotometricInterpretation</tt> if not present.</li>
+<li><tt>PlanarConfiguration</tt> if this field is present with value
+<tt>Planar</tt> is is reset to <tt>Chunky</tt>.</li>
+<li><tt>Compression</tt> always.</li>
+<li><tt>BitsPerSample</tt> if the image is not bilevel.</li>
+<li><tt>SamplesPerPixel</tt> always.</li>
+<li><tt>ExtraSamples</tt> if an alpha channel is present.</li>
+<li><tt>SampleFormat</tt> if not present and the data are 16- or 32-bit
+integers or floating point.</li>
+<li><tt>ColorMap</tt> if the <tt>PhotometricInterpretation</tt> is
+<tt>RGBPalette</tt>.</li>
+<li><tt>ImageWidth</tt> and <tt>ImageLength</tt> always.</li>
+<li><tt>TileWidth</tt>, <tt>TileLength</tt>, <tt>TileOffsets</tt>, and
+<tt>TileByteCounts</tt> if a tiled image is being written.</li>
+<li><tt>RowsPerStrip</tt>, <tt>StripOffsets</tt>, and <tt>StripByteCounts</tt>
+if a tiled image is <i>not</i> being written.</li>
+<li><tt>XResolution</tt>, <tt>YResolution</tt>, and <tt>ResolutionUnit</tt>
+if none of these is present.</li>
+<li><tt>YCbCrSubsampling</tt> and <tt>YCbCrPositioning</tt> 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).</li>
+<li><tt>YCbCrSubsampling</tt>, <tt>YCbCrPositioning</tt>, and
+<tt>ReferenceBlackWhite</tt>: 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).</li>
+</ul>
+
+<p>Some fields may be removed:
+
+<ul>
+<li><tt>BitsPerSample</tt> if the image is bilevel.</li>
+<li><tt>ExtraSamples</tt> if the image does not have an alpha channel.</li>
+<li><tt>ColorMap</tt> if the photometric interpretation is not
+<tt>RGBPalette</tt>.</li>
+<li><tt>TileWidth</tt>, <tt>TileLength</tt>, <tt>TileOffsets</tt>, and
+<tt>TileByteCounts</tt> if tiling <i>is not</i> being used.</li>
+<li><tt>RowsPerStrip</tt>, <tt>StripOffsets</tt>, and <tt>StripByteCounts</tt>
+if tiling <i>is</i> being used.</li>
+<li><tt>YCbCrSubsampling</tt>, <tt>YCbCrPositioning</tt>, and
+<tt>ReferenceBlackWhite</tt> if the compression type is JPEG and the
+color space is grayscale.</li>
+<li><tt>JPEGProc</tt>, <tt>JPEGInterchangeFormat</tt>,
+<tt>JPEGInterchangeFormatLength</tt>, <tt>JPEGRestartInterval</tt>,
+<tt>JPEGLosslessPredictors</tt>, <tt>JPEGPointTransforms</tt>,
+<tt>JPEGQTables</tt>, <tt>JPEGDCTables</tt>, and
+<tt>JPEGACTables</tt> if the compression type is JPEG.</li>
+</ul>
+</p>
+
+<p>Other fields present in the supplied metadata are uninterpreted and will
+be written as supplied.</p>
+
+<p>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.</p>
+
+<p>Setting up the image metadata to write to a TIFF stream may be simplified
+by using the <code>TIFFDirectory</code> class
+which represents a TIFF IFD. A field in a TIFF IFD is represented by an
+instance of <a href="../../plugins/tiff/TIFFField.html">TIFFField</a>. For each
+field to be written a <code>TIFFField</code> may be added to the
+<code>TIFFDirectory</code> and the latter converted to an
+<code>IIOMetadata</code> object by invoking
+<code>TIFFDirectory.getAsMetadata</code>. The
+<code>IIOMetadata</code> object so obtained may then be passed to the TIFF
+writer.</p>
+
+<h5><a name="MapStandardNative"/>
+Mapping of the Standard Metadata Format to TIFF Native Image Metadata</h5>
+
+The derivation of <a href="#ImageMetadata">TIFF native image metadata</a>
+elements from the standard metadata format
+<a href="standard_metadata.html">javax_imageio_1.0</a> is
+given in the following table.
+
+<p>
+<table border="1">
+<tr>
+<th>TIFF Field</th>
+<th>Derivation from Standard Metadata Elements</th>
+</tr>
+<tr>
+<td>
+PhotometricInterpretation
+</td>
+<td>/Chroma/ColorSpaceType@name: "GRAY" and /Chroma/BlackIsZero@value = "FALSE"
+=> WhiteIsZero; "GRAY" and /Document/SubimageInterpretation@value =
+"TransparencyMask" => TransparencyMask; "RGB" and /Chroma/Palette present =>
+PaletteColor; "GRAY" => BlackIsZero; "RGB" => RGB; "YCbCr" => YCbCr;
+"CMYK" => CMYK; "Lab" => CIELab.</td>
+</tr>
+<tr>
+<td>SamplesPerPixel</td>
+<td>/Chroma/NumChannels@value</td>
+</tr>
+<tr>
+<td>ColorMap</td>
+<td>/Chroma/Palette</td>
+</tr>
+<tr>
+<td>Compression</td>
+<td>/Compression/CompressionTypeName@value: "none" => Uncompressed;
+"CCITT RLE" => CCITT 1D; "CCITT T.4" => Group 3 Fax; "CCITT T.6" => Group 4
+Fax; "LZW" => LZW; "Old JPEG" => JPEG; "JPEG" => New JPEG; "ZLib" => ZLib;
+"PackBits" => PackBits; "Deflate" => Deflate.</td>
+</tr>
+<tr>
+<td>PlanarConfiguration</td>
+<td>/Data/PlanarConfiguration@value: "PixelInterleaved" => Chunky;
+"PlaneInterleaved" => Planar.</td>
+</tr>
+<tr>
+<td>SampleFormat</td>
+<td>/Data/SampleFormat@value: "SignedIntegral" => two's complement signed
+integer data; "UnsignedIntegral" => unsigned integer data; "Real" =>
+IEEE floating point data; "Index" => unsigned integer data.
+</td>
+</tr>
+<tr>
+<td>BitsPerSample</td>
+<td>/Data/BitsPerSample@value: space-separated list parsed to char array.</td>
+</tr>
+<tr>
+<td>FillOrder</td>
+<td>/Data/SampleMSB@value: if all values in space-separated list are 0s =>
+right-to-left; otherwise => left-to-right.
+</td>
+</tr>
+<tr>
+<td>XResolution</td>
+<td>(10 / /Dimension/HorizontalPixelSize@value) or
+(10 / (/Dimension/VerticalPixelSize@value *
+/Dimension/PixelAspectRatio@value))</td>
+</tr>
+<tr>
+<td>YResolution</td>
+<td>(10 / /Dimension/VerticalPixelSize@value) or
+(10 / (/Dimension/HorizontalPixelSize@value /
+/Dimension/PixelAspectRatio@value))</td>
+</tr>
+<tr>
+<td>ResolutionUnit</td>
+<td>Centimeter if XResolution or YResolution set; otherwise None.</td>
+</tr>
+<tr>
+<td>Orientation</td>
+<td>/Dimension/ImageOrientation@value</td>
+</tr>
+<tr>
+<td>XPosition</td>
+<td>/Dimension/HorizontalPosition@value / 10</td>
+</tr>
+<tr>
+<td>YPosition</td>
+<td>/Dimension/VerticalPosition@value / 10</td>
+</tr>
+<tr>
+<td>NewSubFileType</td>
+<td>/Document/SubimageInterpretation@value: "TransparencyMask" =>
+transparency mask; "ReducedResolution" => reduced-resolution;
+"SinglePage" => single page.</td>
+</tr>
+<tr>
+<td>DateTime</td>
+<td>/Document/ImageCreationTime@value</td>
+</tr>
+<tr>
+<td>DocumentName, ImageDescription, Make, Model, PageName, Software,
+Artist, HostComputer, InkNames, Copyright</td>
+<td>/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.</td>
+</tr>
+<tr>
+<td>ExtraSamples</td>
+<td>/Transparency/Alpha@value: "premultiplied" => associated alpha, count 1;
+"nonpremultiplied" => unassociated alpha, count 1.</td>
+</tr>
+<tr>
+<td></td>
+<td></td>
+</tr>
+</table>
+</p>
+
+<h4><a name="ExifWrite"/>Writing Exif Images</h4>
+
+The TIFF writer may be used to write an uncompressed Exif image or the
+contents of the <tt>APP1</tt> marker segment of a compressed Exif image.
+
+<h5><a name="ExifWriteTIFF"/>Writing Uncompressed Exif Images</h5>
+
+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:
+
+<ol>
+<a name="ExifStructure"/>
+<li>Image File Header</li>
+<li>Primary IFD</li>
+<li>Primary IFD Value</li>
+<li>Thumbnail IFD</li>
+<li>Thumbnail IFD Value</li>
+<li>Thumbnail Image Data</li>
+<li>Primary Image Data</li>
+</ol>
+
+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:
+
+<pre><code>
+ ImageWriter tiffWriter;
+ ImageWriteParam tiffWriteParam;
+ IIOMetadata tiffStreamMetadata;
+ IIOMetadata primaryIFD;
+ BufferedImage image;
+ BufferedImage thumbnail;
+
+ // Specify uncompressed output.
+ tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
+
+ 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 image data.
+ tiffWriter.writeToSequence(new IIOImage(thumbnail, null, null),
+ tiffWriteParam);
+
+ // Insert the primary image data.
+ tiffWriter.prepareReplacePixels(0, new Rectangle(image.getWidth(),
+ image.getHeight()));
+ tiffWriter.replacePixels(image, tiffWriteParam);
+ tiffWriter.endReplacePixels();
+
+ // End writing.
+ tiffWriter.endWriteSequence();
+ } else {
+ // Write only the primary IFD and image data.
+ tiffWriter.write(tiffStreamMetadata,
+ new IIOImage(image, null, primaryIFD),
+ tiffWriteParam);
+ }
+</code></pre>
+
+<h5><a name="ExifWriteJPEG"/>Writing Compressed Exif Images</h5>
+
+The structure of the embedded TIFF stream in the <tt>APP1</tt> segment of a
+compressed Exif image is identical to the <a href="#ExifStructure">
+uncompressed Exif image structure</a> except that there are no primary
+image data, i.e., the primary IFD does not refer to any image data.
+
+<pre><code>
+ 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));
+</code></pre>
+
+The <code>"unknown"</code> node created above would be appended to the
+<code>"markerSequence"</code> node of the native JPEG image metadata
+and written to the JPEG stream when the primary image is written using
+the JPEG writer.
+
+<h3><a name="StreamMetadata"/>Stream Metadata</h3>
+
+The DTD for the TIFF native stream metadata format is as follows:
+
+<pre>
+<!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 -->
+]>
+</pre>
+
+<h3><a name="ImageMetadata"/>Image Metadata</h3>
+
+The DTD for the TIFF native image metadata format is as follows:
+
+<pre>
+<!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 -->
+]>
+</pre>
+
+@since 1.9
+
+</body>
+</html>
--- a/jdk/src/java.desktop/share/classes/javax/imageio/metadata/package.html Mon Nov 23 10:00:50 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/metadata/package.html Mon Nov 23 12:26:12 2015 -0800
@@ -2,7 +2,7 @@
<html>
<head>
<!--
-Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
+Copyright (c) 2000, 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
@@ -82,6 +82,11 @@
<ul>
<li>
+<A HREF="doc-files/bmp_metadata.html">
+BMP metadata
+</A>
+
+<li>
<A HREF="doc-files/gif_metadata.html">
GIF metadata
</A>
@@ -97,8 +102,8 @@
</A>
<li>
-<A HREF="doc-files/bmp_metadata.html">
-BMP metadata
+<A HREF="doc-files/tiff_metadata.html#StreamMetadata">
+TIFF metadata
</A>
<li>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/BaselineTIFFTagSet.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,2190 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class representing the set of tags found in the baseline TIFF
+ * specification as well as some common additional tags.
+ *
+ * <p> The non-baseline tags included in this class are:
+ * <ul>
+ * <li> {@link #TAG_JPEG_TABLES JPEGTables}
+ * <li> {@link #TAG_ICC_PROFILE ICC Profile}
+ * </ul>
+ *
+ * <p> The non-baseline values of baseline tags included in this class are
+ * <ul>
+ * <li>{@link #TAG_COMPRESSION Compression} tag values:
+ * <ul>
+ * <li>{@link #COMPRESSION_JPEG JPEG-in-TIFF compression}</li>
+ * <li>{@link #COMPRESSION_ZLIB Zlib-in-TIFF compression}</li>
+ * <li>{@link #COMPRESSION_DEFLATE Deflate compression}</li>
+ * </ul>
+ * </li>
+ * <li>{@link #TAG_PHOTOMETRIC_INTERPRETATION PhotometricInterpretation}
+ * tag values:
+ * <ul>
+ * <li>{@link #PHOTOMETRIC_INTERPRETATION_ICCLAB ICCLAB
+ * photometric interpretation}</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @since 1.9
+ * @see <a href="http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf"> TIFF 6.0 Specification</a>
+ */
+public class BaselineTIFFTagSet extends TIFFTagSet {
+
+ private static BaselineTIFFTagSet theInstance = null;
+
+ // Tags from TIFF 6.0 specification
+
+ /**
+ * Constant specifying the "NewSubfileType" tag.
+ *
+ * @see #NEW_SUBFILE_TYPE_REDUCED_RESOLUTION
+ * @see #NEW_SUBFILE_TYPE_SINGLE_PAGE
+ * @see #NEW_SUBFILE_TYPE_TRANSPARENCY
+ */
+ public static final int TAG_NEW_SUBFILE_TYPE = 254;
+
+ /**
+ * A mask to be used with the "NewSubfileType" tag.
+ *
+ * @see #TAG_NEW_SUBFILE_TYPE
+ */
+ public static final int NEW_SUBFILE_TYPE_REDUCED_RESOLUTION = 1;
+
+ /**
+ * A mask to be used with the "NewSubfileType" tag.
+ *
+ * @see #TAG_NEW_SUBFILE_TYPE
+ */
+ public static final int NEW_SUBFILE_TYPE_SINGLE_PAGE = 2;
+
+ /**
+ * A mask to be used with the "NewSubfileType" tag.
+ *
+ * @see #TAG_NEW_SUBFILE_TYPE
+ */
+ public static final int NEW_SUBFILE_TYPE_TRANSPARENCY = 4;
+
+ /**
+ * Constant specifying the "SubfileType" tag.
+ *
+ * @see #SUBFILE_TYPE_FULL_RESOLUTION
+ * @see #SUBFILE_TYPE_REDUCED_RESOLUTION
+ * @see #SUBFILE_TYPE_SINGLE_PAGE
+ */
+ public static final int TAG_SUBFILE_TYPE = 255;
+
+ /**
+ * A value to be used with the "SubfileType" tag.
+ *
+ * @see #TAG_SUBFILE_TYPE
+ */
+ public static final int SUBFILE_TYPE_FULL_RESOLUTION = 1;
+
+ /**
+ * A value to be used with the "SubfileType" tag.
+ *
+ * @see #TAG_SUBFILE_TYPE
+ */
+ public static final int SUBFILE_TYPE_REDUCED_RESOLUTION = 2;
+
+ /**
+ * A value to be used with the "SubfileType" tag.
+ *
+ * @see #TAG_SUBFILE_TYPE
+ */
+ public static final int SUBFILE_TYPE_SINGLE_PAGE = 3;
+
+ /**
+ * Constant specifying the "ImageWidth" tag.
+ */
+ public static final int TAG_IMAGE_WIDTH = 256;
+
+ /**
+ * Constant specifying the "ImageLength" tag.
+ */
+ public static final int TAG_IMAGE_LENGTH = 257;
+
+ /**
+ * Constant specifying the "BitsPerSample" tag.
+ */
+ public static final int TAG_BITS_PER_SAMPLE = 258;
+
+ /**
+ * Constant specifying the "Compression" tag.
+ *
+ * @see #COMPRESSION_NONE
+ * @see #COMPRESSION_CCITT_RLE
+ * @see #COMPRESSION_CCITT_T_4
+ * @see #COMPRESSION_CCITT_T_6
+ * @see #COMPRESSION_LZW
+ * @see #COMPRESSION_OLD_JPEG
+ * @see #COMPRESSION_JPEG
+ * @see #COMPRESSION_ZLIB
+ * @see #COMPRESSION_PACKBITS
+ * @see #COMPRESSION_DEFLATE
+ */
+ public static final int TAG_COMPRESSION = 259;
+
+ /**
+ * A value to be used with the "Compression" tag.
+ *
+ * @see #TAG_COMPRESSION
+ */
+ public static final int COMPRESSION_NONE = 1;
+
+ /**
+ * A value to be used with the "Compression" tag.
+ *
+ * @see #TAG_COMPRESSION
+ */
+ public static final int COMPRESSION_CCITT_RLE = 2;
+
+ /**
+ * A value to be used with the "Compression" tag.
+ *
+ * @see #TAG_COMPRESSION
+ */
+ public static final int COMPRESSION_CCITT_T_4 = 3;
+
+ /**
+ * A value to be used with the "Compression" tag.
+ *
+ * @see #TAG_COMPRESSION
+ */
+ public static final int COMPRESSION_CCITT_T_6 = 4;
+
+ /**
+ * A value to be used with the "Compression" tag.
+ *
+ * @see #TAG_COMPRESSION
+ */
+ public static final int COMPRESSION_LZW = 5;
+
+ /**
+ * A value to be used with the "Compression" tag.
+ *
+ * @see #TAG_COMPRESSION
+ */
+ public static final int COMPRESSION_OLD_JPEG = 6;
+
+ /**
+ * A value to be used with the "Compression" tag.
+ *
+ * @see #TAG_COMPRESSION
+ * @see <a href="http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf">TIFF Specification Supplement 2</a>
+ */
+ public static final int COMPRESSION_JPEG = 7;
+
+ /**
+ * A value to be used with the "Compression" tag.
+ *
+ * @see #TAG_COMPRESSION
+ * @see <a href="http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf"> TIFF Specification Supplement 2</a>
+ */
+ public static final int COMPRESSION_ZLIB = 8;
+
+ /**
+ * A value to be used with the "Compression" tag.
+ *
+ * @see #TAG_COMPRESSION
+ */
+ public static final int COMPRESSION_PACKBITS = 32773;
+
+ /**
+ * A value to be used with the "Compression" tag.
+ *
+ * @see #TAG_COMPRESSION
+ * @see <a href="http://www.isi.edu/in-notes/rfc1951.txt">DEFLATE specification</a>
+ * @see <a href="http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf"> TIFF Specification Supplement 2</a>
+ */
+ public static final int COMPRESSION_DEFLATE = 32946;
+
+ /**
+ * Constant specifying the "PhotometricInterpretation" tag.
+ *
+ * @see #PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO
+ * @see #PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO
+ * @see #PHOTOMETRIC_INTERPRETATION_RGB
+ * @see #PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR
+ * @see #PHOTOMETRIC_INTERPRETATION_TRANSPARENCY_MASK
+ * @see #PHOTOMETRIC_INTERPRETATION_Y_CB_CR
+ * @see #PHOTOMETRIC_INTERPRETATION_CIELAB
+ * @see #PHOTOMETRIC_INTERPRETATION_ICCLAB
+ */
+ public static final int TAG_PHOTOMETRIC_INTERPRETATION = 262;
+
+ /**
+ * A value to be used with the "PhotometricInterpretation" tag.
+ *
+ * @see #TAG_PHOTOMETRIC_INTERPRETATION
+ */
+ public static final int PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO = 0;
+
+ /**
+ * A value to be used with the "PhotometricInterpretation" tag.
+ *
+ * @see #TAG_PHOTOMETRIC_INTERPRETATION
+ */
+ public static final int PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO = 1;
+
+ /**
+ * A value to be used with the "PhotometricInterpretation" tag.
+ *
+ * @see #TAG_PHOTOMETRIC_INTERPRETATION
+ */
+ public static final int PHOTOMETRIC_INTERPRETATION_RGB = 2;
+
+ /**
+ * A value to be used with the "PhotometricInterpretation" tag.
+ *
+ * @see #TAG_PHOTOMETRIC_INTERPRETATION
+ */
+ public static final int PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR = 3;
+
+ /**
+ * A value to be used with the "PhotometricInterpretation" tag.
+ *
+ * @see #TAG_PHOTOMETRIC_INTERPRETATION
+ */
+ public static final int PHOTOMETRIC_INTERPRETATION_TRANSPARENCY_MASK = 4;
+
+ /**
+ * A value to be used with the "PhotometricInterpretation" tag.
+ *
+ * @see #TAG_PHOTOMETRIC_INTERPRETATION
+ */
+ public static final int PHOTOMETRIC_INTERPRETATION_CMYK = 5;
+
+ /**
+ * A value to be used with the "PhotometricInterpretation" tag.
+ *
+ * @see #TAG_PHOTOMETRIC_INTERPRETATION
+ */
+ public static final int PHOTOMETRIC_INTERPRETATION_Y_CB_CR = 6;
+
+ /**
+ * A value to be used with the "PhotometricInterpretation" tag.
+ *
+ * @see #TAG_PHOTOMETRIC_INTERPRETATION
+ */
+ public static final int PHOTOMETRIC_INTERPRETATION_CIELAB = 8;
+
+ /**
+ * A value to be used with the "PhotometricInterpretation" tag.
+ *
+ * @see #TAG_PHOTOMETRIC_INTERPRETATION
+ * @see <a href="http://partners.adobe.com/public/developer/en/tiff/TIFFPM6.pdf">TIFF Specification Supplement 1</a>
+ */
+ public static final int PHOTOMETRIC_INTERPRETATION_ICCLAB = 9;
+
+ /**
+ * Constant specifying the "Threshholding" tag.
+ *
+ * @see #THRESHHOLDING_NONE
+ * @see #THRESHHOLDING_ORDERED_DITHER
+ * @see #THRESHHOLDING_RANDOMIZED_DITHER
+ */
+ public static final int TAG_THRESHHOLDING = 263;
+
+ /**
+ * A value to be used with the "Thresholding" tag.
+ *
+ * @see #TAG_THRESHHOLDING
+ */
+ public static final int THRESHHOLDING_NONE = 1;
+
+ /**
+ * A value to be used with the "Thresholding" tag.
+ *
+ * @see #TAG_THRESHHOLDING
+ */
+ public static final int THRESHHOLDING_ORDERED_DITHER = 2;
+
+ /**
+ * A value to be used with the "Thresholding" tag.
+ *
+ * @see #TAG_THRESHHOLDING
+ */
+ public static final int THRESHHOLDING_RANDOMIZED_DITHER = 3;
+
+ /**
+ * Constant specifying the "Cell_Width" tag.
+ */
+ public static final int TAG_CELL_WIDTH = 264;
+
+ /**
+ * Constant specifying the "cell_length" tag.
+ */
+ public static final int TAG_CELL_LENGTH = 265;
+
+ /**
+ * Constant specifying the "fill_order" tag.
+ *
+ * @see #FILL_ORDER_LEFT_TO_RIGHT
+ * @see #FILL_ORDER_RIGHT_TO_LEFT
+ */
+ public static final int TAG_FILL_ORDER = 266;
+
+ /**
+ * A value to be used with the "FillOrder" tag.
+ *
+ * @see #TAG_FILL_ORDER
+ */
+ public static final int FILL_ORDER_LEFT_TO_RIGHT = 1;
+
+ /**
+ * A value to be used with the "FillOrder" tag.
+ *
+ * @see #TAG_FILL_ORDER
+ */
+ public static final int FILL_ORDER_RIGHT_TO_LEFT = 2;
+
+ /**
+ * Constant specifying the "document_name" tag.
+ */
+ public static final int TAG_DOCUMENT_NAME = 269;
+
+ /**
+ * Constant specifying the "Image_description" tag.
+ */
+ public static final int TAG_IMAGE_DESCRIPTION = 270;
+
+ /**
+ * Constant specifying the "Make" tag.
+ */
+ public static final int TAG_MAKE = 271;
+
+ /**
+ * Constant specifying the "Model" tag.
+ */
+ public static final int TAG_MODEL = 272;
+
+ /**
+ * Constant specifying the "Strip_offsets" tag.
+ */
+ public static final int TAG_STRIP_OFFSETS = 273;
+
+ /**
+ * Constant specifying the "Orientation" tag.
+ *
+ * @see #ORIENTATION_ROW_0_TOP_COLUMN_0_LEFT
+ * @see #ORIENTATION_ROW_0_TOP_COLUMN_0_RIGHT
+ * @see #ORIENTATION_ROW_0_BOTTOM_COLUMN_0_RIGHT
+ * @see #ORIENTATION_ROW_0_BOTTOM_COLUMN_0_LEFT
+ * @see #ORIENTATION_ROW_0_LEFT_COLUMN_0_TOP
+ * @see #ORIENTATION_ROW_0_RIGHT_COLUMN_0_TOP
+ * @see #ORIENTATION_ROW_0_RIGHT_COLUMN_0_BOTTOM
+ * @see #ORIENTATION_ROW_0_LEFT_COLUMN_0_BOTTOM
+ */
+ public static final int TAG_ORIENTATION = 274;
+
+ /**
+ * A value to be used with the "Orientation" tag.
+ *
+ * @see #TAG_ORIENTATION
+ */
+ public static final int ORIENTATION_ROW_0_TOP_COLUMN_0_LEFT = 1;
+
+ /**
+ * A value to be used with the "Orientation" tag.
+ *
+ * @see #TAG_ORIENTATION
+ */
+ public static final int ORIENTATION_ROW_0_TOP_COLUMN_0_RIGHT = 2;
+
+ /**
+ * A value to be used with the "Orientation" tag.
+ *
+ * @see #TAG_ORIENTATION
+ */
+ public static final int ORIENTATION_ROW_0_BOTTOM_COLUMN_0_RIGHT = 3;
+
+ /**
+ * A value to be used with the "Orientation" tag.
+ *
+ * @see #TAG_ORIENTATION
+ */
+ public static final int ORIENTATION_ROW_0_BOTTOM_COLUMN_0_LEFT = 4;
+
+ /**
+ * A value to be used with the "Orientation" tag.
+ *
+ * @see #TAG_ORIENTATION
+ */
+ public static final int ORIENTATION_ROW_0_LEFT_COLUMN_0_TOP = 5;
+
+ /**
+ * A value to be used with the "Orientation" tag.
+ *
+ * @see #TAG_ORIENTATION
+ */
+ public static final int ORIENTATION_ROW_0_RIGHT_COLUMN_0_TOP = 6;
+
+ /**
+ * A value to be used with the "Orientation" tag.
+ *
+ * @see #TAG_ORIENTATION
+ */
+ public static final int ORIENTATION_ROW_0_RIGHT_COLUMN_0_BOTTOM = 7;
+
+ /**
+ * A value to be used with the "Orientation" tag.
+ *
+ * @see #TAG_ORIENTATION
+ */
+ public static final int ORIENTATION_ROW_0_LEFT_COLUMN_0_BOTTOM = 8;
+
+ /**
+ * Constant specifying the "Samples_per_pixel" tag.
+ */
+ public static final int TAG_SAMPLES_PER_PIXEL = 277;
+
+ /**
+ * Constant specifying the "Rows_per_strip" tag.
+ */
+ public static final int TAG_ROWS_PER_STRIP = 278;
+
+ /**
+ * Constant specifying the "Strip_byte_counts" tag.
+ */
+ public static final int TAG_STRIP_BYTE_COUNTS = 279;
+
+ /**
+ * Constant specifying the "Min_sample_value" tag.
+ */
+ public static final int TAG_MIN_SAMPLE_VALUE = 280;
+
+ /**
+ * Constant specifying the "Max_sample_value" tag.
+ */
+ public static final int TAG_MAX_SAMPLE_VALUE = 281;
+
+ /**
+ * Constant specifying the "XResolution" tag.
+ */
+ public static final int TAG_X_RESOLUTION = 282;
+
+ /**
+ * Constant specifying the "YResolution" tag.
+ */
+ public static final int TAG_Y_RESOLUTION = 283;
+
+ /**
+ * Constant specifying the "PlanarConfiguration" tag.
+ *
+ * @see #PLANAR_CONFIGURATION_CHUNKY
+ * @see #PLANAR_CONFIGURATION_PLANAR
+ */
+ public static final int TAG_PLANAR_CONFIGURATION = 284;
+
+ /**
+ * A value to be used with the "PlanarConfiguration" tag.
+ *
+ * @see #TAG_PLANAR_CONFIGURATION
+ */
+ public static final int PLANAR_CONFIGURATION_CHUNKY = 1;
+
+ /**
+ * A value to be used with the "PlanarConfiguration" tag.
+ *
+ * @see #TAG_PLANAR_CONFIGURATION
+ */
+ public static final int PLANAR_CONFIGURATION_PLANAR = 2;
+
+ /**
+ * Constant specifying the "PageName" tag.
+ */
+ public static final int TAG_PAGE_NAME = 285;
+
+ /**
+ * Constant specifying the "XPosition" tag.
+ */
+ public static final int TAG_X_POSITION = 286;
+
+ /**
+ * Constant specifying the "YPosition" tag.
+ */
+ public static final int TAG_Y_POSITION = 287;
+
+ /**
+ * Constant specifying the "FreeOffsets" tag.
+ */
+ public static final int TAG_FREE_OFFSETS = 288;
+
+ /**
+ * Constant specifying the "FreeByteCounts" tag.
+ */
+ public static final int TAG_FREE_BYTE_COUNTS = 289;
+
+ /**
+ * Constant specifying the "GrayResponseUnit" tag.
+ *
+ * @see #GRAY_RESPONSE_UNIT_TENTHS
+ * @see #GRAY_RESPONSE_UNIT_HUNDREDTHS
+ * @see #GRAY_RESPONSE_UNIT_THOUSANDTHS
+ * @see #GRAY_RESPONSE_UNIT_TEN_THOUSANDTHS
+ * @see #GRAY_RESPONSE_UNIT_HUNDRED_THOUSANDTHS
+ */
+ public static final int TAG_GRAY_RESPONSE_UNIT = 290;
+
+ /**
+ * A value to be used with the "GrayResponseUnit" tag.
+ *
+ * @see #TAG_GRAY_RESPONSE_UNIT
+ */
+ public static final int GRAY_RESPONSE_UNIT_TENTHS = 1;
+
+ /**
+ * A value to be used with the "GrayResponseUnit" tag.
+ *
+ * @see #TAG_GRAY_RESPONSE_UNIT
+ */
+ public static final int GRAY_RESPONSE_UNIT_HUNDREDTHS = 2;
+
+ /**
+ * A value to be used with the "GrayResponseUnit" tag.
+ *
+ * @see #TAG_GRAY_RESPONSE_UNIT
+ */
+ public static final int GRAY_RESPONSE_UNIT_THOUSANDTHS = 3;
+
+ /**
+ * A value to be used with the "GrayResponseUnit" tag.
+ *
+ * @see #TAG_GRAY_RESPONSE_UNIT
+ */
+ public static final int GRAY_RESPONSE_UNIT_TEN_THOUSANDTHS = 4;
+
+ /**
+ * A value to be used with the "GrayResponseUnit" tag.
+ *
+ * @see #TAG_GRAY_RESPONSE_UNIT
+ */
+ public static final int GRAY_RESPONSE_UNIT_HUNDRED_THOUSANDTHS = 5;
+
+ /**
+ * Constant specifying the "GrayResponseCurve" tag.
+ */
+ public static final int TAG_GRAY_RESPONSE_CURVE = 291;
+
+ /**
+ * Constant specifying the "T4Options" tag.
+ *
+ * @see #T4_OPTIONS_2D_CODING
+ * @see #T4_OPTIONS_UNCOMPRESSED
+ * @see #T4_OPTIONS_EOL_BYTE_ALIGNED
+ */
+ public static final int TAG_T4_OPTIONS = 292;
+
+ /**
+ * A mask to be used with the "T4Options" tag.
+ *
+ * @see #TAG_T4_OPTIONS
+ */
+ public static final int T4_OPTIONS_2D_CODING = 1;
+
+ /**
+ * A mask to be used with the "T4Options" tag.
+ *
+ * @see #TAG_T4_OPTIONS
+ */
+ public static final int T4_OPTIONS_UNCOMPRESSED = 2;
+
+ /**
+ * A mask to be used with the "T4Options" tag.
+ *
+ * @see #TAG_T4_OPTIONS
+ */
+ public static final int T4_OPTIONS_EOL_BYTE_ALIGNED = 4;
+
+ /**
+ * Constant specifying the "T6Options" tag.
+ *
+ * @see #T6_OPTIONS_UNCOMPRESSED
+ */
+ public static final int TAG_T6_OPTIONS = 293;
+
+ /**
+ * A mask to be used with the "T6Options" tag.
+ *
+ * @see #TAG_T6_OPTIONS
+ */
+ public static final int T6_OPTIONS_UNCOMPRESSED = 2;
+
+ /**
+ * Constant specifying the "ResolutionUnit" tag.
+ *
+ * @see #RESOLUTION_UNIT_NONE
+ * @see #RESOLUTION_UNIT_INCH
+ * @see #RESOLUTION_UNIT_CENTIMETER
+ */
+ public static final int TAG_RESOLUTION_UNIT = 296;
+
+ /**
+ * A value to be used with the "ResolutionUnit" tag.
+ *
+ * @see #TAG_RESOLUTION_UNIT
+ */
+ public static final int RESOLUTION_UNIT_NONE = 1;
+
+ /**
+ * A value to be used with the "ResolutionUnit" tag.
+ *
+ * @see #TAG_RESOLUTION_UNIT
+ */
+ public static final int RESOLUTION_UNIT_INCH = 2;
+
+ /**
+ * A value to be used with the "ResolutionUnit" tag.
+ *
+ * @see #TAG_RESOLUTION_UNIT
+ */
+ public static final int RESOLUTION_UNIT_CENTIMETER = 3;
+
+
+ /**
+ * Constant specifying the "PageNumber" tag.
+ */
+ public static final int TAG_PAGE_NUMBER = 297;
+
+ /**
+ * Constant specifying the "TransferFunction" tag.
+ */
+ public static final int TAG_TRANSFER_FUNCTION = 301;
+
+ /**
+ * Constant specifying the "Software" tag.
+ */
+ public static final int TAG_SOFTWARE = 305;
+
+ /**
+ * Constant specifying the "DateTime" tag.
+ */
+ public static final int TAG_DATE_TIME = 306;
+
+ /**
+ * Constant specifying the "Artist" tag.
+ */
+ public static final int TAG_ARTIST = 315;
+
+ /**
+ * Constant specifying the "HostComputer" tag.
+ */
+ public static final int TAG_HOST_COMPUTER = 316;
+
+ /**
+ * Constant specifying the "Predictor" tag.
+ *
+ * @see #TAG_WHITE_POINT
+ * @see #TAG_PRIMARY_CHROMATICITES
+ * @see #TAG_COLOR_MAP
+ * @see #TAG_HALFTONE_HINTS
+ * @see #TAG_TILE_WIDTH
+ * @see #TAG_TILE_LENGTH
+ * @see #TAG_TILE_OFFSETS
+ * @see #TAG_TILE_BYTE_COUNTS
+ */
+ public static final int TAG_PREDICTOR = 317;
+
+ /**
+ * A value to be used with the "Predictor" tag.
+ *
+ * @see #TAG_PREDICTOR
+ */
+ public static final int PREDICTOR_NONE = 1;
+
+ /**
+ * A value to be used with the "Predictor" tag.
+ *
+ * @see #TAG_PREDICTOR
+ */
+ public static final int PREDICTOR_HORIZONTAL_DIFFERENCING = 2;
+
+ /**
+ * Constant specifying the "WhitePoint" tag.
+ */
+ public static final int TAG_WHITE_POINT = 318;
+
+ /**
+ * Constant specifying the "PrimaryChromaticites" tag.
+ */
+ public static final int TAG_PRIMARY_CHROMATICITES = 319;
+
+ /**
+ * Constant specifying the "ColorMap" tag.
+ */
+ public static final int TAG_COLOR_MAP = 320;
+
+ /**
+ * Constant specifying the "HalftoneHints" tag.
+ */
+ public static final int TAG_HALFTONE_HINTS = 321;
+
+ /**
+ * Constant specifying the "TileWidth" tag.
+ */
+ public static final int TAG_TILE_WIDTH = 322;
+
+ /**
+ * Constant specifying the "TileLength" tag.
+ */
+ public static final int TAG_TILE_LENGTH = 323;
+
+ /**
+ * Constant specifying the "TileOffsets" tag.
+ */
+ public static final int TAG_TILE_OFFSETS = 324;
+
+ /**
+ * Constant specifying the "TileByteCounts" tag.
+ */
+ public static final int TAG_TILE_BYTE_COUNTS = 325;
+
+ /**
+ * Constant specifying the "InkSet" tag.
+ *
+ * @see #INK_SET_CMYK
+ * @see #INK_SET_NOT_CMYK
+ */
+ public static final int TAG_INK_SET = 332;
+
+ /**
+ * A value to be used with the "InkSet" tag.
+ *
+ * @see #TAG_INK_SET
+ */
+ public static final int INK_SET_CMYK = 1;
+
+ /**
+ * A value to be used with the "InkSet" tag.
+ *
+ * @see #TAG_INK_SET
+ */
+ public static final int INK_SET_NOT_CMYK = 2;
+
+ /**
+ * Constant specifying the "InkNames" tag.
+ */
+ public static final int TAG_INK_NAMES = 333;
+
+ /**
+ * Constant specifying the "NumberOfInks" tag.
+ */
+ public static final int TAG_NUMBER_OF_INKS = 334;
+
+ /**
+ * Constant specifying the "DotRange" tag.
+ */
+ public static final int TAG_DOT_RANGE = 336;
+
+ /**
+ * Constant specifying the "TargetPrinter" tag.
+ */
+ public static final int TAG_TARGET_PRINTER = 337;
+
+ /**
+ * Constant specifying the "ExtraSamples" tag.
+ *
+ * @see #EXTRA_SAMPLES_UNSPECIFIED
+ * @see #EXTRA_SAMPLES_ASSOCIATED_ALPHA
+ * @see #EXTRA_SAMPLES_UNASSOCIATED_ALPHA
+ */
+ public static final int TAG_EXTRA_SAMPLES = 338;
+
+ /**
+ * A value to be used with the "ExtraSamples" tag.
+ *
+ * @see #TAG_EXTRA_SAMPLES
+ */
+ public static final int EXTRA_SAMPLES_UNSPECIFIED = 0;
+
+ /**
+ * A value to be used with the "ExtraSamples" tag.
+ *
+ * @see #TAG_EXTRA_SAMPLES
+ */
+ public static final int EXTRA_SAMPLES_ASSOCIATED_ALPHA = 1;
+
+ /**
+ * A value to be used with the "ExtraSamples" tag.
+ *
+ * @see #TAG_EXTRA_SAMPLES
+ */
+ public static final int EXTRA_SAMPLES_UNASSOCIATED_ALPHA = 2;
+
+ /**
+ * Constant specifying the "SampleFormat" tag.
+ *
+ * @see #SAMPLE_FORMAT_UNSIGNED_INTEGER
+ * @see #SAMPLE_FORMAT_SIGNED_INTEGER
+ * @see #SAMPLE_FORMAT_FLOATING_POINT
+ * @see #SAMPLE_FORMAT_UNDEFINED
+ */
+ public static final int TAG_SAMPLE_FORMAT = 339;
+
+ /**
+ * A value to be used with the "SampleFormat" tag.
+ *
+ * @see #TAG_SAMPLE_FORMAT
+ */
+ public static final int SAMPLE_FORMAT_UNSIGNED_INTEGER = 1;
+
+ /**
+ * A value to be used with the "SampleFormat" tag.
+ *
+ * @see #TAG_SAMPLE_FORMAT
+ */
+ public static final int SAMPLE_FORMAT_SIGNED_INTEGER = 2;
+
+ /**
+ * A value to be used with the "SampleFormat" tag.
+ *
+ * @see #TAG_SAMPLE_FORMAT
+ */
+ public static final int SAMPLE_FORMAT_FLOATING_POINT = 3;
+
+ /**
+ * A value to be used with the "SampleFormat" tag.
+ *
+ * @see #TAG_SAMPLE_FORMAT
+ */
+ public static final int SAMPLE_FORMAT_UNDEFINED = 4;
+
+ /**
+ * Constant specifying the "SMinSampleValue" tag.
+ */
+ public static final int TAG_S_MIN_SAMPLE_VALUE = 340;
+
+ /**
+ * Constant specifying the "SMaxSampleValue" tag.
+ */
+ public static final int TAG_S_MAX_SAMPLE_VALUE = 341;
+
+ /**
+ * Constant specifying the "TransferRange" tag.
+ */
+ public static final int TAG_TRANSFER_RANGE = 342;
+
+ /**
+ * Constant specifying the "JPEGTables" tag.
+ *
+ * @see <a href="http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf">TIFF Specification Supplement 2</a>
+ * @see <a href="ftp://ftp.sgi.com/graphics/tiff/TTN2.draft.txt">JPEG-in-TIFF compression</a>
+ */
+ public static final int TAG_JPEG_TABLES = 347;
+
+ /**
+ * Constant specifying the "JPEGProc" tag.
+ */
+ public static final int TAG_JPEG_PROC = 512;
+
+ /**
+ * A value to be used with the "JPEGProc" tag.
+ *
+ * @see #TAG_JPEG_PROC
+ */
+ public static final int JPEG_PROC_BASELINE = 1;
+
+ /**
+ * A value to be used with the "JPEGProc" tag.
+ *
+ * @see #TAG_JPEG_PROC
+ */
+ public static final int JPEG_PROC_LOSSLESS = 14;
+
+ /**
+ * Constant specifying the "JPEGInterchangeFormat" tag.
+ */
+ public static final int TAG_JPEG_INTERCHANGE_FORMAT = 513;
+
+ /**
+ * Constant specifying the "JPEGInterchangeFormatLength" tag.
+ */
+ public static final int TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = 514;
+
+ /**
+ * Constant specifying the "JPEGRestartInterval" tag.
+ */
+ public static final int TAG_JPEG_RESTART_INTERVAL = 515;
+
+ /**
+ * Constant specifying the "JPEGLosslessPredictors" tag.
+ */
+ public static final int TAG_JPEG_LOSSLESS_PREDICTORS = 517;
+
+ /**
+ * Constant specifying the "JPEGPointTransforms" tag.
+ */
+ public static final int TAG_JPEG_POINT_TRANSFORMS = 518;
+
+ /**
+ * Constant specifying the "JPEGQTables" tag.
+ */
+ public static final int TAG_JPEG_Q_TABLES = 519;
+
+ /**
+ * Constant specifying the "JPEGDCTables" tag.
+ */
+ public static final int TAG_JPEG_DC_TABLES = 520;
+
+ /**
+ * Constant specifying the "JPEGACTables" tag.
+ */
+ public static final int TAG_JPEG_AC_TABLES = 521;
+
+ /**
+ * Constant specifying the "YCbCrCoefficients" tag.
+ */
+ public static final int TAG_Y_CB_CR_COEFFICIENTS = 529;
+
+ /**
+ * Constant specifying the "YCbCrSubsampling" tag.
+ */
+ public static final int TAG_Y_CB_CR_SUBSAMPLING = 530;
+
+ /**
+ * Constant specifying the "YCbCrPositioning" tag.
+ *
+ * @see #Y_CB_CR_POSITIONING_CENTERED
+ * @see #Y_CB_CR_POSITIONING_COSITED
+ */
+ public static final int TAG_Y_CB_CR_POSITIONING = 531;
+
+ /**
+ * A value to be used with the "YCbCrPositioning" tag.
+ *
+ * @see #TAG_Y_CB_CR_POSITIONING
+ */
+ public static final int Y_CB_CR_POSITIONING_CENTERED = 1;
+
+ /**
+ * A value to be used with the "YCbCrPositioning" tag.
+ *
+ * @see #TAG_Y_CB_CR_POSITIONING
+ */
+ public static final int Y_CB_CR_POSITIONING_COSITED = 2;
+
+ /**
+ * Constant specifying the "ReferenceBlackWhite" tag.
+ */
+ public static final int TAG_REFERENCE_BLACK_WHITE = 532;
+
+ /**
+ * Constant specifying the "Copyright" tag.
+ */
+ public static final int TAG_COPYRIGHT = 33432;
+
+ // Common non-baseline tags
+
+ // ICC profiles (Spec ICC 1:2001-04, Appendix B)
+
+ // 34675 - Embedded ICC Profile (UNDEFINED/any)
+
+ /**
+ * Constant specifying the "ICC Profile" tag.
+ *
+ * @see <a href="http://www.color.org/ICC1V42.pdf">ICC Specification, section B.4: Embedding ICC profiles in TIFF files</a>
+ */
+ public static final int TAG_ICC_PROFILE = 34675;
+
+ // Artist
+
+ static class Artist extends TIFFTag {
+
+ public Artist() {
+ super("Artist",
+ TAG_ARTIST,
+ 1 << TIFF_ASCII);
+ }
+ }
+
+ // BitsPerSample
+
+ static class BitsPerSample extends TIFFTag {
+
+ public BitsPerSample() {
+ super("BitsPerSample",
+ TAG_BITS_PER_SAMPLE,
+ 1 << TIFF_SHORT);
+ }
+ }
+
+ // CellLength
+
+ static class CellLength extends TIFFTag {
+
+ public CellLength() {
+ super("CellLength",
+ TAG_CELL_LENGTH,
+ 1 << TIFF_SHORT,
+ 1);
+ }
+ }
+
+ // CellWidth tag
+
+ static class CellWidth extends TIFFTag {
+
+ public CellWidth() {
+ super("CellWidth",
+ TAG_CELL_WIDTH,
+ 1 << TIFF_SHORT,
+ 1);
+ }
+ }
+
+ // ColorMap
+
+ static class ColorMap extends TIFFTag {
+
+ public ColorMap() {
+ super("ColorMap",
+ TAG_COLOR_MAP,
+ 1 << TIFF_SHORT);
+ }
+ }
+
+ // Compression
+
+ static class Compression extends TIFFTag {
+
+ public Compression() {
+ super("Compression",
+ TAG_COMPRESSION,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(COMPRESSION_NONE, "Uncompressed");
+ addValueName(COMPRESSION_CCITT_RLE, "CCITT RLE");
+ addValueName(COMPRESSION_CCITT_T_4, "CCITT T.4");
+ addValueName(COMPRESSION_CCITT_T_6, "CCITT T.6");
+ addValueName(COMPRESSION_LZW, "LZW");
+ addValueName(COMPRESSION_OLD_JPEG, "Old JPEG");
+ addValueName(COMPRESSION_JPEG, "JPEG");
+ addValueName(COMPRESSION_ZLIB, "ZLib");
+ addValueName(COMPRESSION_PACKBITS, "PackBits");
+ addValueName(COMPRESSION_DEFLATE, "Deflate"); // Non-baseline
+
+ // 32771 CCITT
+ // 32809 ThunderScan
+ // 32766 NeXT
+ // 32909 Pixar
+ // 34676 SGI
+ // 34677 SGI
+ }
+ }
+
+ // Copyright
+
+ static class Copyright extends TIFFTag {
+
+ public Copyright() {
+ super("Copyright",
+ TAG_COPYRIGHT,
+ 1 << TIFF_ASCII);
+ }
+ }
+
+ // DateTime
+
+ static class DateTime extends TIFFTag {
+
+ public DateTime() {
+ super("DateTime",
+ TAG_DATE_TIME,
+ 1 << TIFF_ASCII,
+ 20);
+ }
+ }
+
+ // DocumentName
+
+ static class DocumentName extends TIFFTag {
+
+ public DocumentName() {
+ super("DocumentName",
+ TAG_DOCUMENT_NAME,
+ 1 << TIFF_ASCII);
+ }
+ }
+
+ // DotRange
+
+ static class DotRange extends TIFFTag {
+
+ public DotRange() {
+ super("DotRange",
+ TAG_DOT_RANGE,
+ (1 << TIFF_BYTE) |
+ (1 << TIFF_SHORT));
+ }
+ }
+
+ // ExtraSamples
+
+ static class ExtraSamples extends TIFFTag {
+
+ public ExtraSamples() {
+ super("ExtraSamples",
+ TAG_EXTRA_SAMPLES,
+ 1 << TIFF_SHORT);
+
+ addValueName(EXTRA_SAMPLES_UNSPECIFIED,
+ "Unspecified");
+ addValueName(EXTRA_SAMPLES_ASSOCIATED_ALPHA,
+ "Associated Alpha");
+ addValueName(EXTRA_SAMPLES_UNASSOCIATED_ALPHA,
+ "Unassociated Alpha");
+ }
+ }
+
+ // FillOrder
+
+ static class FillOrder extends TIFFTag {
+
+ public FillOrder() {
+ super("FillOrder",
+ TAG_FILL_ORDER,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(FILL_ORDER_LEFT_TO_RIGHT, "LeftToRight");
+ addValueName(FILL_ORDER_RIGHT_TO_LEFT, "RightToLeft");
+ }
+ }
+
+ // FreeByteCounts
+
+ static class FreeByteCounts extends TIFFTag {
+
+ public FreeByteCounts() {
+ super("FreeByteCounts",
+ TAG_FREE_BYTE_COUNTS,
+ 1 << TIFF_LONG);
+ }
+ }
+
+ // FreeOffsets
+
+ static class FreeOffsets extends TIFFTag {
+
+ public FreeOffsets() {
+ super("FreeOffsets",
+ TAG_FREE_OFFSETS,
+ 1 << TIFF_LONG);
+ }
+ }
+
+ // GrayResponseCurve
+
+ static class GrayResponseCurve extends TIFFTag {
+
+ public GrayResponseCurve() {
+ super("GrayResponseCurve",
+ TAG_GRAY_RESPONSE_CURVE,
+ 1 << TIFF_SHORT);
+ }
+ }
+
+ // GrayResponseUnit
+
+ static class GrayResponseUnit extends TIFFTag {
+
+ public GrayResponseUnit() {
+ super("GrayResponseUnit",
+ TAG_GRAY_RESPONSE_UNIT,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(GRAY_RESPONSE_UNIT_TENTHS,
+ "Tenths");
+ addValueName(GRAY_RESPONSE_UNIT_HUNDREDTHS,
+ "Hundredths");
+ addValueName(GRAY_RESPONSE_UNIT_THOUSANDTHS,
+ "Thousandths");
+ addValueName(GRAY_RESPONSE_UNIT_TEN_THOUSANDTHS,
+ "Ten-Thousandths");
+ addValueName(GRAY_RESPONSE_UNIT_HUNDRED_THOUSANDTHS,
+ "Hundred-Thousandths");
+ }
+ }
+
+ // HalftoneHints
+
+ static class HalftoneHints extends TIFFTag {
+
+ public HalftoneHints() {
+ super("HalftoneHints",
+ TAG_HALFTONE_HINTS,
+ 1 << TIFF_SHORT,
+ 2);
+ }
+ }
+
+ // HostComputer
+
+ static class HostComputer extends TIFFTag {
+
+ public HostComputer() {
+ super("HostComputer",
+ TAG_HOST_COMPUTER,
+ 1 << TIFF_ASCII);
+ }
+ }
+
+ // ImageDescription
+
+ static class ImageDescription extends TIFFTag {
+
+ public ImageDescription() {
+ super("ImageDescription",
+ TAG_IMAGE_DESCRIPTION,
+ 1 << TIFF_ASCII);
+ }
+ }
+
+ // ImageLength tag
+
+ static class ImageLength extends TIFFTag {
+
+ public ImageLength() {
+ super("ImageLength",
+ TAG_IMAGE_LENGTH,
+ (1 << TIFF_SHORT) |
+ (1 << TIFF_LONG),
+ 1);
+ }
+ }
+
+ // ImageWidth tag
+
+ static class ImageWidth extends TIFFTag {
+
+ public ImageWidth() {
+ super("ImageWidth",
+ TAG_IMAGE_WIDTH,
+ (1 << TIFF_SHORT) |
+ (1 << TIFF_LONG),
+ 1);
+ }
+ }
+
+ // InkNames
+
+ static class InkNames extends TIFFTag {
+
+ public InkNames() {
+ super("InkNames",
+ TAG_INK_NAMES,
+ 1 << TIFF_ASCII);
+ }
+ }
+
+ // InkSet
+
+ static class InkSet extends TIFFTag {
+
+ public InkSet() {
+ super("InkSet",
+ TAG_INK_SET,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(INK_SET_CMYK, "CMYK");
+ addValueName(INK_SET_NOT_CMYK, "Not CMYK");
+ }
+ }
+
+ // JPEGTables (Tech note)
+
+ static class JPEGTables extends TIFFTag {
+
+ public JPEGTables() {
+ super("JPEGTables",
+ TAG_JPEG_TABLES,
+ 1 << TIFF_UNDEFINED);
+ }
+ }
+
+ // JPEGACTables
+
+ static class JPEGACTables extends TIFFTag {
+
+ public JPEGACTables() {
+ super("JPEGACTables",
+ TAG_JPEG_AC_TABLES,
+ 1 << TIFF_LONG);
+ }
+ }
+
+ // JPEGDCTables
+
+ static class JPEGDCTables extends TIFFTag {
+
+ public JPEGDCTables() {
+ super("JPEGDCTables",
+ TAG_JPEG_DC_TABLES,
+ 1 << TIFF_LONG);
+ }
+ }
+
+ // JPEGInterchangeFormat
+
+ static class JPEGInterchangeFormat extends TIFFTag {
+
+ public JPEGInterchangeFormat() {
+ super("JPEGInterchangeFormat",
+ TAG_JPEG_INTERCHANGE_FORMAT,
+ 1 << TIFF_LONG,
+ 1);
+ }
+ }
+
+ // JPEGInterchangeFormatLength
+
+ static class JPEGInterchangeFormatLength extends TIFFTag {
+
+ public JPEGInterchangeFormatLength() {
+ super("JPEGInterchangeFormatLength",
+ TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
+ 1 << TIFF_LONG,
+ 1);
+ }
+ }
+
+ // JPEGLosslessPredictors
+
+ static class JPEGLosslessPredictors extends TIFFTag {
+
+ public JPEGLosslessPredictors() {
+ super("JPEGLosslessPredictors",
+ TAG_JPEG_LOSSLESS_PREDICTORS,
+ 1 << TIFF_SHORT);
+
+ addValueName(1, "A");
+ addValueName(2, "B");
+ addValueName(3, "C");
+ addValueName(4, "A+B-C");
+ addValueName(5, "A+((B-C)/2)");
+ addValueName(6, "B+((A-C)/2)");
+ addValueName(7, "(A+B)/2");
+ }
+ }
+
+ // JPEGPointTransforms
+
+ static class JPEGPointTransforms extends TIFFTag {
+
+ public JPEGPointTransforms() {
+ super("JPEGPointTransforms",
+ TAG_JPEG_POINT_TRANSFORMS,
+ 1 << TIFF_SHORT);
+ }
+ }
+
+ // JPEGProc
+
+ static class JPEGProc extends TIFFTag {
+
+ public JPEGProc() {
+ super("JPEGProc",
+ TAG_JPEG_PROC,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(JPEG_PROC_BASELINE, "Baseline sequential process");
+ addValueName(JPEG_PROC_LOSSLESS,
+ "Lossless process with Huffman coding");
+ }
+ }
+
+ // JPEGQTables
+
+ static class JPEGQTables extends TIFFTag {
+
+ public JPEGQTables() {
+ super("JPEGQTables",
+ TAG_JPEG_Q_TABLES,
+ 1 << TIFF_LONG);
+ }
+ }
+
+ // JPEGRestartInterval
+
+ static class JPEGRestartInterval extends TIFFTag {
+
+ public JPEGRestartInterval() {
+ super("JPEGRestartInterval",
+ TAG_JPEG_RESTART_INTERVAL,
+ 1 << TIFF_SHORT,
+ 1);
+ }
+ }
+
+ // Make
+
+ static class Make extends TIFFTag {
+
+ public Make() {
+ super("Make",
+ TAG_MAKE,
+ 1 << TIFF_ASCII);
+ }
+ }
+
+ // MaxSampleValue
+
+ static class MaxSampleValue extends TIFFTag {
+
+ public MaxSampleValue() {
+ super("MaxSampleValue",
+ TAG_MAX_SAMPLE_VALUE,
+ 1 << TIFF_SHORT);
+ }
+ }
+
+ // MinSampleValue
+
+ static class MinSampleValue extends TIFFTag {
+
+ public MinSampleValue() {
+ super("MinSampleValue",
+ TAG_MIN_SAMPLE_VALUE,
+ 1 << TIFF_SHORT);
+ }
+ }
+
+ // Model
+
+ static class Model extends TIFFTag {
+
+ public Model() {
+ super("Model",
+ TAG_MODEL,
+ 1 << TIFF_ASCII);
+ }
+ }
+
+ // NewSubfileType
+
+ static class NewSubfileType extends TIFFTag {
+
+ public NewSubfileType() {
+ super("NewSubfileType",
+ TAG_NEW_SUBFILE_TYPE,
+ 1 << TIFF_LONG,
+ 1);
+
+ addValueName(0,
+ "Default");
+ addValueName(NEW_SUBFILE_TYPE_REDUCED_RESOLUTION,
+ "ReducedResolution");
+ addValueName(NEW_SUBFILE_TYPE_SINGLE_PAGE,
+ "SinglePage");
+ addValueName(NEW_SUBFILE_TYPE_SINGLE_PAGE |
+ NEW_SUBFILE_TYPE_REDUCED_RESOLUTION,
+ "SinglePage+ReducedResolution");
+ addValueName(NEW_SUBFILE_TYPE_TRANSPARENCY,
+ "Transparency");
+ addValueName(NEW_SUBFILE_TYPE_TRANSPARENCY |
+ NEW_SUBFILE_TYPE_REDUCED_RESOLUTION,
+ "Transparency+ReducedResolution");
+ addValueName(NEW_SUBFILE_TYPE_TRANSPARENCY |
+ NEW_SUBFILE_TYPE_SINGLE_PAGE,
+ "Transparency+SinglePage");
+ addValueName(NEW_SUBFILE_TYPE_TRANSPARENCY |
+ NEW_SUBFILE_TYPE_SINGLE_PAGE |
+ NEW_SUBFILE_TYPE_REDUCED_RESOLUTION,
+ "Transparency+SinglePage+ReducedResolution");
+ }
+ }
+
+ // NumberOfInks
+
+ static class NumberOfInks extends TIFFTag {
+
+ public NumberOfInks() {
+ super("NumberOfInks",
+ TAG_NUMBER_OF_INKS,
+ 1 << TIFF_SHORT,
+ 1);
+ }
+ }
+
+ // Orientation
+
+ static class Orientation extends TIFFTag {
+
+ public Orientation() {
+ super("Orientation",
+ TAG_ORIENTATION,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(ORIENTATION_ROW_0_TOP_COLUMN_0_LEFT,
+ "Row 0=Top, Column 0=Left");
+ addValueName(ORIENTATION_ROW_0_TOP_COLUMN_0_RIGHT,
+ "Row 0=Top, Column 0=Right");
+ addValueName(ORIENTATION_ROW_0_BOTTOM_COLUMN_0_RIGHT,
+ "Row 0=Bottom, Column 0=Right");
+ addValueName(ORIENTATION_ROW_0_BOTTOM_COLUMN_0_LEFT,
+ "Row 0=Bottom, Column 0=Left");
+ addValueName(ORIENTATION_ROW_0_LEFT_COLUMN_0_TOP,
+ "Row 0=Left, Column 0=Top");
+ addValueName(ORIENTATION_ROW_0_RIGHT_COLUMN_0_TOP,
+ "Row 0=Right, Column 0=Top");
+ addValueName(ORIENTATION_ROW_0_RIGHT_COLUMN_0_BOTTOM,
+ "Row 0=Right, Column 0=Bottom");
+ }
+ }
+
+ // PageName
+
+ static class PageName extends TIFFTag {
+
+ public PageName() {
+ super("PageName",
+ TAG_PAGE_NAME,
+ 1 << TIFF_ASCII);
+ }
+ }
+
+ // PageNumber
+
+ static class PageNumber extends TIFFTag {
+
+ public PageNumber() {
+ super("PageNumber",
+ TAG_PAGE_NUMBER,
+ 1 << TIFF_SHORT);
+ }
+ }
+
+ // PhotometricInterpretation
+
+ static class PhotometricInterpretation extends TIFFTag {
+
+ public PhotometricInterpretation() {
+ super("PhotometricInterpretation",
+ TAG_PHOTOMETRIC_INTERPRETATION,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO,
+ "WhiteIsZero");
+ addValueName(PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO,
+ "BlackIsZero");
+ addValueName(PHOTOMETRIC_INTERPRETATION_RGB,
+ "RGB");
+ addValueName(PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR,
+ "Palette Color");
+ addValueName(PHOTOMETRIC_INTERPRETATION_TRANSPARENCY_MASK,
+ "Transparency Mask");
+ addValueName(PHOTOMETRIC_INTERPRETATION_CMYK,
+ "CMYK");
+ addValueName(PHOTOMETRIC_INTERPRETATION_Y_CB_CR,
+ "YCbCr");
+ addValueName(PHOTOMETRIC_INTERPRETATION_CIELAB,
+ "CIELAB");
+ addValueName(PHOTOMETRIC_INTERPRETATION_ICCLAB,
+ "ICCLAB"); // Non-baseline
+ }
+ }
+
+ // PlanarConfiguration
+
+ static class PlanarConfiguration extends TIFFTag {
+
+ public PlanarConfiguration() {
+ super("PlanarConfiguration",
+ TAG_PLANAR_CONFIGURATION,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(PLANAR_CONFIGURATION_CHUNKY, "Chunky");
+ addValueName(PLANAR_CONFIGURATION_PLANAR, "Planar");
+ }
+ }
+
+ // Predictor
+
+ static class Predictor extends TIFFTag {
+
+ public Predictor() {
+ super("Predictor",
+ TAG_PREDICTOR,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(PREDICTOR_NONE,
+ "None");
+ addValueName(PREDICTOR_HORIZONTAL_DIFFERENCING,
+ "Horizontal Differencing");
+ }
+ }
+
+ // PrimaryChromaticities
+
+ static class PrimaryChromaticities extends TIFFTag {
+
+ public PrimaryChromaticities() {
+ super("PrimaryChromaticities",
+ TAG_PRIMARY_CHROMATICITES,
+ 1 << TIFF_RATIONAL,
+ 6);
+ }
+ }
+
+ // ReferenceBlackWhite
+
+ static class ReferenceBlackWhite extends TIFFTag {
+
+ public ReferenceBlackWhite() {
+ super("ReferenceBlackWhite",
+ TAG_REFERENCE_BLACK_WHITE,
+ 1 << TIFF_RATIONAL);
+ }
+ }
+
+ // ResolutionUnit
+
+ static class ResolutionUnit extends TIFFTag {
+
+ public ResolutionUnit() {
+ super("ResolutionUnit",
+ TAG_RESOLUTION_UNIT,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(RESOLUTION_UNIT_NONE, "None");
+ addValueName(RESOLUTION_UNIT_INCH, "Inch");
+ addValueName(RESOLUTION_UNIT_CENTIMETER, "Centimeter");
+ }
+ }
+
+ // RowsPerStrip
+
+ static class RowsPerStrip extends TIFFTag {
+
+ public RowsPerStrip() {
+ super("RowsPerStrip",
+ TAG_ROWS_PER_STRIP,
+ (1 << TIFF_SHORT) |
+ (1 << TIFF_LONG),
+ 1);
+ }
+ }
+
+ // SampleFormat
+
+ static class SampleFormat extends TIFFTag {
+
+ public SampleFormat() {
+ super("SampleFormat",
+ TAG_SAMPLE_FORMAT,
+ 1 << TIFF_SHORT);
+
+ addValueName(SAMPLE_FORMAT_UNSIGNED_INTEGER, "Unsigned Integer");
+ addValueName(SAMPLE_FORMAT_SIGNED_INTEGER, "Signed Integer");
+ addValueName(SAMPLE_FORMAT_FLOATING_POINT, "Floating Point");
+ addValueName(SAMPLE_FORMAT_UNDEFINED, "Undefined");
+ }
+ }
+
+ // SamplesPerPixel
+
+ static class SamplesPerPixel extends TIFFTag {
+
+ public SamplesPerPixel() {
+ super("SamplesPerPixel",
+ TAG_SAMPLES_PER_PIXEL,
+ 1 << TIFF_SHORT,
+ 1);
+ }
+ }
+
+ // SMaxSampleValue
+
+ static class SMaxSampleValue extends TIFFTag {
+
+ public SMaxSampleValue() {
+ super("SMaxSampleValue",
+ TAG_S_MAX_SAMPLE_VALUE,
+ (1 << TIFF_BYTE) |
+ (1 << TIFF_SHORT) |
+ (1 << TIFF_LONG) |
+ (1 << TIFF_RATIONAL) |
+ (1 << TIFF_SBYTE) |
+ (1 << TIFF_SSHORT) |
+ (1 << TIFF_SLONG) |
+ (1 << TIFF_SRATIONAL) |
+ (1 << TIFF_FLOAT) |
+ (1 << TIFF_DOUBLE));
+ }
+ }
+
+ // SMinSampleValue
+
+ static class SMinSampleValue extends TIFFTag {
+
+ public SMinSampleValue() {
+ super("SMinSampleValue",
+ TAG_S_MIN_SAMPLE_VALUE,
+ (1 << TIFF_BYTE) |
+ (1 << TIFF_SHORT) |
+ (1 << TIFF_LONG) |
+ (1 << TIFF_RATIONAL) |
+ (1 << TIFF_SBYTE) |
+ (1 << TIFF_SSHORT) |
+ (1 << TIFF_SLONG) |
+ (1 << TIFF_SRATIONAL) |
+ (1 << TIFF_FLOAT) |
+ (1 << TIFF_DOUBLE));
+ }
+ }
+
+ // Software
+
+ static class Software extends TIFFTag {
+
+ public Software() {
+ super("Software",
+ TAG_SOFTWARE,
+ 1 << TIFF_ASCII);
+ }
+ }
+
+ // StripByteCounts
+
+ static class StripByteCounts extends TIFFTag {
+
+ public StripByteCounts() {
+ super("StripByteCounts",
+ TAG_STRIP_BYTE_COUNTS,
+ (1 << TIFF_SHORT) |
+ (1 << TIFF_LONG));
+ }
+ }
+
+ // StripOffsets
+
+ static class StripOffsets extends TIFFTag {
+
+ public StripOffsets() {
+ super("StripOffsets",
+ TAG_STRIP_OFFSETS,
+ (1 << TIFF_SHORT) |
+ (1 << TIFF_LONG));
+ }
+ }
+
+ // SubfileType (deprecated by TIFF but retained for backward compatibility)
+
+ static class SubfileType extends TIFFTag {
+
+ public SubfileType() {
+ super("SubfileType",
+ TAG_SUBFILE_TYPE,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(SUBFILE_TYPE_FULL_RESOLUTION, "FullResolution");
+ addValueName(SUBFILE_TYPE_REDUCED_RESOLUTION, "ReducedResolution");
+ addValueName(SUBFILE_TYPE_SINGLE_PAGE, "SinglePage");
+ }
+ }
+
+ // T4Options
+
+ static class T4Options extends TIFFTag {
+
+ public T4Options() {
+ super("T4Options",
+ TAG_T4_OPTIONS,
+ 1 << TIFF_LONG,
+ 1);
+
+ addValueName(0,
+ "Default 1DCoding"); // 0x00
+ addValueName(T4_OPTIONS_2D_CODING,
+ "2DCoding"); // 0x01
+ addValueName(T4_OPTIONS_UNCOMPRESSED,
+ "Uncompressed"); // 0x02
+ addValueName(T4_OPTIONS_2D_CODING |
+ T4_OPTIONS_UNCOMPRESSED,
+ "2DCoding+Uncompressed"); // 0x03
+ addValueName(T4_OPTIONS_EOL_BYTE_ALIGNED,
+ "EOLByteAligned"); // 0x04
+ addValueName(T4_OPTIONS_2D_CODING |
+ T4_OPTIONS_EOL_BYTE_ALIGNED,
+ "2DCoding+EOLByteAligned"); // 0x05
+ addValueName(T4_OPTIONS_UNCOMPRESSED |
+ T4_OPTIONS_EOL_BYTE_ALIGNED,
+ "Uncompressed+EOLByteAligned"); // 0x06
+ addValueName(T4_OPTIONS_2D_CODING |
+ T4_OPTIONS_UNCOMPRESSED |
+ T4_OPTIONS_EOL_BYTE_ALIGNED,
+ "2DCoding+Uncompressed+EOLByteAligned"); // 0x07
+ }
+ }
+
+ // T6Options
+
+ static class T6Options extends TIFFTag {
+
+ public T6Options() {
+ super("T6Options",
+ TAG_T6_OPTIONS,
+ 1 << TIFF_LONG,
+ 1);
+
+ addValueName(0,
+ "Default"); // 0x00
+ // 0x01 is not possible as bit 0 is unused and always zero.
+ addValueName(T6_OPTIONS_UNCOMPRESSED,
+ "Uncompressed"); // 0x02
+ }
+ }
+
+ // TargetPrinter
+
+ static class TargetPrinter extends TIFFTag {
+
+ public TargetPrinter() {
+ super("TargetPrinter",
+ TAG_TARGET_PRINTER,
+ 1 << TIFF_ASCII);
+ }
+ }
+
+ // Threshholding
+
+ static class Threshholding extends TIFFTag {
+
+ public Threshholding() {
+ super("Threshholding",
+ TAG_THRESHHOLDING,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(1, "None");
+ addValueName(2, "OrderedDither");
+ addValueName(3, "RandomizedDither");
+ }
+ }
+
+ // TileByteCounts
+
+ static class TileByteCounts extends TIFFTag {
+
+ public TileByteCounts() {
+ super("TileByteCounts",
+ TAG_TILE_BYTE_COUNTS,
+ (1 << TIFF_SHORT) |
+ (1 << TIFF_LONG));
+ }
+ }
+
+ // TileOffsets
+
+ static class TileOffsets extends TIFFTag {
+
+ public TileOffsets() {
+ super("TileOffsets",
+ TAG_TILE_OFFSETS,
+ 1 << TIFF_LONG);
+ }
+ }
+
+ // TileLength tag
+
+ static class TileLength extends TIFFTag {
+
+ public TileLength() {
+ super("TileLength",
+ TAG_TILE_LENGTH,
+ (1 << TIFF_SHORT) |
+ (1 << TIFF_LONG),
+ 1);
+ }
+ }
+
+ // TileWidth tag
+
+ static class TileWidth extends TIFFTag {
+
+ public TileWidth() {
+ super("TileWidth",
+ TAG_TILE_WIDTH,
+ (1 << TIFF_SHORT) |
+ (1 << TIFF_LONG),
+ 1);
+ }
+ }
+
+ // TransferFunction
+
+ static class TransferFunction extends TIFFTag {
+
+ public TransferFunction() {
+ super("TransferFunction",
+ TAG_TRANSFER_FUNCTION,
+ 1 << TIFF_SHORT);
+ }
+ }
+
+ // TransferRange
+
+ static class TransferRange extends TIFFTag {
+
+ public TransferRange() {
+ super("TransferRange",
+ TAG_TRANSFER_RANGE,
+ 1 << TIFF_SHORT,
+ 6);
+ }
+ }
+
+ // WhitePoint
+
+ static class WhitePoint extends TIFFTag {
+
+ public WhitePoint() {
+ super("WhitePoint",
+ TAG_WHITE_POINT,
+ 1 << TIFF_RATIONAL,
+ 2);
+ }
+ }
+
+ // XPosition
+
+ static class XPosition extends TIFFTag {
+
+ public XPosition() {
+ super("XPosition",
+ TAG_X_POSITION,
+ 1 << TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ // XResolution
+
+ static class XResolution extends TIFFTag {
+
+ public XResolution() {
+ super("XResolution",
+ TAG_X_RESOLUTION,
+ 1 << TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ // YCbCrCoefficients
+
+ static class YCbCrCoefficients extends TIFFTag {
+
+ public YCbCrCoefficients() {
+ super("YCbCrCoefficients",
+ TAG_Y_CB_CR_COEFFICIENTS,
+ 1 << TIFF_RATIONAL,
+ 3);
+ }
+ }
+
+ // YCbCrPositioning
+
+ static class YCbCrPositioning extends TIFFTag {
+
+ public YCbCrPositioning() {
+ super("YCbCrPositioning",
+ TAG_Y_CB_CR_POSITIONING,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(Y_CB_CR_POSITIONING_CENTERED, "Centered");
+ addValueName(Y_CB_CR_POSITIONING_COSITED, "Cosited");
+ }
+ }
+
+ // YCbCrSubSampling
+
+ static class YCbCrSubSampling extends TIFFTag {
+
+ public YCbCrSubSampling() {
+ super("YCbCrSubSampling",
+ TAG_Y_CB_CR_SUBSAMPLING,
+ 1 << TIFF_SHORT,
+ 2);
+ }
+ }
+
+ // YPosition
+
+ static class YPosition extends TIFFTag {
+
+ public YPosition() {
+ super("YPosition",
+ TAG_Y_POSITION,
+ 1 << TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ // YResolution
+
+ static class YResolution extends TIFFTag {
+
+ public YResolution() {
+ super("YResolution",
+ TAG_Y_RESOLUTION,
+ 1 << TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ // Non-6.0 tags
+
+ // ICC Profile (Spec. ICC.1:2001-12, File Format for Color Profiles)
+
+ static class ICCProfile extends TIFFTag {
+
+ public ICCProfile() {
+ super("ICC Profile",
+ TAG_ICC_PROFILE,
+ 1 << TIFF_UNDEFINED);
+ }
+ }
+
+ private static List<TIFFTag> tags;
+
+ private static void initTags() {
+ tags = new ArrayList<TIFFTag>(76);
+
+ tags.add(new BaselineTIFFTagSet.Artist());
+ tags.add(new BaselineTIFFTagSet.BitsPerSample());
+ tags.add(new BaselineTIFFTagSet.CellLength());
+ tags.add(new BaselineTIFFTagSet.CellWidth());
+ tags.add(new BaselineTIFFTagSet.ColorMap());
+ tags.add(new BaselineTIFFTagSet.Compression());
+ tags.add(new BaselineTIFFTagSet.Copyright());
+ tags.add(new BaselineTIFFTagSet.DateTime());
+ tags.add(new BaselineTIFFTagSet.DocumentName());
+ tags.add(new BaselineTIFFTagSet.DotRange());
+ tags.add(new BaselineTIFFTagSet.ExtraSamples());
+ tags.add(new BaselineTIFFTagSet.FillOrder());
+ tags.add(new BaselineTIFFTagSet.FreeByteCounts());
+ tags.add(new BaselineTIFFTagSet.FreeOffsets());
+ tags.add(new BaselineTIFFTagSet.GrayResponseCurve());
+ tags.add(new BaselineTIFFTagSet.GrayResponseUnit());
+ tags.add(new BaselineTIFFTagSet.HalftoneHints());
+ tags.add(new BaselineTIFFTagSet.HostComputer());
+ tags.add(new BaselineTIFFTagSet.ImageDescription());
+ tags.add(new BaselineTIFFTagSet.ICCProfile());
+ tags.add(new BaselineTIFFTagSet.ImageLength());
+ tags.add(new BaselineTIFFTagSet.ImageWidth());
+ tags.add(new BaselineTIFFTagSet.InkNames());
+ tags.add(new BaselineTIFFTagSet.InkSet());
+ tags.add(new BaselineTIFFTagSet.JPEGACTables());
+ tags.add(new BaselineTIFFTagSet.JPEGDCTables());
+ tags.add(new BaselineTIFFTagSet.JPEGInterchangeFormat());
+ tags.add(new BaselineTIFFTagSet.JPEGInterchangeFormatLength());
+ tags.add(new BaselineTIFFTagSet.JPEGLosslessPredictors());
+ tags.add(new BaselineTIFFTagSet.JPEGPointTransforms());
+ tags.add(new BaselineTIFFTagSet.JPEGProc());
+ tags.add(new BaselineTIFFTagSet.JPEGQTables());
+ tags.add(new BaselineTIFFTagSet.JPEGRestartInterval());
+ tags.add(new BaselineTIFFTagSet.JPEGTables());
+ tags.add(new BaselineTIFFTagSet.Make());
+ tags.add(new BaselineTIFFTagSet.MaxSampleValue());
+ tags.add(new BaselineTIFFTagSet.MinSampleValue());
+ tags.add(new BaselineTIFFTagSet.Model());
+ tags.add(new BaselineTIFFTagSet.NewSubfileType());
+ tags.add(new BaselineTIFFTagSet.NumberOfInks());
+ tags.add(new BaselineTIFFTagSet.Orientation());
+ tags.add(new BaselineTIFFTagSet.PageName());
+ tags.add(new BaselineTIFFTagSet.PageNumber());
+ tags.add(new BaselineTIFFTagSet.PhotometricInterpretation());
+ tags.add(new BaselineTIFFTagSet.PlanarConfiguration());
+ tags.add(new BaselineTIFFTagSet.Predictor());
+ tags.add(new BaselineTIFFTagSet.PrimaryChromaticities());
+ tags.add(new BaselineTIFFTagSet.ReferenceBlackWhite());
+ tags.add(new BaselineTIFFTagSet.ResolutionUnit());
+ tags.add(new BaselineTIFFTagSet.RowsPerStrip());
+ tags.add(new BaselineTIFFTagSet.SampleFormat());
+ tags.add(new BaselineTIFFTagSet.SamplesPerPixel());
+ tags.add(new BaselineTIFFTagSet.SMaxSampleValue());
+ tags.add(new BaselineTIFFTagSet.SMinSampleValue());
+ tags.add(new BaselineTIFFTagSet.Software());
+ tags.add(new BaselineTIFFTagSet.StripByteCounts());
+ tags.add(new BaselineTIFFTagSet.StripOffsets());
+ tags.add(new BaselineTIFFTagSet.SubfileType());
+ tags.add(new BaselineTIFFTagSet.T4Options());
+ tags.add(new BaselineTIFFTagSet.T6Options());
+ tags.add(new BaselineTIFFTagSet.TargetPrinter());
+ tags.add(new BaselineTIFFTagSet.Threshholding());
+ tags.add(new BaselineTIFFTagSet.TileByteCounts());
+ tags.add(new BaselineTIFFTagSet.TileOffsets());
+ tags.add(new BaselineTIFFTagSet.TileLength());
+ tags.add(new BaselineTIFFTagSet.TileWidth());
+ tags.add(new BaselineTIFFTagSet.TransferFunction());
+ tags.add(new BaselineTIFFTagSet.TransferRange());
+ tags.add(new BaselineTIFFTagSet.WhitePoint());
+ tags.add(new BaselineTIFFTagSet.XPosition());
+ tags.add(new BaselineTIFFTagSet.XResolution());
+ tags.add(new BaselineTIFFTagSet.YCbCrCoefficients());
+ tags.add(new BaselineTIFFTagSet.YCbCrPositioning());
+ tags.add(new BaselineTIFFTagSet.YCbCrSubSampling());
+ tags.add(new BaselineTIFFTagSet.YPosition());
+ tags.add(new BaselineTIFFTagSet.YResolution());
+ }
+
+ private BaselineTIFFTagSet() {
+ super(tags);
+ }
+
+ /**
+ * Returns a shared instance of a <code>BaselineTIFFTagSet</code>.
+ *
+ * @return a <code>BaselineTIFFTagSet</code> instance.
+ */
+ public synchronized static BaselineTIFFTagSet getInstance() {
+ if (theInstance == null) {
+ initTags();
+ theInstance = new BaselineTIFFTagSet();
+ tags = null;
+ }
+ return theInstance;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/ExifGPSTagSet.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,724 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class representing the tags found in an Exif GPS Info IFD.
+ *
+ * <p> The definitions of the data types referenced by the field
+ * definitions may be found in the {@link TIFFTag TIFFTag} class.
+ *
+ * @since 1.9
+ * @see ExifTIFFTagSet
+ */
+public class ExifGPSTagSet extends TIFFTagSet {
+ private static ExifGPSTagSet theInstance = null;
+
+ /**
+ * A tag indicating the GPS tag version (type BYTE, count = 4).
+ *
+ * @see #GPS_VERSION_2_2
+ */
+ public static final int TAG_GPS_VERSION_ID = 0;
+
+ /**
+ * A value to be used with the "GPSVersionID" tag to indicate GPS version
+ * 2.2. The value equals the US-ASCII encoding of the byte array
+ * <code>{'2', '2', '0', '0'}</code>.
+ *
+ * @see #TAG_GPS_VERSION_ID
+ */
+ public static final String GPS_VERSION_2_2 =
+ new String(new byte[] { '2', '2', '0', '0' },
+ StandardCharsets.US_ASCII);
+
+ /**
+ * A tag indicating the North or South latitude (type ASCII, count = 2).
+ *
+ * @see #LATITUDE_REF_NORTH
+ * @see #LATITUDE_REF_SOUTH
+ */
+ public static final int TAG_GPS_LATITUDE_REF = 1;
+
+ /**
+ * A tag indicating the Latitude (type RATIONAL, count = 3).
+ */
+ public static final int TAG_GPS_LATITUDE = 2;
+
+ /**
+ * A tag indicating the East or West Longitude (type ASCII, count = 2).
+ *
+ * @see #LONGITUDE_REF_EAST
+ * @see #LONGITUDE_REF_WEST
+ */
+ public static final int TAG_GPS_LONGITUDE_REF = 3;
+
+ /**
+ * A tag indicating the Longitude (type RATIONAL, count = 3).
+ */
+ public static final int TAG_GPS_LONGITUDE = 4;
+
+ /**
+ * A tag indicating the Altitude reference (type BYTE, count = 1);
+ *
+ * @see #ALTITUDE_REF_SEA_LEVEL
+ * @see #ALTITUDE_REF_SEA_LEVEL_REFERENCE
+ */
+ public static final int TAG_GPS_ALTITUDE_REF = 5;
+
+ /**
+ * A tag indicating the Altitude (type RATIONAL, count = 1).
+ */
+ public static final int TAG_GPS_ALTITUDE = 6;
+
+ /**
+ * A tag indicating the GPS time (atomic clock) (type RATIONAL, count = 3).
+ */
+ public static final int TAG_GPS_TIME_STAMP = 7;
+
+ /**
+ * A tag indicating the GPS satellites used for measurement (type ASCII).
+ */
+ public static final int TAG_GPS_SATELLITES = 8;
+
+ /**
+ * A tag indicating the GPS receiver status (type ASCII, count = 2).
+ *
+ * @see #STATUS_MEASUREMENT_IN_PROGRESS
+ * @see #STATUS_MEASUREMENT_INTEROPERABILITY
+ */
+ public static final int TAG_GPS_STATUS = 9;
+
+ /**
+ * A tag indicating the GPS measurement mode (type ASCII, count = 2).
+ *
+ * @see #MEASURE_MODE_2D
+ * @see #MEASURE_MODE_3D
+ */
+ public static final int TAG_GPS_MEASURE_MODE = 10;
+
+ /**
+ * A tag indicating the Measurement precision (type RATIONAL, count = 1).
+ */
+ public static final int TAG_GPS_DOP = 11;
+
+ /**
+ * A tag indicating the Speed unit (type ASCII, count = 2).
+ *
+ * @see #SPEED_REF_KILOMETERS_PER_HOUR
+ * @see #SPEED_REF_MILES_PER_HOUR
+ * @see #SPEED_REF_KNOTS
+ */
+ public static final int TAG_GPS_SPEED_REF = 12;
+
+ /**
+ * A tag indicating the Speed of GPS receiver (type RATIONAL, count = 1).
+ */
+ public static final int TAG_GPS_SPEED = 13;
+
+ /**
+ * A tag indicating the Reference for direction of movement (type ASCII,
+ * count = 2).
+ *
+ * @see #DIRECTION_REF_TRUE
+ * @see #DIRECTION_REF_MAGNETIC
+ */
+ public static final int TAG_GPS_TRACK_REF = 14;
+
+ /**
+ * A tag indicating the Direction of movement (type RATIONAL, count = 1).
+ */
+ public static final int TAG_GPS_TRACK = 15;
+
+ /**
+ * A tag indicating the Reference for direction of image (type ASCII,
+ * count = 2).
+ *
+ * @see #DIRECTION_REF_TRUE
+ * @see #DIRECTION_REF_MAGNETIC
+ */
+ public static final int TAG_GPS_IMG_DIRECTION_REF = 16;
+
+ /**
+ * A tag indicating the Direction of image (type RATIONAL, count = 1).
+ */
+ public static final int TAG_GPS_IMG_DIRECTION = 17;
+
+ /**
+ * A tag indicating the Geodetic survey data used (type ASCII).
+ */
+ public static final int TAG_GPS_MAP_DATUM = 18;
+
+ /**
+ * A tag indicating the Reference for latitude of destination (type
+ * ASCII, count = 2).
+ *
+ * @see #LATITUDE_REF_NORTH
+ * @see #LATITUDE_REF_SOUTH
+ */
+ public static final int TAG_GPS_DEST_LATITUDE_REF = 19;
+
+ /**
+ * A tag indicating the Latitude of destination (type RATIONAL, count = 3).
+ */
+ public static final int TAG_GPS_DEST_LATITUDE = 20;
+
+ /**
+ * A tag indicating the Reference for longitude of destination (type
+ * ASCII, count = 2).
+ *
+ * @see #LONGITUDE_REF_EAST
+ * @see #LONGITUDE_REF_WEST
+ */
+ public static final int TAG_GPS_DEST_LONGITUDE_REF = 21;
+
+ /**
+ * A tag indicating the Longitude of destination (type RATIONAL,
+ * count = 3).
+ */
+ public static final int TAG_GPS_DEST_LONGITUDE = 22;
+
+ /**
+ * A tag indicating the Reference for bearing of destination (type ASCII,
+ * count = 2).
+ *
+ * @see #DIRECTION_REF_TRUE
+ * @see #DIRECTION_REF_MAGNETIC
+ */
+ public static final int TAG_GPS_DEST_BEARING_REF = 23;
+
+ /**
+ * A tag indicating the Bearing of destination (type RATIONAL, count = 1).
+ */
+ public static final int TAG_GPS_DEST_BEARING = 24;
+
+ /**
+ * A tag indicating the Reference for distance to destination (type ASCII,
+ * count = 2).
+ *
+ * @see #DEST_DISTANCE_REF_KILOMETERS
+ * @see #DEST_DISTANCE_REF_MILES
+ * @see #DEST_DISTANCE_REF_KNOTS
+ */
+ public static final int TAG_GPS_DEST_DISTANCE_REF = 25;
+
+ /**
+ * A tag indicating the Distance to destination (type RATIONAL, count = 1).
+ */
+ public static final int TAG_GPS_DEST_DISTANCE = 26;
+
+ /**
+ * A tag indicating the Name of GPS processing method (type UNDEFINED).
+ */
+ public static final int TAG_GPS_PROCESSING_METHOD = 27;
+
+ /**
+ * A tag indicating the Name of GPS area (type UNDEFINED).
+ */
+ public static final int TAG_GPS_AREA_INFORMATION = 28;
+
+ /**
+ * A tag indicating the GPS date (type ASCII, count 11).
+ */
+ public static final int TAG_GPS_DATE_STAMP = 29;
+
+ /**
+ * A tag indicating the GPS differential correction (type SHORT,
+ * count = 1).
+ *
+ * @see #DIFFERENTIAL_CORRECTION_NONE
+ * @see #DIFFERENTIAL_CORRECTION_APPLIED
+ */
+ public static final int TAG_GPS_DIFFERENTIAL = 30;
+
+ /**
+ * A value to be used with the "GPSLatitudeRef" and
+ * "GPSDestLatitudeRef" tags.
+ *
+ * @see #TAG_GPS_LATITUDE_REF
+ * @see #TAG_GPS_DEST_LATITUDE_REF
+ */
+ public static final String LATITUDE_REF_NORTH = "N";
+
+ /**
+ * A value to be used with the "GPSLatitudeRef" and
+ * "GPSDestLatitudeRef" tags.
+ *
+ * @see #TAG_GPS_LATITUDE_REF
+ * @see #TAG_GPS_DEST_LATITUDE_REF
+ */
+ public static final String LATITUDE_REF_SOUTH = "S";
+
+ /**
+ * A value to be used with the "GPSLongitudeRef" and
+ * "GPSDestLongitudeRef" tags.
+ *
+ * @see #TAG_GPS_LONGITUDE_REF
+ * @see #TAG_GPS_DEST_LONGITUDE_REF
+ */
+ public static final String LONGITUDE_REF_EAST = "E";
+
+ /**
+ * A value to be used with the "GPSLongitudeRef" and
+ * "GPSDestLongitudeRef" tags.
+ *
+ * @see #TAG_GPS_LONGITUDE_REF
+ * @see #TAG_GPS_DEST_LONGITUDE_REF
+ */
+ public static final String LONGITUDE_REF_WEST = "W";
+
+ /**
+ * A value to be used with the "GPSAltitudeRef" tag.
+ *
+ * @see #TAG_GPS_ALTITUDE_REF
+ */
+ public static final int ALTITUDE_REF_SEA_LEVEL = 0;
+
+ /**
+ * A value to be used with the "GPSAltitudeRef" tag.
+ *
+ * @see #TAG_GPS_ALTITUDE_REF
+ */
+ public static final int ALTITUDE_REF_SEA_LEVEL_REFERENCE = 1;
+
+ /**
+ * A value to be used with the "GPSStatus" tag.
+ *
+ * @see #TAG_GPS_STATUS
+ */
+ public static final String STATUS_MEASUREMENT_IN_PROGRESS = "A";
+
+ /**
+ * A value to be used with the "GPSStatus" tag.
+ *
+ * @see #TAG_GPS_STATUS
+ */
+ public static final String STATUS_MEASUREMENT_INTEROPERABILITY = "V";
+
+ /**
+ * A value to be used with the "GPSMeasureMode" tag.
+ *
+ * @see #TAG_GPS_MEASURE_MODE
+ */
+ public static final String MEASURE_MODE_2D = "2";
+
+ /**
+ * A value to be used with the "GPSMeasureMode" tag.
+ *
+ * @see #TAG_GPS_MEASURE_MODE
+ */
+ public static final String MEASURE_MODE_3D = "3";
+
+ /**
+ * A value to be used with the "GPSSpeedRef" tag.
+ *
+ * @see #TAG_GPS_SPEED_REF
+ */
+ public static final String SPEED_REF_KILOMETERS_PER_HOUR = "K";
+
+ /**
+ * A value to be used with the "GPSSpeedRef" tag.
+ *
+ * @see #TAG_GPS_SPEED_REF
+ */
+ public static final String SPEED_REF_MILES_PER_HOUR = "M";
+
+ /**
+ * A value to be used with the "GPSSpeedRef" tag.
+ *
+ * @see #TAG_GPS_SPEED_REF
+ */
+ public static final String SPEED_REF_KNOTS = "N";
+
+ /**
+ * A value to be used with the "GPSTrackRef", "GPSImgDirectionRef",
+ * and "GPSDestBearingRef" tags.
+ *
+ * @see #TAG_GPS_TRACK_REF
+ * @see #TAG_GPS_IMG_DIRECTION_REF
+ * @see #TAG_GPS_DEST_BEARING_REF
+ */
+ public static final String DIRECTION_REF_TRUE = "T";
+
+ /**
+ * A value to be used with the "GPSTrackRef", "GPSImgDirectionRef",
+ * and "GPSDestBearingRef" tags.
+ *
+ * @see #TAG_GPS_TRACK_REF
+ * @see #TAG_GPS_IMG_DIRECTION_REF
+ * @see #TAG_GPS_DEST_BEARING_REF
+ */
+ public static final String DIRECTION_REF_MAGNETIC = "M";
+
+ /**
+ * A value to be used with the "GPSDestDistanceRef" tag.
+ *
+ * @see #TAG_GPS_DEST_DISTANCE_REF
+ */
+ public static final String DEST_DISTANCE_REF_KILOMETERS = "K";
+
+ /**
+ * A value to be used with the "GPSDestDistanceRef" tag.
+ *
+ * @see #TAG_GPS_DEST_DISTANCE_REF
+ */
+ public static final String DEST_DISTANCE_REF_MILES = "M";
+
+ /**
+ * A value to be used with the "GPSDestDistanceRef" tag.
+ *
+ * @see #TAG_GPS_DEST_DISTANCE_REF
+ */
+ public static final String DEST_DISTANCE_REF_KNOTS = "N";
+
+ /**
+ * A value to be used with the "GPSDifferential" tag.
+ *
+ * @see #TAG_GPS_DIFFERENTIAL
+ */
+ public static final int DIFFERENTIAL_CORRECTION_NONE = 0;
+
+ /**
+ * A value to be used with the "GPSDifferential" tag.
+ *
+ * @see #TAG_GPS_DIFFERENTIAL
+ */
+ public static final int DIFFERENTIAL_CORRECTION_APPLIED = 1;
+
+ static class GPSVersionID extends TIFFTag {
+ public GPSVersionID() {
+ super("GPSVersionID",
+ TAG_GPS_VERSION_ID,
+ 1 << TIFFTag.TIFF_BYTE);
+ }
+ }
+
+ static class GPSLatitudeRef extends TIFFTag {
+ public GPSLatitudeRef() {
+ super("GPSLatitudeRef",
+ TAG_GPS_LATITUDE_REF,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSLatitude extends TIFFTag {
+ public GPSLatitude() {
+ super("GPSLatitude",
+ TAG_GPS_LATITUDE,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSLongitudeRef extends TIFFTag {
+ public GPSLongitudeRef() {
+ super("GPSLongitudeRef",
+ TAG_GPS_LONGITUDE_REF,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSLongitude extends TIFFTag {
+ public GPSLongitude() {
+ super("GPSLongitude",
+ TAG_GPS_LONGITUDE,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSAltitudeRef extends TIFFTag {
+ public GPSAltitudeRef() {
+ super("GPSAltitudeRef",
+ TAG_GPS_ALTITUDE_REF,
+ 1 << TIFFTag.TIFF_BYTE);
+
+ addValueName(ALTITUDE_REF_SEA_LEVEL, "Sea level");
+ addValueName(ALTITUDE_REF_SEA_LEVEL_REFERENCE,
+ "Sea level reference (negative value)");
+ }
+ }
+
+ static class GPSAltitude extends TIFFTag {
+ public GPSAltitude() {
+ super("GPSAltitude",
+ TAG_GPS_ALTITUDE,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSTimeStamp extends TIFFTag {
+ public GPSTimeStamp() {
+ super("GPSTimeStamp",
+ TAG_GPS_TIME_STAMP,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSSatellites extends TIFFTag {
+ public GPSSatellites() {
+ super("GPSSatellites",
+ TAG_GPS_SATELLITES,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSStatus extends TIFFTag {
+ public GPSStatus() {
+ super("GPSStatus",
+ TAG_GPS_STATUS,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSMeasureMode extends TIFFTag {
+ public GPSMeasureMode() {
+ super("GPSMeasureMode",
+ TAG_GPS_MEASURE_MODE,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSDOP extends TIFFTag {
+ public GPSDOP() {
+ super("GPSDOP",
+ TAG_GPS_DOP,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSSpeedRef extends TIFFTag {
+ public GPSSpeedRef() {
+ super("GPSSpeedRef",
+ TAG_GPS_SPEED_REF,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSSpeed extends TIFFTag {
+ public GPSSpeed() {
+ super("GPSSpeed",
+ TAG_GPS_SPEED,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSTrackRef extends TIFFTag {
+ public GPSTrackRef() {
+ super("GPSTrackRef",
+ TAG_GPS_TRACK_REF,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSTrack extends TIFFTag {
+ public GPSTrack() {
+ super("GPSTrack",
+ TAG_GPS_TRACK,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSImgDirectionRef extends TIFFTag {
+ public GPSImgDirectionRef() {
+ super("GPSImgDirectionRef",
+ TAG_GPS_IMG_DIRECTION_REF,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSImgDirection extends TIFFTag {
+ public GPSImgDirection() {
+ super("GPSImgDirection",
+ TAG_GPS_IMG_DIRECTION,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSMapDatum extends TIFFTag {
+ public GPSMapDatum() {
+ super("GPSMapDatum",
+ TAG_GPS_MAP_DATUM,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSDestLatitudeRef extends TIFFTag {
+ public GPSDestLatitudeRef() {
+ super("GPSDestLatitudeRef",
+ TAG_GPS_DEST_LATITUDE_REF,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSDestLatitude extends TIFFTag {
+ public GPSDestLatitude() {
+ super("GPSDestLatitude",
+ TAG_GPS_DEST_LATITUDE,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSDestLongitudeRef extends TIFFTag {
+ public GPSDestLongitudeRef() {
+ super("GPSDestLongitudeRef",
+ TAG_GPS_DEST_LONGITUDE_REF,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSDestLongitude extends TIFFTag {
+ public GPSDestLongitude() {
+ super("GPSDestLongitude",
+ TAG_GPS_DEST_LONGITUDE,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSDestBearingRef extends TIFFTag {
+ public GPSDestBearingRef() {
+ super("GPSDestBearingRef",
+ TAG_GPS_DEST_BEARING_REF,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSDestBearing extends TIFFTag {
+ public GPSDestBearing() {
+ super("GPSDestBearing",
+ TAG_GPS_DEST_BEARING,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSDestDistanceRef extends TIFFTag {
+ public GPSDestDistanceRef() {
+ super("GPSDestDistanceRef",
+ TAG_GPS_DEST_DISTANCE_REF,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSDestDistance extends TIFFTag {
+ public GPSDestDistance() {
+ super("GPSDestDistance",
+ TAG_GPS_DEST_DISTANCE,
+ 1 << TIFFTag.TIFF_RATIONAL);
+ }
+ }
+
+ static class GPSProcessingMethod extends TIFFTag {
+ public GPSProcessingMethod() {
+ super("GPSProcessingMethod",
+ TAG_GPS_PROCESSING_METHOD,
+ 1 << TIFFTag.TIFF_UNDEFINED);
+ }
+ }
+
+ static class GPSAreaInformation extends TIFFTag {
+ public GPSAreaInformation() {
+ super("GPSAreaInformation",
+ TAG_GPS_AREA_INFORMATION,
+ 1 << TIFFTag.TIFF_UNDEFINED);
+ }
+ }
+
+ static class GPSDateStamp extends TIFFTag {
+ public GPSDateStamp() {
+ super("GPSDateStamp",
+ TAG_GPS_DATE_STAMP,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class GPSDifferential extends TIFFTag {
+ public GPSDifferential() {
+ super("GPSDifferential",
+ TAG_GPS_DIFFERENTIAL,
+ 1 << TIFFTag.TIFF_SHORT);
+ addValueName(DIFFERENTIAL_CORRECTION_NONE,
+ "Measurement without differential correction");
+ addValueName(DIFFERENTIAL_CORRECTION_APPLIED,
+ "Differential correction applied");
+
+ }
+ }
+
+ private static List<TIFFTag> initTags() {
+ ArrayList<TIFFTag> tags = new ArrayList<TIFFTag>(31);
+
+ tags.add(new GPSVersionID());
+ tags.add(new GPSLatitudeRef());
+ tags.add(new GPSLatitude());
+ tags.add(new GPSLongitudeRef());
+ tags.add(new GPSLongitude());
+ tags.add(new GPSAltitudeRef());
+ tags.add(new GPSAltitude());
+ tags.add(new GPSTimeStamp());
+ tags.add(new GPSSatellites());
+ tags.add(new GPSStatus());
+ tags.add(new GPSMeasureMode());
+ tags.add(new GPSDOP());
+ tags.add(new GPSSpeedRef());
+ tags.add(new GPSSpeed());
+ tags.add(new GPSTrackRef());
+ tags.add(new GPSTrack());
+ tags.add(new GPSImgDirectionRef());
+ tags.add(new GPSImgDirection());
+ tags.add(new GPSMapDatum());
+ tags.add(new GPSDestLatitudeRef());
+ tags.add(new GPSDestLatitude());
+ tags.add(new GPSDestLongitudeRef());
+ tags.add(new GPSDestLongitude());
+ tags.add(new GPSDestBearingRef());
+ tags.add(new GPSDestBearing());
+ tags.add(new GPSDestDistanceRef());
+ tags.add(new GPSDestDistance());
+ tags.add(new GPSProcessingMethod());
+ tags.add(new GPSAreaInformation());
+ tags.add(new GPSDateStamp());
+ tags.add(new GPSDifferential());
+ return tags;
+ }
+
+ private ExifGPSTagSet() {
+ super(initTags());
+ }
+
+ /**
+ * Returns a shared instance of an <code>ExifGPSTagSet</code>.
+ *
+ * @return an <code>ExifGPSTagSet</code> instance.
+ */
+ public synchronized static ExifGPSTagSet getInstance() {
+ if (theInstance == null) {
+ theInstance = new ExifGPSTagSet();
+ }
+ return theInstance;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/ExifInteroperabilityTagSet.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,103 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class representing the tags found in an Exif Interoperability IFD.
+ *
+ * @since 1.9
+ * @see ExifTIFFTagSet
+ */
+public class ExifInteroperabilityTagSet extends TIFFTagSet {
+ /**
+ * A tag indicating the identification of the Interoperability rule
+ * (type ASCII).
+ *
+ * @see #INTEROPERABILITY_INDEX_R98
+ * @see #INTEROPERABILITY_INDEX_THM
+ */
+ public static final int TAG_INTEROPERABILITY_INDEX = 1;
+
+ /**
+ * A value to be used with the "InteroperabilityIndex" tag. Indicates
+ * a file conforming to the R98 file specification of Recommended Exif
+ * Interoperability Rules (ExifR98) or to the DCF basic file stipulated
+ * by the Design Rule for Camera File System (type ASCII).
+ *
+ * @see #TAG_INTEROPERABILITY_INDEX
+ */
+ public static final String INTEROPERABILITY_INDEX_R98 = "R98";
+
+ /**
+ * A value to be used with the "InteroperabilityIndex" tag. Indicates
+ * a file conforming to the DCF thumbnail file stipulated by the Design
+ * rule for Camera File System (type ASCII).
+ *
+ * @see #TAG_INTEROPERABILITY_INDEX
+ */
+ public static final String INTEROPERABILITY_INDEX_THM = "THM";
+
+ private static ExifInteroperabilityTagSet theInstance = null;
+
+ static class InteroperabilityIndex extends TIFFTag {
+
+ public InteroperabilityIndex() {
+ super("InteroperabilityIndex",
+ TAG_INTEROPERABILITY_INDEX,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ private static List<TIFFTag> tags;
+
+ private static void initTags() {
+ tags = new ArrayList<TIFFTag>(42);
+
+ tags.add(new ExifInteroperabilityTagSet.InteroperabilityIndex());
+ }
+
+ private ExifInteroperabilityTagSet() {
+ super(tags);
+ }
+
+ /**
+ * Returns the shared instance of
+ * <code>ExifInteroperabilityTagSet</code>.
+ *
+ * @return the <code>ExifInteroperabilityTagSet</code> instance.
+ */
+ public synchronized static ExifInteroperabilityTagSet getInstance() {
+ if (theInstance == null) {
+ initTags();
+ theInstance = new ExifInteroperabilityTagSet();
+ tags = null;
+ }
+ return theInstance;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/ExifParentTIFFTagSet.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,95 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class containing the TIFF tags used to reference the Exif and GPS IFDs.
+ * This tag set should be added to the root tag set by means of the
+ * {@link TIFFImageReadParam#addAllowedTagSet(TIFFTagSet)
+ * TIFFImageReadParam.addAllowedTagSet} method if Exif
+ * support is desired.
+ *
+ * @since 1.9
+ */
+public class ExifParentTIFFTagSet extends TIFFTagSet {
+
+ private static ExifParentTIFFTagSet theInstance = null;
+
+ // 34665 - Exif IFD Pointer (LONG/1)
+ /** Tag pointing to the Exif IFD (type LONG). */
+ public static final int TAG_EXIF_IFD_POINTER = 34665;
+
+ /** Tag pointing to a GPS info IFD (type LONG). */
+ public static final int TAG_GPS_INFO_IFD_POINTER = 34853;
+
+ // To be inserted into parent (root) TIFFTagSet
+ static class ExifIFDPointer extends TIFFTag {
+
+ public ExifIFDPointer() {
+ super("ExifIFDPointer",
+ TAG_EXIF_IFD_POINTER,
+ ExifTIFFTagSet.getInstance());
+ }
+ }
+
+ // To be inserted into parent (root) TIFFTagSet
+ static class GPSInfoIFDPointer extends TIFFTag {
+
+ public GPSInfoIFDPointer() {
+ super("GPSInfoIFDPointer",
+ TAG_GPS_INFO_IFD_POINTER,
+ ExifGPSTagSet.getInstance());
+ }
+ }
+
+ private static List<TIFFTag> tags;
+
+ private static void initTags() {
+ tags = new ArrayList<TIFFTag>(1);
+ tags.add(new ExifParentTIFFTagSet.ExifIFDPointer());
+ tags.add(new ExifParentTIFFTagSet.GPSInfoIFDPointer());
+ }
+
+ private ExifParentTIFFTagSet() {
+ super(tags);
+ }
+
+ /**
+ * Returns a shared instance of an <code>ExifParentTIFFTagSet</code>.
+ *
+ * @return an <code>ExifParentTIFFTagSet</code> instance.
+ */
+ public synchronized static ExifParentTIFFTagSet getInstance() {
+ if (theInstance == null) {
+ initTags();
+ theInstance = new ExifParentTIFFTagSet();
+ tags = null;
+ }
+ return theInstance;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/ExifTIFFTagSet.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,2007 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class representing the tags found in an Exif IFD. Exif is a
+ * standard for annotating images used by most digital camera
+ * manufacturers. The Exif specification may be found at
+ * <a href="http://www.exif.org/Exif2-2.PDF">
+ * <code>http://www.exif.org/Exif2-2.PDF</code>
+ * </a>.
+ *
+ * <p> The definitions of the data types referenced by the field
+ * definitions may be found in the {@link TIFFTag TIFFTag} class.
+ *
+ * @since 1.9
+ */
+public class ExifTIFFTagSet extends TIFFTagSet {
+
+ private static ExifTIFFTagSet theInstance = null;
+
+ /**
+ * A tag pointing to a GPS info IFD (type LONG). This tag has
+ * been superseded by {@link ExifParentTIFFTagSet#TAG_GPS_INFO_IFD_POINTER}.
+ */
+ public static final int TAG_GPS_INFO_IFD_POINTER = 34853;
+
+ /** A tag pointing to an interoperability IFD (type LONG). */
+ public static final int TAG_INTEROPERABILITY_IFD_POINTER = 40965;
+
+ /**
+ * A tag containing the Exif version number (type UNDEFINED, count =
+ * 4). Conformance to the Exif 2.1 standard is indicated using
+ * the ASCII value "0210" (with no terminating NUL).
+ *
+ * @see #EXIF_VERSION_2_1
+ * @see #EXIF_VERSION_2_2
+ */
+ public static final int TAG_EXIF_VERSION = 36864;
+
+ /**
+ * A value to be used with the "ExifVersion" tag to indicate Exif version
+ * 2.1. The value equals the US-ASCII encoding of the byte array
+ * <code>{'0', '2', '1', '0'}</code>.
+ *
+ * @see #TAG_EXIF_VERSION
+ */
+ public static final String EXIF_VERSION_2_1 =
+ new String(new byte[] { '0', '2', '1', '0' },
+ StandardCharsets.US_ASCII);
+
+ /**
+ * A value to be used with the "ExifVersion" tag to indicate Exif version
+ * 2.2. The value equals the US-ASCII encoding of the byte array
+ * <code>{'0', '2', '2', '0'}</code>.
+ *
+ * @see #TAG_EXIF_VERSION
+ */
+ public static final String EXIF_VERSION_2_2 =
+ new String(new byte[] { '0', '2', '2', '0' },
+ StandardCharsets.US_ASCII);
+
+ /**
+ * A tag indicating the FlashPix version number (type UNDEFINED,
+ * count = 4).
+ */
+ public static final int TAG_FLASHPIX_VERSION = 40960;
+
+ /**
+ * A tag indicating the color space information (type SHORT). The
+ * legal values are given by the <code>COLOR_SPACE_*</code>
+ * constants.
+ *
+ * @see #COLOR_SPACE_SRGB
+ * @see #COLOR_SPACE_UNCALIBRATED
+ */
+ public static final int TAG_COLOR_SPACE = 40961;
+
+ /**
+ * A value to be used with the "ColorSpace" tag.
+ *
+ * @see #TAG_COLOR_SPACE
+ */
+ public static final int COLOR_SPACE_SRGB = 1;
+
+ /**
+ * A value to be used with the "ColorSpace" tag.
+ *
+ * @see #TAG_COLOR_SPACE
+ */
+ public static final int COLOR_SPACE_UNCALIBRATED = 0xFFFF;
+
+ /**
+ * A tag containing the components configuration information (type
+ * UNDEFINED, count = 4).
+ *
+ * @see #COMPONENTS_CONFIGURATION_DOES_NOT_EXIST
+ * @see #COMPONENTS_CONFIGURATION_Y
+ * @see #COMPONENTS_CONFIGURATION_CB
+ * @see #COMPONENTS_CONFIGURATION_CR
+ * @see #COMPONENTS_CONFIGURATION_R
+ * @see #COMPONENTS_CONFIGURATION_G
+ * @see #COMPONENTS_CONFIGURATION_B
+ */
+ public static final int TAG_COMPONENTS_CONFIGURATION = 37121;
+
+ /**
+ * A value to be used with the "ComponentsConfiguration" tag.
+ *
+ * @see #TAG_COMPONENTS_CONFIGURATION
+ */
+ public static final int COMPONENTS_CONFIGURATION_DOES_NOT_EXIST = 0;
+
+ /**
+ * A value to be used with the "ComponentsConfiguration" tag.
+ *
+ * @see #TAG_COMPONENTS_CONFIGURATION
+ */
+ public static final int COMPONENTS_CONFIGURATION_Y = 1;
+
+ /**
+ * A value to be used with the "ComponentsConfiguration" tag.
+ *
+ * @see #TAG_COMPONENTS_CONFIGURATION
+ */
+ public static final int COMPONENTS_CONFIGURATION_CB = 2;
+
+ /**
+ * A value to be used with the "ComponentsConfiguration" tag.
+ *
+ * @see #TAG_COMPONENTS_CONFIGURATION
+ */
+ public static final int COMPONENTS_CONFIGURATION_CR = 3;
+
+ /**
+ * A value to be used with the "ComponentsConfiguration" tag.
+ *
+ * @see #TAG_COMPONENTS_CONFIGURATION
+ */
+ public static final int COMPONENTS_CONFIGURATION_R = 4;
+
+ /**
+ * A value to be used with the "ComponentsConfiguration" tag.
+ *
+ * @see #TAG_COMPONENTS_CONFIGURATION
+ */
+ public static final int COMPONENTS_CONFIGURATION_G = 5;
+
+ /**
+ * A value to be used with the "ComponentsConfiguration" tag.
+ *
+ * @see #TAG_COMPONENTS_CONFIGURATION
+ */
+ public static final int COMPONENTS_CONFIGURATION_B = 6;
+
+ /**
+ * A tag indicating the number of compressed bits per pixel
+ * (type RATIONAL).
+ */
+ public static final int TAG_COMPRESSED_BITS_PER_PIXEL = 37122;
+
+ /**
+ * A tag indicating the pixel X dimension (type SHORT or LONG).
+ * This value records the valid width of the meaningful image for
+ * a compressed file, whether or not there is padding or a restart
+ * marker.
+ */
+ public static final int TAG_PIXEL_X_DIMENSION = 40962;
+
+ /**
+ * A tag indicating the pixel Y dimension (type SHORT or LONG).
+ * This value records the valid height of the meaningful image for
+ * a compressed file, whether or not there is padding or a restart
+ * marker.
+ */
+ public static final int TAG_PIXEL_Y_DIMENSION = 40963;
+
+ /**
+ * A tag indicating a manufacturer-defined maker note (type
+ * UNDEFINED).
+ */
+ public static final int TAG_MAKER_NOTE = 37500;
+
+ /**
+ * A tag indicating a manufacturer-defined marker note (type UNDEFINED).
+ * This tag has been superseded by {@link #TAG_MAKER_NOTE}.
+ */
+ public static final int TAG_MARKER_NOTE = TAG_MAKER_NOTE;
+
+ /**
+ * A tag indicating a user comment (type UNDEFINED). The first 8
+ * bytes are used to specify the character encoding.
+ */
+ public static final int TAG_USER_COMMENT = 37510;
+
+ /**
+ * A tag indicating the name of a related sound file (type ASCII).
+ */
+ public static final int TAG_RELATED_SOUND_FILE = 40964;
+
+ /**
+ * A tag indicating the date and time when the original image was
+ * generated (type ASCII).
+ */
+ public static final int TAG_DATE_TIME_ORIGINAL = 36867;
+
+ /**
+ * A tag indicating the date and time when the image was stored as
+ * digital data (type ASCII).
+ */
+ public static final int TAG_DATE_TIME_DIGITIZED = 36868;
+
+ /**
+ * A tag used to record fractions of seconds for the "DateTime" tag
+ * (type ASCII).
+ */
+ public static final int TAG_SUB_SEC_TIME = 37520;
+
+ /**
+ * A tag used to record fractions of seconds for the
+ * "DateTimeOriginal" tag (type ASCII).
+ */
+ public static final int TAG_SUB_SEC_TIME_ORIGINAL = 37521;
+
+ /**
+ * A tag used to record fractions of seconds for the
+ * "DateTimeDigitized" tag (type ASCII).
+ */
+ public static final int TAG_SUB_SEC_TIME_DIGITIZED = 37522;
+
+ /**
+ * A tag indicating the exposure time, in seconds (type RATIONAL).
+ */
+ public static final int TAG_EXPOSURE_TIME = 33434;
+
+ /**
+ * A tag indicating the F number (type RATIONAL).
+ */
+ public static final int TAG_F_NUMBER = 33437;
+
+ /**
+ * A tag indicating the class of the programs used to set exposure
+ * when the picture was taken (type SHORT).
+ *
+ * @see #EXPOSURE_PROGRAM_NOT_DEFINED
+ * @see #EXPOSURE_PROGRAM_MANUAL
+ * @see #EXPOSURE_PROGRAM_NORMAL_PROGRAM
+ * @see #EXPOSURE_PROGRAM_APERTURE_PRIORITY
+ * @see #EXPOSURE_PROGRAM_SHUTTER_PRIORITY
+ * @see #EXPOSURE_PROGRAM_CREATIVE_PROGRAM
+ * @see #EXPOSURE_PROGRAM_ACTION_PROGRAM
+ * @see #EXPOSURE_PROGRAM_PORTRAIT_MODE
+ * @see #EXPOSURE_PROGRAM_LANDSCAPE_MODE
+ * @see #EXPOSURE_PROGRAM_MAX_RESERVED
+ */
+ public static final int TAG_EXPOSURE_PROGRAM = 34850;
+
+ /**
+ * A value to be used with the "ExposureProgram" tag.
+ *
+ * @see #TAG_EXPOSURE_PROGRAM
+ */
+ public static final int EXPOSURE_PROGRAM_NOT_DEFINED = 0;
+
+ /**
+ * A value to be used with the "ExposureProgram" tag.
+ *
+ * @see #TAG_EXPOSURE_PROGRAM
+ */
+ public static final int EXPOSURE_PROGRAM_MANUAL = 1;
+
+ /**
+ * A value to be used with the "ExposureProgram" tag.
+ *
+ * @see #TAG_EXPOSURE_PROGRAM
+ */
+ public static final int EXPOSURE_PROGRAM_NORMAL_PROGRAM = 2;
+
+ /**
+ * A value to be used with the "ExposureProgram" tag.
+ *
+ * @see #TAG_EXPOSURE_PROGRAM
+ */
+ public static final int EXPOSURE_PROGRAM_APERTURE_PRIORITY = 3;
+
+ /**
+ * A value to be used with the "ExposureProgram" tag.
+ *
+ * @see #TAG_EXPOSURE_PROGRAM
+ */
+ public static final int EXPOSURE_PROGRAM_SHUTTER_PRIORITY = 4;
+
+ /**
+ * A value to be used with the "ExposureProgram" tag.
+ *
+ * @see #TAG_EXPOSURE_PROGRAM
+ */
+ public static final int EXPOSURE_PROGRAM_CREATIVE_PROGRAM = 5;
+
+ /**
+ * A value to be used with the "ExposureProgram" tag.
+ *
+ * @see #TAG_EXPOSURE_PROGRAM
+ */
+ public static final int EXPOSURE_PROGRAM_ACTION_PROGRAM = 6;
+
+ /**
+ * A value to be used with the "ExposureProgram" tag.
+ *
+ * @see #TAG_EXPOSURE_PROGRAM
+ */
+ public static final int EXPOSURE_PROGRAM_PORTRAIT_MODE = 7;
+
+ /**
+ * A value to be used with the "ExposureProgram" tag.
+ *
+ * @see #TAG_EXPOSURE_PROGRAM
+ */
+ public static final int EXPOSURE_PROGRAM_LANDSCAPE_MODE = 8;
+
+ /**
+ * A value to be used with the "ExposureProgram" tag.
+ *
+ * @see #TAG_EXPOSURE_PROGRAM
+ */
+ public static final int EXPOSURE_PROGRAM_MAX_RESERVED = 255;
+
+ /**
+ * A tag indicating the spectral sensitivity of each channel of
+ * the camera used (type ASCII). The tag value is an ASCII string
+ * compatible with the ASTM standard.
+ */
+ public static final int TAG_SPECTRAL_SENSITIVITY = 34852;
+
+ /**
+ * A tag indicating the ISO speed and ISO latitude of the camera
+ * or input device, as specified in ISO 12232<sup>xiv</sup> (type
+ * SHORT).
+ */
+ public static final int TAG_ISO_SPEED_RATINGS= 34855;
+
+ /**
+ * A tag indicating the optoelectric conversion function,
+ * specified in ISO 14254<sup>xv</sup> (type UNDEFINED). OECF is
+ * the relationship between the camera optical input and the image
+ * values.
+ */
+ public static final int TAG_OECF = 34856;
+
+ /**
+ * A tag indicating the shutter speed (type SRATIONAL).
+ */
+ public static final int TAG_SHUTTER_SPEED_VALUE = 37377;
+
+ /**
+ * A tag indicating the lens aperture (type RATIONAL).
+ */
+ public static final int TAG_APERTURE_VALUE = 37378;
+
+ /**
+ * A tag indicating the value of brightness (type SRATIONAL).
+ */
+ public static final int TAG_BRIGHTNESS_VALUE = 37379;
+
+ /**
+ * A tag indicating the exposure bias (type SRATIONAL).
+ */
+ public static final int TAG_EXPOSURE_BIAS_VALUE = 37380;
+
+ /**
+ * A tag indicating the smallest F number of the lens (type
+ * RATIONAL).
+ */
+ public static final int TAG_MAX_APERTURE_VALUE = 37381;
+
+ /**
+ * A tag indicating the distance to the subject, in meters (type
+ * RATIONAL).
+ */
+ public static final int TAG_SUBJECT_DISTANCE = 37382;
+
+ /**
+ * A tag indicating the metering mode (type SHORT).
+ *
+ * @see #METERING_MODE_UNKNOWN
+ * @see #METERING_MODE_AVERAGE
+ * @see #METERING_MODE_CENTER_WEIGHTED_AVERAGE
+ * @see #METERING_MODE_SPOT
+ * @see #METERING_MODE_MULTI_SPOT
+ * @see #METERING_MODE_PATTERN
+ * @see #METERING_MODE_PARTIAL
+ * @see #METERING_MODE_MIN_RESERVED
+ * @see #METERING_MODE_MAX_RESERVED
+ * @see #METERING_MODE_OTHER
+ */
+ public static final int TAG_METERING_MODE = 37383;
+
+ /**
+ * A value to be used with the "MeteringMode" tag.
+ *
+ * @see #TAG_METERING_MODE
+ */
+ public static final int METERING_MODE_UNKNOWN = 0;
+
+ /**
+ * A value to be used with the "MeteringMode" tag.
+ *
+ * @see #TAG_METERING_MODE
+ */
+ public static final int METERING_MODE_AVERAGE = 1;
+
+ /**
+ * A value to be used with the "MeteringMode" tag.
+ *
+ * @see #TAG_METERING_MODE
+ */
+ public static final int METERING_MODE_CENTER_WEIGHTED_AVERAGE = 2;
+
+ /**
+ * A value to be used with the "MeteringMode" tag.
+ *
+ * @see #TAG_METERING_MODE
+ */
+ public static final int METERING_MODE_SPOT = 3;
+
+ /**
+ * A value to be used with the "MeteringMode" tag.
+ *
+ * @see #TAG_METERING_MODE
+ */
+ public static final int METERING_MODE_MULTI_SPOT = 4;
+
+ /**
+ * A value to be used with the "MeteringMode" tag.
+ *
+ * @see #TAG_METERING_MODE
+ */
+ public static final int METERING_MODE_PATTERN = 5;
+
+ /**
+ * A value to be used with the "MeteringMode" tag.
+ *
+ * @see #TAG_METERING_MODE
+ */
+ public static final int METERING_MODE_PARTIAL = 6;
+
+ /**
+ * A value to be used with the "MeteringMode" tag.
+ *
+ * @see #TAG_METERING_MODE
+ */
+ public static final int METERING_MODE_MIN_RESERVED = 7;
+
+ /**
+ * A value to be used with the "MeteringMode" tag.
+ *
+ * @see #TAG_METERING_MODE
+ */
+ public static final int METERING_MODE_MAX_RESERVED = 254;
+
+ /**
+ * A value to be used with the "MeteringMode" tag.
+ *
+ * @see #TAG_METERING_MODE
+ */
+ public static final int METERING_MODE_OTHER = 255;
+
+ /**
+ * A tag indicatingthe kind of light source (type SHORT).
+ *
+ * @see #LIGHT_SOURCE_UNKNOWN
+ * @see #LIGHT_SOURCE_DAYLIGHT
+ * @see #LIGHT_SOURCE_FLUORESCENT
+ * @see #LIGHT_SOURCE_TUNGSTEN
+ * @see #LIGHT_SOURCE_STANDARD_LIGHT_A
+ * @see #LIGHT_SOURCE_STANDARD_LIGHT_B
+ * @see #LIGHT_SOURCE_STANDARD_LIGHT_C
+ * @see #LIGHT_SOURCE_D55
+ * @see #LIGHT_SOURCE_D65
+ * @see #LIGHT_SOURCE_D75
+ * @see #LIGHT_SOURCE_OTHER
+ */
+ public static final int TAG_LIGHT_SOURCE = 37384;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_UNKNOWN = 0;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_DAYLIGHT = 1;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_FLUORESCENT = 2;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_TUNGSTEN = 3;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_FLASH = 4;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_FINE_WEATHER = 9;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_CLOUDY_WEATHER = 10;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_SHADE = 11;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_DAYLIGHT_FLUORESCENT = 12;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_DAY_WHITE_FLUORESCENT = 13;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_COOL_WHITE_FLUORESCENT = 14;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_WHITE_FLUORESCENT = 15;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_STANDARD_LIGHT_A = 17;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_STANDARD_LIGHT_B = 18;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_STANDARD_LIGHT_C = 19;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_D55 = 20;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_D65 = 21;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_D75 = 22;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_D50 = 23;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_ISO_STUDIO_TUNGSTEN = 24;
+
+ /**
+ * A value to be used with the "LightSource" tag.
+ *
+ * @see #TAG_LIGHT_SOURCE
+ */
+ public static final int LIGHT_SOURCE_OTHER = 255;
+
+ /**
+ * A tag indicating the flash firing status and flash return
+ * status (type SHORT).
+ *
+ * @see #FLASH_DID_NOT_FIRE
+ * @see #FLASH_FIRED
+ * @see #FLASH_STROBE_RETURN_LIGHT_NOT_DETECTED
+ * @see #FLASH_STROBE_RETURN_LIGHT_DETECTED
+ */
+ public static final int TAG_FLASH = 37385;
+
+ /**
+ * A value to be used with the "Flash" tag, indicating that the
+ * flash did not fire.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_DID_NOT_FIRE = 0x0;
+
+ /**
+ * A value to be used with the "Flash" tag, indicating that the
+ * flash fired, but the strobe return status is unknown.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_FIRED = 0x1;
+
+ /**
+ * A value to be used with the "Flash" tag, indicating that the
+ * flash fired, but the strobe return light was not detected.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_STROBE_RETURN_LIGHT_NOT_DETECTED = 0x5;
+
+ /**
+ * A value to be used with the "Flash" tag, indicating that the
+ * flash fired, and the strobe return light was detected.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_STROBE_RETURN_LIGHT_DETECTED = 0x7;
+
+ /**
+ * A mask to be used with the "Flash" tag, indicating that the
+ * flash fired.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_MASK_FIRED = 0x1;
+
+ /**
+ * A mask to be used with the "Flash" tag, indicating strobe return
+ * light not detected.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_MASK_RETURN_NOT_DETECTED = 0x4;
+
+ /**
+ * A mask to be used with the "Flash" tag, indicating strobe return
+ * light detected.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_MASK_RETURN_DETECTED = 0x6;
+
+ /**
+ * A mask to be used with the "Flash" tag, indicating compulsory flash
+ * firing mode.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_MASK_MODE_FLASH_FIRING = 0x8;
+
+ /**
+ * A mask to be used with the "Flash" tag, indicating compulsory flash
+ * suppression mode.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_MASK_MODE_FLASH_SUPPRESSION = 0x10;
+
+ /**
+ * A mask to be used with the "Flash" tag, indicating auto mode.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_MASK_MODE_AUTO = 0x18;
+
+ /**
+ * A mask to be used with the "Flash" tag, indicating no flash function
+ * present.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_MASK_FUNCTION_NOT_PRESENT = 0x20;
+
+ /**
+ * A mask to be used with the "Flash" tag, indicating red-eye reduction
+ * supported.
+ *
+ * @see #TAG_FLASH
+ */
+ public static final int FLASH_MASK_RED_EYE_REDUCTION = 0x40;
+
+ /**
+ * A tag indicating the actual focal length of the lens, in
+ * millimeters (type RATIONAL).
+ */
+ public static final int TAG_FOCAL_LENGTH = 37386;
+
+ /**
+ * A tag indicating the location and area of the main subject in
+ * the overall scene.
+ */
+ public static final int TAG_SUBJECT_AREA = 37396;
+
+ /**
+ * A tag indicating the strobe energy at the time the image was
+ * captured, as measured in Beam Candle Power Seconds (BCPS) (type
+ * RATIONAL).
+ */
+ public static final int TAG_FLASH_ENERGY = 41483;
+
+ /**
+ * A tag indicating the camera or input device spatial frequency
+ * table and SFR values in the direction of image width, image
+ * height, and diagonal direction, as specified in ISO
+ * 12233<sup>xvi</sup> (type UNDEFINED).
+ */
+ public static final int TAG_SPATIAL_FREQUENCY_RESPONSE = 41484;
+
+ /**
+ * Indicates the number of pixels in the image width (X) direction
+ * per FocalPlaneResolutionUnit on the camera focal plane (type
+ * RATIONAL).
+ */
+ public static final int TAG_FOCAL_PLANE_X_RESOLUTION = 41486;
+
+ /**
+ * Indicate the number of pixels in the image height (Y) direction
+ * per FocalPlaneResolutionUnit on the camera focal plane (type
+ * RATIONAL).
+ */
+ public static final int TAG_FOCAL_PLANE_Y_RESOLUTION = 41487;
+
+ /**
+ * Indicates the unit for measuring FocalPlaneXResolution and
+ * FocalPlaneYResolution (type SHORT).
+ *
+ * @see #FOCAL_PLANE_RESOLUTION_UNIT_NONE
+ * @see #FOCAL_PLANE_RESOLUTION_UNIT_INCH
+ * @see #FOCAL_PLANE_RESOLUTION_UNIT_CENTIMETER
+ */
+ public static final int TAG_FOCAL_PLANE_RESOLUTION_UNIT = 41488;
+
+ /**
+ * A value to be used with the "FocalPlaneResolutionUnit" tag.
+ *
+ * @see #TAG_FOCAL_PLANE_RESOLUTION_UNIT
+ */
+ public static final int FOCAL_PLANE_RESOLUTION_UNIT_NONE = 1;
+
+ /**
+ * A value to be used with the "FocalPlaneXResolution" tag.
+ *
+ * @see #TAG_FOCAL_PLANE_RESOLUTION_UNIT
+ */
+ public static final int FOCAL_PLANE_RESOLUTION_UNIT_INCH = 2;
+
+ /**
+ * A value to be used with the "FocalPlaneXResolution" tag.
+ *
+ * @see #TAG_FOCAL_PLANE_RESOLUTION_UNIT
+ */
+ public static final int FOCAL_PLANE_RESOLUTION_UNIT_CENTIMETER = 3;
+
+ /**
+ * A tag indicating the column and row of the center pixel of the
+ * main subject in the scene (type SHORT, count = 2).
+ */
+ public static final int TAG_SUBJECT_LOCATION = 41492;
+
+ /**
+ * A tag indicating the exposure index selected on the camera or
+ * input device at the time the image was captured (type
+ * RATIONAL).
+ */
+ public static final int TAG_EXPOSURE_INDEX = 41493;
+
+ /**
+ * A tag indicating the sensor type on the camera or input device
+ * (type SHORT).
+ *
+ * @see #SENSING_METHOD_NOT_DEFINED
+ * @see #SENSING_METHOD_ONE_CHIP_COLOR_AREA_SENSOR
+ * @see #SENSING_METHOD_TWO_CHIP_COLOR_AREA_SENSOR
+ * @see #SENSING_METHOD_THREE_CHIP_COLOR_AREA_SENSOR
+ * @see #SENSING_METHOD_COLOR_SEQUENTIAL_AREA_SENSOR
+ * @see #SENSING_METHOD_TRILINEAR_SENSOR
+ * @see #SENSING_METHOD_COLOR_SEQUENTIAL_LINEAR_SENSOR
+ */
+ public static final int TAG_SENSING_METHOD = 41495;
+
+ /**
+ * A value to be used with the "SensingMethod" tag.
+ *
+ * @see #TAG_SENSING_METHOD
+ */
+ public static final int SENSING_METHOD_NOT_DEFINED = 1;
+
+ /**
+ * A value to be used with the "SensingMethod" tag.
+ *
+ * @see #TAG_SENSING_METHOD
+ */
+ public static final int SENSING_METHOD_ONE_CHIP_COLOR_AREA_SENSOR = 2;
+
+ /**
+ * A value to be used with the "SensingMethod" tag.
+ *
+ * @see #TAG_SENSING_METHOD
+ */
+ public static final int SENSING_METHOD_TWO_CHIP_COLOR_AREA_SENSOR = 3;
+
+ /**
+ * A value to be used with the "SensingMethod" tag.
+ *
+ * @see #TAG_SENSING_METHOD
+ */
+ public static final int SENSING_METHOD_THREE_CHIP_COLOR_AREA_SENSOR = 4;
+
+ /**
+ * A value to be used with the "SensingMethod" tag.
+ *
+ * @see #TAG_SENSING_METHOD
+ */
+ public static final int SENSING_METHOD_COLOR_SEQUENTIAL_AREA_SENSOR = 5;
+
+ /**
+ * A value to be used with the "SensingMethod" tag.
+ *
+ * @see #TAG_SENSING_METHOD
+ */
+ public static final int SENSING_METHOD_TRILINEAR_SENSOR = 7;
+
+ /**
+ * A value to be used with the "SensingMethod" tag.
+ *
+ * @see #TAG_SENSING_METHOD
+ */
+ public static final int SENSING_METHOD_COLOR_SEQUENTIAL_LINEAR_SENSOR = 8;
+
+ /**
+ * A tag indicating the image source (type UNDEFINED).
+ *
+ * @see #FILE_SOURCE_DSC
+ */
+ public static final int TAG_FILE_SOURCE = 41728;
+
+ /**
+ * A value to be used with the "FileSource" tag.
+ *
+ * @see #TAG_FILE_SOURCE
+ */
+ public static final int FILE_SOURCE_DSC = 3;
+
+ /**
+ * A tag indicating the type of scene (type UNDEFINED).
+ *
+ * @see #SCENE_TYPE_DSC
+ */
+ public static final int TAG_SCENE_TYPE = 41729;
+
+ /**
+ * A value to be used with the "SceneType" tag.
+ *
+ * @see #TAG_SCENE_TYPE
+ */
+ public static final int SCENE_TYPE_DSC = 1;
+
+ /**
+ * A tag indicating the color filter array geometric pattern of
+ * the image sensor when a one-chip color area sensor if used
+ * (type UNDEFINED).
+ */
+ public static final int TAG_CFA_PATTERN = 41730;
+
+ /**
+ * A tag indicating the use of special processing on image data,
+ * such as rendering geared to output.
+ */
+ public static final int TAG_CUSTOM_RENDERED = 41985;
+
+ /**
+ * A value to be used with the "CustomRendered" tag.
+ *
+ * @see #TAG_CUSTOM_RENDERED
+ */
+ public static final int CUSTOM_RENDERED_NORMAL = 0;
+
+ /**
+ * A value to be used with the "CustomRendered" tag.
+ *
+ * @see #TAG_CUSTOM_RENDERED
+ */
+ public static final int CUSTOM_RENDERED_CUSTOM = 1;
+
+ /**
+ * A tag indicating the exposure mode set when the image was shot.
+ */
+ public static final int TAG_EXPOSURE_MODE = 41986;
+
+ /**
+ * A value to be used with the "ExposureMode" tag.
+ *
+ * @see #TAG_EXPOSURE_MODE
+ */
+ public static final int EXPOSURE_MODE_AUTO_EXPOSURE = 0;
+
+ /**
+ * A value to be used with the "ExposureMode" tag.
+ *
+ * @see #TAG_EXPOSURE_MODE
+ */
+ public static final int EXPOSURE_MODE_MANUAL_EXPOSURE = 1;
+
+ /**
+ * A value to be used with the "ExposureMode" tag.
+ *
+ * @see #TAG_EXPOSURE_MODE
+ */
+ public static final int EXPOSURE_MODE_AUTO_BRACKET = 2;
+
+ /**
+ * A tag indicating the white balance mode set when the image was shot.
+ */
+ public static final int TAG_WHITE_BALANCE = 41987;
+
+ /**
+ * A value to be used with the "WhiteBalance" tag.
+ *
+ * @see #TAG_WHITE_BALANCE
+ */
+ public static final int WHITE_BALANCE_AUTO = 0;
+
+ /**
+ * A value to be used with the "WhiteBalance" tag.
+ *
+ * @see #TAG_WHITE_BALANCE
+ */
+ public static final int WHITE_BALANCE_MANUAL = 1;
+
+ /**
+ * A tag indicating the digital zoom ratio when the image was shot.
+ */
+ public static final int TAG_DIGITAL_ZOOM_RATIO = 41988;
+
+ /**
+ * A tag indicating the equivalent focal length assuming a 35mm film
+ * camera, in millimeters.
+ */
+ public static final int TAG_FOCAL_LENGTH_IN_35MM_FILM = 41989;
+
+ /**
+ * A tag indicating the type of scene that was shot.
+ */
+ public static final int TAG_SCENE_CAPTURE_TYPE = 41990;
+
+ /**
+ * A value to be used with the "SceneCaptureType" tag.
+ *
+ * @see #TAG_SCENE_CAPTURE_TYPE
+ */
+ public static final int SCENE_CAPTURE_TYPE_STANDARD = 0;
+
+ /**
+ * A value to be used with the "SceneCaptureType" tag.
+ *
+ * @see #TAG_SCENE_CAPTURE_TYPE
+ */
+ public static final int SCENE_CAPTURE_TYPE_LANDSCAPE = 1;
+
+ /**
+ * A value to be used with the "SceneCaptureType" tag.
+ *
+ * @see #TAG_SCENE_CAPTURE_TYPE
+ */
+ public static final int SCENE_CAPTURE_TYPE_PORTRAIT = 2;
+
+ /**
+ * A value to be used with the "SceneCaptureType" tag.
+ *
+ * @see #TAG_SCENE_CAPTURE_TYPE
+ */
+ public static final int SCENE_CAPTURE_TYPE_NIGHT_SCENE = 3;
+
+ /**
+ * A tag indicating the degree of overall image gain adjustment.
+ */
+ public static final int TAG_GAIN_CONTROL = 41991;
+
+ /**
+ * A value to be used with the "GainControl" tag.
+ *
+ * @see #TAG_GAIN_CONTROL
+ */
+ public static final int GAIN_CONTROL_NONE = 0;
+
+ /**
+ * A value to be used with the "GainControl" tag.
+ *
+ * @see #TAG_GAIN_CONTROL
+ */
+ public static final int GAIN_CONTROL_LOW_GAIN_UP = 1;
+
+ /**
+ * A value to be used with the "GainControl" tag.
+ *
+ * @see #TAG_GAIN_CONTROL
+ */
+ public static final int GAIN_CONTROL_HIGH_GAIN_UP = 2;
+
+ /**
+ * A value to be used with the "GainControl" tag.
+ *
+ * @see #TAG_GAIN_CONTROL
+ */
+ public static final int GAIN_CONTROL_LOW_GAIN_DOWN = 3;
+
+ /**
+ * A value to be used with the "GainControl" tag.
+ *
+ * @see #TAG_GAIN_CONTROL
+ */
+ public static final int GAIN_CONTROL_HIGH_GAIN_DOWN = 4;
+
+ /**
+ * A tag indicating the direction of contrast processing applied
+ * by the camera when the image was shot.
+ */
+ public static final int TAG_CONTRAST = 41992;
+
+ /**
+ * A value to be used with the "Contrast" tag.
+ *
+ * @see #TAG_CONTRAST
+ */
+ public static final int CONTRAST_NORMAL = 0;
+
+ /**
+ * A value to be used with the "Contrast" tag.
+ *
+ * @see #TAG_CONTRAST
+ */
+ public static final int CONTRAST_SOFT = 1;
+
+ /**
+ * A value to be used with the "Contrast" tag.
+ *
+ * @see #TAG_CONTRAST
+ */
+ public static final int CONTRAST_HARD = 2;
+
+ /**
+ * A tag indicating the direction of saturation processing
+ * applied by the camera when the image was shot.
+ */
+ public static final int TAG_SATURATION = 41993;
+
+ /**
+ * A value to be used with the "Saturation" tag.
+ *
+ * @see #TAG_SATURATION
+ */
+ public static final int SATURATION_NORMAL = 0;
+
+ /**
+ * A value to be used with the "Saturation" tag.
+ *
+ * @see #TAG_SATURATION
+ */
+ public static final int SATURATION_LOW = 1;
+
+ /**
+ * A value to be used with the "Saturation" tag.
+ *
+ * @see #TAG_SATURATION
+ */
+ public static final int SATURATION_HIGH = 2;
+
+ /**
+ * A tag indicating the direction of sharpness processing
+ * applied by the camera when the image was shot.
+ */
+ public static final int TAG_SHARPNESS = 41994;
+
+ /**
+ * A value to be used with the "Sharpness" tag.
+ *
+ * @see #TAG_SHARPNESS
+ */
+ public static final int SHARPNESS_NORMAL = 0;
+
+ /**
+ * A value to be used with the "Sharpness" tag.
+ *
+ * @see #TAG_SHARPNESS
+ */
+ public static final int SHARPNESS_SOFT = 1;
+
+ /**
+ * A value to be used with the "Sharpness" tag.
+ *
+ * @see #TAG_SHARPNESS
+ */
+ public static final int SHARPNESS_HARD = 2;
+
+ /**
+ * A tag indicating information on the picture-taking conditions
+ * of a particular camera model.
+ */
+ public static final int TAG_DEVICE_SETTING_DESCRIPTION = 41995;
+
+ /**
+ * A tag indicating the distance to the subject.
+ */
+ public static final int TAG_SUBJECT_DISTANCE_RANGE = 41996;
+
+ /**
+ * A value to be used with the "SubjectDistanceRange" tag.
+ *
+ * @see #TAG_SUBJECT_DISTANCE_RANGE
+ */
+ public static final int SUBJECT_DISTANCE_RANGE_UNKNOWN = 0;
+
+ /**
+ * A value to be used with the "SubjectDistanceRange" tag.
+ *
+ * @see #TAG_SUBJECT_DISTANCE_RANGE
+ */
+ public static final int SUBJECT_DISTANCE_RANGE_MACRO = 1;
+
+ /**
+ * A value to be used with the "SubjectDistanceRange" tag.
+ *
+ * @see #TAG_SUBJECT_DISTANCE_RANGE
+ */
+ public static final int SUBJECT_DISTANCE_RANGE_CLOSE_VIEW = 2;
+
+ /**
+ * A value to be used with the "SubjectDistanceRange" tag.
+ *
+ * @see #TAG_SUBJECT_DISTANCE_RANGE
+ */
+ public static final int SUBJECT_DISTANCE_RANGE_DISTANT_VIEW = 3;
+
+ /**
+ * A tag indicating an identifier assigned uniquely to each image.
+ */
+ public static final int TAG_IMAGE_UNIQUE_ID = 42016;
+
+ // Exif 2.1 private
+
+ // GPS Attribute Information
+ // 0 - GPSVersionID (BYTE/4)
+ // 1 - GPSLatitudeRef (ASCII/2)
+ // 2 - GPSLatitude (RATIONAL/3)
+ // 3 - GPSLongitudeRef (ASCII/2)
+ // 4 - GPSLongitude (RATIONAL/3)
+ // 5 - GPSAltitudeRef (BYTE/1)
+ // 6 - GPSAltitude (RATIONAL/1)
+ // 7 - GPSTimeStamp (RATIONAL/3)
+ // 8 - GPSSatellites (ASCII/any)
+ // 9 - GPSStatus (ASCII/2)
+ // 10 - GPSMeasureMode (ASCII/2)
+ // 11 - GPSDOP (RATIONAL/1)
+ // 12 - GPSSpeedRef (ASCII/2)
+ // 13 - GPSSpeed (RATIONAL/1)
+ // 14 - GPSTrackRef (ASCII/2)
+ // 15 - GPSTrack (RATIONAL/1)
+ // 16 - GPSImgDirectionRef (ASCII/2)
+ // 17 - GPSImgDirection (RATIONAL/1)
+ // 18 - GPSMapDatum (ASCII/any)
+ // 19 - GPSDestLatitudeRef (ASCII/2)
+ // 20 - GPSDestLatitude (RATIONAL/3)
+ // 21 - GPSDestLongitudeRef (ASCII/2)
+ // 22 - GPSDestLongitude (RATIONAL/3)
+ // 23 - GPSDestBearingRef (ASCII/2)
+ // 24 - GPSDestBearing (RATIONAL/1)
+ // 25 - GPSDestDistanceRef (ASCII/2)
+ // 26 - GPSDestDistance (RATIONAL/1)
+
+ // 0 - Interoperability Index (ASCII/any)
+
+ // Exif tags
+
+ static class ExifVersion extends TIFFTag {
+
+ public ExifVersion() {
+ super("Exifversion",
+ TAG_EXIF_VERSION,
+ 1 << TIFFTag.TIFF_UNDEFINED,
+ 4);
+ }
+ }
+
+ static class FlashPixVersion extends TIFFTag {
+
+ public FlashPixVersion() {
+ super("FlashPixVersion",
+ TAG_FLASHPIX_VERSION,
+ 1 << TIFFTag.TIFF_UNDEFINED,
+ 4);
+ }
+ }
+
+ static class ColorSpace extends TIFFTag {
+
+ public ColorSpace() {
+ super("ColorSpace",
+ TAG_COLOR_SPACE,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(COLOR_SPACE_SRGB, "sRGB");
+ addValueName(COLOR_SPACE_UNCALIBRATED, "Uncalibrated");
+ }
+ }
+
+ static class ComponentsConfiguration extends TIFFTag {
+
+ public ComponentsConfiguration() {
+ super("ComponentsConfiguration",
+ TAG_COMPONENTS_CONFIGURATION,
+ 1 << TIFFTag.TIFF_UNDEFINED,
+ 4);
+
+ addValueName(COMPONENTS_CONFIGURATION_DOES_NOT_EXIST,
+ "DoesNotExist");
+ addValueName(COMPONENTS_CONFIGURATION_Y, "Y");
+ addValueName(COMPONENTS_CONFIGURATION_CB, "Cb");
+ addValueName(COMPONENTS_CONFIGURATION_CR, "Cr");
+ addValueName(COMPONENTS_CONFIGURATION_R, "R");
+ addValueName(COMPONENTS_CONFIGURATION_G, "G");
+ addValueName(COMPONENTS_CONFIGURATION_B, "B");
+ }
+ }
+
+ static class CompressedBitsPerPixel extends TIFFTag {
+
+ public CompressedBitsPerPixel() {
+ super("CompressedBitsPerPixel",
+ TAG_COMPRESSED_BITS_PER_PIXEL,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class PixelXDimension extends TIFFTag {
+
+ public PixelXDimension() {
+ super("PixelXDimension",
+ TAG_PIXEL_X_DIMENSION,
+ (1 << TIFFTag.TIFF_SHORT) |
+ (1 << TIFFTag.TIFF_LONG),
+ 1);
+ }
+ }
+
+ static class PixelYDimension extends TIFFTag {
+
+ public PixelYDimension() {
+ super("PixelYDimension",
+ TAG_PIXEL_Y_DIMENSION,
+ (1 << TIFFTag.TIFF_SHORT) |
+ (1 << TIFFTag.TIFF_LONG),
+ 1);
+ }
+ }
+
+ static class MakerNote extends TIFFTag {
+
+ public MakerNote() {
+ super("MakerNote",
+ TAG_MAKER_NOTE,
+ 1 << TIFFTag.TIFF_UNDEFINED);
+ }
+ }
+
+ static class UserComment extends TIFFTag {
+
+ public UserComment() {
+ super("UserComment",
+ TAG_USER_COMMENT,
+ 1 << TIFFTag.TIFF_UNDEFINED);
+ }
+ }
+
+ static class RelatedSoundFile extends TIFFTag {
+
+ public RelatedSoundFile() {
+ super("RelatedSoundFile",
+ TAG_RELATED_SOUND_FILE,
+ 1 << TIFFTag.TIFF_ASCII,
+ 13);
+ }
+ }
+
+ static class DateTimeOriginal extends TIFFTag {
+
+ public DateTimeOriginal() {
+ super("DateTimeOriginal",
+ TAG_DATE_TIME_ORIGINAL,
+ 1 << TIFFTag.TIFF_ASCII,
+ 20);
+ }
+ }
+
+ static class DateTimeDigitized extends TIFFTag {
+
+ public DateTimeDigitized() {
+ super("DateTimeDigitized",
+ TAG_DATE_TIME_DIGITIZED,
+ 1 << TIFFTag.TIFF_ASCII,
+ 20);
+ }
+ }
+
+ static class SubSecTime extends TIFFTag {
+
+ public SubSecTime() {
+ super("SubSecTime",
+ TAG_SUB_SEC_TIME,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class SubSecTimeOriginal extends TIFFTag {
+
+ public SubSecTimeOriginal() {
+ super("SubSecTimeOriginal",
+ TAG_SUB_SEC_TIME_ORIGINAL,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class SubSecTimeDigitized extends TIFFTag {
+
+ public SubSecTimeDigitized() {
+ super("SubSecTimeDigitized",
+ TAG_SUB_SEC_TIME_DIGITIZED,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class ExposureTime extends TIFFTag {
+
+ public ExposureTime() {
+ super("ExposureTime",
+ TAG_EXPOSURE_TIME,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class FNumber extends TIFFTag {
+
+ public FNumber() {
+ super("FNumber",
+ TAG_F_NUMBER,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class ExposureProgram extends TIFFTag {
+
+ public ExposureProgram() {
+ super("ExposureProgram",
+ TAG_EXPOSURE_PROGRAM,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(EXPOSURE_PROGRAM_NOT_DEFINED, "Not Defined");
+ addValueName(EXPOSURE_PROGRAM_MANUAL, "Manual");
+ addValueName(EXPOSURE_PROGRAM_NORMAL_PROGRAM, "Normal Program");
+ addValueName(EXPOSURE_PROGRAM_APERTURE_PRIORITY,
+ "Aperture Priority");
+ addValueName(EXPOSURE_PROGRAM_SHUTTER_PRIORITY,
+ "Shutter Priority");
+ addValueName(EXPOSURE_PROGRAM_CREATIVE_PROGRAM,
+ "Creative Program");
+ addValueName(EXPOSURE_PROGRAM_ACTION_PROGRAM, "Action Program");
+ addValueName(EXPOSURE_PROGRAM_PORTRAIT_MODE, "Portrait Mode");
+ addValueName(EXPOSURE_PROGRAM_LANDSCAPE_MODE, "Landscape Mode");
+ }
+ }
+
+ static class SpectralSensitivity extends TIFFTag {
+ public SpectralSensitivity() {
+ super("SpectralSensitivity",
+ TAG_SPECTRAL_SENSITIVITY,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ static class ISOSpeedRatings extends TIFFTag {
+
+ public ISOSpeedRatings() {
+ super("ISOSpeedRatings",
+ TAG_ISO_SPEED_RATINGS,
+ 1 << TIFFTag.TIFF_SHORT);
+ }
+ }
+
+ static class OECF extends TIFFTag {
+
+ public OECF() {
+ super("OECF",
+ TAG_OECF,
+ 1 << TIFFTag.TIFF_UNDEFINED);
+ }
+ }
+
+ static class ShutterSpeedValue extends TIFFTag {
+
+ public ShutterSpeedValue() {
+ super("ShutterSpeedValue",
+ TAG_SHUTTER_SPEED_VALUE,
+ 1 << TIFFTag.TIFF_SRATIONAL,
+ 1);
+ }
+ }
+
+ static class ApertureValue extends TIFFTag {
+
+ public ApertureValue() {
+ super("ApertureValue",
+ TAG_APERTURE_VALUE,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class BrightnessValue extends TIFFTag {
+
+ public BrightnessValue() {
+ super("BrightnessValue",
+ TAG_BRIGHTNESS_VALUE,
+ 1 << TIFFTag.TIFF_SRATIONAL,
+ 1);
+ }
+ }
+
+ static class ExposureBiasValue extends TIFFTag {
+
+ public ExposureBiasValue() {
+ super("ExposureBiasValue",
+ TAG_EXPOSURE_BIAS_VALUE,
+ 1 << TIFFTag.TIFF_SRATIONAL,
+ 1);
+ }
+ }
+
+ static class MaxApertureValue extends TIFFTag {
+
+ public MaxApertureValue() {
+ super("MaxApertureValue",
+ TAG_MAX_APERTURE_VALUE,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class SubjectDistance extends TIFFTag {
+
+ public SubjectDistance() {
+ super("SubjectDistance",
+ TAG_SUBJECT_DISTANCE,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class MeteringMode extends TIFFTag {
+
+ public MeteringMode() {
+ super("MeteringMode",
+ TAG_METERING_MODE,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(METERING_MODE_UNKNOWN, "Unknown");
+ addValueName(METERING_MODE_AVERAGE, "Average");
+ addValueName(METERING_MODE_CENTER_WEIGHTED_AVERAGE,
+ "CenterWeightedAverage");
+ addValueName(METERING_MODE_SPOT, "Spot");
+ addValueName(METERING_MODE_MULTI_SPOT, "MultiSpot");
+ addValueName(METERING_MODE_PATTERN, "Pattern");
+ addValueName(METERING_MODE_PARTIAL, "Partial");
+ addValueName(METERING_MODE_OTHER, "Other");
+ }
+ }
+
+ static class LightSource extends TIFFTag {
+
+ public LightSource() {
+ super("LightSource",
+ TAG_LIGHT_SOURCE,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(LIGHT_SOURCE_UNKNOWN, "Unknown");
+ addValueName(LIGHT_SOURCE_DAYLIGHT, "Daylight");
+ addValueName(LIGHT_SOURCE_FLUORESCENT, "Fluorescent");
+ addValueName(LIGHT_SOURCE_TUNGSTEN, "Tungsten");
+ addValueName(LIGHT_SOURCE_STANDARD_LIGHT_A, "Standard Light A");
+ addValueName(LIGHT_SOURCE_STANDARD_LIGHT_B, "Standard Light B");
+ addValueName(LIGHT_SOURCE_STANDARD_LIGHT_C, "Standard Light C");
+ addValueName(LIGHT_SOURCE_D55, "D55");
+ addValueName(LIGHT_SOURCE_D65, "D65");
+ addValueName(LIGHT_SOURCE_D75, "D75");
+ addValueName(LIGHT_SOURCE_OTHER, "Other");
+ }
+ }
+
+ static class Flash extends TIFFTag {
+
+ public Flash() {
+ super("Flash",
+ TAG_FLASH,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(FLASH_DID_NOT_FIRE, "Flash Did Not Fire");
+ addValueName(FLASH_FIRED, "Flash Fired");
+ addValueName(FLASH_STROBE_RETURN_LIGHT_NOT_DETECTED,
+ "Strobe Return Light Not Detected");
+ addValueName(FLASH_STROBE_RETURN_LIGHT_DETECTED,
+ "Strobe Return Light Detected");
+ }
+ }
+
+ static class FocalLength extends TIFFTag {
+
+ public FocalLength() {
+ super("FocalLength",
+ TAG_FOCAL_LENGTH,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class SubjectArea extends TIFFTag {
+
+ public SubjectArea() {
+ super("SubjectArea",
+ TAG_SUBJECT_AREA,
+ 1 << TIFFTag.TIFF_SHORT);
+ }
+ }
+
+ static class FlashEnergy extends TIFFTag {
+
+ public FlashEnergy() {
+ super("FlashEnergy",
+ TAG_FLASH_ENERGY,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class SpatialFrequencyResponse extends TIFFTag {
+
+ public SpatialFrequencyResponse() {
+ super("SpatialFrequencyResponse",
+ TAG_SPATIAL_FREQUENCY_RESPONSE,
+ 1 << TIFFTag.TIFF_UNDEFINED);
+ }
+ }
+
+ static class FocalPlaneXResolution extends TIFFTag {
+
+ public FocalPlaneXResolution() {
+ super("FocalPlaneXResolution",
+ TAG_FOCAL_PLANE_X_RESOLUTION,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class FocalPlaneYResolution extends TIFFTag {
+
+ public FocalPlaneYResolution() {
+ super("FocalPlaneYResolution",
+ TAG_FOCAL_PLANE_Y_RESOLUTION,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class FocalPlaneResolutionUnit extends TIFFTag {
+
+ public FocalPlaneResolutionUnit() {
+ super("FocalPlaneResolutionUnit",
+ TAG_FOCAL_PLANE_RESOLUTION_UNIT,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(FOCAL_PLANE_RESOLUTION_UNIT_NONE, "None");
+ addValueName(FOCAL_PLANE_RESOLUTION_UNIT_INCH, "Inch");
+ addValueName(FOCAL_PLANE_RESOLUTION_UNIT_CENTIMETER, "Centimeter");
+ }
+ }
+
+ static class SubjectLocation extends TIFFTag {
+
+ public SubjectLocation() {
+ super("SubjectLocation",
+ TAG_SUBJECT_LOCATION,
+ 1 << TIFFTag.TIFF_SHORT,
+ 2);
+ }
+ }
+
+ static class ExposureIndex extends TIFFTag {
+
+ public ExposureIndex() {
+ super("ExposureIndex",
+ TAG_EXPOSURE_INDEX,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class SensingMethod extends TIFFTag {
+
+ public SensingMethod() {
+ super("SensingMethod",
+ TAG_SENSING_METHOD,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(SENSING_METHOD_NOT_DEFINED, "Not Defined");
+ addValueName(SENSING_METHOD_ONE_CHIP_COLOR_AREA_SENSOR,
+ "One-chip color area sensor");
+ addValueName(SENSING_METHOD_TWO_CHIP_COLOR_AREA_SENSOR,
+ "Two-chip color area sensor");
+ addValueName(SENSING_METHOD_THREE_CHIP_COLOR_AREA_SENSOR,
+ "Three-chip color area sensor");
+ addValueName(SENSING_METHOD_COLOR_SEQUENTIAL_AREA_SENSOR,
+ "Color sequential area sensor");
+ addValueName(SENSING_METHOD_TRILINEAR_SENSOR, "Trilinear sensor");
+ addValueName(SENSING_METHOD_COLOR_SEQUENTIAL_LINEAR_SENSOR,
+ "Color sequential linear sensor");
+ }
+ }
+
+ static class FileSource extends TIFFTag {
+
+ public FileSource() {
+ super("FileSource",
+ TAG_FILE_SOURCE,
+ 1 << TIFFTag.TIFF_UNDEFINED,
+ 1);
+
+ addValueName(FILE_SOURCE_DSC, "DSC");
+ }
+ }
+
+ static class SceneType extends TIFFTag {
+
+ public SceneType() {
+ super("SceneType",
+ TAG_SCENE_TYPE,
+ 1 << TIFFTag.TIFF_UNDEFINED,
+ 1);
+
+ addValueName(SCENE_TYPE_DSC, "A directly photographed image");
+ }
+ }
+
+ static class CFAPattern extends TIFFTag {
+
+ public CFAPattern() {
+ super("CFAPattern",
+ TAG_CFA_PATTERN,
+ 1 << TIFFTag.TIFF_UNDEFINED);
+ }
+ }
+
+ static class CustomRendered extends TIFFTag {
+
+ public CustomRendered() {
+ super("CustomRendered",
+ TAG_CUSTOM_RENDERED,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(CUSTOM_RENDERED_NORMAL, "Normal process");
+ addValueName(CUSTOM_RENDERED_CUSTOM, "Custom process");
+ }
+ }
+
+ static class ExposureMode extends TIFFTag {
+
+ public ExposureMode() {
+ super("ExposureMode",
+ TAG_EXPOSURE_MODE,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(EXPOSURE_MODE_AUTO_EXPOSURE, "Auto exposure");
+ addValueName(EXPOSURE_MODE_MANUAL_EXPOSURE, "Manual exposure");
+ addValueName(EXPOSURE_MODE_AUTO_BRACKET, "Auto bracket");
+ }
+ }
+
+ static class WhiteBalance extends TIFFTag {
+
+ public WhiteBalance() {
+ super("WhiteBalance",
+ TAG_WHITE_BALANCE,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(WHITE_BALANCE_AUTO, "Auto white balance");
+ addValueName(WHITE_BALANCE_MANUAL, "Manual white balance");
+ }
+ }
+
+ static class DigitalZoomRatio extends TIFFTag {
+
+ public DigitalZoomRatio() {
+ super("DigitalZoomRatio",
+ TAG_DIGITAL_ZOOM_RATIO,
+ 1 << TIFFTag.TIFF_RATIONAL,
+ 1);
+ }
+ }
+
+ static class FocalLengthIn35mmFilm extends TIFFTag {
+
+ public FocalLengthIn35mmFilm() {
+ super("FocalLengthIn35mmFilm",
+ TAG_FOCAL_LENGTH_IN_35MM_FILM,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+ }
+ }
+
+ static class SceneCaptureType extends TIFFTag {
+
+ public SceneCaptureType() {
+ super("SceneCaptureType",
+ TAG_SCENE_CAPTURE_TYPE,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(SCENE_CAPTURE_TYPE_STANDARD, "Standard");
+ addValueName(SCENE_CAPTURE_TYPE_LANDSCAPE, "Landscape");
+ addValueName(SCENE_CAPTURE_TYPE_PORTRAIT, "Portrait");
+ addValueName(SCENE_CAPTURE_TYPE_NIGHT_SCENE, "Night scene");
+ }
+ }
+
+ static class GainControl extends TIFFTag {
+
+ public GainControl() {
+ super("GainControl",
+ TAG_GAIN_CONTROL,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(GAIN_CONTROL_NONE, "None");
+ addValueName(GAIN_CONTROL_LOW_GAIN_UP, "Low gain up");
+ addValueName(GAIN_CONTROL_HIGH_GAIN_UP, "High gain up");
+ addValueName(GAIN_CONTROL_LOW_GAIN_DOWN, "Low gain down");
+ addValueName(GAIN_CONTROL_HIGH_GAIN_DOWN, "High gain down");
+ }
+ }
+
+ static class Contrast extends TIFFTag {
+
+ public Contrast() {
+ super("Contrast",
+ TAG_CONTRAST,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(CONTRAST_NORMAL, "Normal");
+ addValueName(CONTRAST_SOFT, "Soft");
+ addValueName(CONTRAST_HARD, "Hard");
+ }
+ }
+
+ static class Saturation extends TIFFTag {
+
+ public Saturation() {
+ super("Saturation",
+ TAG_SATURATION,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(SATURATION_NORMAL, "Normal");
+ addValueName(SATURATION_LOW, "Low saturation");
+ addValueName(SATURATION_HIGH, "High saturation");
+ }
+ }
+
+ static class Sharpness extends TIFFTag {
+
+ public Sharpness() {
+ super("Sharpness",
+ TAG_SHARPNESS,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(SHARPNESS_NORMAL, "Normal");
+ addValueName(SHARPNESS_SOFT, "Soft");
+ addValueName(SHARPNESS_HARD, "Hard");
+ }
+ }
+
+ static class DeviceSettingDescription extends TIFFTag {
+
+ public DeviceSettingDescription() {
+ super("DeviceSettingDescription",
+ TAG_DEVICE_SETTING_DESCRIPTION,
+ 1 << TIFFTag.TIFF_UNDEFINED);
+ }
+ }
+
+ static class SubjectDistanceRange extends TIFFTag {
+
+ public SubjectDistanceRange() {
+ super("SubjectDistanceRange",
+ TAG_SUBJECT_DISTANCE_RANGE,
+ 1 << TIFFTag.TIFF_SHORT,
+ 1);
+
+ addValueName(SUBJECT_DISTANCE_RANGE_UNKNOWN, "unknown");
+ addValueName(SUBJECT_DISTANCE_RANGE_MACRO, "Macro");
+ addValueName(SUBJECT_DISTANCE_RANGE_CLOSE_VIEW, "Close view");
+ addValueName(SUBJECT_DISTANCE_RANGE_DISTANT_VIEW, "Distant view");
+ }
+ }
+
+ static class ImageUniqueID extends TIFFTag {
+
+ public ImageUniqueID() {
+ super("ImageUniqueID",
+ TAG_IMAGE_UNIQUE_ID,
+ 1 << TIFFTag.TIFF_ASCII,
+ 33);
+ }
+ }
+
+ static class InteroperabilityIFD extends TIFFTag {
+ public InteroperabilityIFD() {
+ super("InteroperabilityIFD",
+ TAG_INTEROPERABILITY_IFD_POINTER,
+ ExifInteroperabilityTagSet.getInstance());
+ }
+ }
+
+ private static List<TIFFTag> tags;
+
+ private static void initTags() {
+ tags = new ArrayList<TIFFTag>(42);
+
+ tags.add(new ExifTIFFTagSet.ExifVersion());
+ tags.add(new ExifTIFFTagSet.FlashPixVersion());
+ tags.add(new ExifTIFFTagSet.ColorSpace());
+ tags.add(new ExifTIFFTagSet.ComponentsConfiguration());
+ tags.add(new ExifTIFFTagSet.CompressedBitsPerPixel());
+ tags.add(new ExifTIFFTagSet.PixelXDimension());
+ tags.add(new ExifTIFFTagSet.PixelYDimension());
+ tags.add(new ExifTIFFTagSet.MakerNote());
+ tags.add(new ExifTIFFTagSet.UserComment());
+ tags.add(new ExifTIFFTagSet.RelatedSoundFile());
+ tags.add(new ExifTIFFTagSet.DateTimeOriginal());
+ tags.add(new ExifTIFFTagSet.DateTimeDigitized());
+ tags.add(new ExifTIFFTagSet.SubSecTime());
+ tags.add(new ExifTIFFTagSet.SubSecTimeOriginal());
+ tags.add(new ExifTIFFTagSet.SubSecTimeDigitized());
+ tags.add(new ExifTIFFTagSet.ExposureTime());
+ tags.add(new ExifTIFFTagSet.FNumber());
+ tags.add(new ExifTIFFTagSet.ExposureProgram());
+ tags.add(new ExifTIFFTagSet.SpectralSensitivity());
+ tags.add(new ExifTIFFTagSet.ISOSpeedRatings());
+ tags.add(new ExifTIFFTagSet.OECF());
+ tags.add(new ExifTIFFTagSet.ShutterSpeedValue());
+ tags.add(new ExifTIFFTagSet.ApertureValue());
+ tags.add(new ExifTIFFTagSet.BrightnessValue());
+ tags.add(new ExifTIFFTagSet.ExposureBiasValue());
+ tags.add(new ExifTIFFTagSet.MaxApertureValue());
+ tags.add(new ExifTIFFTagSet.SubjectDistance());
+ tags.add(new ExifTIFFTagSet.MeteringMode());
+ tags.add(new ExifTIFFTagSet.LightSource());
+ tags.add(new ExifTIFFTagSet.Flash());
+ tags.add(new ExifTIFFTagSet.FocalLength());
+ tags.add(new ExifTIFFTagSet.SubjectArea());
+ tags.add(new ExifTIFFTagSet.FlashEnergy());
+ tags.add(new ExifTIFFTagSet.SpatialFrequencyResponse());
+ tags.add(new ExifTIFFTagSet.FocalPlaneXResolution());
+ tags.add(new ExifTIFFTagSet.FocalPlaneYResolution());
+ tags.add(new ExifTIFFTagSet.FocalPlaneResolutionUnit());
+ tags.add(new ExifTIFFTagSet.SubjectLocation());
+ tags.add(new ExifTIFFTagSet.ExposureIndex());
+ tags.add(new ExifTIFFTagSet.SensingMethod());
+ tags.add(new ExifTIFFTagSet.FileSource());
+ tags.add(new ExifTIFFTagSet.SceneType());
+ tags.add(new ExifTIFFTagSet.CFAPattern());
+ tags.add(new ExifTIFFTagSet.CustomRendered());
+ tags.add(new ExifTIFFTagSet.ExposureMode());
+ tags.add(new ExifTIFFTagSet.WhiteBalance());
+ tags.add(new ExifTIFFTagSet.DigitalZoomRatio());
+ tags.add(new ExifTIFFTagSet.FocalLengthIn35mmFilm());
+ tags.add(new ExifTIFFTagSet.SceneCaptureType());
+ tags.add(new ExifTIFFTagSet.GainControl());
+ tags.add(new ExifTIFFTagSet.Contrast());
+ tags.add(new ExifTIFFTagSet.Saturation());
+ tags.add(new ExifTIFFTagSet.Sharpness());
+ tags.add(new ExifTIFFTagSet.DeviceSettingDescription());
+ tags.add(new ExifTIFFTagSet.SubjectDistanceRange());
+ tags.add(new ExifTIFFTagSet.ImageUniqueID());
+ tags.add(new ExifTIFFTagSet.InteroperabilityIFD());
+ }
+
+ private ExifTIFFTagSet() {
+ super(tags);
+ }
+
+ /**
+ * Returns a shared instance of an <code>ExifTIFFTagSet</code>.
+ *
+ * @return an <code>ExifTIFFTagSet</code> instance.
+ */
+ public synchronized static ExifTIFFTagSet getInstance() {
+ if (theInstance == null) {
+ initTags();
+ theInstance = new ExifTIFFTagSet();
+ tags = null;
+ }
+ return theInstance;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/FaxTIFFTagSet.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,146 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class representing the extra tags found in a
+ * <a href="http://tools.ietf.org/html/rfc2306"> TIFF-F</a> (RFC 2036) file.
+ *
+ * @since 1.9
+ */
+public class FaxTIFFTagSet extends TIFFTagSet {
+
+ private static FaxTIFFTagSet theInstance = null;
+
+ /** Tag indicating the number of bad fax lines (type SHORT or LONG). */
+ public static final int TAG_BAD_FAX_LINES = 326;
+
+ /**
+ * Tag indicating the number of lines of clean fax data (type
+ * SHORT).
+ *
+ * @see #CLEAN_FAX_DATA_NO_ERRORS
+ * @see #CLEAN_FAX_DATA_ERRORS_CORRECTED
+ * @see #CLEAN_FAX_DATA_ERRORS_UNCORRECTED
+ */
+ public static final int TAG_CLEAN_FAX_DATA = 327;
+
+ /**
+ * A value to be used with the "CleanFaxData" tag.
+ *
+ * @see #TAG_CLEAN_FAX_DATA
+ */
+ public static final int CLEAN_FAX_DATA_NO_ERRORS = 0;
+
+ /**
+ * A value to be used with the "CleanFaxData" tag.
+ *
+ * @see #TAG_CLEAN_FAX_DATA
+ */
+ public static final int CLEAN_FAX_DATA_ERRORS_CORRECTED = 1;
+
+ /**
+ * A value to be used with the "CleanFaxData" tag.
+ *
+ * @see #TAG_CLEAN_FAX_DATA
+ */
+ public static final int CLEAN_FAX_DATA_ERRORS_UNCORRECTED = 2;
+
+ /**
+ * Tag indicating the number of consecutive bad lines (type
+ * SHORT or LONG).
+ */
+ public static final int TAG_CONSECUTIVE_BAD_LINES = 328;
+
+ static class BadFaxLines extends TIFFTag {
+
+ public BadFaxLines() {
+ super("BadFaxLines",
+ TAG_BAD_FAX_LINES,
+ 1 << TIFF_SHORT |
+ 1 << TIFF_LONG,
+ 1);
+ }
+ }
+
+ static class CleanFaxData extends TIFFTag {
+
+ public CleanFaxData() {
+ super("CleanFaxData",
+ TAG_CLEAN_FAX_DATA,
+ 1 << TIFF_SHORT,
+ 1);
+
+ addValueName(CLEAN_FAX_DATA_NO_ERRORS,
+ "No errors");
+ addValueName(CLEAN_FAX_DATA_ERRORS_CORRECTED,
+ "Errors corrected");
+ addValueName(CLEAN_FAX_DATA_ERRORS_UNCORRECTED,
+ "Errors uncorrected");
+ }
+ }
+
+ static class ConsecutiveBadFaxLines extends TIFFTag {
+
+ public ConsecutiveBadFaxLines() {
+ super("ConsecutiveBadFaxLines",
+ TAG_CONSECUTIVE_BAD_LINES,
+ 1 << TIFF_SHORT |
+ 1 << TIFF_LONG,
+ 1);
+ }
+ }
+
+ private static List<TIFFTag> tags;
+
+ private static void initTags() {
+ tags = new ArrayList<TIFFTag>(42);
+
+ tags.add(new FaxTIFFTagSet.BadFaxLines());
+ tags.add(new FaxTIFFTagSet.CleanFaxData());
+ tags.add(new FaxTIFFTagSet.ConsecutiveBadFaxLines());
+ }
+
+ private FaxTIFFTagSet() {
+ super(tags);
+ }
+
+ /**
+ * Returns a shared instance of a <code>FaxTIFFTagSet</code>.
+ *
+ * @return a <code>FaxTIFFTagSet</code> instance.
+ */
+ public synchronized static FaxTIFFTagSet getInstance() {
+ if (theInstance == null) {
+ initTags();
+ theInstance = new FaxTIFFTagSet();
+ tags = null;
+ }
+ return theInstance;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/GeoTIFFTagSet.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,152 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class representing the tags found in a GeoTIFF IFD. GeoTIFF is a
+ * standard for annotating georeferenced or geocoded raster imagery.
+ * The GeoTIFF specification may be found at <a
+ * href="http://www.remotesensing.org/geotiff/spec/geotiffhome.html">
+ * <code>http://www.remotesensing.org/geotiff/spec/geotiffhome.html</code>
+ * </a>. This class does <i>not</i> handle the <i>GeoKey</i>s referenced
+ * from a <i>GeoKeyDirectoryTag</i> as those are not TIFF tags per se.
+ *
+ * <p>The definitions of the data types referenced by the field
+ * definitions may be found in the {@link TIFFTag TIFFTag} class.</p>
+ *
+ * @since 1.9
+ */
+public class GeoTIFFTagSet extends TIFFTagSet {
+
+ private static GeoTIFFTagSet theInstance = null;
+
+ /**
+ * A tag used to specify the size of raster pixel spacing in
+ * model space units.
+ */
+ public static final int TAG_MODEL_PIXEL_SCALE = 33550;
+
+ /**
+ * A tag used to specify the transformation matrix between the raster
+ * space and the model space.
+ */
+ public static final int TAG_MODEL_TRANSFORMATION = 34264;
+
+ /** A tag used to store raster-to-model tiepoint pairs. */
+ public static final int TAG_MODEL_TIE_POINT = 33922;
+
+ /** A tag used to store the <i>GeoKey</i> directory. */
+ public static final int TAG_GEO_KEY_DIRECTORY = 34735;
+
+ /** A tag used to store all <code>double</code>-values <i>GeoKey</i>s. */
+ public static final int TAG_GEO_DOUBLE_PARAMS = 34736;
+
+ /** A tag used to store all ASCII-values <i>GeoKey</i>s. */
+ public static final int TAG_GEO_ASCII_PARAMS = 34737;
+
+ // GeoTIFF tags
+
+ static class ModelPixelScale extends TIFFTag {
+ public ModelPixelScale() {
+ super("ModelPixelScaleTag",
+ TAG_MODEL_PIXEL_SCALE,
+ 1 << TIFFTag.TIFF_DOUBLE);
+ }
+ }
+
+ static class ModelTransformation extends TIFFTag {
+ public ModelTransformation() {
+ super("ModelTransformationTag",
+ TAG_MODEL_TRANSFORMATION,
+ 1 << TIFFTag.TIFF_DOUBLE);
+ }
+ }
+
+ static class ModelTiePoint extends TIFFTag {
+ public ModelTiePoint() {
+ super("ModelTiePointTag",
+ TAG_MODEL_TIE_POINT,
+ 1 << TIFFTag.TIFF_DOUBLE);
+ }
+ }
+
+ static class GeoKeyDirectory extends TIFFTag {
+ public GeoKeyDirectory() {
+ super("GeoKeyDirectory",
+ TAG_GEO_KEY_DIRECTORY,
+ 1 << TIFFTag.TIFF_SHORT);
+ }
+ }
+
+ static class GeoDoubleParams extends TIFFTag {
+ public GeoDoubleParams() {
+ super("GeoDoubleParams",
+ TAG_GEO_DOUBLE_PARAMS,
+ 1 << TIFFTag.TIFF_DOUBLE);
+ }
+ }
+
+ static class GeoAsciiParams extends TIFFTag {
+ public GeoAsciiParams() {
+ super("GeoAsciiParams",
+ TAG_GEO_ASCII_PARAMS,
+ 1 << TIFFTag.TIFF_ASCII);
+ }
+ }
+
+ private static List<TIFFTag> tags;
+
+ private static void initTags() {
+ tags = new ArrayList<TIFFTag>(42);
+
+ tags.add(new GeoTIFFTagSet.ModelPixelScale());
+ tags.add(new GeoTIFFTagSet.ModelTransformation());
+ tags.add(new GeoTIFFTagSet.ModelTiePoint());
+ tags.add(new GeoTIFFTagSet.GeoKeyDirectory());
+ tags.add(new GeoTIFFTagSet.GeoDoubleParams());
+ tags.add(new GeoTIFFTagSet.GeoAsciiParams());
+ }
+
+ private GeoTIFFTagSet() {
+ super(tags);
+ }
+
+ /**
+ * Returns a shared instance of a <code>GeoTIFFTagSet</code>.
+ *
+ * @return a <code>GeoTIFFTagSet</code> instance.
+ */
+ public synchronized static GeoTIFFTagSet getInstance() {
+ if (theInstance == null) {
+ initTags();
+ theInstance = new GeoTIFFTagSet();
+ tags = null;
+ }
+ return theInstance;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFDirectory.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,471 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import javax.imageio.metadata.IIOInvalidTreeException;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataFormatImpl;
+import com.sun.imageio.plugins.tiff.TIFFIFD;
+import com.sun.imageio.plugins.tiff.TIFFImageMetadata;
+
+/**
+ * A convenience class for simplifying interaction with TIFF native
+ * image metadata. A TIFF image metadata tree represents an Image File
+ * Directory (IFD) from a TIFF 6.0 stream. An IFD consists of a number of
+ * IFD Entries each of which associates an identifying tag number with
+ * a compatible value. A <code>TIFFDirectory</code> instance corresponds
+ * to an IFD and contains a set of {@link TIFFField}s each of which
+ * corresponds to an IFD Entry in the IFD.
+ *
+ * <p>When reading, a <code>TIFFDirectory</code> may be created by passing
+ * the value returned by {@link javax.imageio.ImageReader#getImageMetadata
+ * ImageReader.getImageMetadata()} to {@link #createFromMetadata
+ * createFromMetadata()}. The {@link TIFFField}s in the directory may then
+ * be obtained using the accessor methods provided in this class.</p>
+ *
+ * <p>When writing, an {@link IIOMetadata} object for use by one of the
+ * <code>write()</code> methods of {@link javax.imageio.ImageWriter} may be
+ * created from a <code>TIFFDirectory</code> by {@link #getAsMetadata()}.
+ * The <code>TIFFDirectory</code> itself may be created by construction or
+ * from the <code>IIOMetadata</code> object returned by
+ * {@link javax.imageio.ImageWriter#getDefaultImageMetadata
+ * ImageWriter.getDefaultImageMetadata()}. The <code>TIFFField</code>s in the
+ * directory may be set using the mutator methods provided in this class.</p>
+ *
+ * <p>A <code>TIFFDirectory</code> is aware of the tag numbers in the
+ * group of {@link TIFFTagSet}s associated with it. When
+ * a <code>TIFFDirectory</code> is created from a native image metadata
+ * object, these tag sets are derived from the <tt>tagSets</tt> attribute
+ * of the <tt>TIFFIFD</tt> node.</p>
+ *
+ * <p>A <code>TIFFDirectory</code> might also have a parent {@link TIFFTag}.
+ * This will occur if the directory represents an IFD other than the root
+ * IFD of the image. The parent tag is the tag of the IFD Entry which is a
+ * pointer to the IFD represented by this <code>TIFFDirectory</code>. The
+ * {@link TIFFTag#isIFDPointer} method of this parent <code>TIFFTag</code>
+ * must return <code>true</code>. When a <code>TIFFDirectory</code> is
+ * created from a native image metadata object, the parent tag set is set
+ * from the <tt>parentTagName</tt> attribute of the corresponding
+ * <tt>TIFFIFD</tt> node. Note that a <code>TIFFDirectory</code> instance
+ * which has a non-<code>null</code> parent tag will be contained in the
+ * data field of a <code>TIFFField</code> instance which has a tag field
+ * equal to the contained directory's parent tag.</p>
+ *
+ * <p>As an example consider an Exif image. The <code>TIFFDirectory</code>
+ * instance corresponding to the Exif IFD in the Exif stream would have parent
+ * tag {@link ExifParentTIFFTagSet#TAG_EXIF_IFD_POINTER TAG_EXIF_IFD_POINTER}
+ * and would include {@link ExifTIFFTagSet} in its group of known tag sets.
+ * The <code>TIFFDirectory</code> corresponding to this Exif IFD will be
+ * contained in the data field of a <code>TIFFField</code> which will in turn
+ * be contained in the <code>TIFFDirectory</code> corresponding to the primary
+ * IFD of the Exif image which will itself have a <code>null</code>-valued
+ * parent tag.</p>
+ *
+ * <p><b>Note that this implementation is not synchronized. </b>If multiple
+ * threads use a <code>TIFFDirectory</code> instance concurrently, and at
+ * least one of the threads modifies the directory, for example, by adding
+ * or removing <code>TIFFField</code>s or <code>TIFFTagSet</code>s, it
+ * <i>must</i> be synchronized externally.</p>
+ *
+ * @since 1.9
+ * @see IIOMetadata
+ * @see TIFFField
+ * @see TIFFTag
+ * @see TIFFTagSet
+ */
+public class TIFFDirectory implements Cloneable {
+
+ /** The largest low-valued tag number in the TIFF 6.0 specification. */
+ private static final int MAX_LOW_FIELD_TAG_NUM =
+ BaselineTIFFTagSet.TAG_REFERENCE_BLACK_WHITE;
+
+ /** The <code>TIFFTagSets</code> associated with this directory. */
+ private List<TIFFTagSet> tagSets;
+
+ /** The parent <code>TIFFTag</code> of this directory. */
+ private TIFFTag parentTag;
+
+ /**
+ * The fields in this directory which have a low tag number. These are
+ * managed as an array for efficiency as they are the most common fields.
+ */
+ private TIFFField[] lowFields = new TIFFField[MAX_LOW_FIELD_TAG_NUM + 1];
+
+ /** The number of low tag numbered fields in the directory. */
+ private int numLowFields = 0;
+
+ /**
+ * A mapping of <code>Integer</code> tag numbers to <code>TIFFField</code>s
+ * for fields which are not low tag numbered.
+ */
+ private Map<Integer,TIFFField> highFields = new TreeMap<Integer,TIFFField>();
+
+ /**
+ * Creates a <code>TIFFDirectory</code> instance from the contents of
+ * an image metadata object. The supplied object must support an image
+ * metadata format supported by the TIFF {@link javax.imageio.ImageWriter}
+ * plug-in. This will usually be either the TIFF native image metadata
+ * format <tt>javax_imageio_tiff_image_1.0</tt> or the Java
+ * Image I/O standard metadata format <tt>javax_imageio_1.0</tt>.
+ *
+ * @param tiffImageMetadata A metadata object which supports a compatible
+ * image metadata format.
+ *
+ * @return A <code>TIFFDirectory</code> populated from the contents of
+ * the supplied metadata object.
+ *
+ * @throws NullPointerException if <code>tiffImageMetadata</code>
+ * is <code>null</code>.
+ * @throws IllegalArgumentException if <code>tiffImageMetadata</code>
+ * does not support a compatible image metadata format.
+ * @throws IIOInvalidTreeException if the supplied metadata object
+ * cannot be parsed.
+ */
+ public static TIFFDirectory
+ createFromMetadata(IIOMetadata tiffImageMetadata)
+ throws IIOInvalidTreeException {
+
+ if(tiffImageMetadata == null) {
+ throw new NullPointerException("tiffImageMetadata == null");
+ }
+
+ TIFFImageMetadata tim;
+ if(tiffImageMetadata instanceof TIFFImageMetadata) {
+ tim = (TIFFImageMetadata)tiffImageMetadata;
+ } else {
+ // Create a native metadata object.
+ ArrayList<TIFFTagSet> l = new ArrayList<TIFFTagSet>(1);
+ l.add(BaselineTIFFTagSet.getInstance());
+ tim = new TIFFImageMetadata(l);
+
+ // Determine the format name to use.
+ String formatName = null;
+ if(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME.equals
+ (tiffImageMetadata.getNativeMetadataFormatName())) {
+ formatName = TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME;
+ } else {
+ String[] extraNames =
+ tiffImageMetadata.getExtraMetadataFormatNames();
+ if(extraNames != null) {
+ for(int i = 0; i < extraNames.length; i++) {
+ if(TIFFImageMetadata.NATIVE_METADATA_FORMAT_NAME.equals
+ (extraNames[i])) {
+ formatName = extraNames[i];
+ break;
+ }
+ }
+ }
+
+ if(formatName == null) {
+ if(tiffImageMetadata.isStandardMetadataFormatSupported()) {
+ formatName =
+ IIOMetadataFormatImpl.standardMetadataFormatName;
+ } else {
+ throw new IllegalArgumentException
+ ("Parameter does not support required metadata format!");
+ }
+ }
+ }
+
+ // Set the native metadata object from the tree.
+ tim.setFromTree(formatName,
+ tiffImageMetadata.getAsTree(formatName));
+ }
+
+ return tim.getRootIFD();
+ }
+
+ /**
+ * Converts a <code>TIFFDirectory</code> to a <code>TIFFIFD</code>.
+ */
+ private static TIFFIFD getDirectoryAsIFD(TIFFDirectory dir) {
+ if(dir instanceof TIFFIFD) {
+ return (TIFFIFD)dir;
+ }
+
+ TIFFIFD ifd = new TIFFIFD(Arrays.asList(dir.getTagSets()),
+ dir.getParentTag());
+ TIFFField[] fields = dir.getTIFFFields();
+ int numFields = fields.length;
+ for(int i = 0; i < numFields; i++) {
+ TIFFField f = fields[i];
+ TIFFTag tag = f.getTag();
+ if(tag.isIFDPointer()) {
+ TIFFDirectory subIFD =
+ getDirectoryAsIFD((TIFFDirectory)f.getData());
+ f = new TIFFField(tag, f.getType(), (long)f.getCount(), subIFD);
+ }
+ ifd.addTIFFField(f);
+ }
+
+ return ifd;
+ }
+
+ /**
+ * Constructs a <code>TIFFDirectory</code> which is aware of a given
+ * group of {@link TIFFTagSet}s. An optional parent {@link TIFFTag}
+ * may also be specified.
+ *
+ * @param tagSets The <code>TIFFTagSets</code> associated with this
+ * directory.
+ * @param parentTag The parent <code>TIFFTag</code> of this directory;
+ * may be <code>null</code>.
+ * @throws NullPointerException if <code>tagSets</code> is
+ * <code>null</code>.
+ */
+ public TIFFDirectory(TIFFTagSet[] tagSets, TIFFTag parentTag) {
+ if(tagSets == null) {
+ throw new NullPointerException("tagSets == null!");
+ }
+ this.tagSets = new ArrayList<TIFFTagSet>(tagSets.length);
+ int numTagSets = tagSets.length;
+ for(int i = 0; i < numTagSets; i++) {
+ this.tagSets.add(tagSets[i]);
+ }
+ this.parentTag = parentTag;
+ }
+
+ /**
+ * Returns the {@link TIFFTagSet}s of which this directory is aware.
+ *
+ * @return The <code>TIFFTagSet</code>s associated with this
+ * <code>TIFFDirectory</code>.
+ */
+ public TIFFTagSet[] getTagSets() {
+ return tagSets.toArray(new TIFFTagSet[tagSets.size()]);
+ }
+
+ /**
+ * Adds an element to the group of {@link TIFFTagSet}s of which this
+ * directory is aware.
+ *
+ * @param tagSet The <code>TIFFTagSet</code> to add.
+ * @throws NullPointerException if <code>tagSet</code> is
+ * <code>null</code>.
+ */
+ public void addTagSet(TIFFTagSet tagSet) {
+ if(tagSet == null) {
+ throw new NullPointerException("tagSet == null");
+ }
+
+ if(!tagSets.contains(tagSet)) {
+ tagSets.add(tagSet);
+ }
+ }
+
+ /**
+ * Removes an element from the group of {@link TIFFTagSet}s of which this
+ * directory is aware.
+ *
+ * @param tagSet The <code>TIFFTagSet</code> to remove.
+ * @throws NullPointerException if <code>tagSet</code> is
+ * <code>null</code>.
+ */
+ public void removeTagSet(TIFFTagSet tagSet) {
+ if(tagSet == null) {
+ throw new NullPointerException("tagSet == null");
+ }
+
+ if(tagSets.contains(tagSet)) {
+ tagSets.remove(tagSet);
+ }
+ }
+
+ /**
+ * Returns the parent {@link TIFFTag} of this directory if one
+ * has been defined or <code>null</code> otherwise.
+ *
+ * @return The parent <code>TIFFTag</code> of this
+ * <code>TIFFDiectory</code> or <code>null</code>.
+ */
+ public TIFFTag getParentTag() {
+ return parentTag;
+ }
+
+ /**
+ * Returns the {@link TIFFTag} which has tag number equal to
+ * <code>tagNumber</code> or <code>null</code> if no such tag
+ * exists in the {@link TIFFTagSet}s associated with this
+ * directory.
+ *
+ * @param tagNumber The tag number of interest.
+ * @return The corresponding <code>TIFFTag</code> or <code>null</code>.
+ */
+ public TIFFTag getTag(int tagNumber) {
+ return TIFFIFD.getTag(tagNumber, tagSets);
+ }
+
+ /**
+ * Returns the number of {@link TIFFField}s in this directory.
+ *
+ * @return The number of <code>TIFFField</code>s in this
+ * <code>TIFFDirectory</code>.
+ */
+ public int getNumTIFFFields() {
+ return numLowFields + highFields.size();
+ }
+
+ /**
+ * Determines whether a TIFF field with the given tag number is
+ * contained in this directory.
+ *
+ * @param tagNumber The tag number.
+ * @return Whether a {@link TIFFTag} with tag number equal to
+ * <code>tagNumber</code> is present in this <code>TIFFDirectory</code>.
+ */
+ public boolean containsTIFFField(int tagNumber) {
+ return (tagNumber >= 0 && tagNumber <= MAX_LOW_FIELD_TAG_NUM &&
+ lowFields[tagNumber] != null) ||
+ highFields.containsKey(Integer.valueOf(tagNumber));
+ }
+
+ /**
+ * Adds a TIFF field to the directory.
+ *
+ * @param f The field to add.
+ * @throws NullPointerException if <code>f</code> is <code>null</code>.
+ */
+ public void addTIFFField(TIFFField f) {
+ if(f == null) {
+ throw new NullPointerException("f == null");
+ }
+ int tagNumber = f.getTagNumber();
+ if(tagNumber >= 0 && tagNumber <= MAX_LOW_FIELD_TAG_NUM) {
+ if(lowFields[tagNumber] == null) {
+ numLowFields++;
+ }
+ lowFields[tagNumber] = f;
+ } else {
+ highFields.put(Integer.valueOf(tagNumber), f);
+ }
+ }
+
+ /**
+ * Retrieves a TIFF field from the directory.
+ *
+ * @param tagNumber The tag number of the tag associated with the field.
+ * @return A <code>TIFFField</code> with the requested tag number of
+ * <code>null</code> if no such field is present.
+ */
+ public TIFFField getTIFFField(int tagNumber) {
+ TIFFField f;
+ if(tagNumber >= 0 && tagNumber <= MAX_LOW_FIELD_TAG_NUM) {
+ f = lowFields[tagNumber];
+ } else {
+ f = highFields.get(Integer.valueOf(tagNumber));
+ }
+ return f;
+ }
+
+ /**
+ * Removes a TIFF field from the directory.
+ *
+ * @param tagNumber The tag number of the tag associated with the field.
+ */
+ public void removeTIFFField(int tagNumber) {
+ if(tagNumber >= 0 && tagNumber <= MAX_LOW_FIELD_TAG_NUM) {
+ if(lowFields[tagNumber] != null) {
+ numLowFields--;
+ lowFields[tagNumber] = null;
+ }
+ } else {
+ highFields.remove(Integer.valueOf(tagNumber));
+ }
+ }
+
+ /**
+ * Retrieves all TIFF fields from the directory.
+ *
+ * @return An array of all TIFF fields in order of numerically increasing
+ * tag number.
+ */
+ public TIFFField[] getTIFFFields() {
+ // Allocate return value.
+ TIFFField[] fields = new TIFFField[numLowFields + highFields.size()];
+
+ // Copy any low-index fields.
+ int nextIndex = 0;
+ for(int i = 0; i <= MAX_LOW_FIELD_TAG_NUM; i++) {
+ if(lowFields[i] != null) {
+ fields[nextIndex++] = lowFields[i];
+ if(nextIndex == numLowFields) break;
+ }
+ }
+
+ // Copy any high-index fields.
+ if(!highFields.isEmpty()) {
+ Iterator<Integer> keys = highFields.keySet().iterator();
+ while(keys.hasNext()) {
+ fields[nextIndex++] = highFields.get(keys.next());
+ }
+ }
+
+ return fields;
+ }
+
+ /**
+ * Removes all TIFF fields from the directory.
+ */
+ public void removeTIFFFields() {
+ Arrays.fill(lowFields, (Object)null);
+ numLowFields = 0;
+ highFields.clear();
+ }
+
+ /**
+ * Converts the directory to a metadata object.
+ *
+ * @return A metadata instance initialized from the contents of this
+ * <code>TIFFDirectory</code>.
+ */
+ public IIOMetadata getAsMetadata() {
+ return new TIFFImageMetadata(getDirectoryAsIFD(this));
+ }
+
+ /**
+ * Clones the directory and all the fields contained therein.
+ *
+ * @return A clone of this <code>TIFFDirectory</code>.
+ * @throws CloneNotSupportedException if the instance cannot be cloned.
+ */
+ @Override
+ public TIFFDirectory clone() throws CloneNotSupportedException {
+ TIFFDirectory dir = (TIFFDirectory) super.clone();
+ dir.tagSets = new ArrayList<TIFFTagSet>(tagSets);
+ dir.parentTag = getParentTag();
+ TIFFField[] fields = getTIFFFields();
+ for(TIFFField field : fields) {
+ dir.addTIFFField(field.clone());
+ }
+
+ return dir;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFField.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,1421 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.util.StringTokenizer;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import com.sun.imageio.plugins.tiff.TIFFFieldNode;
+import com.sun.imageio.plugins.tiff.TIFFIFD;
+
+/**
+ * A class representing a field in a TIFF 6.0 Image File Directory.
+ *
+ * <p> A field in a TIFF Image File Directory (IFD) is defined as a
+ * tag number accompanied by a sequence of values of identical data type.
+ * TIFF 6.0 defines 12 data types; a 13th type <code>IFD</code> is
+ * defined in TIFF Tech Note 1 of TIFF Specification Supplement 1. These
+ * TIFF data types are referred to by Java constants and mapped internally
+ * onto Java language data types and type names as follows:
+ *
+ * <br>
+ * <br>
+ * <table border="1">
+ * <caption>TIFF Data Type to Java Data Type Mapping</caption>
+ *
+ * <tr>
+ * <th>
+ * <b>TIFF Data Type</b>
+ * </th>
+ * <th>
+ * <b>Java Constant</b>
+ * </th>
+ * <th>
+ * <b>Java Data Type</b>
+ * </th>
+ * <th>
+ * <b>Java Type Name</b>
+ * </th>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>BYTE</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_BYTE}
+ * </td>
+ * <td>
+ * <code>byte</code>
+ * </td>
+ * <td>
+ * <code>"Byte"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>ASCII</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_ASCII}
+ * </td>
+ * <td>
+ * <code>String</code>
+ * </td>
+ * <td>
+ * <code>"Ascii"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>SHORT</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_SHORT}
+ * </td>
+ * <td>
+ * <code>char</code>
+ * </td>
+ * <td>
+ * <code>"Short"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>LONG</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_LONG}
+ * </td>
+ * <td>
+ * <code>long</code>
+ * </td>
+ * <td>
+ * <code>"Long"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>RATIONAL</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_RATIONAL}
+ * </td>
+ * <td>
+ * <code>long[2]</code> {numerator, denominator}
+ * </td>
+ * <td>
+ * <code>"Rational"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>SBYTE</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_SBYTE}
+ * </td>
+ * <td>
+ * <code>byte</code>
+ * </td>
+ * <td>
+ * <code>"SByte"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>UNDEFINED</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_UNDEFINED}
+ * </td>
+ * <td>
+ * <code>byte</code>
+ * </td>
+ * <td>
+ * <code>"Undefined"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>SSHORT</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_SSHORT}
+ * </td>
+ * <td>
+ * <code>short</code>
+ * </td>
+ * <td>
+ * <code>"SShort"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>SLONG</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_SLONG}
+ * </td>
+ * <td>
+ * <code>int</code>
+ * </td>
+ * <td>
+ * <code>"SLong"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>SRATIONAL</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_SRATIONAL}
+ * </td>
+ * <td>
+ * <code>int[2]</code> {numerator, denominator}
+ * </td>
+ * <td>
+ * <code>"SRational"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>FLOAT</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_FLOAT}
+ * </td>
+ * <td>
+ * <code>float</code>
+ * </td>
+ * <td>
+ * <code>"Float"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>DOUBLE</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_DOUBLE}
+ * </td>
+ * <td>
+ * <code>double</code>
+ * </td>
+ * <td>
+ * <code>"Double"</code>
+ * </td>
+ * </tr>
+ *
+ * <tr>
+ * <td>
+ * <tt>IFD</tt>
+ * </td>
+ * <td>
+ * {@link TIFFTag#TIFF_IFD_POINTER}
+ * </td>
+ * <td>
+ * <code>long</code>
+ * </td>
+ * <td>
+ * <code>"IFDPointer"</code>
+ * </td>
+ * </tr>
+ *
+ * </table>
+ *
+ * @since 1.9
+ * @see TIFFDirectory
+ * @see TIFFTag
+ */
+public class TIFFField implements Cloneable {
+
+ private static final String[] typeNames = {
+ null,
+ "Byte", "Ascii", "Short", "Long", "Rational",
+ "SByte", "Undefined", "SShort", "SLong", "SRational",
+ "Float", "Double", "IFDPointer"
+ };
+
+ private static final boolean[] isIntegral = {
+ false,
+ true, false, true, true, false,
+ true, true, true, true, false,
+ false, false, false
+ };
+
+ /** The tag. */
+ private TIFFTag tag;
+
+ /** The tag number. */
+ private int tagNumber;
+
+ /** The tag type. */
+ private int type;
+
+ /** The number of data items present in the field. */
+ private int count;
+
+ /** The field data. */
+ private Object data;
+
+ /** The IFD contents if available. This will usually be a TIFFIFD. */
+ private TIFFDirectory dir;
+
+ /** The default constructor. */
+ private TIFFField() {}
+
+ private static String getAttribute(Node node, String attrName) {
+ NamedNodeMap attrs = node.getAttributes();
+ return attrs.getNamedItem(attrName).getNodeValue();
+ }
+
+ private static void initData(Node node,
+ int[] otype, int[] ocount, Object[] odata) {
+ int type;
+ int count;
+ Object data = null;
+
+ String typeName = node.getNodeName();
+ typeName = typeName.substring(4);
+ typeName = typeName.substring(0, typeName.length() - 1);
+ type = TIFFField.getTypeByName(typeName);
+ if (type == -1) {
+ throw new IllegalArgumentException("typeName = " + typeName);
+ }
+
+ Node child = node.getFirstChild();
+
+ count = 0;
+ while (child != null) {
+ String childTypeName = child.getNodeName().substring(4);
+ if (!typeName.equals(childTypeName)) {
+ // warning
+ }
+
+ ++count;
+ child = child.getNextSibling();
+ }
+
+ if (count > 0) {
+ data = createArrayForType(type, count);
+ child = node.getFirstChild();
+ int idx = 0;
+ while (child != null) {
+ String value = getAttribute(child, "value");
+
+ String numerator, denominator;
+ int slashPos;
+
+ switch (type) {
+ case TIFFTag.TIFF_ASCII:
+ ((String[])data)[idx] = value;
+ break;
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_SBYTE:
+ ((byte[])data)[idx] =
+ (byte)Integer.parseInt(value);
+ break;
+ case TIFFTag.TIFF_SHORT:
+ ((char[])data)[idx] =
+ (char)Integer.parseInt(value);
+ break;
+ case TIFFTag.TIFF_SSHORT:
+ ((short[])data)[idx] =
+ (short)Integer.parseInt(value);
+ break;
+ case TIFFTag.TIFF_SLONG:
+ ((int[])data)[idx] =
+ Integer.parseInt(value);
+ break;
+ case TIFFTag.TIFF_LONG:
+ case TIFFTag.TIFF_IFD_POINTER:
+ ((long[])data)[idx] =
+ Long.parseLong(value);
+ break;
+ case TIFFTag.TIFF_FLOAT:
+ ((float[])data)[idx] =
+ Float.parseFloat(value);
+ break;
+ case TIFFTag.TIFF_DOUBLE:
+ ((double[])data)[idx] =
+ Double.parseDouble(value);
+ break;
+ case TIFFTag.TIFF_SRATIONAL:
+ slashPos = value.indexOf("/");
+ numerator = value.substring(0, slashPos);
+ denominator = value.substring(slashPos + 1);
+
+ ((int[][])data)[idx] = new int[2];
+ ((int[][])data)[idx][0] =
+ Integer.parseInt(numerator);
+ ((int[][])data)[idx][1] =
+ Integer.parseInt(denominator);
+ break;
+ case TIFFTag.TIFF_RATIONAL:
+ slashPos = value.indexOf("/");
+ numerator = value.substring(0, slashPos);
+ denominator = value.substring(slashPos + 1);
+
+ ((long[][])data)[idx] = new long[2];
+ ((long[][])data)[idx][0] =
+ Long.parseLong(numerator);
+ ((long[][])data)[idx][1] =
+ Long.parseLong(denominator);
+ break;
+ default:
+ // error
+ }
+
+ idx++;
+ child = child.getNextSibling();
+ }
+ }
+
+ otype[0] = type;
+ ocount[0] = count;
+ odata[0] = data;
+ }
+
+ /**
+ * Creates a <code>TIFFField</code> from a TIFF native image
+ * metadata node. If the value of the <tt>"tagNumber"</tt> attribute
+ * of the node is not found in <code>tagSet</code> then a new
+ * <code>TIFFTag</code> with name <code>TIFFTag.UNKNOWN_TAG_NAME</code>
+ * will be created and assigned to the field.
+ *
+ * @param tagSet The <code>TIFFTagSet</code> to which the
+ * <code>TIFFTag</code> of the field belongs.
+ * @param node A native TIFF image metadata <code>TIFFField</code> node.
+ * @throws NullPointerException if <code>node</code> is
+ * <code>null</code>.
+ * @throws IllegalArgumentException if the name of the node is not
+ * <code>"TIFFField"</code>.
+ * @return A new {@code TIFFField}.
+ */
+ public static TIFFField createFromMetadataNode(TIFFTagSet tagSet,
+ Node node) {
+ if (node == null) {
+ throw new NullPointerException("node == null!");
+ }
+ String name = node.getNodeName();
+ if (!name.equals("TIFFField")) {
+ throw new IllegalArgumentException("!name.equals(\"TIFFField\")");
+ }
+
+ int tagNumber = Integer.parseInt(getAttribute(node, "number"));
+ TIFFTag tag = null;
+ if (tagSet != null) {
+ tag = tagSet.getTag(tagNumber);
+ }
+
+ int type = TIFFTag.TIFF_UNDEFINED;
+ int count = 0;
+ Object data = null;
+
+ Node child = node.getFirstChild();
+ if (child != null) {
+ String typeName = child.getNodeName();
+ if (typeName.equals("TIFFUndefined")) {
+ String values = getAttribute(child, "value");
+ StringTokenizer st = new StringTokenizer(values, ",");
+ count = st.countTokens();
+
+ byte[] bdata = new byte[count];
+ for (int i = 0; i < count; i++) {
+ bdata[i] = (byte)Integer.parseInt(st.nextToken());
+ }
+
+ type = TIFFTag.TIFF_UNDEFINED;
+ data = bdata;
+ } else {
+ int[] otype = new int[1];
+ int[] ocount = new int[1];
+ Object[] odata = new Object[1];
+
+ initData(node.getFirstChild(), otype, ocount, odata);
+ type = otype[0];
+ count = ocount[0];
+ data = odata[0];
+ }
+ } else if (tag != null) {
+ int t = TIFFTag.MAX_DATATYPE;
+ while(t >= TIFFTag.MIN_DATATYPE && !tag.isDataTypeOK(t)) {
+ t--;
+ }
+ type = t;
+ }
+
+ if (tag == null) {
+ tag = new TIFFTag(TIFFTag.UNKNOWN_TAG_NAME, tagNumber, 1 << type);
+ }
+
+ return new TIFFField(tag, type, count, data);
+ }
+
+ /**
+ * Constructs a <code>TIFFField</code> with arbitrary data. The
+ * <code>type</code> parameter must be a value for which
+ * {@link TIFFTag#isDataTypeOK tag.isDataTypeOK()}
+ * returns <code>true</code>. The <code>data</code> parameter must
+ * be an array of a Java type appropriate for the type of the TIFF
+ * field.
+ *
+ * <p>Note that the value (data) of the <code>TIFFField</code>
+ * will always be the actual field value regardless of the number of
+ * bytes required for that value. This is the case despite the fact
+ * that the TIFF <i>IFD Entry</i> corresponding to the field may
+ * actually contain the offset to the value of the field rather than
+ * the value itself (the latter occurring if and only if the
+ * value fits into 4 bytes). In other words, the value of the
+ * field will already have been read from the TIFF stream. (An exception
+ * to this case may occur when the field represents the contents of a
+ * non-baseline IFD. In that case the data will be a <code>long[]</code>
+ * containing the offset to the IFD and the <code>TIFFDirectory</code>
+ * returned by {@link #getDirectory()} will be its contents.)
+ *
+ * @param tag The tag to associated with this field.
+ * @param type One of the <code>TIFFTag.TIFF_*</code> constants
+ * indicating the data type of the field as written to the TIFF stream.
+ * @param count The number of data values.
+ * @param data The actual data content of the field.
+ *
+ * @throws NullPointerException if <code>tag == null</code>.
+ * @throws IllegalArgumentException if <code>type</code> is not
+ * one of the <code>TIFFTag.TIFF_*</code> data type constants.
+ * @throws IllegalArgumentException if <code>type</code> is an unacceptable
+ * data type for the supplied <code>TIFFTag</code>.
+ * @throws IllegalArgumentException if <code>count < 0</code>.
+ * @throws NullPointerException if <code>data == null</code>.
+ * @throws IllegalArgumentException if <code>data</code> is an instance of
+ * a class incompatible with the specified type.
+ * @throws IllegalArgumentException if the size of the data array is wrong.
+ */
+ public TIFFField(TIFFTag tag, int type, int count, Object data) {
+ if(tag == null) {
+ throw new NullPointerException("tag == null!");
+ } else if(type < TIFFTag.MIN_DATATYPE || type > TIFFTag.MAX_DATATYPE) {
+ throw new IllegalArgumentException("Unknown data type "+type);
+ } else if(!tag.isDataTypeOK(type)) {
+ throw new IllegalArgumentException("Illegal data type " + type
+ + " for " + tag.getName() + " tag");
+ } else if(count < 0) {
+ throw new IllegalArgumentException("count < 0!");
+ } else if(data == null) {
+ throw new NullPointerException("data == null!");
+ }
+
+ boolean isDataArrayCorrect = false;
+
+ switch (type) {
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_SBYTE:
+ case TIFFTag.TIFF_UNDEFINED:
+ isDataArrayCorrect = data instanceof byte[]
+ && ((byte[])data).length == count;
+ break;
+ case TIFFTag.TIFF_ASCII:
+ isDataArrayCorrect = data instanceof String[]
+ && ((String[])data).length == count;
+ break;
+ case TIFFTag.TIFF_SHORT:
+ isDataArrayCorrect = data instanceof char[]
+ && ((char[])data).length == count;
+ break;
+ case TIFFTag.TIFF_LONG:
+ isDataArrayCorrect = data instanceof long[]
+ && ((long[])data).length == count;
+ break;
+ case TIFFTag.TIFF_IFD_POINTER:
+ isDataArrayCorrect = data instanceof long[]
+ && ((long[])data).length == 1;
+ break;
+ case TIFFTag.TIFF_RATIONAL:
+ isDataArrayCorrect = data instanceof long[][]
+ && ((long[][])data).length == count
+ && ((long[][])data)[0].length == 2;
+ break;
+ case TIFFTag.TIFF_SSHORT:
+ isDataArrayCorrect = data instanceof short[]
+ && ((short[])data).length == count;
+ break;
+ case TIFFTag.TIFF_SLONG:
+ isDataArrayCorrect = data instanceof int[]
+ && ((int[])data).length == count;
+ break;
+ case TIFFTag.TIFF_SRATIONAL:
+ isDataArrayCorrect = data instanceof int[][]
+ && ((int[][])data).length == count
+ && ((int[][])data)[0].length == 2;
+ break;
+ case TIFFTag.TIFF_FLOAT:
+ isDataArrayCorrect = data instanceof float[]
+ && ((float[])data).length == count;
+ break;
+ case TIFFTag.TIFF_DOUBLE:
+ isDataArrayCorrect = data instanceof double[]
+ && ((double[])data).length == count;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown data type "+type);
+ }
+
+ if (!isDataArrayCorrect) {
+ throw new IllegalArgumentException
+ ("Illegal class or length for data array");
+ }
+
+ this.tag = tag;
+ this.tagNumber = tag.getNumber();
+ this.type = type;
+ this.count = count;
+ this.data = data;
+ }
+
+ /**
+ * Constructs a data array using {@link #createArrayForType
+ * createArrayForType()} and invokes
+ * {@link #TIFFField(TIFFTag,int,int,Object)} with the supplied
+ * parameters and the created array.
+ *
+ * @param tag The tag to associated with this field.
+ * @param type One of the <code>TIFFTag.TIFF_*</code> constants
+ * indicating the data type of the field as written to the TIFF stream.
+ * @param count The number of data values.
+ * @throws NullPointerException if <code>tag == null</code>.
+ * @throws IllegalArgumentException if <code>type</code> is not
+ * one of the <code>TIFFTag.TIFF_*</code> data type constants.
+ * @throws IllegalArgumentException if <code>type</code> is an unacceptable
+ * data type for the supplied <code>TIFFTag</code>.
+ * @throws IllegalArgumentException if <code>count < 0</code>.
+ * @see #TIFFField(TIFFTag,int,int,Object)
+ */
+ public TIFFField(TIFFTag tag, int type, int count) {
+ this(tag, type, count, createArrayForType(type, count));
+ }
+
+ /**
+ * Constructs a <code>TIFFField</code> with a single non-negative integral
+ * value.
+ * The field will have type
+ * {@link TIFFTag#TIFF_SHORT TIFF_SHORT} if
+ * <code>val < 65536</code> and type
+ * {@link TIFFTag#TIFF_LONG TIFF_LONG} otherwise. The count
+ * of the field will be unity.
+ *
+ * @param tag The tag to associate with this field.
+ * @param value The value to associate with this field.
+ * @throws NullPointerException if <code>tag == null</code>.
+ * @throws IllegalArgumentException if the derived type is unacceptable
+ * for the supplied <code>TIFFTag</code>.
+ * @throws IllegalArgumentException if <code>value < 0</code>.
+ */
+ public TIFFField(TIFFTag tag, int value) {
+ if(tag == null) {
+ throw new NullPointerException("tag == null!");
+ }
+ if (value < 0) {
+ throw new IllegalArgumentException("value < 0!");
+ }
+
+ this.tag = tag;
+ this.tagNumber = tag.getNumber();
+ this.count = 1;
+
+ if (value < 65536) {
+ if (!tag.isDataTypeOK(TIFFTag.TIFF_SHORT)) {
+ throw new IllegalArgumentException("Illegal data type "
+ + TIFFTag.TIFF_SHORT + " for " + tag.getName() + " tag");
+ }
+ this.type = TIFFTag.TIFF_SHORT;
+ char[] cdata = new char[1];
+ cdata[0] = (char)value;
+ this.data = cdata;
+ } else {
+ if (!tag.isDataTypeOK(TIFFTag.TIFF_LONG)) {
+ throw new IllegalArgumentException("Illegal data type "
+ + TIFFTag.TIFF_LONG + " for " + tag.getName() + " tag");
+ }
+ this.type = TIFFTag.TIFF_LONG;
+ long[] ldata = new long[1];
+ ldata[0] = value;
+ this.data = ldata;
+ }
+ }
+
+ /**
+ * Constructs a <code>TIFFField</code> with an IFD offset and contents.
+ * The offset will be stored as the data of this field as
+ * <code>long[] {offset}</code>. The directory will not be cloned. The count
+ * of the field will be unity.
+ *
+ * @param tag The tag to associated with this field.
+ * @param type One of the constants <code>TIFFTag.TIFF_LONG</code> or
+ * <code>TIFFTag.TIFF_IFD_POINTER</code>.
+ * @param offset The IFD offset.
+ * @param dir The directory.
+ *
+ * @throws NullPointerException if <code>tag == null</code>.
+ * @throws IllegalArgumentException if <code>type</code> is neither
+ * <code>TIFFTag.TIFF_LONG</code> nor <code>TIFFTag.TIFF_IFD_POINTER</code>.
+ * @throws IllegalArgumentException if <code>type</code> is an unacceptable
+ * data type for the supplied <code>TIFFTag</code>.
+ * @throws IllegalArgumentException if <code>offset</code> is non-positive.
+ * @throws NullPointerException if <code>dir == null</code>.
+ *
+ * @see #TIFFField(TIFFTag,int,int,Object)
+ */
+ public TIFFField(TIFFTag tag, int type, long offset, TIFFDirectory dir) {
+ this(tag, type, 1, new long[] {offset});
+ if (type != TIFFTag.TIFF_LONG && type != TIFFTag.TIFF_IFD_POINTER) {
+ throw new IllegalArgumentException("type " + type
+ + " is neither TIFFTag.TIFF_LONG nor TIFFTag.TIFF_IFD_POINTER");
+ } else if (offset <= 0) {
+ throw new IllegalArgumentException("offset " + offset
+ + " is non-positive");
+ } else if (dir == null) {
+ throw new NullPointerException("dir == null");
+ }
+ this.dir = dir;
+ }
+
+ /**
+ * Retrieves the tag associated with this field.
+ *
+ * @return The associated <code>TIFFTag</code>.
+ */
+ public TIFFTag getTag() {
+ return tag;
+ }
+
+ /**
+ * Retrieves the tag number in the range <code>[0, 65535]</code>.
+ *
+ * @return The tag number.
+ */
+ public int getTagNumber() {
+ return tagNumber;
+ }
+
+ /**
+ * Returns the type of the data stored in the field. For a TIFF 6.0
+ * stream, the value will equal one of the <code>TIFFTag.TIFF_*</code>
+ * constants. For future revisions of TIFF, higher values are possible.
+ *
+ * @return The data type of the field value.
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Returns the name of the supplied data type constant.
+ *
+ * @param dataType One of the <code>TIFFTag.TIFF_*</code> constants
+ * indicating the data type of the field as written to the TIFF stream.
+ * @return The type name corresponding to the supplied type constant.
+ * @throws IllegalArgumentException if <code>dataType</code> is not
+ * one of the <code>TIFFTag.TIFF_*</code> data type constants.
+ */
+ public static String getTypeName(int dataType) {
+ if (dataType < TIFFTag.MIN_DATATYPE ||
+ dataType > TIFFTag.MAX_DATATYPE) {
+ throw new IllegalArgumentException("Unknown data type "+dataType);
+ }
+
+ return typeNames[dataType];
+ }
+
+ /**
+ * Returns the data type constant corresponding to the supplied data
+ * type name. If the name is unknown <code>-1</code> will be returned.
+ *
+ * @param typeName The type name.
+ * @return One of the <code>TIFFTag.TIFF_*</code> constants or
+ * <code>-1</code> if the name is not recognized.
+ */
+ public static int getTypeByName(String typeName) {
+ for (int i = TIFFTag.MIN_DATATYPE; i <= TIFFTag.MAX_DATATYPE; i++) {
+ if (typeName.equals(typeNames[i])) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Creates an array appropriate for the indicated data type.
+ *
+ * @param dataType One of the <code>TIFFTag.TIFF_*</code> data type
+ * constants.
+ * @param count The number of values in the array.
+ * @return An array appropriate for the specified data type.
+ *
+ * @throws IllegalArgumentException if <code>dataType</code> is not
+ * one of the <code>TIFFTag.TIFF_*</code> data type constants.
+ * @throws IllegalArgumentException if <code>count < 0</code>.
+ */
+ public static Object createArrayForType(int dataType, int count) {
+ if(count < 0) {
+ throw new IllegalArgumentException("count < 0!");
+ }
+ switch (dataType) {
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_SBYTE:
+ case TIFFTag.TIFF_UNDEFINED:
+ return new byte[count];
+ case TIFFTag.TIFF_ASCII:
+ return new String[count];
+ case TIFFTag.TIFF_SHORT:
+ return new char[count];
+ case TIFFTag.TIFF_LONG:
+ case TIFFTag.TIFF_IFD_POINTER:
+ return new long[count];
+ case TIFFTag.TIFF_RATIONAL:
+ return new long[count][2];
+ case TIFFTag.TIFF_SSHORT:
+ return new short[count];
+ case TIFFTag.TIFF_SLONG:
+ return new int[count];
+ case TIFFTag.TIFF_SRATIONAL:
+ return new int[count][2];
+ case TIFFTag.TIFF_FLOAT:
+ return new float[count];
+ case TIFFTag.TIFF_DOUBLE:
+ return new double[count];
+ default:
+ throw new IllegalArgumentException("Unknown data type "+dataType);
+ }
+ }
+
+ /**
+ * Returns the <code>TIFFField</code> as a node named either
+ * <tt>"TIFFField"</tt> or <tt>"TIFFIFD"</tt> as described in the
+ * TIFF native image metadata specification. The node will be named
+ * <tt>"TIFFIFD"</tt> if and only if the field's data object is an
+ * instance of {@link TIFFDirectory} or equivalently
+ * {@link TIFFTag#isIFDPointer getTag.isIFDPointer()} returns
+ * <code>true</code>.
+ *
+ * @return a <code>Node</code> named <tt>"TIFFField"</tt> or
+ * <tt>"TIFFIFD"</tt>.
+ */
+ public Node getAsNativeNode() {
+ return new TIFFFieldNode(this);
+ }
+
+ /**
+ * Indicates whether the value associated with the field is of
+ * integral data type.
+ *
+ * @return Whether the field type is integral.
+ */
+ public boolean isIntegral() {
+ return isIntegral[type];
+ }
+
+ /**
+ * Returns the number of data items present in the field. For
+ * <code>TIFFTag.TIFF_ASCII</code> fields, the value returned is the
+ * number of <code>String</code>s, not the total length of the
+ * data as in the file representation.
+ *
+ * @return The number of data items present in the field.
+ */
+ public int getCount() {
+ return count;
+ }
+
+ /**
+ * Returns a reference to the data object associated with the field.
+ *
+ * @return The data object of the field.
+ */
+ public Object getData() {
+ return data;
+ }
+
+ /**
+ * Returns the data as an uninterpreted array of
+ * <code>byte</code>s. The type of the field must be one of
+ * <code>TIFFTag.TIFF_BYTE</code>, <code>TIFF_SBYTE</code>, or
+ * <code>TIFF_UNDEFINED</code>.
+ *
+ * <p> For data in <code>TIFFTag.TIFF_BYTE</code> format, the application
+ * must take care when promoting the data to longer integral types
+ * to avoid sign extension.
+ *
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_BYTE</code>, <code>TIFF_SBYTE</code>, or
+ * <code>TIFF_UNDEFINED</code>.
+ * @return The data as an uninterpreted array of bytes.
+ */
+ public byte[] getAsBytes() {
+ return (byte[])data;
+ }
+
+ /**
+ * Returns <code>TIFFTag.TIFF_SHORT</code> data as an array of
+ * <code>char</code>s (unsigned 16-bit integers).
+ *
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_SHORT</code>.
+ * @return The data as an array of {@code char}s.
+ */
+ public char[] getAsChars() {
+ return (char[])data;
+ }
+
+ /**
+ * Returns <code>TIFFTag.TIFF_SSHORT</code> data as an array of
+ * <code>short</code>s (signed 16-bit integers).
+ *
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_SSHORT</code>.
+ * @return The data as an array of {@code short}s.
+ */
+ public short[] getAsShorts() {
+ return (short[])data;
+ }
+
+ /**
+ * Returns <code>TIFFTag.TIFF_SLONG</code> data as an array of
+ * <code>int</code>s (signed 32-bit integers).
+ *
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_SHORT</code>, <code>TIFF_SSHORT</code>, or
+ * <code>TIFF_SLONG</code>.
+ * @return The data as an array of {@code int}s.
+ */
+ public int[] getAsInts() {
+ if (data instanceof int[]) {
+ return (int[])data;
+ } else if (data instanceof char[]){
+ char[] cdata = (char[])data;
+ int[] idata = new int[cdata.length];
+ for (int i = 0; i < cdata.length; i++) {
+ idata[i] = cdata[i] & 0xffff;
+ }
+ return idata;
+ } else if (data instanceof short[]){
+ short[] sdata = (short[])data;
+ int[] idata = new int[sdata.length];
+ for (int i = 0; i < sdata.length; i++) {
+ idata[i] = (int)sdata[i];
+ }
+ return idata;
+ } else {
+ throw new ClassCastException("Data not char[], short[], or int[]!");
+ }
+ }
+
+ /**
+ * Returns <code>TIFFTag.TIFF_LONG</code> or
+ * <code>TIFF_IFD_POINTER</code> data as an array of
+ * <code>long</code>s (signed 64-bit integers).
+ *
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_LONG</code> or <code>TIFF_IFD_POINTER</code>.
+ * @return The data as an array of {@code long}s.
+ */
+ public long[] getAsLongs() {
+ return (long[])data;
+ }
+
+ /**
+ * Returns <code>TIFFTag.TIFF_FLOAT</code> data as an array of
+ * <code>float</code>s (32-bit floating-point values).
+ *
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_FLOAT</code>.
+ * @return The data as an array of {@code float}s.
+ */
+ public float[] getAsFloats() {
+ return (float[])data;
+ }
+
+ /**
+ * Returns <code>TIFFTag.TIFF_DOUBLE</code> data as an array of
+ * <code>double</code>s (64-bit floating-point values).
+ *
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_DOUBLE</code>.
+ * @return The data as an array of {@code double}s.
+ */
+ public double[] getAsDoubles() {
+ return (double[])data;
+ }
+
+ /**
+ * Returns <code>TIFFTag.TIFF_SRATIONAL</code> data as an array of
+ * 2-element arrays of <code>int</code>s.
+ *
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_SRATIONAL</code>.
+ * @return The data as an array of signed rationals.
+ */
+ public int[][] getAsSRationals() {
+ return (int[][])data;
+ }
+
+ /**
+ * Returns <code>TIFFTag.TIFF_RATIONAL</code> data as an array of
+ * 2-element arrays of <code>long</code>s.
+ *
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_RATIONAL</code>.
+ * @return The data as an array of unsigned rationals.
+ */
+ public long[][] getAsRationals() {
+ return (long[][])data;
+ }
+
+ /**
+ * Returns data in any format as an <code>int</code>.
+ *
+ * <p> <code>TIFFTag.TIFF_BYTE</code> values are treated as unsigned; that
+ * is, no sign extension will take place and the returned value
+ * will be in the range [0, 255]. <code>TIFF_SBYTE</code> data
+ * will be returned in the range [-128, 127].
+ *
+ * <p> A <code>TIFF_UNDEFINED</code> value is treated as though
+ * it were a <code>TIFF_BYTE</code>.
+ *
+ * <p> Data in <code>TIFF_SLONG</code>, <code>TIFF_LONG</code>,
+ * <code>TIFF_FLOAT</code>, <code>TIFF_DOUBLE</code> or
+ * <code>TIFF_IFD_POINTER</code> format are simply cast to
+ * <code>int</code> and may suffer from truncation.
+ *
+ * <p> Data in <code>TIFF_SRATIONAL</code> or
+ * <code>TIFF_RATIONAL</code> format are evaluated by dividing the
+ * numerator into the denominator using double-precision
+ * arithmetic and then casting to <code>int</code>. Loss of
+ * precision and truncation may occur.
+ *
+ * <p> Data in <code>TIFF_ASCII</code> format will be parsed as by
+ * the <code>Double.parseDouble</code> method, with the result
+ * case to <code>int</code>.
+ *
+ * @param index The index of the data.
+ * @return The data at the given index as an {@code int}.
+ */
+ public int getAsInt(int index) {
+ switch (type) {
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_UNDEFINED:
+ return ((byte[])data)[index] & 0xff;
+ case TIFFTag.TIFF_SBYTE:
+ return ((byte[])data)[index];
+ case TIFFTag.TIFF_SHORT:
+ return ((char[])data)[index] & 0xffff;
+ case TIFFTag.TIFF_SSHORT:
+ return ((short[])data)[index];
+ case TIFFTag.TIFF_SLONG:
+ return ((int[])data)[index];
+ case TIFFTag.TIFF_LONG:
+ case TIFFTag.TIFF_IFD_POINTER:
+ return (int)((long[])data)[index];
+ case TIFFTag.TIFF_FLOAT:
+ return (int)((float[])data)[index];
+ case TIFFTag.TIFF_DOUBLE:
+ return (int)((double[])data)[index];
+ case TIFFTag.TIFF_SRATIONAL:
+ int[] ivalue = getAsSRational(index);
+ return (int)((double)ivalue[0]/ivalue[1]);
+ case TIFFTag.TIFF_RATIONAL:
+ long[] lvalue = getAsRational(index);
+ return (int)((double)lvalue[0]/lvalue[1]);
+ case TIFFTag.TIFF_ASCII:
+ String s = ((String[])data)[index];
+ return (int)Double.parseDouble(s);
+ default:
+ throw new ClassCastException(); // should never happen
+ }
+ }
+
+ /**
+ * Returns data in any format as a <code>long</code>.
+ *
+ * <p> <code>TIFFTag.TIFF_BYTE</code> and <code>TIFF_UNDEFINED</code> data
+ * are treated as unsigned; that is, no sign extension will take
+ * place and the returned value will be in the range [0, 255].
+ * <code>TIFF_SBYTE</code> data will be returned in the range
+ * [-128, 127].
+ *
+ * <p> Data in <code>TIFF_ASCII</code> format will be parsed as by
+ * the <code>Double.parseDouble</code> method, with the result
+ * cast to <code>long</code>.
+ *
+ * @param index The index of the data.
+ * @return The data at the given index as a {@code long}.
+ */
+ public long getAsLong(int index) {
+ switch (type) {
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_UNDEFINED:
+ return ((byte[])data)[index] & 0xff;
+ case TIFFTag.TIFF_SBYTE:
+ return ((byte[])data)[index];
+ case TIFFTag.TIFF_SHORT:
+ return ((char[])data)[index] & 0xffff;
+ case TIFFTag.TIFF_SSHORT:
+ return ((short[])data)[index];
+ case TIFFTag.TIFF_SLONG:
+ return ((int[])data)[index];
+ case TIFFTag.TIFF_LONG:
+ case TIFFTag.TIFF_IFD_POINTER:
+ return ((long[])data)[index];
+ case TIFFTag.TIFF_SRATIONAL:
+ int[] ivalue = getAsSRational(index);
+ return (long)((double)ivalue[0]/ivalue[1]);
+ case TIFFTag.TIFF_RATIONAL:
+ long[] lvalue = getAsRational(index);
+ return (long)((double)lvalue[0]/lvalue[1]);
+ case TIFFTag.TIFF_ASCII:
+ String s = ((String[])data)[index];
+ return (long)Double.parseDouble(s);
+ default:
+ throw new ClassCastException(); // should never happen
+ }
+ }
+
+ /**
+ * Returns data in any format as a <code>float</code>.
+ *
+ * <p> <code>TIFFTag.TIFF_BYTE</code> and <code>TIFF_UNDEFINED</code> data
+ * are treated as unsigned; that is, no sign extension will take
+ * place and the returned value will be in the range [0, 255].
+ * <code>TIFF_SBYTE</code> data will be returned in the range
+ * [-128, 127].
+ *
+ * <p> Data in <code>TIFF_SLONG</code>, <code>TIFF_LONG</code>,
+ * <code>TIFF_DOUBLE</code>, or <code>TIFF_IFD_POINTER</code> format are
+ * simply cast to <code>float</code> and may suffer from
+ * truncation.
+ *
+ * <p> Data in <code>TIFF_SRATIONAL</code> or
+ * <code>TIFF_RATIONAL</code> format are evaluated by dividing the
+ * numerator into the denominator using double-precision
+ * arithmetic and then casting to <code>float</code>.
+ *
+ * <p> Data in <code>TIFF_ASCII</code> format will be parsed as by
+ * the <code>Double.parseDouble</code> method, with the result
+ * cast to <code>float</code>.
+ *
+ * @param index The index of the data.
+ * @return The data at the given index as a {@code float}.
+ */
+ public float getAsFloat(int index) {
+ switch (type) {
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_UNDEFINED:
+ return ((byte[])data)[index] & 0xff;
+ case TIFFTag.TIFF_SBYTE:
+ return ((byte[])data)[index];
+ case TIFFTag.TIFF_SHORT:
+ return ((char[])data)[index] & 0xffff;
+ case TIFFTag.TIFF_SSHORT:
+ return ((short[])data)[index];
+ case TIFFTag.TIFF_SLONG:
+ return ((int[])data)[index];
+ case TIFFTag.TIFF_LONG:
+ case TIFFTag.TIFF_IFD_POINTER:
+ return ((long[])data)[index];
+ case TIFFTag.TIFF_FLOAT:
+ return ((float[])data)[index];
+ case TIFFTag.TIFF_DOUBLE:
+ return (float)((double[])data)[index];
+ case TIFFTag.TIFF_SRATIONAL:
+ int[] ivalue = getAsSRational(index);
+ return (float)((double)ivalue[0]/ivalue[1]);
+ case TIFFTag.TIFF_RATIONAL:
+ long[] lvalue = getAsRational(index);
+ return (float)((double)lvalue[0]/lvalue[1]);
+ case TIFFTag.TIFF_ASCII:
+ String s = ((String[])data)[index];
+ return (float)Double.parseDouble(s);
+ default:
+ throw new ClassCastException(); // should never happen
+ }
+ }
+
+ /**
+ * Returns data in any format as a <code>double</code>.
+ *
+ * <p> <code>TIFFTag.TIFF_BYTE</code> and <code>TIFF_UNDEFINED</code> data
+ * are treated as unsigned; that is, no sign extension will take
+ * place and the returned value will be in the range [0, 255].
+ * <code>TIFF_SBYTE</code> data will be returned in the range
+ * [-128, 127].
+ *
+ * <p> Data in <code>TIFF_SRATIONAL</code> or
+ * <code>TIFF_RATIONAL</code> format are evaluated by dividing the
+ * numerator into the denominator using double-precision
+ * arithmetic.
+ *
+ * <p> Data in <code>TIFF_ASCII</code> format will be parsed as by
+ * the <code>Double.parseDouble</code> method.
+ *
+ * @param index The index of the data.
+ * @return The data at the given index as a {@code double}.
+ */
+ public double getAsDouble(int index) {
+ switch (type) {
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_UNDEFINED:
+ return ((byte[])data)[index] & 0xff;
+ case TIFFTag.TIFF_SBYTE:
+ return ((byte[])data)[index];
+ case TIFFTag.TIFF_SHORT:
+ return ((char[])data)[index] & 0xffff;
+ case TIFFTag.TIFF_SSHORT:
+ return ((short[])data)[index];
+ case TIFFTag.TIFF_SLONG:
+ return ((int[])data)[index];
+ case TIFFTag.TIFF_LONG:
+ case TIFFTag.TIFF_IFD_POINTER:
+ return ((long[])data)[index];
+ case TIFFTag.TIFF_FLOAT:
+ return ((float[])data)[index];
+ case TIFFTag.TIFF_DOUBLE:
+ return ((double[])data)[index];
+ case TIFFTag.TIFF_SRATIONAL:
+ int[] ivalue = getAsSRational(index);
+ return (double)ivalue[0]/ivalue[1];
+ case TIFFTag.TIFF_RATIONAL:
+ long[] lvalue = getAsRational(index);
+ return (double)lvalue[0]/lvalue[1];
+ case TIFFTag.TIFF_ASCII:
+ String s = ((String[])data)[index];
+ return Double.parseDouble(s);
+ default:
+ throw new ClassCastException(); // should never happen
+ }
+ }
+
+ /**
+ * Returns a <code>TIFFTag.TIFF_ASCII</code> value as a
+ * <code>String</code>.
+ *
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_ASCII</code>.
+ *
+ * @param index The index of the data.
+ * @return The data at the given index as a {@code String}.
+ */
+ public String getAsString(int index) {
+ return ((String[])data)[index];
+ }
+
+ /**
+ * Returns a <code>TIFFTag.TIFF_SRATIONAL</code> data item as a
+ * two-element array of <code>int</code>s.
+ *
+ * @param index The index of the data.
+ * @return The data at the given index as a signed rational.
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_SRATIONAL</code>.
+ */
+ public int[] getAsSRational(int index) {
+ return ((int[][])data)[index];
+ }
+
+ /**
+ * Returns a TIFFTag.TIFF_RATIONAL data item as a two-element array
+ * of ints.
+ *
+ * @param index The index of the data.
+ * @return The data at the given index as an unsigned rational.
+ * @throws ClassCastException if the field is not of type
+ * <code>TIFF_RATIONAL</code>.
+ */
+ public long[] getAsRational(int index) {
+ return ((long[][])data)[index];
+ }
+
+
+ /**
+ * Returns a <code>String</code> containing a human-readable
+ * version of the data item. Data of type
+ * <code>TIFFTag.TIFF_RATIONAL</code> or <code>TIFF_SRATIONAL</code> are
+ * represented as a pair of integers separated by a
+ * <code>'/'</code> character.
+ *
+ * @param index The index of the data.
+ * @return The data at the given index as a {@code String}.
+ * @throws ClassCastException if the field is not of one of the
+ * legal field types.
+ */
+ public String getValueAsString(int index) {
+ switch (type) {
+ case TIFFTag.TIFF_ASCII:
+ return ((String[])data)[index];
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_UNDEFINED:
+ return Integer.toString(((byte[])data)[index] & 0xff);
+ case TIFFTag.TIFF_SBYTE:
+ return Integer.toString(((byte[])data)[index]);
+ case TIFFTag.TIFF_SHORT:
+ return Integer.toString(((char[])data)[index] & 0xffff);
+ case TIFFTag.TIFF_SSHORT:
+ return Integer.toString(((short[])data)[index]);
+ case TIFFTag.TIFF_SLONG:
+ return Integer.toString(((int[])data)[index]);
+ case TIFFTag.TIFF_LONG:
+ case TIFFTag.TIFF_IFD_POINTER:
+ return Long.toString(((long[])data)[index]);
+ case TIFFTag.TIFF_FLOAT:
+ return Float.toString(((float[])data)[index]);
+ case TIFFTag.TIFF_DOUBLE:
+ return Double.toString(((double[])data)[index]);
+ case TIFFTag.TIFF_SRATIONAL:
+ int[] ivalue = getAsSRational(index);
+ String srationalString;
+ if(ivalue[1] != 0 && ivalue[0] % ivalue[1] == 0) {
+ // If the denominator is a non-zero integral divisor
+ // of the numerator then convert the fraction to be
+ // with respect to a unity denominator.
+ srationalString =
+ Integer.toString(ivalue[0] / ivalue[1]) + "/1";
+ } else {
+ // Use the values directly.
+ srationalString =
+ Integer.toString(ivalue[0]) +
+ "/" +
+ Integer.toString(ivalue[1]);
+ }
+ return srationalString;
+ case TIFFTag.TIFF_RATIONAL:
+ long[] lvalue = getAsRational(index);
+ String rationalString;
+ if(lvalue[1] != 0L && lvalue[0] % lvalue[1] == 0) {
+ // If the denominator is a non-zero integral divisor
+ // of the numerator then convert the fraction to be
+ // with respect to a unity denominator.
+ rationalString =
+ Long.toString(lvalue[0] / lvalue[1]) + "/1";
+ } else {
+ // Use the values directly.
+ rationalString =
+ Long.toString(lvalue[0]) +
+ "/" +
+ Long.toString(lvalue[1]);
+ }
+ return rationalString;
+ default:
+ throw new ClassCastException(); // should never happen
+ }
+ }
+
+ /**
+ * Returns whether the field has a <code>TIFFDirectory</code>.
+ *
+ * @return true if and only if getDirectory() returns non-null.
+ */
+ public boolean hasDirectory() {
+ return getDirectory() != null;
+ }
+
+ /**
+ * Returns the associated <code>TIFFDirectory</code>, if available. If no
+ * directory is set, then <code>null</code> will be returned.
+ *
+ * @return the TIFFDirectory instance or null.
+ */
+ public TIFFDirectory getDirectory() {
+ return dir;
+ }
+
+ /**
+ * Clones the field and all the information contained therein.
+ *
+ * @return A clone of this <code>TIFFField</code>.
+ * @throws CloneNotSupportedException if the instance cannot be cloned.
+ */
+ @Override
+ public TIFFField clone() throws CloneNotSupportedException {
+ TIFFField field = (TIFFField)super.clone();
+
+ Object fieldData;
+ switch (type) {
+ case TIFFTag.TIFF_BYTE:
+ case TIFFTag.TIFF_UNDEFINED:
+ case TIFFTag.TIFF_SBYTE:
+ fieldData = ((byte[])data).clone();
+ break;
+ case TIFFTag.TIFF_SHORT:
+ fieldData = ((char[])data).clone();
+ break;
+ case TIFFTag.TIFF_SSHORT:
+ fieldData = ((short[])data).clone();
+ break;
+ case TIFFTag.TIFF_SLONG:
+ fieldData = ((int[])data).clone();
+ break;
+ case TIFFTag.TIFF_LONG:
+ case TIFFTag.TIFF_IFD_POINTER:
+ fieldData = ((long[])data).clone();
+ break;
+ case TIFFTag.TIFF_FLOAT:
+ fieldData = ((float[])data).clone();
+ break;
+ case TIFFTag.TIFF_DOUBLE:
+ fieldData = ((double[])data).clone();
+ break;
+ case TIFFTag.TIFF_SRATIONAL:
+ fieldData = ((int[][])data).clone();
+ break;
+ case TIFFTag.TIFF_RATIONAL:
+ fieldData = ((long[][])data).clone();
+ break;
+ case TIFFTag.TIFF_ASCII:
+ fieldData = ((String[])data).clone();
+ break;
+ default:
+ throw new ClassCastException(); // should never happen
+ }
+
+ field.tag = tag;
+ field.tagNumber = tagNumber;
+ field.type = type;
+ field.count = count;
+ field.data = fieldData;
+ field.dir = dir != null ? dir.clone() : null;
+
+ return field;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFImageReadParam.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,116 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.imageio.ImageReadParam;
+
+/**
+ * A subclass of {@link ImageReadParam} allowing control over
+ * the TIFF reading process.
+ *
+ * <p> Because TIFF is an extensible format, the reader requires
+ * information about any tags used by TIFF extensions in order to emit
+ * meaningful metadata. Also, TIFF extensions may define new
+ * compression types. Both types of information about extensions may
+ * be provided by this interface.
+ *
+ * <p> Additional TIFF tags must be organized into
+ * <code>TIFFTagSet</code>s. A <code>TIFFTagSet</code> may be
+ * provided to the reader by means of the
+ * <code>addAllowedTagSet</code> method. By default, the tag sets
+ * <code>BaselineTIFFTagSet</code>, <code>FaxTIFFTagSet</code>,
+ * <code>ExifParentTIFFTagSet</code>, and <code>GeoTIFFTagSet</code>
+ * are included.
+ *
+ * @since 1.9
+ */
+public class TIFFImageReadParam extends ImageReadParam {
+
+ private List<TIFFTagSet> allowedTagSets = new ArrayList<TIFFTagSet>(4);
+
+ /**
+ * Constructs a <code>TIFFImageReadParam</code>. Tags defined by
+ * the <code>TIFFTagSet</code>s <code>BaselineTIFFTagSet</code>,
+ * <code>FaxTIFFTagSet</code>, <code>ExifParentTIFFTagSet</code>, and
+ * <code>GeoTIFFTagSet</code> will be supported.
+ *
+ * @see BaselineTIFFTagSet
+ * @see FaxTIFFTagSet
+ * @see ExifParentTIFFTagSet
+ * @see GeoTIFFTagSet
+ */
+ public TIFFImageReadParam() {
+ addAllowedTagSet(BaselineTIFFTagSet.getInstance());
+ addAllowedTagSet(FaxTIFFTagSet.getInstance());
+ addAllowedTagSet(ExifParentTIFFTagSet.getInstance());
+ addAllowedTagSet(GeoTIFFTagSet.getInstance());
+ }
+
+ /**
+ * Adds a <code>TIFFTagSet</code> object to the list of allowed
+ * tag sets.
+ *
+ * @param tagSet a <code>TIFFTagSet</code>.
+ *
+ * @throws IllegalArgumentException if <code>tagSet</code> is
+ * <code>null</code>.
+ */
+ public void addAllowedTagSet(TIFFTagSet tagSet) {
+ if (tagSet == null) {
+ throw new IllegalArgumentException("tagSet == null!");
+ }
+ allowedTagSets.add(tagSet);
+ }
+
+ /**
+ * Removes a <code>TIFFTagSet</code> object from the list of
+ * allowed tag sets. Removal is based on the <code>equals</code>
+ * method of the <code>TIFFTagSet</code>, which is normally
+ * defined as reference equality.
+ *
+ * @param tagSet a <code>TIFFTagSet</code>.
+ *
+ * @throws IllegalArgumentException if <code>tagSet</code> is
+ * <code>null</code>.
+ */
+ public void removeAllowedTagSet(TIFFTagSet tagSet) {
+ if (tagSet == null) {
+ throw new IllegalArgumentException("tagSet == null!");
+ }
+ allowedTagSets.remove(tagSet);
+ }
+
+ /**
+ * Returns a <code>List</code> containing the allowed
+ * <code>TIFFTagSet</code> objects.
+ *
+ * @return a <code>List</code> of <code>TIFFTagSet</code>s.
+ */
+ public List<TIFFTagSet> getAllowedTagSets() {
+ return allowedTagSets;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFTag.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,413 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * A class defining the notion of a TIFF tag. A TIFF tag is a key
+ * that may appear in an Image File Directory (IFD). In the IFD
+ * each tag has some data associated with it, which may consist of zero
+ * or more values of a given data type. The combination of a tag and a
+ * value is known as an IFD Entry or TIFF Field.
+ *
+ * <p> The actual tag values used in the root IFD of a standard ("baseline")
+ * tiff stream are defined in the {@link BaselineTIFFTagSet
+ * BaselineTIFFTagSet} class.
+ *
+ * @since 1.9
+ * @see BaselineTIFFTagSet
+ * @see TIFFField
+ * @see TIFFTagSet
+ */
+public class TIFFTag {
+
+ // TIFF 6.0 + Adobe PageMaker(R) 6.0 TIFF Technical Notes 1 IFD data type
+
+ /** Flag for 8 bit unsigned integers. */
+ public static final int TIFF_BYTE = 1;
+
+ /** Flag for null-terminated ASCII strings. */
+ public static final int TIFF_ASCII = 2;
+
+ /** Flag for 16 bit unsigned integers. */
+ public static final int TIFF_SHORT = 3;
+
+ /** Flag for 32 bit unsigned integers. */
+ public static final int TIFF_LONG = 4;
+
+ /** Flag for pairs of 32 bit unsigned integers. */
+ public static final int TIFF_RATIONAL = 5;
+
+ /** Flag for 8 bit signed integers. */
+ public static final int TIFF_SBYTE = 6;
+
+ /** Flag for 8 bit uninterpreted bytes. */
+ public static final int TIFF_UNDEFINED = 7;
+
+ /** Flag for 16 bit signed integers. */
+ public static final int TIFF_SSHORT = 8;
+
+ /** Flag for 32 bit signed integers. */
+ public static final int TIFF_SLONG = 9;
+
+ /** Flag for pairs of 32 bit signed integers. */
+ public static final int TIFF_SRATIONAL = 10;
+
+ /** Flag for 32 bit IEEE floats. */
+ public static final int TIFF_FLOAT = 11;
+
+ /** Flag for 64 bit IEEE doubles. */
+ public static final int TIFF_DOUBLE = 12;
+
+ /**
+ * Flag for IFD pointer defined in TIFF Tech Note 1 in
+ * TIFF Specification Supplement 1.
+ */
+ public static final int TIFF_IFD_POINTER = 13;
+
+ /**
+ * The numerically smallest constant representing a TIFF data type.
+ */
+ public static final int MIN_DATATYPE = TIFF_BYTE;
+
+ /**
+ * The numerically largest constant representing a TIFF data type.
+ */
+ public static final int MAX_DATATYPE = TIFF_IFD_POINTER;
+
+ /**
+ * The name assigned to a tag with an unknown tag number. Such
+ * a tag may be created for example when reading an IFD and a
+ * tag number is encountered which is not in any of the
+ * <code>TIFFTagSet</code>s known to the reader.
+ */
+ public static final String UNKNOWN_TAG_NAME = "UnknownTag";
+
+ /**
+ * Disallowed data type mask.
+ */
+ private static final int DISALLOWED_DATATYPES_MASK = ~0x3fff;
+
+ private static final int[] SIZE_OF_TYPE = {
+ 0, // 0 = n/a
+ 1, // 1 = byte
+ 1, // 2 = ascii
+ 2, // 3 = short
+ 4, // 4 = long
+ 8, // 5 = rational
+ 1, // 6 = sbyte
+ 1, // 7 = undefined
+ 2, // 8 = sshort
+ 4, // 9 = slong
+ 8, // 10 = srational
+ 4, // 11 = float
+ 8, // 12 = double
+ 4, // 13 = IFD_POINTER
+ };
+
+ private int number;
+ private String name;
+ private int dataTypes;
+ private int count;
+ private TIFFTagSet tagSet = null;
+
+ // Mnemonic names for integral enumerated constants
+ private SortedMap<Integer,String> valueNames = null;
+
+ /**
+ * Constructs a <code>TIFFTag</code> with a given name, tag number, set
+ * of legal data types, and value count. A negative value count signifies
+ * that either an arbitrary number of values is legal or the required count
+ * is determined by the values of other fields in the IFD. A non-negative
+ * count specifies the number of values which an associated field must
+ * contain. The tag will have no associated <code>TIFFTagSet</code>.
+ *
+ * <p> If there are mnemonic names to be associated with the legal
+ * data values for the tag, {@link #addValueName(int, String)
+ * addValueName()} should be called on the new instance for each name.
+ * Mnemonic names apply only to tags which have integral data type.</p>
+ *
+ * <p> See the documentation for {@link #getDataTypes()
+ * getDataTypes()} for an explanation of how the set
+ * of data types is to be converted into a bit mask.</p>
+ *
+ * @param name the name of the tag.
+ * @param number the number used to represent the tag.
+ * @param dataTypes a bit mask indicating the set of legal data
+ * types for this tag.
+ * @param count the value count for this tag.
+ * @throws NullPointerException if name is null.
+ * @throws IllegalArgumentException if number is negative or dataTypes
+ * is negative or specifies an out of range type.
+ */
+ public TIFFTag(String name, int number, int dataTypes, int count) {
+ if (name == null) {
+ throw new NullPointerException("name == null");
+ } else if (number < 0) {
+ throw new IllegalArgumentException("number (" + number + ") < 0");
+ } else if (dataTypes < 0
+ || (dataTypes & DISALLOWED_DATATYPES_MASK) != 0) {
+ throw new IllegalArgumentException("dataTypes out of range");
+ }
+
+ this.name = name;
+ this.number = number;
+ this.dataTypes = dataTypes;
+ this.count = count;
+ }
+
+ /**
+ * Constructs a <code>TIFFTag</code> with a given name, tag number and
+ * <code>TIFFTagSet</code> to which it refers. The legal data types are
+ * set to include {@link #TIFF_LONG} and {@link #TIFF_IFD_POINTER} and the
+ * value count is unity. The <code>TIFFTagSet</code> will
+ * represent the set of <code>TIFFTag</code>s which appear in the IFD
+ * pointed to. A <code>TIFFTag</code> represents an IFD pointer if and
+ * only if <code>tagSet</code> is non-<code>null</code> or the data
+ * type <code>TIFF_IFD_POINTER</code> is legal.
+ *
+ * @param name the name of the tag.
+ * @param number the number used to represent the tag.
+ * @param tagSet the <code>TIFFTagSet</code> to which this tag belongs.
+ * @throws NullPointerException if name or tagSet is null.
+ * @throws IllegalArgumentException if number is negative.
+ *
+ * @see #TIFFTag(String, int, int, int)
+ */
+ public TIFFTag(String name, int number, TIFFTagSet tagSet) {
+ this(name, number,
+ 1 << TIFFTag.TIFF_LONG | 1 << TIFFTag.TIFF_IFD_POINTER, 1);
+ if (tagSet == null) {
+ throw new NullPointerException("tagSet == null");
+ }
+ this.tagSet = tagSet;
+ }
+
+ /**
+ * Constructs a <code>TIFFTag</code> with a given name, tag number,
+ * and set of legal data types. The value count of the tag will be
+ * undefined and it will have no associated <code>TIFFTagSet</code>.
+ *
+ * @param name the name of the tag.
+ * @param number the number used to represent the tag.
+ * @param dataTypes a bit mask indicating the set of legal data
+ * types for this tag.
+ * @throws NullPointerException if name is null.
+ * @throws IllegalArgumentException if number is negative or dataTypes
+ * is negative or specifies an out of range type.
+ *
+ * @see #TIFFTag(String, int, int, int)
+ */
+ public TIFFTag(String name, int number, int dataTypes) {
+ this(name, number, dataTypes, -1);
+ }
+
+ /**
+ * Returns the number of bytes used to store a value of the given
+ * data type.
+ *
+ * @param dataType the data type to be queried.
+ *
+ * @return the number of bytes used to store the given data type.
+ *
+ * @throws IllegalArgumentException if <code>datatype</code> is
+ * less than <code>MIN_DATATYPE</code> or greater than
+ * <code>MAX_DATATYPE</code>.
+ */
+ public static int getSizeOfType(int dataType) {
+ if (dataType < MIN_DATATYPE ||dataType > MAX_DATATYPE) {
+ throw new IllegalArgumentException("dataType out of range!");
+ }
+
+ return SIZE_OF_TYPE[dataType];
+ }
+
+ /**
+ * Returns the name of the tag, as it will appear in image metadata.
+ *
+ * @return the tag name, as a <code>String</code>.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the integer used to represent the tag.
+ *
+ * @return the tag number, as an <code>int</code>.
+ */
+ public int getNumber() {
+ return number;
+ }
+
+ /**
+ * Returns a bit mask indicating the set of data types that may
+ * be used to store the data associated with the tag.
+ * For example, a tag that can store both SHORT and LONG values
+ * would return a value of:
+ *
+ * <pre>
+ * (1 << TIFFTag.TIFF_SHORT) | (1 << TIFFTag.TIFF_LONG)
+ * </pre>
+ *
+ * @return an <code>int</code> containing a bitmask encoding the
+ * set of valid data types.
+ */
+ public int getDataTypes() {
+ return dataTypes;
+ }
+
+ /**
+ * Returns the value count of this tag. If this value is positive, it
+ * represents the required number of values for a <code>TIFFField</code>
+ * which has this tag. If the value is negative, the count is undefined.
+ * In the latter case the count may be derived, e.g., the number of values
+ * of the <code>BitsPerSample</code> field is <code>SamplesPerPixel</code>,
+ * or it may be variable as in the case of most <code>US-ASCII</code>
+ * fields.
+ *
+ * @return the value count of this tag.
+ */
+ public int getCount() {
+ return count;
+ }
+
+ /**
+ * Returns <code>true</code> if the given data type
+ * may be used for the data associated with this tag.
+ *
+ * @param dataType the data type to be queried, one of
+ * <code>TIFF_BYTE</code>, <code>TIFF_SHORT</code>, etc.
+ *
+ * @return a <code>boolean</code> indicating whether the given
+ * data type may be used with this tag.
+ *
+ * @throws IllegalArgumentException if <code>datatype</code> is
+ * less than <code>MIN_DATATYPE</code> or greater than
+ * <code>MAX_DATATYPE</code>.
+ */
+ public boolean isDataTypeOK(int dataType) {
+ if (dataType < MIN_DATATYPE || dataType > MAX_DATATYPE) {
+ throw new IllegalArgumentException("datatype not in range!");
+ }
+ return (dataTypes & (1 << dataType)) != 0;
+ }
+
+ /**
+ * Returns the <code>TIFFTagSet</code> of which this tag is a part.
+ *
+ * @return the containing <code>TIFFTagSet</code>.
+ */
+ public TIFFTagSet getTagSet() {
+ return tagSet;
+ }
+
+ /**
+ * Returns <code>true</code> if this tag is used to point to an IFD
+ * structure containing additional tags. A <code>TIFFTag</code> represents
+ * an IFD pointer if and only if its <code>TIFFTagSet</code> is
+ * non-<code>null</code> or the data type <code>TIFF_IFD_POINTER</code> is
+ * legal. This condition will be satisfied if and only if either
+ * <code>getTagSet() != null</code> or
+ * <code>isDataTypeOK(TIFF_IFD_POINTER) == true</code>.
+ *
+ * <p>Many TIFF extensions use the IFD mechanism in order to limit the
+ * number of new tags that may appear in the root IFD.</p>
+ *
+ * @return <code>true</code> if this tag points to an IFD.
+ */
+ public boolean isIFDPointer() {
+ return tagSet != null || isDataTypeOK(TIFF_IFD_POINTER);
+ }
+
+ /**
+ * Returns <code>true</code> if there are mnemonic names associated with
+ * the set of legal values for the data associated with this tag. Mnemonic
+ * names apply only to tags which have integral data type.
+ *
+ * @return <code>true</code> if mnemonic value names are available.
+ */
+ public boolean hasValueNames() {
+ return valueNames != null;
+ }
+
+ /**
+ * Adds a mnemonic name for a particular value that this tag's data may take
+ * on. Mnemonic names apply only to tags which have integral data type.
+ *
+ * @param value the data value.
+ * @param name the name to associate with the value.
+ */
+ protected void addValueName(int value, String name) {
+ if (valueNames == null) {
+ valueNames = new TreeMap<Integer,String>();
+ }
+ valueNames.put(Integer.valueOf(value), name);
+ }
+
+ /**
+ * Returns the mnemonic name associated with a particular value
+ * that this tag's data may take on, or <code>null</code> if
+ * no name is present. Mnemonic names apply only to tags which have
+ * integral data type.
+ *
+ * @param value the data value.
+ *
+ * @return the mnemonic name associated with the value, as a
+ * <code>String</code>.
+ */
+ public String getValueName(int value) {
+ if (valueNames == null) {
+ return null;
+ }
+ return valueNames.get(Integer.valueOf(value));
+ }
+
+ /**
+ * Returns an array of values for which mnemonic names are defined. The
+ * method {@link #getValueName(int) getValueName()} will return
+ * non-{@code null} only for values contained in the returned array.
+ * Mnemonic names apply only to tags which have integral data type.
+ *
+ * @return the values for which there is a mnemonic name.
+ */
+ public int[] getNamedValues() {
+ int[] intValues = null;
+ if (valueNames != null) {
+ Set<Integer> values = valueNames.keySet();
+ Iterator<Integer> iter = values.iterator();
+ intValues = new int[values.size()];
+ int i = 0;
+ while (iter.hasNext()) {
+ intValues[i++] = iter.next();
+ }
+ }
+ return intValues;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFTagSet.java Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,165 @@
+/*
+ * 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 javax.imageio.plugins.tiff;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * A class representing a set of TIFF tags. Each tag in the set must
+ * have a unique number (this is a limitation of the TIFF
+ * specification itself).
+ *
+ * <p> This class and its subclasses are responsible for mapping
+ * between raw tag numbers and <code>TIFFTag</code> objects, which
+ * contain additional information about each tag, such as the tag's
+ * name, legal data types, and mnemonic names for some or all of ts
+ * data values.
+ *
+ * @since 1.9
+ * @see TIFFTag
+ */
+public class TIFFTagSet {
+
+ private SortedMap<Integer,TIFFTag> allowedTagsByNumber = new TreeMap<Integer,TIFFTag>();
+
+ private SortedMap<String,TIFFTag> allowedTagsByName = new TreeMap<String,TIFFTag>();
+
+ /**
+ * Constructs a TIFFTagSet.
+ */
+ private TIFFTagSet() {}
+
+ /**
+ * Constructs a <code>TIFFTagSet</code>, given a <code>List</code>
+ * of <code>TIFFTag</code> objects.
+ *
+ * @param tags a <code>List</code> object containing
+ * <code>TIFFTag</code> objects to be added to this tag set.
+ *
+ * @throws IllegalArgumentException if <code>tags</code> is
+ * <code>null</code>, or contains objects that are not instances
+ * of the <code>TIFFTag</code> class.
+ */
+ public TIFFTagSet(List<TIFFTag> tags) {
+ if (tags == null) {
+ throw new IllegalArgumentException("tags == null!");
+ }
+ Iterator<TIFFTag> iter = tags.iterator();
+ while (iter.hasNext()) {
+ Object o = iter.next();
+ if (!(o instanceof TIFFTag)) {
+ throw new IllegalArgumentException(
+ "tags contains a non-TIFFTag!");
+ }
+ TIFFTag tag = (TIFFTag)o;
+
+ allowedTagsByNumber.put(Integer.valueOf(tag.getNumber()), tag);
+ allowedTagsByName.put(tag.getName(), tag);
+ }
+ }
+
+ /**
+ * Returns the <code>TIFFTag</code> from this set that is
+ * associated with the given tag number, or <code>null</code> if
+ * no tag exists for that number.
+ *
+ * @param tagNumber the number of the tag to be retrieved.
+ *
+ * @return the numbered <code>TIFFTag</code>, or <code>null</code>.
+ */
+ public TIFFTag getTag(int tagNumber) {
+ return allowedTagsByNumber.get(Integer.valueOf(tagNumber));
+ }
+
+ /**
+ * Returns the <code>TIFFTag</code> having the given tag name, or
+ * <code>null</code> if the named tag does not belong to this tag set.
+ *
+ * @param tagName the name of the tag to be retrieved, as a
+ * <code>String</code>.
+ *
+ * @return the named <code>TIFFTag</code>, or <code>null</code>.
+ *
+ * @throws IllegalArgumentException if <code>tagName</code> is
+ * <code>null</code>.
+ */
+ public TIFFTag getTag(String tagName) {
+ if (tagName == null) {
+ throw new IllegalArgumentException("tagName == null!");
+ }
+ return allowedTagsByName.get(tagName);
+ }
+
+ /**
+ * Retrieves an unmodifiable numerically increasing set of tag numbers.
+ *
+ * <p>The returned object is unmodifiable and contains the tag
+ * numbers of all <code>TIFFTag</code>s in this <code>TIFFTagSet</code>
+ * sorted into ascending order according to
+ * {@link Integer#compareTo(Object)}.</p>
+ *
+ * @return All tag numbers in this set.
+ */
+ public SortedSet<Integer> getTagNumbers() {
+ Set<Integer> tagNumbers = allowedTagsByNumber.keySet();
+ SortedSet<Integer> sortedTagNumbers;
+ if(tagNumbers instanceof SortedSet) {
+ sortedTagNumbers = (SortedSet<Integer>)tagNumbers;
+ } else {
+ sortedTagNumbers = new TreeSet<Integer>(tagNumbers);
+ }
+
+ return Collections.unmodifiableSortedSet(sortedTagNumbers);
+ }
+
+ /**
+ * Retrieves an unmodifiable lexicographically increasing set of tag names.
+ *
+ * <p>The returned object is unmodifiable and contains the tag
+ * names of all <code>TIFFTag</code>s in this <code>TIFFTagSet</code>
+ * sorted into ascending order according to
+ * {@link String#compareTo(Object)}.</p>
+ *
+ * @return All tag names in this set.
+ */
+ public SortedSet<String> getTagNames() {
+ Set<String> tagNames = allowedTagsByName.keySet();
+ SortedSet<String> sortedTagNames;
+ if(tagNames instanceof SortedSet) {
+ sortedTagNames = (SortedSet<String>)tagNames;
+ } else {
+ sortedTagNames = new TreeSet<String>(tagNames);
+ }
+
+ return Collections.unmodifiableSortedSet(sortedTagNames);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/plugins/tiff/package.html Mon Nov 23 12:26:12 2015 -0800
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+<!--
+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.
+-->
+</head>
+
+<body bgcolor="white">
+
+<body>
+Public classes used by the built-in TIFF plug-ins.
+
+<p>
+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
+<a HREF="../../metadata/doc-files/tiff_metadata.html">TIFF metadata format
+specification and usage notes</a>.
+
+<br>
+<br>
+<br>
+
+@since 1.9
+</body>
+</html>
--- a/jdk/src/java.desktop/share/classes/javax/imageio/spi/IIORegistry.java Mon Nov 23 10:00:50 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/spi/IIORegistry.java Mon Nov 23 12:26:12 2015 -0800
@@ -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.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 @@
registerServiceProvider(new BMPImageWriterSpi());
registerServiceProvider(new WBMPImageReaderSpi());
registerServiceProvider(new WBMPImageWriterSpi());
+ registerServiceProvider(new TIFFImageReaderSpi());
+ registerServiceProvider(new TIFFImageWriterSpi());
registerServiceProvider(new PNGImageReaderSpi());
registerServiceProvider(new PNGImageWriterSpi());
registerServiceProvider(new JPEGImageReaderSpi());