Started breaking up TIFFImageEncoder into constituent classes
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/commons/branches/Temp_URI_Resolution@1387604 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/java/org/apache/xmlgraphics/image/codec/tiff/CompressionValue.java b/src/java/org/apache/xmlgraphics/image/codec/tiff/CompressionValue.java
new file mode 100644
index 0000000..b1fe2ed
--- /dev/null
+++ b/src/java/org/apache/xmlgraphics/image/codec/tiff/CompressionValue.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.xmlgraphics.image.codec.tiff;
+
+
+/** Enumerates the possible compression values for TIFF images. */
+public enum CompressionValue {
+ /** No compression. */
+ NONE(1),
+ /**
+ * Modified Huffman Compression (CCITT Group 3 1D facsimile compression).
+ * <p><b>Not currently supported.</b>
+ */
+ GROUP3_1D(2),
+ /**
+ * CCITT T.4 bilevel compression (CCITT Group 3 2D facsimile compression).
+ * <p><b>Not currently supported.</b>
+ */
+ GROUP3_2D(3),
+ /**
+ * CCITT T.6 bilevel compression (CCITT Group 4 facsimile compression).
+ * <p><b>Not currently supported.</b>
+ */
+ GROUP4(4),
+ /** LZW compression. <p><b>Not supported.</b> */
+ LZW(5),
+ /**
+ * Code for original JPEG-in-TIFF compression which has been depricated (for many good reasons)
+ * in favor of Tech Note 2 JPEG compression (compression scheme 7).
+ * <p><b>Not supported.</b>
+ */
+ JPEG_BROKEN(6),
+ /** <a href="ftp://ftp.sgi.com/graphics/tiff/TTN2.draft.txt"> JPEG-in-TIFF</a> compression. */
+ JPEG_TTN2(7),
+ /** Byte-oriented run-length encoding "PackBits" compression. */
+ PACKBITS(32773),
+ /**
+ * <a href="http://info.internet.isi.edu:80/in-notes/rfc/files/rfc1951.txt">
+ * DEFLATE</a> lossless compression (also known as "Zip-in-TIFF").
+ */
+ DEFLATE(32946);
+
+ private final int compressionValue;
+
+ private CompressionValue(int compressionValue) {
+ this.compressionValue = compressionValue;
+ }
+
+ int getValue() {
+ return compressionValue;
+ }
+
+ /**
+ * Gets the compression value given the name of the compression type.
+ * @param name the compression name
+ * @return the compression value
+ */
+ public static CompressionValue getValue(String name) {
+ if (name == null) {
+ return PACKBITS;
+ }
+ for (CompressionValue cv : CompressionValue.values()) {
+ if (cv.toString().equalsIgnoreCase(name)) {
+ return cv;
+ }
+ }
+ throw new IllegalArgumentException("Unknown compression value: " + name);
+ }
+}
diff --git a/src/java/org/apache/xmlgraphics/image/codec/tiff/ExtraSamplesType.java b/src/java/org/apache/xmlgraphics/image/codec/tiff/ExtraSamplesType.java
new file mode 100644
index 0000000..5600092
--- /dev/null
+++ b/src/java/org/apache/xmlgraphics/image/codec/tiff/ExtraSamplesType.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.xmlgraphics.image.codec.tiff;
+
+import java.awt.image.ColorModel;
+
+enum ExtraSamplesType {
+ UNSPECIFIED(0),
+ ASSOCIATED_ALPHA(1),
+ UNASSOCIATED_ALPHA(2);
+
+ private final int typeValue;
+
+ private ExtraSamplesType(int value) {
+ this.typeValue = value;
+ }
+
+ static ExtraSamplesType getValue(ColorModel colorModel, int numExtraSamples) {
+ if (numExtraSamples == 1 && colorModel.hasAlpha()) {
+ return colorModel.isAlphaPremultiplied() ? ASSOCIATED_ALPHA : UNASSOCIATED_ALPHA;
+ }
+ return UNSPECIFIED;
+ }
+
+ int getValue() {
+ return typeValue;
+ }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/xmlgraphics/image/codec/tiff/ImageInfo.java b/src/java/org/apache/xmlgraphics/image/codec/tiff/ImageInfo.java
new file mode 100644
index 0000000..f30286a
--- /dev/null
+++ b/src/java/org/apache/xmlgraphics/image/codec/tiff/ImageInfo.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.xmlgraphics.image.codec.tiff;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.RenderedImage;
+
+final class ImageInfo {
+
+ // Default values
+ private static final int DEFAULT_ROWS_PER_STRIP = 8;
+
+ private final int numExtraSamples;
+ private final ExtraSamplesType extraSampleType;
+ private final ImageType imageType;
+ private final int colormapSize;
+ private final char[] colormap;
+ private final int tileWidth;
+ private final int tileHeight;
+ private final int numTiles;
+ private final long bytesPerRow;
+ private final long bytesPerTile;
+
+ private ImageInfo(ImageInfoBuilder builder) {
+ this.numExtraSamples = builder.numExtraSamples;
+ this.extraSampleType = builder.extraSampleType;
+ this.imageType = builder.imageType;
+ this.colormapSize = builder.colormapSize;
+ this.colormap = copyColormap(builder.colormap);
+ this.tileWidth = builder.tileWidth;
+ this.tileHeight = builder.tileHeight;
+ this.numTiles = builder.numTiles;
+ this.bytesPerRow = builder.bytesPerRow;
+ this.bytesPerTile = builder.bytesPerTile;
+ }
+
+ private static char[] copyColormap(char[] colorMap) {
+ if (colorMap == null) {
+ return null;
+ }
+ char[] copy = new char[colorMap.length];
+ System.arraycopy(colorMap, 0, copy, 0, colorMap.length);
+ return copy;
+ }
+
+ private static int getNumberOfExtraSamplesForColorSpace(ColorSpace colorSpace,
+ ImageType imageType, int numBands) {
+ if (imageType == ImageType.GENERIC) {
+ return numBands - 1;
+ } else if (numBands > 1) {
+ return numBands - colorSpace.getNumComponents();
+ } else {
+ return 0;
+ }
+ }
+
+ private static char[] createColormap(final int sizeOfColormap, byte[] r, byte[] g, byte[] b) {
+ int redIndex = 0;
+ int greenIndex = sizeOfColormap;
+ int blueIndex = 2 * sizeOfColormap;
+ char[] colormap = new char[sizeOfColormap * 3];
+ for (int i = 0; i < sizeOfColormap; i++) {
+ // beware of sign extended bytes
+ colormap[redIndex++] = convertColorToColormapChar(0xff & r[i]);
+ colormap[greenIndex++] = convertColorToColormapChar(0xff & g[i]);
+ colormap[blueIndex++] = convertColorToColormapChar(0xff & b[i]);
+ }
+ return colormap;
+ }
+
+ private static char convertColorToColormapChar(int color) {
+ return (char) (color << 8 | color);
+ }
+
+ int getNumberOfExtraSamples() {
+ return numExtraSamples;
+ }
+
+ ExtraSamplesType getExtraSamplesType() {
+ return extraSampleType;
+ }
+
+ ImageType getType() {
+ return imageType;
+ }
+
+ int getColormapSize() {
+ return colormapSize;
+ }
+
+ char[] getColormap() {
+ return copyColormap(colormap);
+ }
+
+ int getTileWidth() {
+ return tileWidth;
+ }
+
+ int getTileHeight() {
+ return tileHeight;
+ }
+
+ int getNumTiles() {
+ return numTiles;
+ }
+
+ long getBytesPerRow() {
+ return bytesPerRow;
+ }
+
+ long getBytesPerTile() {
+ return bytesPerTile;
+ }
+
+ static ImageInfo newInstance(RenderedImage im, int dataTypeSize, int numBands,
+ ColorModel colorModel, TIFFEncodeParam params) {
+ ImageInfoBuilder builder = new ImageInfoBuilder();
+ if (colorModel instanceof IndexColorModel) { // Bilevel or palette
+ IndexColorModel indexColorModel = (IndexColorModel) colorModel;
+ int colormapSize = indexColorModel.getMapSize();
+ byte[] r = new byte[colormapSize];
+ indexColorModel.getReds(r);
+ byte[] g = new byte[colormapSize];
+ indexColorModel.getGreens(g);
+ byte[] b = new byte[colormapSize];
+ indexColorModel.getBlues(b);
+
+ builder.imageType = ImageType.getTypeFromRGB(colormapSize, r, g, b, dataTypeSize,
+ numBands);
+ if (builder.imageType == ImageType.PALETTE) {
+ builder.colormap = createColormap(colormapSize, r, g, b);
+ builder.colormapSize = colormapSize * 3;
+ }
+ } else if (colorModel == null) {
+ if (dataTypeSize == 1 && numBands == 1) { // bilevel
+ builder.imageType = ImageType.BILEVEL_BLACK_IS_ZERO;
+ } else {
+ builder.imageType = ImageType.GENERIC;
+ builder.numExtraSamples = numBands > 1 ? numBands - 1 : 0;
+ }
+ } else {
+ ColorSpace colorSpace = colorModel.getColorSpace();
+ builder.imageType = ImageType.getTypeFromColorSpace(colorSpace, params);
+ builder.numExtraSamples = getNumberOfExtraSamplesForColorSpace(colorSpace,
+ builder.imageType, numBands);
+ builder.extraSampleType = ExtraSamplesType.getValue(colorModel,
+ builder.numExtraSamples);
+ }
+
+ // Initialize tile dimensions.
+ final int width = im.getWidth();
+ final int height = im.getHeight();
+ if (params.getWriteTiled()) {
+ builder.tileWidth = params.getTileWidth() > 0 ? params.getTileWidth() : width;
+ builder.tileHeight = params.getTileHeight() > 0 ? params.getTileHeight() : height;
+ // NB: Parentheses are used in this statement for correct rounding.
+ builder.numTiles = ((width + builder.tileWidth - 1) / builder.tileWidth)
+ * ((height + builder.tileHeight - 1) / builder.tileHeight);
+ } else {
+ builder.tileWidth = width;
+ builder.tileHeight = params.getTileHeight() > 0 ? params.getTileHeight()
+ : DEFAULT_ROWS_PER_STRIP;
+ builder.numTiles = (int) Math.ceil(height / (double) builder.tileHeight);
+ }
+ builder.setBytesPerRow(dataTypeSize, numBands)
+ .setBytesPerTile();
+ return builder.build();
+ }
+
+ private static final class ImageInfoBuilder {
+ private ImageType imageType = ImageType.UNSUPPORTED;
+ private int numExtraSamples;
+ private char[] colormap;
+ private int colormapSize;
+ private ExtraSamplesType extraSampleType = ExtraSamplesType.UNSPECIFIED;
+ private int tileWidth;
+ private int tileHeight;
+ private int numTiles;
+ private long bytesPerRow;
+ private long bytesPerTile;
+
+ private ImageInfoBuilder setBytesPerRow(int dataTypeSize, int numBands) {
+ bytesPerRow = (long) Math.ceil((dataTypeSize / 8.0) * tileWidth * numBands);
+ return this;
+ }
+
+ private ImageInfoBuilder setBytesPerTile() {
+ bytesPerTile = bytesPerRow * tileHeight;
+ return this;
+ }
+
+ private ImageInfo build() {
+ return new ImageInfo(this);
+ }
+ }
+}
diff --git a/src/java/org/apache/xmlgraphics/image/codec/tiff/ImageType.java b/src/java/org/apache/xmlgraphics/image/codec/tiff/ImageType.java
new file mode 100644
index 0000000..188ea1f
--- /dev/null
+++ b/src/java/org/apache/xmlgraphics/image/codec/tiff/ImageType.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.xmlgraphics.image.codec.tiff;
+
+import java.awt.color.ColorSpace;
+
+import org.apache.xmlgraphics.image.codec.util.PropertyUtil;
+
+enum ImageType {
+ UNSUPPORTED(-1),
+ BILEVEL_WHITE_IS_ZERO(0),
+ BILEVEL_BLACK_IS_ZERO(1),
+ GRAY(1),
+ PALETTE(3),
+ RGB(2),
+ CMYK(5),
+ YCBCR(6),
+ CIELAB(8),
+ GENERIC(1);
+
+ private final int photometricInterpretation;
+
+ private ImageType(int photometricInterpretation) {
+ this.photometricInterpretation = photometricInterpretation;
+ }
+
+ int getPhotometricInterpretation() {
+ return photometricInterpretation;
+ }
+
+ static ImageType getTypeFromRGB(int mapSize, byte[] r, byte[] g, byte[] b,
+ int dataTypeSize, int numBands) {
+ if (numBands == 1) {
+ if (dataTypeSize == 1) { // Bilevel image
+ if (mapSize != 2) {
+ throw new IllegalArgumentException(PropertyUtil.getString("TIFFImageEncoder7"));
+ }
+
+ if (isBlackZero(r, g, b)) {
+ return BILEVEL_BLACK_IS_ZERO;
+ } else if (isWhiteZero(r, g, b)) {
+ return BILEVEL_WHITE_IS_ZERO;
+ }
+ }
+ return PALETTE;
+ }
+ return UNSUPPORTED;
+ }
+
+ private static boolean rgbIsValueAt(byte[] r, byte[] g, byte[] b, byte value, int i) {
+ return r[i] == value && g[i] == value && b[i] == value;
+ }
+
+ private static boolean bilevelColorValue(byte[] r, byte[] g, byte[] b, int blackValue,
+ int whiteValue) {
+ return rgbIsValueAt(r, g, b, (byte) blackValue, 0)
+ && rgbIsValueAt(r, g, b, (byte) whiteValue, 1);
+ }
+
+ private static boolean isBlackZero(byte[] r, byte[] g, byte[] b) {
+ return bilevelColorValue(r, g, b, 0, 0xff);
+ }
+
+ private static boolean isWhiteZero(byte[] r, byte[] g, byte[] b) {
+ return bilevelColorValue(r, g, b, 0xff, 0);
+ }
+
+ static ImageType getTypeFromColorSpace(ColorSpace colorSpace, TIFFEncodeParam params) {
+ switch (colorSpace.getType()) {
+ case ColorSpace.TYPE_CMYK:
+ return CMYK;
+ case ColorSpace.TYPE_GRAY:
+ return GRAY;
+ case ColorSpace.TYPE_Lab:
+ return CIELAB;
+ case ColorSpace.TYPE_RGB:
+ if (params.getJPEGCompressRGBToYCbCr()) {
+ return ImageType.YCBCR;
+ } else {
+ return ImageType.RGB;
+ }
+ case ColorSpace.TYPE_YCbCr:
+ return YCBCR;
+ default:
+ return GENERIC;
+ }
+ }
+}
diff --git a/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFEncodeParam.java b/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFEncodeParam.java
index 5afe0d2..d70e76c 100644
--- a/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFEncodeParam.java
+++ b/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFEncodeParam.java
@@ -45,59 +45,9 @@
*/
public class TIFFEncodeParam implements ImageEncodeParam {
- /** No compression. */
- public static final int COMPRESSION_NONE = 1;
+ private CompressionValue compression = CompressionValue.NONE;
- /**
- * Modified Huffman Compression (CCITT Group 3 1D facsimile compression).
- * <p><b>Not currently supported.</b>
- */
- public static final int COMPRESSION_GROUP3_1D = 2;
-
- /**
- * CCITT T.4 bilevel compression (CCITT Group 3 2D facsimile compression).
- * <p><b>Not currently supported.</b>
- */
- public static final int COMPRESSION_GROUP3_2D = 3;
-
- /**
- * CCITT T.6 bilevel compression (CCITT Group 4 facsimile compression).
- * <p><b>Not currently supported.</b>
- */
- public static final int COMPRESSION_GROUP4 = 4;
-
- /**
- * LZW compression.
- * <p><b>Not supported.</b>
- */
- public static final int COMPRESSION_LZW = 5;
-
- /**
- * Code for original JPEG-in-TIFF compression which has been
- * depricated (for many good reasons) in favor of Tech Note 2
- * JPEG compression (compression scheme 7).
- * <p><b>Not supported.</b>
- */
- public static final int COMPRESSION_JPEG_BROKEN = 6;
-
- /**
- * <a href="ftp://ftp.sgi.com/graphics/tiff/TTN2.draft.txt">
- * JPEG-in-TIFF</a> compression.
- */
- public static final int COMPRESSION_JPEG_TTN2 = 7;
-
- /** Byte-oriented run-length encoding "PackBits" compression. */
- public static final int COMPRESSION_PACKBITS = 32773;
-
- /**
- * <a href="http://info.internet.isi.edu:80/in-notes/rfc/files/rfc1951.txt">
- * DEFLATE</a> lossless compression (also known as "Zip-in-TIFF").
- */
- public static final int COMPRESSION_DEFLATE = 32946;
-
- private int compression = COMPRESSION_NONE;
-
- private boolean writeTiled = false;
+ private boolean writeTiled;
private int tileWidth;
private int tileHeight;
@@ -117,10 +67,8 @@
//nop
}
- /**
- * Returns the value of the compression parameter.
- */
- public int getCompression() {
+ /** Returns the value of the compression parameter. */
+ public CompressionValue getCompression() {
return compression;
}
@@ -141,12 +89,12 @@
*
* @param compression The compression type.
*/
- public void setCompression(int compression) {
+ public void setCompression(CompressionValue compression) {
switch(compression) {
- case COMPRESSION_NONE:
- case COMPRESSION_PACKBITS:
- case COMPRESSION_DEFLATE:
+ case NONE:
+ case PACKBITS:
+ case DEFLATE:
// Do nothing.
break;
default:
diff --git a/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImageDecoder.java b/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImageDecoder.java
index 30375cb..0ddaeb2 100644
--- a/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImageDecoder.java
+++ b/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImageDecoder.java
@@ -22,7 +22,6 @@
import java.awt.image.RenderedImage;
import java.io.IOException;
-import org.apache.xmlgraphics.image.codec.util.ImageDecodeParam;
import org.apache.xmlgraphics.image.codec.util.ImageDecoderImpl;
import org.apache.xmlgraphics.image.codec.util.PropertyUtil;
import org.apache.xmlgraphics.image.codec.util.SeekableStream;
@@ -74,8 +73,7 @@
public static final int TIFF_ICC_PROFILE = 34675;
- public TIFFImageDecoder(SeekableStream input,
- ImageDecodeParam param) {
+ public TIFFImageDecoder(SeekableStream input, TIFFDecodeParam param) {
super(input, param);
}
@@ -87,6 +85,6 @@
if ((page < 0) || (page >= getNumPages())) {
throw new IOException(PropertyUtil.getString("TIFFImageDecoder0"));
}
- return new TIFFImage(input, (TIFFDecodeParam)param, page);
+ return new TIFFImage(input, (TIFFDecodeParam) param, page);
}
}
diff --git a/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImageEncoder.java b/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImageEncoder.java
index 8b70ce2..5a29ef1 100644
--- a/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImageEncoder.java
+++ b/src/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImageEncoder.java
@@ -20,7 +20,6 @@
package org.apache.xmlgraphics.image.codec.tiff;
import java.awt.Rectangle;
-import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import java.awt.image.ComponentSampleModel;
import java.awt.image.DataBuffer;
@@ -61,37 +60,13 @@
*/
public class TIFFImageEncoder extends ImageEncoderImpl {
- // Image Types
- private static final int TIFF_UNSUPPORTED = -1;
- private static final int TIFF_BILEVEL_WHITE_IS_ZERO = 0;
- private static final int TIFF_BILEVEL_BLACK_IS_ZERO = 1;
- private static final int TIFF_GRAY = 2;
- private static final int TIFF_PALETTE = 3;
- private static final int TIFF_RGB = 4;
- private static final int TIFF_CMYK = 5;
- private static final int TIFF_YCBCR = 6;
- private static final int TIFF_CIELAB = 7;
- private static final int TIFF_GENERIC = 8;
-
- // Compression types
- private static final int COMP_NONE = 1;
- private static final int COMP_JPEG_TTN2 = 7;
- private static final int COMP_PACKBITS = 32773;
- private static final int COMP_DEFLATE = 32946;
-
// Incidental tags
private static final int TIFF_JPEG_TABLES = 347;
private static final int TIFF_YCBCR_SUBSAMPLING = 530;
private static final int TIFF_YCBCR_POSITIONING = 531;
private static final int TIFF_REF_BLACK_WHITE = 532;
- // ExtraSamples types
- private static final int EXTRA_SAMPLE_UNSPECIFIED = 0;
- private static final int EXTRA_SAMPLE_ASSOCIATED_ALPHA = 1;
- private static final int EXTRA_SAMPLE_UNASSOCIATED_ALPHA = 2;
- // Default values
- private static final int DEFAULT_ROWS_PER_STRIP = 8;
public TIFFImageEncoder(OutputStream output, ImageEncodeParam param) {
super(output, param);
@@ -197,9 +172,9 @@
private int encode(RenderedImage im, TIFFEncodeParam encodeParam,
int ifdOffset, boolean isLast) throws IOException {
// Currently all images are stored uncompressed.
- int compression = encodeParam.getCompression();
+ CompressionValue compression = encodeParam.getCompression();
- if (compression == COMP_JPEG_TTN2) {
+ if (compression == CompressionValue.JPEG_TTN2) {
throw new IllegalArgumentException(PropertyUtil.getString("TIFFImageEncoder12"));
}
@@ -214,263 +189,31 @@
// Get SampleModel.
SampleModel sampleModel = im.getSampleModel();
-
- // Retrieve and verify sample size.
- int[] sampleSize = sampleModel.getSampleSize();
- for (int i = 1; i < sampleSize.length; i++) {
- if (sampleSize[i] != sampleSize[0]) {
- throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder0"));
- }
- }
-
- // Check low bit limits.
- int numBands = sampleModel.getNumBands();
- if ((sampleSize[0] == 1 || sampleSize[0] == 4) && numBands != 1) {
- throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder1"));
- }
-
- // Retrieve and verify data type.
- int dataType = sampleModel.getDataType();
- switch(dataType) {
- case DataBuffer.TYPE_BYTE:
- if (sampleSize[0] != 1 && sampleSize[0] == 4 && // todo does this make sense??
- sampleSize[0] != 8) { // we get error only for 4
- throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder2"));
- }
- break;
- case DataBuffer.TYPE_SHORT:
- case DataBuffer.TYPE_USHORT:
- if (sampleSize[0] != 16) {
- throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder3"));
- }
- break;
- case DataBuffer.TYPE_INT:
- case DataBuffer.TYPE_FLOAT:
- if (sampleSize[0] != 32) {
- throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder4"));
- }
- break;
- default:
- throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder5"));
- }
-
- boolean dataTypeIsShort =
- dataType == DataBuffer.TYPE_SHORT ||
- dataType == DataBuffer.TYPE_USHORT;
-
ColorModel colorModel = im.getColorModel();
- if (colorModel != null &&
- colorModel instanceof IndexColorModel &&
- dataType != DataBuffer.TYPE_BYTE) {
- // Don't support (unsigned) short palette-color images.
- throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder6"));
- }
- IndexColorModel icm = null;
- int sizeOfColormap = 0;
- char[] colormap = null;
+ int[] sampleSize = sampleModel.getSampleSize();
+ int dataTypeSize = sampleSize[0];
+ int numBands = sampleModel.getNumBands();
+ int dataType = sampleModel.getDataType();
+ validateImage(dataTypeSize, sampleSize, numBands, dataType, colorModel);
+
+ boolean dataTypeIsShort = dataType == DataBuffer.TYPE_SHORT
+ || dataType == DataBuffer.TYPE_USHORT;
// Set image type.
- int imageType = TIFF_UNSUPPORTED;
- int numExtraSamples = 0;
- int extraSampleType = EXTRA_SAMPLE_UNSPECIFIED;
- if (colorModel instanceof IndexColorModel) { // Bilevel or palette
- icm = (IndexColorModel)colorModel;
- int mapSize = icm.getMapSize();
+ ImageInfo imageInfo = ImageInfo.newInstance(im, dataTypeSize, numBands, colorModel,
+ encodeParam);
- if (sampleSize[0] == 1 && numBands == 1) { // Bilevel image
-
- if (mapSize != 2) {
- throw new IllegalArgumentException(PropertyUtil.getString("TIFFImageEncoder7"));
- }
-
- byte[] r = new byte[mapSize];
- icm.getReds(r);
- byte[] g = new byte[mapSize];
- icm.getGreens(g);
- byte[] b = new byte[mapSize];
- icm.getBlues(b);
-
- if ((r[0] & 0xff) == 0 &&
- (r[1] & 0xff) == 255 &&
- (g[0] & 0xff) == 0 &&
- (g[1] & 0xff) == 255 &&
- (b[0] & 0xff) == 0 &&
- (b[1] & 0xff) == 255) {
-
- imageType = TIFF_BILEVEL_BLACK_IS_ZERO;
-
- } else if ((r[0] & 0xff) == 255 &&
- (r[1] & 0xff) == 0 &&
- (g[0] & 0xff) == 255 &&
- (g[1] & 0xff) == 0 &&
- (b[0] & 0xff) == 255 &&
- (b[1] & 0xff) == 0) {
-
- imageType = TIFF_BILEVEL_WHITE_IS_ZERO;
-
- } else {
- imageType = TIFF_PALETTE;
- }
-
- } else if (numBands == 1) { // Non-bilevel image.
- // Palette color image.
- imageType = TIFF_PALETTE;
- }
- } else if (colorModel == null) {
-
- if (sampleSize[0] == 1 && numBands == 1) { // bilevel
- imageType = TIFF_BILEVEL_BLACK_IS_ZERO;
- } else { // generic image
- imageType = TIFF_GENERIC;
- if (numBands > 1) {
- numExtraSamples = numBands - 1;
- }
- }
-
- } else { // colorModel is non-null but not an IndexColorModel
- ColorSpace colorSpace = colorModel.getColorSpace();
-
- switch(colorSpace.getType()) {
- case ColorSpace.TYPE_CMYK:
- imageType = TIFF_CMYK;
- break;
- case ColorSpace.TYPE_GRAY:
- imageType = TIFF_GRAY;
- break;
- case ColorSpace.TYPE_Lab:
- imageType = TIFF_CIELAB;
- break;
- case ColorSpace.TYPE_RGB:
- if (compression == COMP_JPEG_TTN2
- && encodeParam.getJPEGCompressRGBToYCbCr()) {
- imageType = TIFF_YCBCR;
- } else {
- imageType = TIFF_RGB;
- }
- break;
- case ColorSpace.TYPE_YCbCr:
- imageType = TIFF_YCBCR;
- break;
- default:
- imageType = TIFF_GENERIC; // generic
- break;
- }
-
- if (imageType == TIFF_GENERIC) {
- numExtraSamples = numBands - 1;
- } else if (numBands > 1) {
- numExtraSamples = numBands - colorSpace.getNumComponents();
- }
-
- if (numExtraSamples == 1 && colorModel.hasAlpha()) {
- extraSampleType = colorModel.isAlphaPremultiplied() ?
- EXTRA_SAMPLE_ASSOCIATED_ALPHA :
- EXTRA_SAMPLE_UNASSOCIATED_ALPHA;
- }
- }
-
- if (imageType == TIFF_UNSUPPORTED) {
+ if (imageInfo.getType() == ImageType.UNSUPPORTED) {
throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder8"));
}
- int photometricInterpretation = -1;
- switch (imageType) {
-
- case TIFF_BILEVEL_WHITE_IS_ZERO:
- photometricInterpretation = 0;
- break;
-
- case TIFF_BILEVEL_BLACK_IS_ZERO:
- photometricInterpretation = 1;
- break;
-
- case TIFF_GRAY:
- case TIFF_GENERIC:
- // Since the CS_GRAY colorspace is always of type black_is_zero
- photometricInterpretation = 1;
- break;
-
- case TIFF_PALETTE:
- photometricInterpretation = 3;
-
- icm = (IndexColorModel)colorModel;
- sizeOfColormap = icm.getMapSize();
-
- byte[] r = new byte[sizeOfColormap];
- icm.getReds(r);
- byte[] g = new byte[sizeOfColormap];
- icm.getGreens(g);
- byte[] b = new byte[sizeOfColormap];
- icm.getBlues(b);
-
- int redIndex = 0, greenIndex = sizeOfColormap;
- int blueIndex = 2 * sizeOfColormap;
- colormap = new char[sizeOfColormap * 3];
- for (int i = 0; i < sizeOfColormap; i++) {
- int tmp = 0xff & r[i]; // beware of sign extended bytes
- colormap[redIndex++] = (char)((tmp << 8) | tmp);
- tmp = 0xff & g[i];
- colormap[greenIndex++] = (char)((tmp << 8) | tmp);
- tmp = 0xff & b[i];
- colormap[blueIndex++] = (char)((tmp << 8) | tmp);
- }
-
- sizeOfColormap *= 3;
-
- break;
-
- case TIFF_RGB:
- photometricInterpretation = 2;
- break;
-
- case TIFF_CMYK:
- photometricInterpretation = 5;
- break;
-
- case TIFF_YCBCR:
- photometricInterpretation = 6;
- break;
-
- case TIFF_CIELAB:
- photometricInterpretation = 8;
- break;
-
- default:
- throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder8"));
- }
-
- // Initialize tile dimensions.
- int tileWidth;
- int tileHeight;
- if (isTiled) {
- tileWidth = encodeParam.getTileWidth() > 0 ?
- encodeParam.getTileWidth() : im.getTileWidth();
- tileHeight = encodeParam.getTileHeight() > 0 ?
- encodeParam.getTileHeight() : im.getTileHeight();
- } else {
- tileWidth = width;
-
- tileHeight = encodeParam.getTileHeight() > 0 ?
- encodeParam.getTileHeight() : DEFAULT_ROWS_PER_STRIP;
- }
-
- int numTiles;
- if (isTiled) {
- // NB: Parentheses are used in this statement for correct rounding.
- numTiles =
- ((width + tileWidth - 1) / tileWidth) *
- ((height + tileHeight - 1) / tileHeight);
- } else {
- numTiles = (int)Math.ceil((double)height / (double)tileHeight);
- }
+ final int numTiles = imageInfo.getNumTiles();
+ final long bytesPerTile = imageInfo.getBytesPerTile();
+ final long bytesPerRow = imageInfo.getBytesPerRow();
+ final int tileHeight = imageInfo.getTileHeight();
+ final int tileWidth = imageInfo.getTileWidth();
long[] tileByteCounts = new long[numTiles];
-
- long bytesPerRow =
- (long)Math.ceil((sampleSize[0] / 8.0) * tileWidth * numBands);
-
- long bytesPerTile = bytesPerRow * tileHeight;
-
for (int i = 0; i < numTiles; i++) {
tileByteCounts[i] = bytesPerTile;
}
@@ -480,12 +223,7 @@
long lastStripRows = height - (tileHeight * (numTiles - 1));
tileByteCounts[numTiles - 1] = lastStripRows * bytesPerRow;
}
-
- long totalBytesOfData = bytesPerTile * (numTiles - 1) +
- tileByteCounts[numTiles - 1];
-
- // The data will be written after the IFD: create the array here
- // but fill it in later.
+ long totalBytesOfData = bytesPerTile * (numTiles - 1) + tileByteCounts[numTiles - 1];
long[] tileOffsets = new long[numTiles];
// Basic fields - have to be in increasing numerical order.
@@ -502,7 +240,7 @@
// ResolutionUnit 296
// Create Directory
- SortedSet fields = new TreeSet();
+ SortedSet<TIFFField> fields = new TreeSet<TIFFField>();
// Image Width
fields.add(new TIFFField(TIFFImageDecoder.TIFF_IMAGE_WIDTH,
@@ -516,7 +254,7 @@
char [] shortSampleSize = new char[numBands];
for (int i = 0; i < numBands; i++) {
- shortSampleSize[i] = (char)sampleSize[i];
+ shortSampleSize[i] = (char) dataTypeSize;
}
fields.add(new TIFFField(TIFFImageDecoder.TIFF_BITS_PER_SAMPLE,
TIFFField.TIFF_SHORT, numBands,
@@ -524,12 +262,12 @@
fields.add(new TIFFField(TIFFImageDecoder.TIFF_COMPRESSION,
TIFFField.TIFF_SHORT, 1,
- new char[] {(char)compression}));
+ new char[] {(char)compression.getValue()}));
fields.add(
new TIFFField(TIFFImageDecoder.TIFF_PHOTOMETRIC_INTERPRETATION,
TIFFField.TIFF_SHORT, 1,
- new char[] {(char)photometricInterpretation}));
+ new char[] {(char) imageInfo.getType().getPhotometricInterpretation()}));
if (!isTiled) {
fields.add(new TIFFField(TIFFImageDecoder.TIFF_STRIP_OFFSETS,
@@ -551,10 +289,10 @@
tileByteCounts));
}
- if (colormap != null) {
+ if (imageInfo.getColormap() != null) {
fields.add(new TIFFField(TIFFImageDecoder.TIFF_COLORMAP,
- TIFFField.TIFF_SHORT, sizeOfColormap,
- colormap));
+ TIFFField.TIFF_SHORT, imageInfo.getColormapSize(),
+ imageInfo.getColormap()));
}
if (isTiled) {
@@ -575,13 +313,13 @@
tileByteCounts));
}
- if (numExtraSamples > 0) {
- char[] extraSamples = new char[numExtraSamples];
- for (int i = 0; i < numExtraSamples; i++) {
- extraSamples[i] = (char)extraSampleType;
+ if (imageInfo.getNumberOfExtraSamples() > 0) {
+ char[] extraSamples = new char[imageInfo.getNumberOfExtraSamples()];
+ for (int i = 0; i < imageInfo.getNumberOfExtraSamples(); i++) {
+ extraSamples[i] = (char) imageInfo.getExtraSamplesType().getValue();
}
fields.add(new TIFFField(TIFFImageDecoder.TIFF_EXTRA_SAMPLES,
- TIFFField.TIFF_SHORT, numExtraSamples,
+ TIFFField.TIFF_SHORT, imageInfo.getNumberOfExtraSamples(),
extraSamples));
}
@@ -610,7 +348,7 @@
// use it if available.
}
- if (imageType == TIFF_YCBCR) {
+ if (imageInfo.getType() == ImageType.YCBCR) {
// YCbCrSubSampling: 2 is the default so we must write 1 as
// we do not (yet) do any subsampling.
char subsampleH = 1;
@@ -625,7 +363,7 @@
fields.add(new TIFFField(TIFF_YCBCR_POSITIONING,
TIFFField.TIFF_SHORT, 1,
new char[]
- {(char)((compression == COMP_JPEG_TTN2) ? 1 : 2)}));
+ {(char) ((compression == CompressionValue.JPEG_TTN2) ? 1 : 2)}));
// Reference black/white.
long[][] refbw;
@@ -691,16 +429,16 @@
Deflater deflater = null;
boolean jpegRGBToYCbCr = false;
- if (compression == COMP_NONE) {
+ if (compression == CompressionValue.NONE) {
// Determine the number of bytes of padding necessary between
// the end of the IFD and the first data segment such that the
// alignment of the data conforms to the specification (required
// for uncompressed data only).
int numBytesPadding = 0;
- if (sampleSize[0] == 16 && tileOffsets[0] % 2 != 0) {
+ if (dataTypeSize == 16 && tileOffsets[0] % 2 != 0) {
numBytesPadding = 1;
tileOffsets[0]++;
- } else if (sampleSize[0] == 32 && tileOffsets[0] % 4 != 0) {
+ } else if (dataTypeSize == 32 && tileOffsets[0] % 4 != 0) {
numBytesPadding = (int)(4 - tileOffsets[0] % 4);
tileOffsets[0] += numBytesPadding;
}
@@ -758,11 +496,11 @@
int bufSize = 0;
switch(compression) {
- case COMP_PACKBITS:
- bufSize = (int)(bytesPerTile + ((bytesPerRow + 127) / 128) * tileHeight);
+ case PACKBITS:
+ bufSize = (int) (bytesPerTile + ((bytesPerRow + 127) / 128) * tileHeight);
break;
- case COMP_DEFLATE:
- bufSize = (int)bytesPerTile;
+ case DEFLATE:
+ bufSize = (int) bytesPerTile;
deflater = new Deflater(encodeParam.getDeflateLevel());
break;
default:
@@ -781,16 +519,16 @@
// Whether to test for contiguous data.
boolean checkContiguous =
- ((sampleSize[0] == 1 &&
+ ((dataTypeSize == 1 &&
sampleModel instanceof MultiPixelPackedSampleModel &&
dataType == DataBuffer.TYPE_BYTE) ||
- (sampleSize[0] == 8 &&
+ (dataTypeSize == 8 &&
sampleModel instanceof ComponentSampleModel));
// Also create a buffer to hold tileHeight lines of the
// data to be written to the file, so we can use array writes.
byte[] bpixels = null;
- if (compression != COMP_JPEG_TTN2) {
+ if (compression != CompressionValue.JPEG_TTN2) {
if (dataType == DataBuffer.TYPE_BYTE) {
bpixels = new byte[tileHeight * tileWidth * numBands];
} else if (dataTypeIsShort) {
@@ -816,9 +554,9 @@
im.getData(new Rectangle(col, row, tileWidth, rows));
boolean useDataBuffer = false;
- if (compression != COMP_JPEG_TTN2) { // JPEG access Raster
+ if (compression != CompressionValue.JPEG_TTN2) { // JPEG access Raster
if (checkContiguous) {
- if (sampleSize[0] == 8) { // 8-bit
+ if (dataTypeSize == 8) { // 8-bit
ComponentSampleModel csm =
(ComponentSampleModel)src.getSampleModel();
int[] bankIndices = csm.getBankIndices();
@@ -866,7 +604,7 @@
int pixel = 0;
int k = 0;
- switch (sampleSize[0]) {
+ switch (dataTypeSize) {
case 1:
@@ -881,18 +619,18 @@
src.getSampleModelTranslateX(),
row -
src.getSampleModelTranslateY());
- if (lineStride == (int)bytesPerRow) {
+ if (lineStride == bytesPerRow) {
System.arraycopy(btmp, inOffset,
bpixels, 0,
- (int)bytesPerRow * rows);
+ (int) bytesPerRow * rows);
} else {
int outOffset = 0;
for (int j = 0; j < rows; j++) {
System.arraycopy(btmp, inOffset,
bpixels, outOffset,
- (int)bytesPerRow);
+ (int) bytesPerRow);
inOffset += lineStride;
- outOffset += (int)bytesPerRow;
+ outOffset += bytesPerRow;
}
}
} else {
@@ -927,16 +665,16 @@
}
}
- if (compression == COMP_NONE) {
+ if (compression == CompressionValue.NONE) {
output.write(bpixels, 0, rows * ((tileWidth + 7) / 8));
- } else if (compression == COMP_PACKBITS) {
+ } else if (compression == CompressionValue.PACKBITS) {
int numCompressedBytes =
compressPackBits(bpixels, rows,
- (int)bytesPerRow,
+ bytesPerRow,
compressBuf);
tileByteCounts[tileNum++] = numCompressedBytes;
output.write(compressBuf, 0, numCompressedBytes);
- } else if (compression == COMP_DEFLATE) {
+ } else if (compression == CompressionValue.DEFLATE) {
int numCompressedBytes =
deflate(deflater, bpixels, compressBuf);
tileByteCounts[tileNum++] = numCompressedBytes;
@@ -966,16 +704,16 @@
}
}
- if (compression == COMP_NONE) {
+ if (compression == CompressionValue.NONE) {
output.write(bpixels, 0, rows * ((tileWidth + 1) / 2));
- } else if (compression == COMP_PACKBITS) {
+ } else if (compression == CompressionValue.PACKBITS) {
int numCompressedBytes =
compressPackBits(bpixels, rows,
- (int)bytesPerRow,
+ bytesPerRow,
compressBuf);
tileByteCounts[tileNum++] = numCompressedBytes;
output.write(compressBuf, 0, numCompressedBytes);
- } else if (compression == COMP_DEFLATE) {
+ } else if (compression == CompressionValue.DEFLATE) {
int numCompressedBytes =
deflate(deflater, bpixels, compressBuf);
tileByteCounts[tileNum++] = numCompressedBytes;
@@ -985,7 +723,7 @@
case 8:
- if (compression != COMP_JPEG_TTN2) {
+ if (compression != CompressionValue.JPEG_TTN2) {
if (useDataBuffer) {
byte[] btmp =
((DataBufferByte)src.getDataBuffer()).getData();
@@ -997,19 +735,19 @@
row -
src.getSampleModelTranslateY());
int lineStride = csm.getScanlineStride();
- if (lineStride == (int)bytesPerRow) {
+ if (lineStride == bytesPerRow) {
System.arraycopy(btmp,
inOffset,
bpixels, 0,
- (int)bytesPerRow * rows);
+ (int) bytesPerRow * rows);
} else {
int outOffset = 0;
for (int j = 0; j < rows; j++) {
System.arraycopy(btmp, inOffset,
bpixels, outOffset,
- (int)bytesPerRow);
+ (int) bytesPerRow);
inOffset += lineStride;
- outOffset += (int)bytesPerRow;
+ outOffset += bytesPerRow;
}
}
} else {
@@ -1019,16 +757,16 @@
}
}
- if (compression == COMP_NONE) {
+ if (compression == CompressionValue.NONE) {
output.write(bpixels, 0, size);
- } else if (compression == COMP_PACKBITS) {
+ } else if (compression == CompressionValue.PACKBITS) {
int numCompressedBytes =
compressPackBits(bpixels, rows,
- (int)bytesPerRow,
+ bytesPerRow,
compressBuf);
tileByteCounts[tileNum++] = numCompressedBytes;
output.write(compressBuf, 0, numCompressedBytes);
- } else if (compression == COMP_DEFLATE) {
+ } else if (compression == CompressionValue.DEFLATE) {
int numCompressedBytes =
deflate(deflater, bpixels, compressBuf);
tileByteCounts[tileNum++] = numCompressedBytes;
@@ -1045,16 +783,16 @@
bpixels[ls++] = (byte) (value & 0x00ff);
}
- if (compression == COMP_NONE) {
+ if (compression == CompressionValue.NONE) {
output.write(bpixels, 0, size*2);
- } else if (compression == COMP_PACKBITS) {
+ } else if (compression == CompressionValue.PACKBITS) {
int numCompressedBytes =
compressPackBits(bpixels, rows,
- (int)bytesPerRow,
+ bytesPerRow,
compressBuf);
tileByteCounts[tileNum++] = numCompressedBytes;
output.write(compressBuf, 0, numCompressedBytes);
- } else if (compression == COMP_DEFLATE) {
+ } else if (compression == CompressionValue.DEFLATE) {
int numCompressedBytes =
deflate(deflater, bpixels, compressBuf);
tileByteCounts[tileNum++] = numCompressedBytes;
@@ -1082,16 +820,16 @@
bpixels[lf++] = (byte)( value & 0x000000ff);
}
}
- if (compression == COMP_NONE) {
+ if (compression == CompressionValue.NONE) {
output.write(bpixels, 0, size*4);
- } else if (compression == COMP_PACKBITS) {
+ } else if (compression == CompressionValue.PACKBITS) {
int numCompressedBytes =
compressPackBits(bpixels, rows,
- (int)bytesPerRow,
+ bytesPerRow,
compressBuf);
tileByteCounts[tileNum++] = numCompressedBytes;
output.write(compressBuf, 0, numCompressedBytes);
- } else if (compression == COMP_DEFLATE) {
+ } else if (compression == CompressionValue.DEFLATE) {
int numCompressedBytes =
deflate(deflater, bpixels, compressBuf);
tileByteCounts[tileNum++] = numCompressedBytes;
@@ -1103,7 +841,7 @@
}
}
- if (compression == COMP_NONE) {
+ if (compression == CompressionValue.NONE) {
// Write an extra byte for IFD word alignment if needed.
if (skipByte) {
output.write((byte)0);
@@ -1209,6 +947,50 @@
return nextIFDOffset;
}
+ private void validateImage(int dataTypeSize, int[] sampleSize, int numBands, int dataType,
+ ColorModel colorModel) {
+ // Retrieve and verify sample size.
+ for (int i = 1; i < sampleSize.length; i++) {
+ if (sampleSize[i] != dataTypeSize) {
+ throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder0"));
+ }
+ }
+
+ // Check low bit limits.
+ if ((dataTypeSize == 1 || dataTypeSize == 4) && numBands != 1) {
+ throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder1"));
+ }
+
+ // Retrieve and verify data type.
+ switch (dataType) {
+ case DataBuffer.TYPE_BYTE:
+ if (dataTypeSize != 1 && dataTypeSize == 4 && // todo does this make sense??
+ dataTypeSize != 8) { // we get error only for 4
+ throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder2"));
+ }
+ break;
+ case DataBuffer.TYPE_SHORT:
+ case DataBuffer.TYPE_USHORT:
+ if (dataTypeSize != 16) {
+ throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder3"));
+ }
+ break;
+ case DataBuffer.TYPE_INT:
+ case DataBuffer.TYPE_FLOAT:
+ if (dataTypeSize != 32) {
+ throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder4"));
+ }
+ break;
+ default:
+ throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder5"));
+ }
+
+ if (colorModel instanceof IndexColorModel && dataType != DataBuffer.TYPE_BYTE) {
+ // Don't support (unsigned) short palette-color images.
+ throw new RuntimeException(PropertyUtil.getString("TIFFImageEncoder6"));
+ }
+ }
+
/**
* Calculates the size of the IFD.
*/
@@ -1515,12 +1297,12 @@
* Performs PackBits compression on a tile of data.
*/
private static int compressPackBits(byte[] data, int numRows,
- int bytesPerRow, byte[] compData) {
+ long bytesPerRow, byte[] compData) {
int inOffset = 0;
int outOffset = 0;
for (int i = 0; i < numRows; i++) {
- outOffset = packBits(data, inOffset, bytesPerRow,
+ outOffset = packBits(data, inOffset, (int) bytesPerRow,
compData, outOffset);
inOffset += bytesPerRow;
}
diff --git a/src/java/org/apache/xmlgraphics/image/writer/internal/TIFFImageWriter.java b/src/java/org/apache/xmlgraphics/image/writer/internal/TIFFImageWriter.java
index 9af7a0e..0907e0c 100644
--- a/src/java/org/apache/xmlgraphics/image/writer/internal/TIFFImageWriter.java
+++ b/src/java/org/apache/xmlgraphics/image/writer/internal/TIFFImageWriter.java
@@ -23,6 +23,7 @@
import java.io.IOException;
import java.io.OutputStream;
+import org.apache.xmlgraphics.image.codec.tiff.CompressionValue;
import org.apache.xmlgraphics.image.codec.tiff.TIFFEncodeParam;
import org.apache.xmlgraphics.image.codec.tiff.TIFFField;
import org.apache.xmlgraphics.image.codec.tiff.TIFFImageDecoder;
@@ -57,21 +58,9 @@
private TIFFEncodeParam createTIFFEncodeParams(ImageWriterParams params) {
TIFFEncodeParam encodeParams = new TIFFEncodeParam();
if (params == null) {
- encodeParams.setCompression(TIFFEncodeParam.COMPRESSION_NONE);
+ encodeParams.setCompression(CompressionValue.NONE);
} else {
- if (params.getCompressionMethod() == null) {
- //PackBits as default
- encodeParams.setCompression(TIFFEncodeParam.COMPRESSION_PACKBITS);
- } else if ("PackBits".equalsIgnoreCase(params.getCompressionMethod())) {
- encodeParams.setCompression(TIFFEncodeParam.COMPRESSION_PACKBITS);
- } else if ("NONE".equalsIgnoreCase(params.getCompressionMethod())) {
- encodeParams.setCompression(TIFFEncodeParam.COMPRESSION_NONE);
- } else if ("Deflate".equalsIgnoreCase(params.getCompressionMethod())) {
- encodeParams.setCompression(TIFFEncodeParam.COMPRESSION_DEFLATE);
- } else {
- throw new UnsupportedOperationException("Compression method not supported: "
- + params.getCompressionMethod());
- }
+ encodeParams.setCompression(CompressionValue.getValue(params.getCompressionMethod()));
if (params.getResolution() != null) {
int numPixX;
diff --git a/test/java/org/apache/xmlgraphics/image/codec/tiff/ImageInfoTestCase.java b/test/java/org/apache/xmlgraphics/image/codec/tiff/ImageInfoTestCase.java
new file mode 100644
index 0000000..9885fac
--- /dev/null
+++ b/test/java/org/apache/xmlgraphics/image/codec/tiff/ImageInfoTestCase.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.xmlgraphics.image.codec.tiff;
+
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.RenderedImage;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import static org.apache.xmlgraphics.image.codec.tiff.ExtraSamplesType.UNSPECIFIED;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.BILEVEL_BLACK_IS_ZERO;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.BILEVEL_WHITE_IS_ZERO;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.CIELAB;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.CMYK;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.GENERIC;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.GRAY;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.RGB;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.YCBCR;
+
+public class ImageInfoTestCase {
+
+ private ColorSpace colorSpace;
+ private ColorModel colorModel;
+ private RenderedImage image;
+ private TIFFEncodeParam params;
+
+ @Before
+ public void setUp() {
+ colorSpace = mock(ColorSpace.class);
+ colorModel = new TestColorModel(colorSpace, true);
+ image = mock(RenderedImage.class);
+ params = mock(TIFFEncodeParam.class);
+ }
+
+ @Test
+ public void testNullColorModel() {
+ testImageInfo(ImageInfo.newInstance(image, 1, 1, null, params),
+ BILEVEL_BLACK_IS_ZERO, 0, null, 0, UNSPECIFIED);
+
+ for (int i = 2; i < 10; i += 2) {
+ testImageInfo(ImageInfo.newInstance(image, 1, i, null, params),
+ GENERIC, i - 1, null, 0, UNSPECIFIED);
+ }
+ }
+
+ @Test
+ public void testNonIndexColorModel() {
+ testTheColorSpaceType(ColorSpace.TYPE_CMYK, false, CMYK);
+ testTheColorSpaceType(ColorSpace.TYPE_GRAY, false, GRAY);
+ testTheColorSpaceType(ColorSpace.TYPE_RGB, true, YCBCR);
+ testTheColorSpaceType(ColorSpace.TYPE_RGB, false, RGB);
+ }
+
+ private void testTheColorSpaceType(int colorSpaceType, boolean getJpegCompress, ImageType expectedType) {
+ when(colorSpace.getType()).thenReturn(colorSpaceType);
+ TIFFEncodeParam params = mock(TIFFEncodeParam.class);
+ when(params.getJPEGCompressRGBToYCbCr()).thenReturn(getJpegCompress);
+
+ testImageInfo(ImageInfo.newInstance(image, 1, 1, colorModel, params),
+ expectedType, 0, null, 0, UNSPECIFIED);
+ }
+
+ @Test
+ public void testNonIndexColorModelWithNumBandsGreaterThan1() {
+ testWithNumOfBandsGreaterThan1(ColorSpace.TYPE_GRAY, GRAY, 3, 1);
+ testWithNumOfBandsGreaterThan1(ColorSpace.TYPE_Lab, CIELAB, 6, 3);
+ testWithNumOfBandsGreaterThan1(ColorSpace.TYPE_CMYK, CMYK, 5, 2);
+ }
+
+ private void testWithNumOfBandsGreaterThan1(int colorSpaceType, ImageType type, int numBands,
+ int numComponents) {
+ when(colorSpace.getType()).thenReturn(colorSpaceType);
+ when(colorSpace.getNumComponents()).thenReturn(numComponents);
+ testImageInfo(ImageInfo.newInstance(image, 2, numBands, colorModel, params),
+ type, numBands - numComponents, null, 0, UNSPECIFIED);
+ }
+
+ private void testImageInfo(ImageInfo imageInfo, ImageType imageType, int numExtraSamples,
+ char[] colormap, int colormapSize, ExtraSamplesType extraSamplesType) {
+ assertEquals(imageType, imageInfo.getType());
+ assertEquals(numExtraSamples, imageInfo.getNumberOfExtraSamples());
+ assertArrayEquals(colormap, imageInfo.getColormap());
+ assertEquals(colormapSize, imageInfo.getColormapSize());
+ assertEquals(extraSamplesType, imageInfo.getExtraSamplesType());
+ }
+
+ @Test
+ public void testIndexColorModel() {
+ byte[] blackIsZero = new byte[] {0, (byte) 0xff};
+ IndexColorModel icm = new IndexColorModel(1, 2, blackIsZero, blackIsZero, blackIsZero);
+ testImageInfo(ImageInfo.newInstance(image, 1, 1, icm, params),
+ BILEVEL_BLACK_IS_ZERO, 0, null, 0, UNSPECIFIED);
+
+ byte[] whiteIsZero = new byte[] {(byte) 0xff, 0};
+ icm = new IndexColorModel(1, 2, whiteIsZero, whiteIsZero, whiteIsZero);
+ testImageInfo(ImageInfo.newInstance(image, 1, 1, icm, params),
+ BILEVEL_WHITE_IS_ZERO, 0, null, 0, UNSPECIFIED);
+ }
+
+ @Test
+ public void testTileWidthHeight() {
+ when(params.getWriteTiled()).thenReturn(true);
+
+ when(image.getWidth()).thenReturn(10);
+ when(image.getHeight()).thenReturn(10);
+
+ for (int i = 1; i < 10000; i += 200) {
+ when(params.getTileWidth()).thenReturn(i);
+ when(params.getTileHeight()).thenReturn(i);
+ int numTiles = ((10 + i - 1) / i) * ((10 + i - 1) / i);
+ long bytesPerRow = (long) Math.ceil((1 / 8.0) * i * 1);
+ long bytesPerTile = bytesPerRow * i;
+
+ testTileOnImageInfo(ImageInfo.newInstance(image, 1, 1, colorModel, params),
+ i, i, numTiles, bytesPerRow, bytesPerTile);
+ }
+ }
+
+ private void testTileOnImageInfo(ImageInfo imageInfo, int tileWidth, int tileHeight,
+ int numTiles, long bytesPerRow, long bytesPerTile) {
+ assertEquals(tileWidth, imageInfo.getTileWidth());
+ assertEquals(tileHeight, imageInfo.getTileHeight());
+ assertEquals(numTiles, imageInfo.getNumTiles());
+ assertEquals(bytesPerRow, imageInfo.getBytesPerRow());
+ assertEquals(bytesPerTile, imageInfo.getBytesPerTile());
+ }
+
+ @Test
+ public void testGetColormap() {
+ ImageInfo sut = ImageInfo.newInstance(image, 1, 1,
+ new IndexColorModel(1, 2, new byte[2], new byte[2], new byte[2], new byte[2]), params);
+ char[] colormap = sut.getColormap();
+ assertEquals(0, colormap[0]);
+ colormap[0] = 1;
+ // Assert that getColormap() returns a defensive copy
+ assertEquals(0, sut.getColormap()[0]);
+ }
+
+ private static final class TestColorModel extends ColorModel {
+
+ protected TestColorModel(ColorSpace cspace, boolean isAlphaPremultiplied) {
+ super(1, new int[] {1, 1}, cspace, isAlphaPremultiplied, isAlphaPremultiplied, 1, 1);
+ }
+
+ @Override
+ public int getRed(int pixel) {
+ return 0;
+ }
+
+ @Override
+ public int getGreen(int pixel) {
+ return 0;
+ }
+
+ @Override
+ public int getBlue(int pixel) {
+ return 0;
+ }
+
+ @Override
+ public int getAlpha(int pixel) {
+ return 0;
+ }
+ }
+}
diff --git a/test/java/org/apache/xmlgraphics/image/codec/tiff/ImageTypeTestCase.java b/test/java/org/apache/xmlgraphics/image/codec/tiff/ImageTypeTestCase.java
new file mode 100644
index 0000000..6761617
--- /dev/null
+++ b/test/java/org/apache/xmlgraphics/image/codec/tiff/ImageTypeTestCase.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.xmlgraphics.image.codec.tiff;
+
+import java.awt.color.ColorSpace;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.BILEVEL_BLACK_IS_ZERO;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.BILEVEL_WHITE_IS_ZERO;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.CIELAB;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.CMYK;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.GENERIC;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.GRAY;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.PALETTE;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.RGB;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.UNSUPPORTED;
+import static org.apache.xmlgraphics.image.codec.tiff.ImageType.YCBCR;
+
+public class ImageTypeTestCase {
+ private static final class ColorContainer {
+ private final byte[] r;
+ private final byte[] g;
+ private final byte[] b;
+
+ private ColorContainer(byte[] r, byte[] g, byte[] b) {
+ this.r = r;
+ this.b = b;
+ this.g = g;
+ }
+ }
+
+ private ColorContainer blackIsZero;
+ private ColorContainer whiteIsZero;
+
+ @Before
+ public void setUp() {
+ byte[] blackSetToZero = {0, (byte) 0xff};
+ blackIsZero = new ColorContainer(blackSetToZero, blackSetToZero, blackSetToZero);
+ byte[] whiteSetToZero = {(byte) 0xff, 0};
+ whiteIsZero = new ColorContainer(whiteSetToZero, whiteSetToZero, whiteSetToZero);
+ }
+
+ @Test
+ public void testPhotometricInterpretationValue() {
+ assertEquals(0, BILEVEL_WHITE_IS_ZERO.getPhotometricInterpretation());
+ assertEquals(1, BILEVEL_BLACK_IS_ZERO.getPhotometricInterpretation());
+ assertEquals(1, GRAY.getPhotometricInterpretation());
+ assertEquals(3, PALETTE.getPhotometricInterpretation());
+ assertEquals(2, RGB.getPhotometricInterpretation());
+ assertEquals(5, CMYK.getPhotometricInterpretation());
+ assertEquals(6, YCBCR.getPhotometricInterpretation());
+ assertEquals(8, CIELAB.getPhotometricInterpretation());
+ assertEquals(1, GENERIC.getPhotometricInterpretation());
+ }
+
+ @Test
+ public void testGetTypeFromRGB() {
+ assertEquals(BILEVEL_BLACK_IS_ZERO, ImageType.getTypeFromRGB(2,
+ blackIsZero.r, blackIsZero.g, blackIsZero.b, 1, 1));
+ assertEquals(BILEVEL_WHITE_IS_ZERO, ImageType.getTypeFromRGB(2,
+ whiteIsZero.r, whiteIsZero.g, whiteIsZero.b, 1, 1));
+ // Test all other values (i.e. not including 0xff)
+ for (int b = 0; b < 255; b++) {
+ assertEquals(PALETTE, ImageType.getTypeFromRGB(2,
+ make2ByteArray(0, b), make2ByteArray(0, b), make2ByteArray(0, b), 1, 1));
+ assertEquals(PALETTE, ImageType.getTypeFromRGB(2,
+ make2ByteArray(b, 0), make2ByteArray(b, 0), make2ByteArray(b, 0), 1, 1));
+ if (b != 1) {
+ assertEquals(UNSUPPORTED, ImageType.getTypeFromRGB(2, null, null, null, 1, b));
+ }
+ }
+ }
+
+ private byte[] make2ByteArray(int b1, int b2) {
+ return new byte[] {(byte) b1, (byte) b2};
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testException() {
+ assertEquals(UNSUPPORTED, ImageType.getTypeFromRGB(1, null, null, null, 1, 1));
+ }
+
+ @Test
+ public void testGetTypeFromColorSpace() {
+ testIndividualColorSpaceType(CMYK, ColorSpace.TYPE_CMYK, false);
+ testIndividualColorSpaceType(GRAY, ColorSpace.TYPE_GRAY, false);
+ testIndividualColorSpaceType(CIELAB, ColorSpace.TYPE_Lab, false);
+ testIndividualColorSpaceType(YCBCR, ColorSpace.TYPE_YCbCr, false);
+ testIndividualColorSpaceType(YCBCR, ColorSpace.TYPE_RGB, true);
+ testIndividualColorSpaceType(RGB, ColorSpace.TYPE_RGB, false);
+ }
+
+ private void testIndividualColorSpaceType(ImageType expected, int type, boolean getJpegCompress) {
+ ColorSpace colorSpace = mock(ColorSpace.class);
+ when(colorSpace.getType()).thenReturn(type);
+ TIFFEncodeParam params = mock(TIFFEncodeParam.class);
+ when(params.getJPEGCompressRGBToYCbCr()).thenReturn(getJpegCompress);
+
+ assertEquals(expected, ImageType.getTypeFromColorSpace(colorSpace, params));
+ }
+}
diff --git a/test/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImageEncoderTestCase.java b/test/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImageEncoderTestCase.java
new file mode 100644
index 0000000..1603679
--- /dev/null
+++ b/test/java/org/apache/xmlgraphics/image/codec/tiff/TIFFImageEncoderTestCase.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.xmlgraphics.image.codec.tiff;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import org.apache.commons.io.IOUtils;
+
+public class TIFFImageEncoderTestCase {
+
+ @Test
+ public void testGrayImage() throws IOException {
+ testImage(BufferedImage.TYPE_BYTE_GRAY, "gray.tiff");
+ }
+
+ @Test
+ public void testBilevelImage() throws IOException {
+ testImage(BufferedImage.TYPE_BYTE_BINARY, "bilevel.tiff");
+ }
+
+ private void testImage(int imageType, String imageFileName) throws IOException {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ TIFFImageEncoder encoder = new TIFFImageEncoder(byteStream, null);
+ encoder.encode(getImage(imageType));
+ byte[] actualArray = IOUtils.toByteArray(getClass().getResource(imageFileName).openStream());
+ assertArrayEquals(byteStream.toByteArray(), actualArray);
+ }
+
+ private RenderedImage getImage(int imageType) {
+ BufferedImage img = new BufferedImage(400, 400, imageType);
+ img.getSampleModel();
+ Graphics gfx = img.getGraphics();
+ gfx.setColor(Color.RED);
+ gfx.fillRect(10, 10, 100, 100);
+ gfx.setColor(Color.WHITE);
+ gfx.fillRect(50, 50, 100, 100);
+ return img;
+ }
+}
diff --git a/test/java/org/apache/xmlgraphics/image/codec/tiff/bilevel.tiff b/test/java/org/apache/xmlgraphics/image/codec/tiff/bilevel.tiff
new file mode 100644
index 0000000..23ec9da
--- /dev/null
+++ b/test/java/org/apache/xmlgraphics/image/codec/tiff/bilevel.tiff
Binary files differ
diff --git a/test/java/org/apache/xmlgraphics/image/codec/tiff/gray.tiff b/test/java/org/apache/xmlgraphics/image/codec/tiff/gray.tiff
new file mode 100644
index 0000000..3f66bd5
--- /dev/null
+++ b/test/java/org/apache/xmlgraphics/image/codec/tiff/gray.tiff
Binary files differ