8143342: Integrate Java Image I/O support for TIFF per JEP 262

Port TIFF reader and writer plugins from JAI Image I/O Tools to JDK 9

Reviewed-by: prr, serb
This commit is contained in:
Brian Burkhalter 2015-11-23 12:26:12 -08:00
parent d1c0c842fb
commit 17a615784e
68 changed files with 28827 additions and 57 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -29,6 +29,7 @@ import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
@ -47,64 +48,15 @@ import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
import java.util.Arrays;
//import javax.imageio.ImageTypeSpecifier;
import java.util.Iterator;
import javax.imageio.IIOException;
import javax.imageio.IIOImage;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.spi.ImageWriterSpi;
public class ImageUtil {
/* XXX testing only
public static void main(String[] args) {
ImageTypeSpecifier bilevel =
ImageTypeSpecifier.createIndexed(new byte[] {(byte)0, (byte)255},
new byte[] {(byte)0, (byte)255},
new byte[] {(byte)0, (byte)255},
null, 1,
DataBuffer.TYPE_BYTE);
ImageTypeSpecifier gray =
ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false);
ImageTypeSpecifier grayAlpha =
ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false,
false);
ImageTypeSpecifier rgb =
ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[] {0, 1, 2},
DataBuffer.TYPE_BYTE,
false,
false);
ImageTypeSpecifier rgba =
ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[] {0, 1, 2, 3},
DataBuffer.TYPE_BYTE,
true,
false);
ImageTypeSpecifier packed =
ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
0xff000000,
0x00ff0000,
0x0000ff00,
0x000000ff,
DataBuffer.TYPE_BYTE,
false);
SampleModel bandedSM =
new java.awt.image.BandedSampleModel(DataBuffer.TYPE_BYTE,
1, 1, 15);
System.out.println(createColorModel(bilevel.getSampleModel()));
System.out.println(createColorModel(gray.getSampleModel()));
System.out.println(createColorModel(grayAlpha.getSampleModel()));
System.out.println(createColorModel(rgb.getSampleModel()));
System.out.println(createColorModel(rgba.getSampleModel()));
System.out.println(createColorModel(packed.getSampleModel()));
System.out.println(createColorModel(bandedSM));
}
*/
/**
* Creates a <code>ColorModel</code> that may be used with the
* specified <code>SampleModel</code>. If a suitable
@ -1162,4 +1114,78 @@ public class ImageUtil {
// pixel stride.
return ImageUtil.isBinary(sm);
}
/**
* Gets the destination image type.
*/
public static final ImageTypeSpecifier
getDestinationType(ImageReadParam param,
Iterator<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;
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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() { }
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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&nbsp;&lt;&nbsp;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&nbsp;&lt;&nbsp;3</code>.
*/
public abstract void toRGB(float x0, float x1, float x2, float[] rgb);
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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)
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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&#174; 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,&nbsp;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;
};
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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() {}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}
}
}

View File

@ -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);
}
}

View File

@ -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
@ -81,6 +81,11 @@ Each of the standard plug-ins supports a so-called "native" metadata
format, which encodes its metadata losslessly:
<ul>
<li>
<A HREF="doc-files/bmp_metadata.html">
BMP metadata
</A>
<li>
<A HREF="doc-files/gif_metadata.html">
GIF metadata
@ -97,8 +102,8 @@ PNG metadata
</A>
<li>
<A HREF="doc-files/bmp_metadata.html">
BMP metadata
<A HREF="doc-files/tiff_metadata.html#StreamMetadata">
TIFF metadata
</A>
<li>

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}

View File

@ -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 &lt;&lt; TIFFTag.TIFF_SHORT) | (1 &lt;&lt; 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()&nbsp;!=&nbsp;null</code> or
* <code>isDataTypeOK(TIFF_IFD_POINTER)&nbsp;==&nbsp;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;
}
}

View File

@ -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);
}
}

View File

@ -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>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -49,6 +49,8 @@ import com.sun.imageio.plugins.bmp.BMPImageReaderSpi;
import com.sun.imageio.plugins.bmp.BMPImageWriterSpi;
import com.sun.imageio.plugins.wbmp.WBMPImageReaderSpi;
import com.sun.imageio.plugins.wbmp.WBMPImageWriterSpi;
import com.sun.imageio.plugins.tiff.TIFFImageReaderSpi;
import com.sun.imageio.plugins.tiff.TIFFImageWriterSpi;
import sun.awt.AppContext;
import java.util.ServiceLoader;
import java.util.ServiceConfigurationError;
@ -168,6 +170,8 @@ public final class IIORegistry extends ServiceRegistry {
registerServiceProvider(new BMPImageWriterSpi());
registerServiceProvider(new WBMPImageReaderSpi());
registerServiceProvider(new WBMPImageWriterSpi());
registerServiceProvider(new TIFFImageReaderSpi());
registerServiceProvider(new TIFFImageWriterSpi());
registerServiceProvider(new PNGImageReaderSpi());
registerServiceProvider(new PNGImageWriterSpi());
registerServiceProvider(new JPEGImageReaderSpi());