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:
parent
d1c0c842fb
commit
17a615784e
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.Rectangle;
|
||||||
import java.awt.Transparency;
|
import java.awt.Transparency;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.ColorSpace;
|
||||||
|
import java.awt.color.ICC_ColorSpace;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.ColorModel;
|
import java.awt.image.ColorModel;
|
||||||
import java.awt.image.ComponentColorModel;
|
import java.awt.image.ComponentColorModel;
|
||||||
@ -47,64 +48,15 @@ import java.awt.image.SampleModel;
|
|||||||
import java.awt.image.SinglePixelPackedSampleModel;
|
import java.awt.image.SinglePixelPackedSampleModel;
|
||||||
import java.awt.image.WritableRaster;
|
import java.awt.image.WritableRaster;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
//import javax.imageio.ImageTypeSpecifier;
|
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
import javax.imageio.IIOImage;
|
import javax.imageio.IIOImage;
|
||||||
|
import javax.imageio.ImageReadParam;
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import javax.imageio.ImageWriter;
|
import javax.imageio.ImageWriter;
|
||||||
import javax.imageio.spi.ImageWriterSpi;
|
import javax.imageio.spi.ImageWriterSpi;
|
||||||
|
|
||||||
public class ImageUtil {
|
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
|
* Creates a <code>ColorModel</code> that may be used with the
|
||||||
* specified <code>SampleModel</code>. If a suitable
|
* specified <code>SampleModel</code>. If a suitable
|
||||||
@ -1162,4 +1114,78 @@ public class ImageUtil {
|
|||||||
// pixel stride.
|
// pixel stride.
|
||||||
return ImageUtil.isBinary(sm);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
# Common properties
|
# Common properties
|
||||||
ImageUtil0=The supplied Raster does not represent a binary data set.
|
ImageUtil0=The supplied Raster does not represent a binary data set.
|
||||||
ImageUtil1=The provided sample model is null.
|
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.
|
GetNumImages0=Input has not been set.
|
||||||
GetNumImages1=seekForwardOnly and allowSearch cannot both be true.
|
GetNumImages1=seekForwardOnly and allowSearch cannot both be true.
|
||||||
|
|
||||||
|
@ -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() { }
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package com.sun.imageio.plugins.tiff;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract class that performs simple color conversion on 3-banded source
|
||||||
|
* images, for use with the TIFF Image I/O plug-in.
|
||||||
|
*/
|
||||||
|
public abstract class TIFFColorConverter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance of a <code>TIFFColorConverter</code>.
|
||||||
|
*/
|
||||||
|
public TIFFColorConverter() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an RGB triple into the native color space of this
|
||||||
|
* TIFFColorConverter, and stores the result in the first three
|
||||||
|
* entries of the <code>result</code> array.
|
||||||
|
*
|
||||||
|
* @param r the red value.
|
||||||
|
* @param g the green value.
|
||||||
|
* @param b the blue value.
|
||||||
|
* @param result an array of <code>float</code>s containing three elements.
|
||||||
|
* @throws NullPointerException if <code>result</code> is
|
||||||
|
* <code>null</code>.
|
||||||
|
* @throws ArrayIndexOutOfBoundsException if
|
||||||
|
* <code>result.length < 3</code>.
|
||||||
|
*/
|
||||||
|
public abstract void fromRGB(float r, float g, float b, float[] result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a triple in the native color space of this
|
||||||
|
* TIFFColorConverter into an RGB triple, and stores the result in
|
||||||
|
* the first three entries of the <code>rgb</code> array.
|
||||||
|
*
|
||||||
|
* @param x0 the value of channel 0.
|
||||||
|
* @param x1 the value of channel 1.
|
||||||
|
* @param x2 the value of channel 2.
|
||||||
|
* @param rgb an array of <code>float</code>s containing three elements.
|
||||||
|
* @throws NullPointerException if <code>rgb</code> is
|
||||||
|
* <code>null</code>.
|
||||||
|
* @throws ArrayIndexOutOfBoundsException if
|
||||||
|
* <code>rgb.length < 3</code>.
|
||||||
|
*/
|
||||||
|
public abstract void toRGB(float x0, float x1, float x2, float[] rgb);
|
||||||
|
}
|
@ -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;
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package com.sun.imageio.plugins.tiff;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import javax.imageio.ImageWriteParam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A subclass of {@link ImageWriteParam ImageWriteParam}
|
||||||
|
* allowing control over the TIFF writing process. The set of innately
|
||||||
|
* supported compression types is listed in the following table:
|
||||||
|
*
|
||||||
|
* <table border=1>
|
||||||
|
* <caption><b>Supported Compression Types</b></caption>
|
||||||
|
* <tr><th>Compression Type</th> <th>Description</th> <th>Reference</th></tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>CCITT RLE</td>
|
||||||
|
* <td>Modified Huffman compression</td>
|
||||||
|
* <td>TIFF 6.0 Specification, Section 10</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>CCITT T.4</td>
|
||||||
|
* <td>CCITT T.4 bilevel encoding/Group 3 facsimile compression</td>
|
||||||
|
* <td>TIFF 6.0 Specification, Section 11</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>CCITT T.6</td>
|
||||||
|
* <td>CCITT T.6 bilevel encoding/Group 4 facsimile compression</td>
|
||||||
|
* <td>TIFF 6.0 Specification, Section 11</td></tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>LZW</td>
|
||||||
|
* <td>LZW compression</td>
|
||||||
|
* <td>TIFF 6.0 Specification, Section 13</td></tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>JPEG</td>
|
||||||
|
* <td>"New" JPEG-in-TIFF compression</td>
|
||||||
|
* <td><a href="ftp://ftp.sgi.com/graphics/tiff/TTN2.draft.txt">TIFF
|
||||||
|
* Technical Note #2</a></td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>ZLib</td>
|
||||||
|
* <td>"Deflate/Inflate" compression (see note following this table)</td>
|
||||||
|
* <td><a href="http://partners.adobe.com/asn/developer/pdfs/tn/TIFFphotoshop.pdf">
|
||||||
|
* Adobe Photoshop® TIFF Technical Notes</a> (PDF)</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>PackBits</td>
|
||||||
|
* <td>Byte-oriented, run length compression</td>
|
||||||
|
* <td>TIFF 6.0 Specification, Section 9</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Deflate</td>
|
||||||
|
* <td>"Zip-in-TIFF" compression (see note following this table)</td>
|
||||||
|
* <td><a href="http://www.isi.edu/in-notes/rfc1950.txt">
|
||||||
|
* ZLIB Compressed Data Format Specification</a>,
|
||||||
|
* <a href="http://www.isi.edu/in-notes/rfc1951.txt">
|
||||||
|
* DEFLATE Compressed Data Format Specification</a></td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>Exif JPEG</td>
|
||||||
|
* <td>Exif-specific JPEG compression (see note following this table)</td>
|
||||||
|
* <td><a href="http://www.exif.org/Exif2-2.PDF">Exif 2.2 Specification</a>
|
||||||
|
* (PDF), section 4.5.5, "Basic Structure of Thumbnail Data"</td>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Old-style JPEG compression as described in section 22 of the TIFF 6.0
|
||||||
|
* Specification is <i>not</i> supported.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p> The CCITT compression types are applicable to bilevel (1-bit)
|
||||||
|
* images only. The JPEG compression type is applicable to byte
|
||||||
|
* grayscale (1-band) and RGB (3-band) images only.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* ZLib and Deflate compression are identical except for the value of the
|
||||||
|
* TIFF Compression field: for ZLib the Compression field has value 8
|
||||||
|
* whereas for Deflate it has value 32946 (0x80b2). In both cases each
|
||||||
|
* image segment (strip or tile) is written as a single complete zlib data
|
||||||
|
* stream.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* "Exif JPEG" is a compression type used when writing the contents of an
|
||||||
|
* APP1 Exif marker segment for inclusion in a JPEG native image metadata
|
||||||
|
* tree. The contents appended to the output when this compression type is
|
||||||
|
* used are a function of whether an empty or non-empty image is written.
|
||||||
|
* If the image is empty, then a TIFF IFD adhering to the specification of
|
||||||
|
* a compressed Exif primary IFD is appended. If the image is non-empty,
|
||||||
|
* then a complete IFD and image adhering to the specification of a
|
||||||
|
* compressed Exif thumbnail IFD and image are appended. Note that the
|
||||||
|
* data of the empty image may <i>not</i> later be appended using the pixel
|
||||||
|
* replacement capability of the TIFF writer.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p> If ZLib/Deflate or JPEG compression is used, the compression quality
|
||||||
|
* may be set. For ZLib/Deflate the supplied floating point quality value is
|
||||||
|
* rescaled to the range <tt>[1, 9]</tt> and truncated to an integer
|
||||||
|
* to derive the Deflate compression level. For JPEG the floating point
|
||||||
|
* quality value is passed directly to the JPEG writer plug-in which
|
||||||
|
* interprets it in the usual way.</p>
|
||||||
|
*
|
||||||
|
* <p> The <code>canWriteTiles</code> and
|
||||||
|
* <code>canWriteCompressed</code> methods will return
|
||||||
|
* <code>true</code>; the <code>canOffsetTiles</code> and
|
||||||
|
* <code>canWriteProgressive</code> methods will return
|
||||||
|
* <code>false</code>.</p>
|
||||||
|
*
|
||||||
|
* <p> If tiles are being written, then each of their dimensions will be
|
||||||
|
* rounded to the nearest multiple of 16 per the TIFF specification. If
|
||||||
|
* JPEG-in-TIFF compression is being used, and tiles are being written
|
||||||
|
* each tile dimension will be rounded to the nearest multiple of 8 times
|
||||||
|
* the JPEG minimum coded unit (MCU) in that dimension. If JPEG-in-TIFF
|
||||||
|
* compression is being used and strips are being written, the number of
|
||||||
|
* rows per strip is rounded to a multiple of 8 times the maximum MCU over
|
||||||
|
* both dimensions.</p>
|
||||||
|
*/
|
||||||
|
public class TIFFImageWriteParam extends ImageWriteParam {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a <code>TIFFImageWriteParam</code> instance
|
||||||
|
* for a given <code>Locale</code>.
|
||||||
|
*
|
||||||
|
* @param locale the <code>Locale</code> for which messages
|
||||||
|
* should be localized.
|
||||||
|
*/
|
||||||
|
public TIFFImageWriteParam(Locale locale) {
|
||||||
|
super(locale);
|
||||||
|
this.canWriteCompressed = true;
|
||||||
|
this.canWriteTiles = true;
|
||||||
|
this.compressionTypes = TIFFImageWriter.TIFFCompressionTypes;
|
||||||
|
};
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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() {}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<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.
|
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
|
||||||
This code is free software; you can redistribute it and/or modify it
|
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:
|
format, which encodes its metadata losslessly:
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>
|
||||||
|
<A HREF="doc-files/bmp_metadata.html">
|
||||||
|
BMP metadata
|
||||||
|
</A>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<A HREF="doc-files/gif_metadata.html">
|
<A HREF="doc-files/gif_metadata.html">
|
||||||
GIF metadata
|
GIF metadata
|
||||||
@ -97,8 +102,8 @@ PNG metadata
|
|||||||
</A>
|
</A>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<A HREF="doc-files/bmp_metadata.html">
|
<A HREF="doc-files/tiff_metadata.html#StreamMetadata">
|
||||||
BMP metadata
|
TIFF metadata
|
||||||
</A>
|
</A>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,413 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
package javax.imageio.plugins.tiff;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedMap;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class defining the notion of a TIFF tag. A TIFF tag is a key
|
||||||
|
* that may appear in an Image File Directory (IFD). In the IFD
|
||||||
|
* each tag has some data associated with it, which may consist of zero
|
||||||
|
* or more values of a given data type. The combination of a tag and a
|
||||||
|
* value is known as an IFD Entry or TIFF Field.
|
||||||
|
*
|
||||||
|
* <p> The actual tag values used in the root IFD of a standard ("baseline")
|
||||||
|
* tiff stream are defined in the {@link BaselineTIFFTagSet
|
||||||
|
* BaselineTIFFTagSet} class.
|
||||||
|
*
|
||||||
|
* @since 1.9
|
||||||
|
* @see BaselineTIFFTagSet
|
||||||
|
* @see TIFFField
|
||||||
|
* @see TIFFTagSet
|
||||||
|
*/
|
||||||
|
public class TIFFTag {
|
||||||
|
|
||||||
|
// TIFF 6.0 + Adobe PageMaker(R) 6.0 TIFF Technical Notes 1 IFD data type
|
||||||
|
|
||||||
|
/** Flag for 8 bit unsigned integers. */
|
||||||
|
public static final int TIFF_BYTE = 1;
|
||||||
|
|
||||||
|
/** Flag for null-terminated ASCII strings. */
|
||||||
|
public static final int TIFF_ASCII = 2;
|
||||||
|
|
||||||
|
/** Flag for 16 bit unsigned integers. */
|
||||||
|
public static final int TIFF_SHORT = 3;
|
||||||
|
|
||||||
|
/** Flag for 32 bit unsigned integers. */
|
||||||
|
public static final int TIFF_LONG = 4;
|
||||||
|
|
||||||
|
/** Flag for pairs of 32 bit unsigned integers. */
|
||||||
|
public static final int TIFF_RATIONAL = 5;
|
||||||
|
|
||||||
|
/** Flag for 8 bit signed integers. */
|
||||||
|
public static final int TIFF_SBYTE = 6;
|
||||||
|
|
||||||
|
/** Flag for 8 bit uninterpreted bytes. */
|
||||||
|
public static final int TIFF_UNDEFINED = 7;
|
||||||
|
|
||||||
|
/** Flag for 16 bit signed integers. */
|
||||||
|
public static final int TIFF_SSHORT = 8;
|
||||||
|
|
||||||
|
/** Flag for 32 bit signed integers. */
|
||||||
|
public static final int TIFF_SLONG = 9;
|
||||||
|
|
||||||
|
/** Flag for pairs of 32 bit signed integers. */
|
||||||
|
public static final int TIFF_SRATIONAL = 10;
|
||||||
|
|
||||||
|
/** Flag for 32 bit IEEE floats. */
|
||||||
|
public static final int TIFF_FLOAT = 11;
|
||||||
|
|
||||||
|
/** Flag for 64 bit IEEE doubles. */
|
||||||
|
public static final int TIFF_DOUBLE = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag for IFD pointer defined in TIFF Tech Note 1 in
|
||||||
|
* TIFF Specification Supplement 1.
|
||||||
|
*/
|
||||||
|
public static final int TIFF_IFD_POINTER = 13;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The numerically smallest constant representing a TIFF data type.
|
||||||
|
*/
|
||||||
|
public static final int MIN_DATATYPE = TIFF_BYTE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The numerically largest constant representing a TIFF data type.
|
||||||
|
*/
|
||||||
|
public static final int MAX_DATATYPE = TIFF_IFD_POINTER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name assigned to a tag with an unknown tag number. Such
|
||||||
|
* a tag may be created for example when reading an IFD and a
|
||||||
|
* tag number is encountered which is not in any of the
|
||||||
|
* <code>TIFFTagSet</code>s known to the reader.
|
||||||
|
*/
|
||||||
|
public static final String UNKNOWN_TAG_NAME = "UnknownTag";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disallowed data type mask.
|
||||||
|
*/
|
||||||
|
private static final int DISALLOWED_DATATYPES_MASK = ~0x3fff;
|
||||||
|
|
||||||
|
private static final int[] SIZE_OF_TYPE = {
|
||||||
|
0, // 0 = n/a
|
||||||
|
1, // 1 = byte
|
||||||
|
1, // 2 = ascii
|
||||||
|
2, // 3 = short
|
||||||
|
4, // 4 = long
|
||||||
|
8, // 5 = rational
|
||||||
|
1, // 6 = sbyte
|
||||||
|
1, // 7 = undefined
|
||||||
|
2, // 8 = sshort
|
||||||
|
4, // 9 = slong
|
||||||
|
8, // 10 = srational
|
||||||
|
4, // 11 = float
|
||||||
|
8, // 12 = double
|
||||||
|
4, // 13 = IFD_POINTER
|
||||||
|
};
|
||||||
|
|
||||||
|
private int number;
|
||||||
|
private String name;
|
||||||
|
private int dataTypes;
|
||||||
|
private int count;
|
||||||
|
private TIFFTagSet tagSet = null;
|
||||||
|
|
||||||
|
// Mnemonic names for integral enumerated constants
|
||||||
|
private SortedMap<Integer,String> valueNames = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a <code>TIFFTag</code> with a given name, tag number, set
|
||||||
|
* of legal data types, and value count. A negative value count signifies
|
||||||
|
* that either an arbitrary number of values is legal or the required count
|
||||||
|
* is determined by the values of other fields in the IFD. A non-negative
|
||||||
|
* count specifies the number of values which an associated field must
|
||||||
|
* contain. The tag will have no associated <code>TIFFTagSet</code>.
|
||||||
|
*
|
||||||
|
* <p> If there are mnemonic names to be associated with the legal
|
||||||
|
* data values for the tag, {@link #addValueName(int, String)
|
||||||
|
* addValueName()} should be called on the new instance for each name.
|
||||||
|
* Mnemonic names apply only to tags which have integral data type.</p>
|
||||||
|
*
|
||||||
|
* <p> See the documentation for {@link #getDataTypes()
|
||||||
|
* getDataTypes()} for an explanation of how the set
|
||||||
|
* of data types is to be converted into a bit mask.</p>
|
||||||
|
*
|
||||||
|
* @param name the name of the tag.
|
||||||
|
* @param number the number used to represent the tag.
|
||||||
|
* @param dataTypes a bit mask indicating the set of legal data
|
||||||
|
* types for this tag.
|
||||||
|
* @param count the value count for this tag.
|
||||||
|
* @throws NullPointerException if name is null.
|
||||||
|
* @throws IllegalArgumentException if number is negative or dataTypes
|
||||||
|
* is negative or specifies an out of range type.
|
||||||
|
*/
|
||||||
|
public TIFFTag(String name, int number, int dataTypes, int count) {
|
||||||
|
if (name == null) {
|
||||||
|
throw new NullPointerException("name == null");
|
||||||
|
} else if (number < 0) {
|
||||||
|
throw new IllegalArgumentException("number (" + number + ") < 0");
|
||||||
|
} else if (dataTypes < 0
|
||||||
|
|| (dataTypes & DISALLOWED_DATATYPES_MASK) != 0) {
|
||||||
|
throw new IllegalArgumentException("dataTypes out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
|
this.number = number;
|
||||||
|
this.dataTypes = dataTypes;
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a <code>TIFFTag</code> with a given name, tag number and
|
||||||
|
* <code>TIFFTagSet</code> to which it refers. The legal data types are
|
||||||
|
* set to include {@link #TIFF_LONG} and {@link #TIFF_IFD_POINTER} and the
|
||||||
|
* value count is unity. The <code>TIFFTagSet</code> will
|
||||||
|
* represent the set of <code>TIFFTag</code>s which appear in the IFD
|
||||||
|
* pointed to. A <code>TIFFTag</code> represents an IFD pointer if and
|
||||||
|
* only if <code>tagSet</code> is non-<code>null</code> or the data
|
||||||
|
* type <code>TIFF_IFD_POINTER</code> is legal.
|
||||||
|
*
|
||||||
|
* @param name the name of the tag.
|
||||||
|
* @param number the number used to represent the tag.
|
||||||
|
* @param tagSet the <code>TIFFTagSet</code> to which this tag belongs.
|
||||||
|
* @throws NullPointerException if name or tagSet is null.
|
||||||
|
* @throws IllegalArgumentException if number is negative.
|
||||||
|
*
|
||||||
|
* @see #TIFFTag(String, int, int, int)
|
||||||
|
*/
|
||||||
|
public TIFFTag(String name, int number, TIFFTagSet tagSet) {
|
||||||
|
this(name, number,
|
||||||
|
1 << TIFFTag.TIFF_LONG | 1 << TIFFTag.TIFF_IFD_POINTER, 1);
|
||||||
|
if (tagSet == null) {
|
||||||
|
throw new NullPointerException("tagSet == null");
|
||||||
|
}
|
||||||
|
this.tagSet = tagSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a <code>TIFFTag</code> with a given name, tag number,
|
||||||
|
* and set of legal data types. The value count of the tag will be
|
||||||
|
* undefined and it will have no associated <code>TIFFTagSet</code>.
|
||||||
|
*
|
||||||
|
* @param name the name of the tag.
|
||||||
|
* @param number the number used to represent the tag.
|
||||||
|
* @param dataTypes a bit mask indicating the set of legal data
|
||||||
|
* types for this tag.
|
||||||
|
* @throws NullPointerException if name is null.
|
||||||
|
* @throws IllegalArgumentException if number is negative or dataTypes
|
||||||
|
* is negative or specifies an out of range type.
|
||||||
|
*
|
||||||
|
* @see #TIFFTag(String, int, int, int)
|
||||||
|
*/
|
||||||
|
public TIFFTag(String name, int number, int dataTypes) {
|
||||||
|
this(name, number, dataTypes, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bytes used to store a value of the given
|
||||||
|
* data type.
|
||||||
|
*
|
||||||
|
* @param dataType the data type to be queried.
|
||||||
|
*
|
||||||
|
* @return the number of bytes used to store the given data type.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if <code>datatype</code> is
|
||||||
|
* less than <code>MIN_DATATYPE</code> or greater than
|
||||||
|
* <code>MAX_DATATYPE</code>.
|
||||||
|
*/
|
||||||
|
public static int getSizeOfType(int dataType) {
|
||||||
|
if (dataType < MIN_DATATYPE ||dataType > MAX_DATATYPE) {
|
||||||
|
throw new IllegalArgumentException("dataType out of range!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return SIZE_OF_TYPE[dataType];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the tag, as it will appear in image metadata.
|
||||||
|
*
|
||||||
|
* @return the tag name, as a <code>String</code>.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the integer used to represent the tag.
|
||||||
|
*
|
||||||
|
* @return the tag number, as an <code>int</code>.
|
||||||
|
*/
|
||||||
|
public int getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a bit mask indicating the set of data types that may
|
||||||
|
* be used to store the data associated with the tag.
|
||||||
|
* For example, a tag that can store both SHORT and LONG values
|
||||||
|
* would return a value of:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* (1 << TIFFTag.TIFF_SHORT) | (1 << TIFFTag.TIFF_LONG)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return an <code>int</code> containing a bitmask encoding the
|
||||||
|
* set of valid data types.
|
||||||
|
*/
|
||||||
|
public int getDataTypes() {
|
||||||
|
return dataTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value count of this tag. If this value is positive, it
|
||||||
|
* represents the required number of values for a <code>TIFFField</code>
|
||||||
|
* which has this tag. If the value is negative, the count is undefined.
|
||||||
|
* In the latter case the count may be derived, e.g., the number of values
|
||||||
|
* of the <code>BitsPerSample</code> field is <code>SamplesPerPixel</code>,
|
||||||
|
* or it may be variable as in the case of most <code>US-ASCII</code>
|
||||||
|
* fields.
|
||||||
|
*
|
||||||
|
* @return the value count of this tag.
|
||||||
|
*/
|
||||||
|
public int getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if the given data type
|
||||||
|
* may be used for the data associated with this tag.
|
||||||
|
*
|
||||||
|
* @param dataType the data type to be queried, one of
|
||||||
|
* <code>TIFF_BYTE</code>, <code>TIFF_SHORT</code>, etc.
|
||||||
|
*
|
||||||
|
* @return a <code>boolean</code> indicating whether the given
|
||||||
|
* data type may be used with this tag.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if <code>datatype</code> is
|
||||||
|
* less than <code>MIN_DATATYPE</code> or greater than
|
||||||
|
* <code>MAX_DATATYPE</code>.
|
||||||
|
*/
|
||||||
|
public boolean isDataTypeOK(int dataType) {
|
||||||
|
if (dataType < MIN_DATATYPE || dataType > MAX_DATATYPE) {
|
||||||
|
throw new IllegalArgumentException("datatype not in range!");
|
||||||
|
}
|
||||||
|
return (dataTypes & (1 << dataType)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the <code>TIFFTagSet</code> of which this tag is a part.
|
||||||
|
*
|
||||||
|
* @return the containing <code>TIFFTagSet</code>.
|
||||||
|
*/
|
||||||
|
public TIFFTagSet getTagSet() {
|
||||||
|
return tagSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if this tag is used to point to an IFD
|
||||||
|
* structure containing additional tags. A <code>TIFFTag</code> represents
|
||||||
|
* an IFD pointer if and only if its <code>TIFFTagSet</code> is
|
||||||
|
* non-<code>null</code> or the data type <code>TIFF_IFD_POINTER</code> is
|
||||||
|
* legal. This condition will be satisfied if and only if either
|
||||||
|
* <code>getTagSet() != null</code> or
|
||||||
|
* <code>isDataTypeOK(TIFF_IFD_POINTER) == true</code>.
|
||||||
|
*
|
||||||
|
* <p>Many TIFF extensions use the IFD mechanism in order to limit the
|
||||||
|
* number of new tags that may appear in the root IFD.</p>
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if this tag points to an IFD.
|
||||||
|
*/
|
||||||
|
public boolean isIFDPointer() {
|
||||||
|
return tagSet != null || isDataTypeOK(TIFF_IFD_POINTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if there are mnemonic names associated with
|
||||||
|
* the set of legal values for the data associated with this tag. Mnemonic
|
||||||
|
* names apply only to tags which have integral data type.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if mnemonic value names are available.
|
||||||
|
*/
|
||||||
|
public boolean hasValueNames() {
|
||||||
|
return valueNames != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a mnemonic name for a particular value that this tag's data may take
|
||||||
|
* on. Mnemonic names apply only to tags which have integral data type.
|
||||||
|
*
|
||||||
|
* @param value the data value.
|
||||||
|
* @param name the name to associate with the value.
|
||||||
|
*/
|
||||||
|
protected void addValueName(int value, String name) {
|
||||||
|
if (valueNames == null) {
|
||||||
|
valueNames = new TreeMap<Integer,String>();
|
||||||
|
}
|
||||||
|
valueNames.put(Integer.valueOf(value), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the mnemonic name associated with a particular value
|
||||||
|
* that this tag's data may take on, or <code>null</code> if
|
||||||
|
* no name is present. Mnemonic names apply only to tags which have
|
||||||
|
* integral data type.
|
||||||
|
*
|
||||||
|
* @param value the data value.
|
||||||
|
*
|
||||||
|
* @return the mnemonic name associated with the value, as a
|
||||||
|
* <code>String</code>.
|
||||||
|
*/
|
||||||
|
public String getValueName(int value) {
|
||||||
|
if (valueNames == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return valueNames.get(Integer.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of values for which mnemonic names are defined. The
|
||||||
|
* method {@link #getValueName(int) getValueName()} will return
|
||||||
|
* non-{@code null} only for values contained in the returned array.
|
||||||
|
* Mnemonic names apply only to tags which have integral data type.
|
||||||
|
*
|
||||||
|
* @return the values for which there is a mnemonic name.
|
||||||
|
*/
|
||||||
|
public int[] getNamedValues() {
|
||||||
|
int[] intValues = null;
|
||||||
|
if (valueNames != null) {
|
||||||
|
Set<Integer> values = valueNames.keySet();
|
||||||
|
Iterator<Integer> iter = values.iterator();
|
||||||
|
intValues = new int[values.size()];
|
||||||
|
int i = 0;
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
intValues[i++] = iter.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return intValues;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* 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.bmp.BMPImageWriterSpi;
|
||||||
import com.sun.imageio.plugins.wbmp.WBMPImageReaderSpi;
|
import com.sun.imageio.plugins.wbmp.WBMPImageReaderSpi;
|
||||||
import com.sun.imageio.plugins.wbmp.WBMPImageWriterSpi;
|
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 sun.awt.AppContext;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.ServiceConfigurationError;
|
import java.util.ServiceConfigurationError;
|
||||||
@ -168,6 +170,8 @@ public final class IIORegistry extends ServiceRegistry {
|
|||||||
registerServiceProvider(new BMPImageWriterSpi());
|
registerServiceProvider(new BMPImageWriterSpi());
|
||||||
registerServiceProvider(new WBMPImageReaderSpi());
|
registerServiceProvider(new WBMPImageReaderSpi());
|
||||||
registerServiceProvider(new WBMPImageWriterSpi());
|
registerServiceProvider(new WBMPImageWriterSpi());
|
||||||
|
registerServiceProvider(new TIFFImageReaderSpi());
|
||||||
|
registerServiceProvider(new TIFFImageWriterSpi());
|
||||||
registerServiceProvider(new PNGImageReaderSpi());
|
registerServiceProvider(new PNGImageReaderSpi());
|
||||||
registerServiceProvider(new PNGImageWriterSpi());
|
registerServiceProvider(new PNGImageWriterSpi());
|
||||||
registerServiceProvider(new JPEGImageReaderSpi());
|
registerServiceProvider(new JPEGImageReaderSpi());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user