mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 04:25:29 -04:00
#253: Fix for non-subsampled YCbCr encoded JPEG-in-TIFF being decoded as RGB.
This commit is contained in:
parent
788b11e4fa
commit
458ef92af5
@ -47,17 +47,30 @@ public final class YCbCrConverter {
|
||||
buildYCCtoRGBtable();
|
||||
}
|
||||
|
||||
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, final int offset) {
|
||||
double y = (yCbCr[offset] & 0xff);
|
||||
double cb = (yCbCr[offset + 1] & 0xff) - 128;
|
||||
double cr = (yCbCr[offset + 2] & 0xff) - 128;
|
||||
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, double[] referenceBW, final int offset) {
|
||||
double y;
|
||||
double cb;
|
||||
double cr;
|
||||
|
||||
if (referenceBW == null) {
|
||||
// Default case
|
||||
y = (yCbCr[offset] & 0xff);
|
||||
cb = (yCbCr[offset + 1] & 0xff) - 128;
|
||||
cr = (yCbCr[offset + 2] & 0xff) - 128;
|
||||
}
|
||||
else {
|
||||
// Custom values
|
||||
y = ((yCbCr[offset] & 0xff) - referenceBW[0]) * 255.0 / (referenceBW[1] - referenceBW[0]);
|
||||
cb = ((yCbCr[offset + 1] & 0xff) - referenceBW[2]) * 127.0 / (referenceBW[3] - referenceBW[2]);
|
||||
cr = ((yCbCr[offset + 2] & 0xff) - referenceBW[4]) * 127.0 / (referenceBW[5] - referenceBW[4]);
|
||||
}
|
||||
|
||||
double lumaRed = coefficients[0];
|
||||
double lumaGreen = coefficients[1];
|
||||
double lumaBlue = coefficients[2];
|
||||
|
||||
int red = (int) Math.round(cr * (2 - 2 * lumaRed) + y);
|
||||
int blue = (int) Math.round(cb * (2 - 2 * lumaBlue) + y);
|
||||
int red = (int) Math.round(cr * (2.0 - 2.0 * lumaRed) + y);
|
||||
int blue = (int) Math.round(cb * (2.0 - 2.0 * lumaBlue) + y);
|
||||
int green = (int) Math.round((y - lumaRed * red - lumaBlue * blue) / lumaGreen);
|
||||
|
||||
rgb[offset] = clamp(red);
|
||||
|
@ -52,10 +52,13 @@ import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import javax.imageio.plugins.jpeg.JPEGImageReadParam;
|
||||
import javax.imageio.spi.IIORegistry;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
@ -65,6 +68,7 @@ import java.awt.*;
|
||||
import java.awt.color.CMMException;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Constructor;
|
||||
@ -148,6 +152,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
// NOTE: DO NOT MODIFY OR EXPOSE THIS ARRAY OUTSIDE PACKAGE!
|
||||
static final double[] CCIR_601_1_COEFFICIENTS = new double[] {299.0 / 1000.0, 587.0 / 1000.0, 114.0 / 1000.0};
|
||||
static final double[] REFERENCE_BLACK_WHITE_YCC_DEFAULT = new double[] {0, 255, 128, 255, 128, 255};
|
||||
|
||||
private CompoundDirectory IFDs;
|
||||
private Directory currentIFD;
|
||||
@ -807,6 +812,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
WritableRaster rowRaster = rawType.createBufferedImage(stripTileWidth, 1).getRaster();
|
||||
Rectangle clip = new Rectangle(srcRegion);
|
||||
int row = 0;
|
||||
Boolean needsCSConversion = null;
|
||||
|
||||
switch (compression) {
|
||||
// TIFF Baseline
|
||||
@ -830,7 +836,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
int[] yCbCrSubsampling = null;
|
||||
int yCbCrPos = 1;
|
||||
// double[] yCbCrCoefficients = null;
|
||||
|
||||
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
|
||||
// getRawImageType does the lookup/conversion for these
|
||||
if (rowRaster.getNumBands() != 3) {
|
||||
@ -868,16 +874,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
else {
|
||||
yCbCrSubsampling = new int[] {2, 2};
|
||||
}
|
||||
|
||||
// Entry coefficients = currentIFD.getEntryById(TIFF.TAG_YCBCR_COEFFICIENTS);
|
||||
// if (coefficients != null) {
|
||||
// Rational[] value = (Rational[]) coefficients.getValue();
|
||||
// yCbCrCoefficients = new double[] {value[0].doubleValue(), value[1].doubleValue(), value[2].doubleValue()};
|
||||
// }
|
||||
// else {
|
||||
// // Default to y CCIR Recommendation 601-1 values
|
||||
// yCbCrCoefficients = YCbCrUpsamplerStream.CCIR_601_1_COEFFICIENTS;
|
||||
// }
|
||||
}
|
||||
|
||||
// Read data
|
||||
@ -974,8 +970,8 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// http://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#abbrev
|
||||
jpegReader.getStreamMetadata();
|
||||
}
|
||||
else {
|
||||
processWarningOccurred("Missing JPEGTables for TIFF with compression: 7 (JPEG)");
|
||||
else if (tilesDown * tilesAcross > 1) {
|
||||
processWarningOccurred("Missing JPEGTables for tiled/striped TIFF with compression: 7 (JPEG)");
|
||||
// ...and the JPEG reader will probably choke on missing tables...
|
||||
}
|
||||
|
||||
@ -991,7 +987,8 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
int colsInTile = Math.min(stripTileWidth, width - col);
|
||||
|
||||
// Read only tiles that lies within region
|
||||
if (new Rectangle(col, row, colsInTile, rowsInTile).intersects(srcRegion)) {
|
||||
Rectangle tileRect = new Rectangle(col, row, colsInTile, rowsInTile);
|
||||
if (tileRect.intersects(srcRegion)) {
|
||||
imageInput.seek(stripTileOffsets[i]);
|
||||
|
||||
int length = stripTileByteCounts != null ? (int) stripTileByteCounts[i] : Short.MAX_VALUE;
|
||||
@ -1000,7 +997,13 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
jpegReader.setInput(subStream);
|
||||
jpegParam.setSourceRegion(new Rectangle(0, 0, colsInTile, rowsInTile));
|
||||
|
||||
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR || interpretation == TIFFBaseline.PHOTOMETRIC_RGB) {
|
||||
// TODO: If we have non-standard reference B/W or yCbCr coefficients,
|
||||
// we might still have to do extra color space conversion...
|
||||
if (needsCSConversion == null) {
|
||||
needsCSConversion = needsCSConversion(interpretation, jpegReader.getImageMetadata(0));
|
||||
}
|
||||
|
||||
if (!needsCSConversion) {
|
||||
jpegParam.setDestinationOffset(new Point(col - srcRegion.x, row - srcRegion.y));
|
||||
jpegParam.setDestination(destination);
|
||||
jpegReader.read(0, jpegParam);
|
||||
@ -1013,7 +1016,6 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
destination.getRaster().setDataElements(col - srcRegion.x, row - srcRegion.y, raster);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
@ -1127,13 +1129,18 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
// Read data
|
||||
processImageStarted(imageIndex); // Better yet, would be to delegate read progress here...
|
||||
|
||||
imageInput.seek(realJPEGOffset);
|
||||
|
||||
try (ImageInputStream stream = new SubImageInputStream(imageInput, length)) {
|
||||
jpegReader.setInput(stream);
|
||||
jpegParam.setSourceRegion(srcRegion);
|
||||
|
||||
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR || interpretation == TIFFBaseline.PHOTOMETRIC_RGB) {
|
||||
if (needsCSConversion == null) {
|
||||
needsCSConversion = needsCSConversion(interpretation, jpegReader.getImageMetadata(0));
|
||||
}
|
||||
|
||||
if (!needsCSConversion) {
|
||||
jpegParam.setDestination(destination);
|
||||
jpegReader.read(0, jpegParam);
|
||||
}
|
||||
@ -1141,6 +1148,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// Otherwise, it's likely CMYK or some other interpretation we don't need to convert.
|
||||
// We'll have to use readAsRaster and later apply color space conversion ourselves
|
||||
Raster raster = jpegReader.readRaster(0, jpegParam);
|
||||
normalizeColor(interpretation, ((DataBufferByte) raster.getDataBuffer()).getData());
|
||||
destination.getRaster().setDataElements(0, 0, raster);
|
||||
}
|
||||
}
|
||||
@ -1232,7 +1240,11 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
jpegParam.setDestinationOffset(new Point(col - srcRegion.x, row - srcRegion.y));
|
||||
jpegParam.setDestination(destination);
|
||||
|
||||
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR || interpretation == TIFFBaseline.PHOTOMETRIC_RGB) {
|
||||
if (needsCSConversion == null) {
|
||||
needsCSConversion = needsCSConversion(interpretation, jpegReader.getImageMetadata(0));
|
||||
}
|
||||
|
||||
if (!needsCSConversion) {
|
||||
jpegParam.setDestination(destination);
|
||||
jpegReader.read(0, jpegParam);
|
||||
}
|
||||
@ -1240,6 +1252,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// Otherwise, it's likely CMYK or some other interpretation we don't need to convert.
|
||||
// We'll have to use readAsRaster and later apply color space conversion ourselves
|
||||
Raster raster = jpegReader.readRaster(0, jpegParam);
|
||||
normalizeColor(interpretation, ((DataBufferByte) raster.getDataBuffer()).getData());
|
||||
destination.getRaster().setDataElements(0, 0, raster);
|
||||
}
|
||||
}
|
||||
@ -1295,6 +1308,40 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
return destination;
|
||||
}
|
||||
|
||||
private boolean needsCSConversion(final int photometricInterpretation, final IIOMetadata imageMetadata) throws IOException {
|
||||
if (imageMetadata == null) {
|
||||
// Assume we're ok
|
||||
return false;
|
||||
}
|
||||
|
||||
IIOMetadataNode stdTree = (IIOMetadataNode) imageMetadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||
|
||||
NodeList csTypes = stdTree.getElementsByTagName("ColorSpaceType");
|
||||
|
||||
if (csTypes != null && csTypes.getLength() > 0) {
|
||||
IIOMetadataNode csType = (IIOMetadataNode) csTypes.item(0);
|
||||
String csName = csType.getAttribute("name");
|
||||
|
||||
if ("YCbCr".equals(csName) && photometricInterpretation == TIFFExtension.PHOTOMETRIC_YCBCR
|
||||
|| "RGB".equals(csName) && photometricInterpretation == TIFFBaseline.PHOTOMETRIC_RGB
|
||||
|| "GRAY".equals(csName) && photometricInterpretation == TIFFBaseline.PHOTOMETRIC_BLACK_IS_ZERO) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// CMYK, or may happen because the JPEG stream is not subsampled,
|
||||
// fooling the JPEGImageReader to believe the data is RGB, while it is YCbCr
|
||||
if (DEBUG) {
|
||||
System.out.println("Incompatible JPEG CS/PhotometricInterpretation: " + csName + "/" + photometricInterpretation);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't really know, assume it's ok...
|
||||
return false;
|
||||
}
|
||||
|
||||
private ImageReader createJPEGDelegate() throws IIOException {
|
||||
// TIFF is strictly ISO JPEG, so we should probably stick to the standard reader
|
||||
try {
|
||||
@ -1624,7 +1671,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizeColor(int photometricInterpretation, byte[] data) {
|
||||
private void normalizeColor(int photometricInterpretation, byte[] data) throws IIOException {
|
||||
switch (photometricInterpretation) {
|
||||
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
||||
// Inverse values
|
||||
@ -1637,7 +1684,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
case TIFFExtension.PHOTOMETRIC_CIELAB:
|
||||
case TIFFExtension.PHOTOMETRIC_ICCLAB:
|
||||
case TIFFExtension.PHOTOMETRIC_ITULAB:
|
||||
// TODO: Whitepoint may be encoded in separate tag
|
||||
// TODO: White point may be encoded in separate tag
|
||||
CIELabColorConverter converter = new CIELabColorConverter(
|
||||
photometricInterpretation == TIFFExtension.PHOTOMETRIC_CIELAB
|
||||
? Illuminant.D65
|
||||
@ -1673,19 +1720,31 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
break;
|
||||
|
||||
case TIFFExtension.PHOTOMETRIC_YCBCR:
|
||||
Entry coefficients = currentIFD.getEntryById(TIFF.TAG_YCBCR_COEFFICIENTS);
|
||||
// Default: CCIR Recommendation 601-1: 299/1000, 587/1000 and 114/1000
|
||||
double[] coefficients = getValueAsDoubleArray(TIFF.TAG_YCBCR_COEFFICIENTS, "YCbCrCoefficients", false, 3);
|
||||
|
||||
if (coefficients == null) {
|
||||
// "Default" [0, 255, 128, 255, 128, 255] for YCbCr (real default is [0, 255, 0, 255, 0, 255] for RGB)
|
||||
double[] referenceBW = getValueAsDoubleArray(TIFF.TAG_REFERENCE_BLACK_WHITE, "ReferenceBlackWhite", false, 6);
|
||||
|
||||
if ((coefficients == null || Arrays.equals(coefficients, CCIR_601_1_COEFFICIENTS))
|
||||
&& (referenceBW == null || Arrays.equals(referenceBW, REFERENCE_BLACK_WHITE_YCC_DEFAULT))) {
|
||||
// Fast, default conversion
|
||||
for (int i = 0; i < data.length; i += 3) {
|
||||
YCbCrConverter.convertYCbCr2RGB(data, data, i);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Rational[] value = (Rational[]) coefficients.getValue();
|
||||
double[] yCbCrCoefficients = new double[] {value[0].doubleValue(), value[1].doubleValue(), value[2].doubleValue()};
|
||||
// If one of the values are null, we'll need the other here...
|
||||
if (coefficients == null) {
|
||||
coefficients = CCIR_601_1_COEFFICIENTS;
|
||||
}
|
||||
|
||||
if (referenceBW != null && Arrays.equals(referenceBW, REFERENCE_BLACK_WHITE_YCC_DEFAULT)) {
|
||||
referenceBW = null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < data.length; i += 3) {
|
||||
YCbCrConverter.convertYCbCr2RGB(data, data, yCbCrCoefficients, i);
|
||||
YCbCrConverter.convertYCbCr2RGB(data, data, coefficients, referenceBW, i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1693,7 +1752,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizeColor(int photometricInterpretation, short[] data) {
|
||||
private void normalizeColor(int photometricInterpretation, short[] data) throws IIOException {
|
||||
switch (photometricInterpretation) {
|
||||
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
||||
// Inverse values
|
||||
@ -1706,7 +1765,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
case TIFFExtension.PHOTOMETRIC_CIELAB:
|
||||
case TIFFExtension.PHOTOMETRIC_ICCLAB:
|
||||
case TIFFExtension.PHOTOMETRIC_ITULAB:
|
||||
// TODO: Whitepoint may be encoded in separate tag
|
||||
// TODO: White point may be encoded in separate tag
|
||||
CIELabColorConverter converter = new CIELabColorConverter(
|
||||
photometricInterpretation == TIFFExtension.PHOTOMETRIC_ITULAB
|
||||
? Illuminant.D65
|
||||
@ -1744,23 +1803,26 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
break;
|
||||
|
||||
case TIFFExtension.PHOTOMETRIC_YCBCR:
|
||||
double[] coefficients;
|
||||
// Default: CCIR Recommendation 601-1: 299/1000, 587/1000 and 114/1000
|
||||
double[] coefficients = getValueAsDoubleArray(TIFF.TAG_YCBCR_COEFFICIENTS, "YCbCrCoefficients", false, 3);
|
||||
|
||||
Entry coefficientsTag = currentIFD.getEntryById(TIFF.TAG_YCBCR_COEFFICIENTS);
|
||||
if (coefficientsTag != null) {
|
||||
Rational[] value = (Rational[]) coefficientsTag.getValue();
|
||||
coefficients = new double[] {value[0].doubleValue(), value[1].doubleValue(), value[2].doubleValue()};
|
||||
}
|
||||
else {
|
||||
// "Default" [0, 255, 128, 255, 128, 255] for YCbCr (real default is [0, 255, 0, 255, 0, 255] for RGB)
|
||||
double[] referenceBW = getValueAsDoubleArray(TIFF.TAG_REFERENCE_BLACK_WHITE, "ReferenceBlackWhite", false, 6);
|
||||
|
||||
// If one of the values are null, we'll need the other here...
|
||||
if (coefficients == null) {
|
||||
coefficients = CCIR_601_1_COEFFICIENTS;
|
||||
}
|
||||
|
||||
for (int i = 0; i < data.length; i += 3) {
|
||||
convertYCbCr2RGB(data, data, coefficients, i);
|
||||
}
|
||||
}
|
||||
if (referenceBW != null && Arrays.equals(referenceBW, REFERENCE_BLACK_WHITE_YCC_DEFAULT)) {
|
||||
referenceBW = null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < data.length; i += 3) {
|
||||
convertYCbCr2RGB(data, data, coefficients, referenceBW, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void normalizeColor(int photometricInterpretation, int[] data) {
|
||||
switch (photometricInterpretation) {
|
||||
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
||||
@ -1792,21 +1854,30 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private void convertYCbCr2RGB(final short[] yCbCr, final short[] rgb, final double[] coefficients, final int offset) {
|
||||
int y;
|
||||
int cb;
|
||||
int cr;
|
||||
private void convertYCbCr2RGB(final short[] yCbCr, final short[] rgb, final double[] coefficients, final double[] referenceBW, final int offset) {
|
||||
double y;
|
||||
double cb;
|
||||
double cr;
|
||||
|
||||
y = (yCbCr[offset + 0] & 0xffff);
|
||||
if (referenceBW == null) {
|
||||
// Default case
|
||||
y = (yCbCr[offset] & 0xffff);
|
||||
cb = (yCbCr[offset + 1] & 0xffff) - 32768;
|
||||
cr = (yCbCr[offset + 2] & 0xffff) - 32768;
|
||||
}
|
||||
else {
|
||||
// Custom values
|
||||
y = ((yCbCr[offset] & 0xffff) - referenceBW[0]) * (65535.0) / (referenceBW[1] - referenceBW[0]);
|
||||
cb = ((yCbCr[offset + 1] & 0xffff) - referenceBW[2]) * 32767.0 / (referenceBW[3] - referenceBW[2]);
|
||||
cr = ((yCbCr[offset + 2] & 0xffff) - referenceBW[4]) * 32767.0 / (referenceBW[5] - referenceBW[4]);
|
||||
}
|
||||
|
||||
double lumaRed = coefficients[0];
|
||||
double lumaGreen = coefficients[1];
|
||||
double lumaBlue = coefficients[2];
|
||||
|
||||
int red = (int) Math.round(cr * (2 - 2 * lumaRed) + y);
|
||||
int blue = (int) Math.round(cb * (2 - 2 * lumaBlue) + y);
|
||||
int red = (int) Math.round(cr * (2.0 - 2.0 * lumaRed) + y);
|
||||
int blue = (int) Math.round(cb * (2.0 - 2.0 * lumaBlue) + y);
|
||||
int green = (int) Math.round((y - lumaRed * (red) - lumaBlue * (blue)) / lumaGreen);
|
||||
|
||||
short r = clampShort(red);
|
||||
@ -1902,6 +1973,57 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
return value;
|
||||
}
|
||||
|
||||
private double[] getValueAsDoubleArray(final int tag, final String tagName, final boolean required, final int expectedLength) throws IIOException {
|
||||
Entry entry = currentIFD.getEntryById(tag);
|
||||
|
||||
if (entry == null) {
|
||||
if (required) {
|
||||
throw new IIOException("Missing TIFF tag " + tagName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (expectedLength > 0 && entry.valueCount() != expectedLength) {
|
||||
if (required) {
|
||||
throw new IIOException(String.format("Unexpected value count for %s: %d (expected %d values)", tagName, entry.valueCount(), expectedLength));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
double[] value;
|
||||
|
||||
if (entry.valueCount() == 1) {
|
||||
// For single entries, this will be a boxed type
|
||||
value = new double[] {((Number) entry.getValue()).doubleValue()};
|
||||
}
|
||||
else if (entry.getValue() instanceof float[]) {
|
||||
float[] floats = (float[]) entry.getValue();
|
||||
value = new double[floats.length];
|
||||
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
value[i] = floats[i];
|
||||
}
|
||||
}
|
||||
else if (entry.getValue() instanceof double[]) {
|
||||
value = (double[]) entry.getValue();
|
||||
}
|
||||
else if (entry.getValue() instanceof Rational[]) {
|
||||
Rational[] rationals = (Rational[]) entry.getValue();
|
||||
value = new double[rationals.length];
|
||||
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
value[i] = rationals[i].doubleValue();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IIOException(String.format("Unsupported %s type: %s (%s)", tagName, entry.getTypeName(), entry.getValue().getClass()));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private ICC_Profile getICCProfile() throws IOException {
|
||||
Entry entry = currentIFD.getEntryById(TIFF.TAG_ICC_PROFILE);
|
||||
|
||||
|
@ -27,6 +27,7 @@ package com.twelvemonkeys.imageio.plugins.tiff;/*
|
||||
*/
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@ -237,6 +238,59 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadYCbCrJPEGAssumedRGB() throws IOException {
|
||||
// Problematic test data, which is YCbCr encoded (as correctly specified by the PhotometricInterpretation tag,
|
||||
// but the JPEGImageReader will detect the data as RGB due to non-subsampled data and SOF ids.
|
||||
TestData testData = new TestData(getClassLoaderResource("/tiff/xerox-jpeg-ycbcr-weird-coefficients.tif"), new Dimension(2482, 3520));
|
||||
|
||||
try (ImageInputStream stream = testData.getInputStream()) {
|
||||
TIFFImageReader reader = createReader();
|
||||
reader.setInput(stream);
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
// TODO: There's a bug in reading with source region for the raster case...
|
||||
// param.setSourceRegion(new Rectangle(8, 8));
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
// assertEquals(new Dimension(8, 8), new Dimension(image.getWidth(), image.getHeight()));
|
||||
assertEquals(testData.getDimension(0), new Dimension(image.getWidth(), image.getHeight()));
|
||||
|
||||
// The pixel at 0, 0 should be white(-ish), not red!
|
||||
// NOTE: The image contains some weird custom YCbCr coefficients, which are roughly
|
||||
// 0.299, 0.587, 0.144, instead of the standard 0.299, 0.587, 0.114 (the last/blue coefficient differs)
|
||||
// this will make the background bright purple, rather than pure white as it would have been
|
||||
// with standard coefficients. Could be a typo/bug in the encoder or intentional.
|
||||
// Some/most software ignores the custom coefficients, and decodes the image as white background...
|
||||
int argb = image.getRGB(0, 0);
|
||||
assertEquals("Alpha", 0xff, (argb >>> 24) & 0xff);
|
||||
assertEquals("Red", 0xff, (argb >> 16) & 0xff);
|
||||
assertEquals("Green", 0xf2, (argb >> 8) & 0xff);
|
||||
assertEquals("Blue", 0xff, argb & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Known issue")
|
||||
@Test
|
||||
public void testReadJPEGRasterCaseWithSrcRegion() throws IOException {
|
||||
// Problematic test data, which is YCbCr encoded (as correctly specified by the PhotometricInterpretation tag,
|
||||
// but the JPEGImageReader will detect the data as RGB due to non-subsampled data and SOF ids.
|
||||
TestData testData = new TestData(getClassLoaderResource("/tiff/xerox-jpeg-ycbcr-weird-coefficients.tif"), new Dimension(2482, 3520));
|
||||
|
||||
try (ImageInputStream stream = testData.getInputStream()) {
|
||||
TIFFImageReader reader = createReader();
|
||||
reader.setInput(stream);
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(8, 8));
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(new Dimension(8, 8), new Dimension(image.getWidth(), image.getHeight()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testColorMap8Bit() throws IOException {
|
||||
TestData testData = new TestData(getClassLoaderResource("/tiff/scan-lzw-8bit-colormap.tiff"), new Dimension(2550, 3300));
|
||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user