mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 03:55:28 -04:00
#184 Support for PlanarConfiguration 2 + bonus changes.
This commit is contained in:
parent
867ca61755
commit
b6e44c5bff
@ -193,6 +193,8 @@ final class EXIFEntry extends AbstractEntry {
|
||||
return "Flash";
|
||||
case EXIF.TAG_FOCAL_LENGTH:
|
||||
return "FocalLength";
|
||||
case EXIF.TAG_SENSING_METHOD:
|
||||
return "SensingMethod";
|
||||
case EXIF.TAG_FILE_SOURCE:
|
||||
return "FileSource";
|
||||
case EXIF.TAG_SCENE_TYPE:
|
||||
@ -219,6 +221,8 @@ final class EXIFEntry extends AbstractEntry {
|
||||
return "Saturation";
|
||||
case EXIF.TAG_SHARPNESS:
|
||||
return "Sharpness";
|
||||
case EXIF.TAG_IMAGE_UNIQUE_ID:
|
||||
return "ImageUniqueID";
|
||||
|
||||
case EXIF.TAG_FLASHPIX_VERSION:
|
||||
return "FlashpixVersion";
|
||||
|
@ -115,7 +115,6 @@ import java.util.zip.InflaterInputStream;
|
||||
public class TIFFImageReader extends ImageReaderBase {
|
||||
// TODOs ImageIO basic functionality:
|
||||
// TODO: Thumbnail support
|
||||
// TODO: TIFFImageWriter + Spi
|
||||
|
||||
// TODOs Full BaseLine support:
|
||||
// TODO: Support ExtraSamples (an array, if multiple extra samples!)
|
||||
@ -129,10 +128,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// http://download.java.net/media/jai-imageio/javadoc/1.1/com/sun/media/imageio/plugins/tiff/package-summary.html#ImageMetadata
|
||||
|
||||
// TODOs Extension support
|
||||
// TODO: Support PlanarConfiguration 2, look at PCXImageReader
|
||||
// TODO: Auto-rotate based on Orientation
|
||||
// TODO: Support ICCProfile (fully)
|
||||
// TODO: Support Compression 3 & 4 (CCITT T.4 & T.6)
|
||||
// TODO: Support Compression 34712 (JPEG2000)? Depends on JPEG2000 ImageReader
|
||||
// TODO: Support Compression 34661 (JBIG)? Depends on JBIG ImageReader
|
||||
|
||||
@ -143,6 +139,9 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
// Source region
|
||||
// Subsampling
|
||||
// IIOMetadata (stay close to Sun's TIFF metadata)
|
||||
// Support ICCProfile
|
||||
// Support PlanarConfiguration 2
|
||||
// Support Compression 3 & 4 (CCITT T.4 & T.6)
|
||||
|
||||
final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.tiff.debug"));
|
||||
|
||||
@ -317,7 +316,23 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
int bitsPerSample = getBitsPerSample();
|
||||
int dataType = getDataType(sampleFormat, bitsPerSample);
|
||||
|
||||
// TODO: Validate CS using ColorSpaces.validateProfile
|
||||
int opaqueSamplesPerPixel = getOpaqueSamplesPerPixel(interpretation);
|
||||
|
||||
// Spec says ExtraSamples are mandatory of extra samples, however known encoders
|
||||
// (ie. SeaShore) writes ARGB TIFFs without ExtraSamples.
|
||||
long[] extraSamples = getValueAsLongArray(TIFF.TAG_EXTRA_SAMPLES, "ExtraSamples", false);
|
||||
if (extraSamples == null && samplesPerPixel > opaqueSamplesPerPixel) {
|
||||
// TODO: Log warning!
|
||||
// First extra is alpha, rest is "unspecified"
|
||||
extraSamples = new long[samplesPerPixel - opaqueSamplesPerPixel];
|
||||
extraSamples[0] = TIFFBaseline.EXTRASAMPLE_UNASSOCIATED_ALPHA;
|
||||
}
|
||||
|
||||
// Determine alpha
|
||||
boolean hasAlpha = extraSamples != null;
|
||||
boolean isAlphaPremultiplied = hasAlpha && extraSamples[0] == TIFFBaseline.EXTRASAMPLE_ASSOCIATED_ALPHA;
|
||||
int significantSamples = opaqueSamplesPerPixel + (hasAlpha ? 1 : 0);
|
||||
|
||||
// Read embedded cs
|
||||
ICC_Profile profile = getICCProfile();
|
||||
ColorSpace cs;
|
||||
@ -327,11 +342,10 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
||||
// WhiteIsZero
|
||||
// NOTE: We handle this by inverting the values when reading, as Java has no ColorModel that easily supports this.
|
||||
// TODO: Consider returning null?
|
||||
case TIFFBaseline.PHOTOMETRIC_BLACK_IS_ZERO:
|
||||
// BlackIsZero
|
||||
// Gray scale or B/W
|
||||
switch (samplesPerPixel) {
|
||||
switch (significantSamples) {
|
||||
case 1:
|
||||
// TIFF 6.0 Spec says: 1, 4 or 8 for baseline (1 for bi-level, 4/8 for gray)
|
||||
// ImageTypeSpecifier supports 1, 2, 4, 8 or 16 bits per sample, we'll support 32 bits as well.
|
||||
@ -347,6 +361,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
return ImageTypeSpecifiers.createGrayscale(bitsPerSample, dataType);
|
||||
}
|
||||
else if (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4 || bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
|
||||
// TODO: Should use packed format for 1/2/4
|
||||
return ImageTypeSpecifiers.createInterleaved(cs, new int[] {0}, dataType, false, false);
|
||||
}
|
||||
|
||||
@ -364,28 +379,26 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
cs = profile == null ? ColorSpace.getInstance(ColorSpace.CS_GRAY) : ColorSpaces.createColorSpace(profile);
|
||||
|
||||
// ExtraSamples 0=unspecified, 1=associated (pre-multiplied), 2=unassociated (TODO: Support unspecified, not alpha)
|
||||
long[] extraSamples = getValueAsLongArray(TIFF.TAG_EXTRA_SAMPLES, "ExtraSamples", true);
|
||||
|
||||
if (cs == ColorSpace.getInstance(ColorSpace.CS_GRAY) && (bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32)) {
|
||||
switch (planarConfiguration) {
|
||||
case TIFFBaseline.PLANARCONFIG_CHUNKY:
|
||||
return ImageTypeSpecifiers.createGrayscale(bitsPerSample, dataType, extraSamples[0] == 1);
|
||||
return ImageTypeSpecifiers.createGrayscale(bitsPerSample, dataType, isAlphaPremultiplied);
|
||||
case TIFFExtension.PLANARCONFIG_PLANAR:
|
||||
return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, dataType, true, extraSamples[0] == 1);
|
||||
return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, dataType, true, isAlphaPremultiplied);
|
||||
}
|
||||
}
|
||||
else if (bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
|
||||
else if (/*bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4 ||*/ bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
|
||||
// TODO: Should use packed format for 1/2/4 chunky.
|
||||
// TODO: For 1/2/4 bit planar, we might need to fix while reading... Look at IFFImageReader?
|
||||
switch (planarConfiguration) {
|
||||
case TIFFBaseline.PLANARCONFIG_CHUNKY:
|
||||
return ImageTypeSpecifiers.createInterleaved(cs, new int[] {0, 1}, dataType, true, extraSamples[0] == 1);
|
||||
return ImageTypeSpecifiers.createInterleaved(cs, new int[] {0, 1}, dataType, true, isAlphaPremultiplied);
|
||||
case TIFFExtension.PLANARCONFIG_PLANAR:
|
||||
return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, dataType, true, extraSamples[0] == 1);
|
||||
return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, dataType, true, isAlphaPremultiplied);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IIOException(String.format("Unsupported BitsPerSample for Gray + Alpha TIFF (expected 8, 16 or 32): %d", bitsPerSample));
|
||||
// TODO: More samples might be ok, if multiple alpha or unknown samples
|
||||
|
||||
default:
|
||||
throw new IIOException(String.format("Unsupported SamplesPerPixel/BitsPerSample combination for Bi-level/Gray TIFF (expected 1/1, 1/2, 1/4, 1/8, 1/16 or 1/32, or 2/8, 2/16 or 2/32): %d/%d", samplesPerPixel, bitsPerSample));
|
||||
@ -403,15 +416,12 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
cs = profile == null ? ColorSpace.getInstance(ColorSpace.CS_sRGB) : ColorSpaces.createColorSpace(profile);
|
||||
|
||||
switch (samplesPerPixel) {
|
||||
switch (significantSamples) {
|
||||
case 3:
|
||||
if (bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
|
||||
switch (planarConfiguration) {
|
||||
case TIFFBaseline.PLANARCONFIG_CHUNKY:
|
||||
if (bitsPerSample == 8 && cs.isCS_sRGB()) {
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
|
||||
}
|
||||
|
||||
// "TYPE_3_BYTE_RGB" if cs.isCS_sRGB()
|
||||
return ImageTypeSpecifiers.createInterleaved(cs, new int[] {0, 1, 2}, dataType, false, false);
|
||||
|
||||
case TIFFExtension.PLANARCONFIG_PLANAR:
|
||||
@ -420,27 +430,18 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
case 4:
|
||||
if (bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
|
||||
// ExtraSamples 0=unspecified, 1=associated (pre-multiplied), 2=unassociated (TODO: Support unspecified, not alpha)
|
||||
long[] extraSamples = getValueAsLongArray(TIFF.TAG_EXTRA_SAMPLES, "ExtraSamples", true);
|
||||
|
||||
switch (planarConfiguration) {
|
||||
case TIFFBaseline.PLANARCONFIG_CHUNKY:
|
||||
if (bitsPerSample == 8 && cs.isCS_sRGB()) {
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
|
||||
}
|
||||
|
||||
return ImageTypeSpecifiers.createInterleaved(cs, new int[]{ 0, 1, 2, 3}, dataType, true, extraSamples[0] == 1);
|
||||
// "TYPE_4_BYTE_RGBA" if cs.isCS_sRGB()
|
||||
return ImageTypeSpecifiers.createInterleaved(cs, new int[] {0, 1, 2, 3}, dataType, true, isAlphaPremultiplied);
|
||||
|
||||
case TIFFExtension.PLANARCONFIG_PLANAR:
|
||||
return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, dataType, true, extraSamples[0] == 1);
|
||||
return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, dataType, true, isAlphaPremultiplied);
|
||||
}
|
||||
}
|
||||
else if (bitsPerSample == 4) {
|
||||
long[] extraSamples = getValueAsLongArray(TIFF.TAG_EXTRA_SAMPLES, "ExtraSamples", true);
|
||||
|
||||
return ImageTypeSpecifier.createPacked(cs, 0xF000, 0xF00, 0xF0, 0xF, DataBuffer.TYPE_USHORT, extraSamples[0] == 1);
|
||||
return ImageTypeSpecifiers.createPacked(cs, 0xF000, 0xF00, 0xF0, 0xF, DataBuffer.TYPE_USHORT, isAlphaPremultiplied);
|
||||
}
|
||||
// TODO: More samples might be ok, if multiple alpha or unknown samples
|
||||
default:
|
||||
throw new IIOException(String.format("Unsupported SamplesPerPixels/BitsPerSample combination for RGB TIFF (expected 3/8, 4/8, 3/16 or 4/16): %d/%d", samplesPerPixel, bitsPerSample));
|
||||
}
|
||||
@ -452,8 +453,8 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
else if (bitsPerSample <= 0 || bitsPerSample > 16) {
|
||||
throw new IIOException("Bad BitsPerSample value for Palette TIFF (expected <= 16): " + bitsPerSample);
|
||||
}
|
||||
// NOTE: If ExtraSamples is used, PlanarConfiguration must be taken into account also for pixel data
|
||||
|
||||
// NOTE: If ExtraSamples is used, PlanarConfiguration must be taken into account also for pixel data
|
||||
Entry colorMap = currentIFD.getEntryById(TIFF.TAG_COLOR_MAP);
|
||||
if (colorMap == null) {
|
||||
throw new IIOException("Missing ColorMap for Palette TIFF");
|
||||
@ -483,7 +484,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
cs = profile == null ? ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK) : ColorSpaces.createColorSpace(profile);
|
||||
|
||||
switch (samplesPerPixel) {
|
||||
switch (significantSamples) {
|
||||
case 4:
|
||||
if (bitsPerSample == 8 || bitsPerSample == 16) {
|
||||
switch (planarConfiguration) {
|
||||
@ -495,19 +496,14 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
case 5:
|
||||
if (bitsPerSample == 8 || bitsPerSample == 16) {
|
||||
// ExtraSamples 0=unspecified, 1=associated (pre-multiplied), 2=unassociated (TODO: Support unspecified, not alpha)
|
||||
long[] extraSamples = getValueAsLongArray(TIFF.TAG_EXTRA_SAMPLES, "ExtraSamples", true);
|
||||
|
||||
switch (planarConfiguration) {
|
||||
case TIFFBaseline.PLANARCONFIG_CHUNKY:
|
||||
return ImageTypeSpecifiers.createInterleaved(cs, new int[] {0, 1, 2, 3, 4}, dataType, true, extraSamples[0] == 1);
|
||||
return ImageTypeSpecifiers.createInterleaved(cs, new int[] {0, 1, 2, 3, 4}, dataType, true, isAlphaPremultiplied);
|
||||
case TIFFExtension.PLANARCONFIG_PLANAR:
|
||||
return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, dataType, true, extraSamples[0] == 1);
|
||||
return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, dataType, true, isAlphaPremultiplied);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: More samples might be ok, if multiple alpha or unknown samples, consult ExtraSamples
|
||||
|
||||
default:
|
||||
throw new IIOException(
|
||||
String.format("Unsupported TIFF SamplesPerPixels/BitsPerSample combination for Separated TIFF (expected 4/8, 4/16, 5/8 or 5/16): %d/%s", samplesPerPixel, bitsPerSample)
|
||||
@ -515,13 +511,42 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
case TIFFBaseline.PHOTOMETRIC_MASK:
|
||||
// Transparency mask
|
||||
|
||||
case TIFFExtension.PHOTOMETRIC_CIELAB:
|
||||
case TIFFExtension.PHOTOMETRIC_ICCLAB:
|
||||
case TIFFExtension.PHOTOMETRIC_ITULAB:
|
||||
// L*a*b* color. Handled using conversion to linear RGB
|
||||
case TIFFCustom.PHOTOMETRIC_LOGL:
|
||||
case TIFFCustom.PHOTOMETRIC_LOGLUV:
|
||||
// Log
|
||||
case TIFFCustom.PHOTOMETRIC_CFA:
|
||||
case TIFFCustom.PHOTOMETRIC_LINEAR_RAW:
|
||||
// RAW (DNG)
|
||||
throw new IIOException("Unsupported TIFF PhotometricInterpretation value: " + interpretation);
|
||||
default:
|
||||
throw new IIOException("Unknown TIFF PhotometricInterpretation value: " + interpretation);
|
||||
}
|
||||
}
|
||||
|
||||
private int getOpaqueSamplesPerPixel(final int photometricInterpretation) throws IIOException {
|
||||
switch (photometricInterpretation) {
|
||||
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
||||
case TIFFBaseline.PHOTOMETRIC_BLACK_IS_ZERO:
|
||||
case TIFFBaseline.PHOTOMETRIC_PALETTE:
|
||||
case TIFFBaseline.PHOTOMETRIC_MASK:
|
||||
return 1;
|
||||
case TIFFBaseline.PHOTOMETRIC_RGB:
|
||||
case TIFFExtension.PHOTOMETRIC_YCBCR:
|
||||
case TIFFExtension.PHOTOMETRIC_CIELAB:
|
||||
case TIFFExtension.PHOTOMETRIC_ICCLAB:
|
||||
case TIFFExtension.PHOTOMETRIC_ITULAB:
|
||||
return 3;
|
||||
case TIFFExtension.PHOTOMETRIC_SEPARATED:
|
||||
return getValueAsIntWithDefault(TIFF.TAG_NUMBER_OF_INKS, 4);
|
||||
default:
|
||||
throw new IIOException("Unknown TIFF PhotometricInterpretation value: " + photometricInterpretation);
|
||||
}
|
||||
}
|
||||
|
||||
private int getDataType(int sampleFormat, int bitsPerSample) throws IIOException {
|
||||
switch (sampleFormat) {
|
||||
case TIFFBaseline.SAMPLEFORMAT_UINT:
|
||||
@ -623,9 +648,14 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
else {
|
||||
int bitsPerSample = (int) value[0];
|
||||
|
||||
for (int i = 1; i < value.length; i++) {
|
||||
if (value[i] != bitsPerSample) {
|
||||
throw new IIOException("Variable BitsPerSample not supported: " + Arrays.toString(value));
|
||||
if (value.length == 3 && (value[0] == 5 && value[1] == 6 && value[2] == 5)) {
|
||||
// Special case for UINT_565. We're good.
|
||||
}
|
||||
else {
|
||||
for (int i = 1; i < value.length; i++) {
|
||||
if (value[i] != bitsPerSample) {
|
||||
throw new IIOException("Variable BitsPerSample not supported: " + Arrays.toString(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -719,7 +749,7 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
|
||||
int tilesAcross = (width + stripTileWidth - 1) / stripTileWidth;
|
||||
int tilesDown = (height + stripTileHeight - 1) / stripTileHeight;
|
||||
// WritableRaster rowRaster = rawType.getColorModel().createCompatibleWritableRaster(stripTileWidth, 1);
|
||||
// TODO: If extrasamples, we might need to create a raster with more samples...
|
||||
WritableRaster rowRaster = rawType.createBufferedImage(stripTileWidth, 1).getRaster();
|
||||
Rectangle clip = new Rectangle(srcRegion);
|
||||
int row = 0;
|
||||
@ -1310,120 +1340,169 @@ public class TIFFImageReader extends ImageReaderBase {
|
||||
final int colsInTile, final int rowsInTile, final DataInput input)
|
||||
throws IOException {
|
||||
|
||||
DataBuffer dataBuffer = tileRowRaster.getDataBuffer();
|
||||
int bands = dataBuffer.getNumBanks();
|
||||
boolean banded = bands > 1;
|
||||
|
||||
switch (tileRowRaster.getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
byte[] rowDataByte = ((DataBufferByte) tileRowRaster.getDataBuffer()).getData();
|
||||
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
for (int band = 0; band < bands; band++) {
|
||||
byte[] rowDataByte = ((DataBufferByte) dataBuffer).getData(band);
|
||||
WritableRaster destChannel = banded
|
||||
? raster.createWritableChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), 0, 0, new int[] {band})
|
||||
: raster;
|
||||
Raster srcChannel = banded
|
||||
? tileRowRaster.createChild(tileRowRaster.getMinX(), 0, tileRowRaster.getWidth(), 1, 0, 0, new int[] {band})
|
||||
: tileRowRaster;
|
||||
|
||||
input.readFully(rowDataByte);
|
||||
|
||||
if (row % ySub == 0 && row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataByte);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
System.arraycopy(rowDataByte, x * xSub, rowDataByte, x, numBands);
|
||||
}
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
|
||||
raster.setDataElements(startCol, (row - srcRegion.y) / ySub, tileRowRaster);
|
||||
input.readFully(rowDataByte);
|
||||
|
||||
if (row % ySub == 0 && row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataByte);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
System.arraycopy(rowDataByte, x * xSub, rowDataByte, x, numBands);
|
||||
}
|
||||
}
|
||||
|
||||
destChannel.setDataElements(startCol, (row - srcRegion.y) / ySub, srcChannel);
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
|
||||
break;
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
case DataBuffer.TYPE_SHORT:
|
||||
short[] rowDataShort = tileRowRaster.getTransferType() == DataBuffer.TYPE_USHORT
|
||||
? ((DataBufferUShort) tileRowRaster.getDataBuffer()).getData()
|
||||
: ((DataBufferShort) tileRowRaster.getDataBuffer()).getData();
|
||||
for (int band = 0; band < bands; band++) {
|
||||
short[] rowDataShort = dataBuffer.getDataType() == DataBuffer.TYPE_USHORT
|
||||
? ((DataBufferUShort) dataBuffer).getData(band)
|
||||
: ((DataBufferShort) dataBuffer).getData(band);
|
||||
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
WritableRaster destChannel = banded
|
||||
? raster.createWritableChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), 0, 0, new int[] {band})
|
||||
: raster;
|
||||
Raster srcChannel = banded
|
||||
? tileRowRaster.createChild(tileRowRaster.getMinX(), 0, tileRowRaster.getWidth(), 1, 0, 0, new int[] {band})
|
||||
: tileRowRaster;
|
||||
|
||||
readFully(input, rowDataShort);
|
||||
|
||||
if (row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataShort);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
System.arraycopy(rowDataShort, x * xSub, rowDataShort, x, numBands);
|
||||
}
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
|
||||
raster.setDataElements(startCol, row - srcRegion.y, tileRowRaster);
|
||||
// TODO: Possible speedup ~30%!:
|
||||
readFully(input, rowDataShort);
|
||||
|
||||
if (row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataShort);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
System.arraycopy(rowDataShort, x * xSub, rowDataShort, x, numBands);
|
||||
}
|
||||
}
|
||||
|
||||
destChannel.setDataElements(startCol, row - srcRegion.y, srcChannel);
|
||||
// TODO: Possible speedup ~30%!:
|
||||
// raster.setDataElements(startCol, row - srcRegion.y, colsInTile, 1, rowDataShort);
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
|
||||
break;
|
||||
case DataBuffer.TYPE_INT:
|
||||
int[] rowDataInt = ((DataBufferInt) tileRowRaster.getDataBuffer()).getData();
|
||||
for (int band = 0; band < bands; band++) {
|
||||
int[] rowDataInt = ((DataBufferInt) dataBuffer).getData(band);
|
||||
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
WritableRaster destChannel = banded
|
||||
? raster.createWritableChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), 0, 0, new int[] {band})
|
||||
: raster;
|
||||
Raster srcChannel = banded
|
||||
? tileRowRaster.createChild(tileRowRaster.getMinX(), 0, tileRowRaster.getWidth(), 1, 0, 0, new int[] {band})
|
||||
: tileRowRaster;
|
||||
|
||||
readFully(input, rowDataInt);
|
||||
|
||||
if (row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataInt);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
System.arraycopy(rowDataInt, x * xSub, rowDataInt, x, numBands);
|
||||
}
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
|
||||
raster.setDataElements(startCol, row - srcRegion.y, tileRowRaster);
|
||||
readFully(input, rowDataInt);
|
||||
|
||||
if (row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataInt);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
System.arraycopy(rowDataInt, x * xSub, rowDataInt, x, numBands);
|
||||
}
|
||||
}
|
||||
|
||||
destChannel.setDataElements(startCol, row - srcRegion.y, srcChannel);
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DataBuffer.TYPE_FLOAT:
|
||||
float[] rowDataFloat = ((DataBufferFloat) tileRowRaster.getDataBuffer()).getData();
|
||||
for (int band = 0; band < bands; band++) {
|
||||
float[] rowDataFloat = ((DataBufferFloat) tileRowRaster.getDataBuffer()).getData(band);
|
||||
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
WritableRaster destChannel = banded
|
||||
? raster.createWritableChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), 0, 0, new int[] {band})
|
||||
: raster;
|
||||
Raster srcChannel = banded
|
||||
? tileRowRaster.createChild(tileRowRaster.getMinX(), 0, tileRowRaster.getWidth(), 1, 0, 0, new int[] {band})
|
||||
: tileRowRaster;
|
||||
|
||||
readFully(input, rowDataFloat);
|
||||
|
||||
if (row >= srcRegion.y) {
|
||||
// normalizeBlack(interpretation, rowDataFloat);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
System.arraycopy(rowDataFloat, x * xSub, rowDataFloat, x, numBands);
|
||||
}
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
|
||||
raster.setDataElements(startCol, row - srcRegion.y, tileRowRaster);
|
||||
readFully(input, rowDataFloat);
|
||||
|
||||
if (row >= srcRegion.y) {
|
||||
// TODO: Allow param to decide tone mapping strategy, like in the HDRImageReader
|
||||
clamp(rowDataFloat);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
System.arraycopy(rowDataFloat, x * xSub, rowDataFloat, x, numBands);
|
||||
}
|
||||
}
|
||||
|
||||
destChannel.setDataElements(startCol, row - srcRegion.y, srcChannel);
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void clamp(float[] rowDataFloat) {
|
||||
for (int i = 0; i < rowDataFloat.length; i++) {
|
||||
if (rowDataFloat[i] > 1) {
|
||||
rowDataFloat[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Candidate util method (with off/len + possibly byte order)
|
||||
private void readFully(final DataInput input, final float[] rowDataFloat) throws IOException {
|
||||
if (input instanceof ImageInputStream) {
|
||||
|
@ -50,8 +50,8 @@ final class TIFFProviderInfo extends ReaderWriterProviderInfo {
|
||||
new String[] {"com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi"},
|
||||
"com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriter",
|
||||
new String[] {"com.twelvemkonkeys.imageio.plugins.tif.TIFFImageWriterSpi"},
|
||||
false, TIFFMedataFormat.SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, "com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadata", null, null,
|
||||
true, TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME, "TODO", null, null
|
||||
false, TIFFMedataFormat.SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, "TODO", null, null,
|
||||
true, TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME, "com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadata", null, null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -95,8 +95,33 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
||||
new TestData(getClassLoaderResource("/tiff/ccitt/group3_2d_fill.tif"), new Dimension(6, 4)), // B/W, CCITT T4 2D
|
||||
new TestData(getClassLoaderResource("/tiff/ccitt/group3_2d_lsb2msb.tif"), new Dimension(6, 4)), // B/W, CCITT T4 2D, LSB
|
||||
new TestData(getClassLoaderResource("/tiff/ccitt/group4.tif"), new Dimension(6, 4)), // B/W, CCITT T6 1D
|
||||
new TestData(getClassLoaderResource("/tiff/fivepages-scan-causingerrors.tif"), new Dimension(2480, 3518)) // B/W, CCITT T4
|
||||
);
|
||||
new TestData(getClassLoaderResource("/tiff/fivepages-scan-causingerrors.tif"), new Dimension(2480, 3518)), // B/W, CCITT T4
|
||||
// Gray
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-02.tif"), new Dimension(73, 43)), // Gray 2 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-04.tif"), new Dimension(73, 43)), // Gray 4 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-08.tif"), new Dimension(73, 43)), // Gray 8 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-16.tif"), new Dimension(73, 43)), // Gray 16 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-minisblack-32.tif"), new Dimension(73, 43)), // Gray 32 bit/sample
|
||||
// Palette
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-02.tif"), new Dimension(73, 43)), // Palette 2 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-04.tif"), new Dimension(73, 43)), // Palette 4 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-08.tif"), new Dimension(73, 43)), // Palette 8 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-16.tif"), new Dimension(73, 43)), // Palette 16 bit/sample
|
||||
// RGB Interleaved (PlanarConfiguration: 1)
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-08.tif"), new Dimension(73, 43)), // RGB 8 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-16.tif"), new Dimension(73, 43)), // RGB 16 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-32.tif"), new Dimension(73, 43)), // RGB 32 bit/sample
|
||||
// RGB Planar (PlanarConfiguration: 2)
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-08.tif"), new Dimension(73, 43)), // RGB 8 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-16.tif"), new Dimension(73, 43)), // RGB 16 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-32.tif"), new Dimension(73, 43)), // RGB 32 bit FP samples!
|
||||
// Separated (CMYK) Interleaved (PlanarConfiguration: 1)
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-separated-contig-08.tif"), new Dimension(73, 43)), // CMYK 8 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-separated-contig-16.tif"), new Dimension(73, 43)), // CMYK 16 bit/sample
|
||||
// Separated (CMYK) Planar (PlanarConfiguration: 2)
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-separated-planar-08.tif"), new Dimension(73, 43)), // CMYK 8 bit/sample
|
||||
new TestData(getClassLoaderResource("/tiff/depth/flower-separated-planar-16.tif"), new Dimension(73, 43)) // CMYK 16 bit/sample
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -228,4 +253,77 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
||||
verify(warningListener, atLeastOnce()).warningOccurred(eq(reader), contains("ICC profile"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlanarEqualInterleavedRGB() throws IOException {
|
||||
TestData expectedData = new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-08.tif"), new Dimension(73, 43));
|
||||
TestData testData = new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-08.tif"), new Dimension(73, 43));
|
||||
|
||||
try (ImageInputStream expectedStream = expectedData.getInputStream(); ImageInputStream stream = testData.getInputStream()) {
|
||||
TIFFImageReader reader = createReader();
|
||||
|
||||
reader.setInput(expectedStream);
|
||||
BufferedImage expected = reader.read(0, null);
|
||||
|
||||
reader.setInput(stream);
|
||||
BufferedImage actual = reader.read(0, null);
|
||||
|
||||
assertImageDataEquals("", expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlanarEqualInterleavedRGB16() throws IOException {
|
||||
TestData expectedData = new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-16.tif"), new Dimension(73, 43));
|
||||
TestData testData = new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-16.tif"), new Dimension(73, 43));
|
||||
|
||||
try (ImageInputStream expectedStream = expectedData.getInputStream(); ImageInputStream stream = testData.getInputStream()) {
|
||||
TIFFImageReader reader = createReader();
|
||||
|
||||
reader.setInput(expectedStream);
|
||||
BufferedImage expected = reader.read(0, null);
|
||||
|
||||
reader.setInput(stream);
|
||||
BufferedImage actual = reader.read(0, null);
|
||||
|
||||
assertImageDataEquals("", expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlanarEqualInterleavedSeparated() throws IOException {
|
||||
TestData expectedData = new TestData(getClassLoaderResource("/tiff/depth/flower-separated-contig-08.tif"), new Dimension(73, 43));
|
||||
TestData testData = new TestData(getClassLoaderResource("/tiff/depth/flower-separated-planar-08.tif"), new Dimension(73, 43));
|
||||
|
||||
try (ImageInputStream expectedStream = expectedData.getInputStream(); ImageInputStream stream = testData.getInputStream()) {
|
||||
TIFFImageReader reader = createReader();
|
||||
|
||||
reader.setInput(expectedStream);
|
||||
BufferedImage expected = reader.read(0, null);
|
||||
|
||||
reader.setInput(stream);
|
||||
BufferedImage actual = reader.read(0, null);
|
||||
|
||||
assertImageDataEquals("", expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlanarEqualInterleavedSeparated16() throws IOException {
|
||||
TestData expectedData = new TestData(getClassLoaderResource("/tiff/depth/flower-separated-contig-16.tif"), new Dimension(73, 43));
|
||||
TestData testData = new TestData(getClassLoaderResource("/tiff/depth/flower-separated-planar-16.tif"), new Dimension(73, 43));
|
||||
|
||||
try (ImageInputStream expectedStream = expectedData.getInputStream(); ImageInputStream stream = testData.getInputStream()) {
|
||||
TIFFImageReader reader = createReader();
|
||||
|
||||
reader.setInput(expectedStream);
|
||||
BufferedImage expected = reader.read(0, null);
|
||||
|
||||
reader.setInput(stream);
|
||||
BufferedImage actual = reader.read(0, null);
|
||||
|
||||
assertImageDataEquals("", expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
These sample TIFF image files are prepared by Bob Friesenhahn
|
||||
<bfriesen@simple.dallas.tx.us> using a development version of
|
||||
GraphicsMagick 1.2.
|
||||
|
||||
See the file summary.txt for a description of the images.
|
||||
|
||||
These files are hereby placed in the public domain.
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,37 @@
|
||||
|
||||
flower-minisblack-02.tif 73x43 2-bit minisblack gray image
|
||||
flower-minisblack-04.tif 73x43 4-bit minisblack gray image
|
||||
flower-minisblack-06.tif 73x43 6-bit minisblack gray image
|
||||
flower-minisblack-08.tif 73x43 8-bit minisblack gray image
|
||||
flower-minisblack-10.tif 73x43 10-bit minisblack gray image
|
||||
flower-minisblack-12.tif 73x43 12-bit minisblack gray image
|
||||
flower-minisblack-14.tif 73x43 14-bit minisblack gray image
|
||||
flower-minisblack-16.tif 73x43 16-bit minisblack gray image
|
||||
flower-minisblack-24.tif 73x43 24-bit minisblack gray image
|
||||
flower-minisblack-32.tif 73x43 32-bit minisblack gray image
|
||||
flower-palette-02.tif 73x43 4-entry colormapped image
|
||||
flower-palette-04.tif 73x43 16-entry colormapped image
|
||||
flower-palette-08.tif 73x43 256-entry colormapped image
|
||||
flower-palette-16.tif 73x43 65536-entry colormapped image
|
||||
flower-rgb-contig-02.tif 73x43 2-bit contiguous RGB image
|
||||
flower-rgb-contig-04.tif 73x43 4-bit contiguous RGB image
|
||||
flower-rgb-contig-08.tif 73x43 8-bit contiguous RGB image
|
||||
flower-rgb-contig-10.tif 73x43 10-bit contiguous RGB image
|
||||
flower-rgb-contig-12.tif 73x43 12-bit contiguous RGB image
|
||||
flower-rgb-contig-14.tif 73x43 14-bit contiguous RGB image
|
||||
flower-rgb-contig-16.tif 73x43 16-bit contiguous RGB image
|
||||
flower-rgb-contig-24.tif 73x43 24-bit contiguous RGB image
|
||||
flower-rgb-contig-32.tif 73x43 32-bit contiguous RGB image
|
||||
flower-rgb-planar-02.tif 73x43 2-bit seperated RGB image
|
||||
flower-rgb-planar-04.tif 73x43 4-bit seperated RGB image
|
||||
flower-rgb-planar-08.tif 73x43 8-bit seperated RGB image
|
||||
flower-rgb-planar-10.tif 73x43 10-bit seperated RGB image
|
||||
flower-rgb-planar-12.tif 73x43 12-bit seperated RGB image
|
||||
flower-rgb-planar-14.tif 73x43 14-bit seperated RGB image
|
||||
flower-rgb-planar-16.tif 73x43 16-bit seperated RGB image
|
||||
flower-rgb-planar-24.tif 73x43 24-bit seperated RGB image
|
||||
flower-rgb-planar-32.tif 73x43 32-bit seperated RGB image
|
||||
flower-separated-contig-08.tif 73x43 8-bit contiguous CMYK image
|
||||
flower-separated-contig-16.tif 73x43 16-bit contiguous CMYK image
|
||||
flower-separated-planar-08.tif 73x43 8-bit separated CMYK image
|
||||
flower-separated-planar-16.tif 73x43 16-bit separated CMYK image
|
Loading…
x
Reference in New Issue
Block a user