#975: Fix for broken decoding gray + extra samples

This commit is contained in:
Harald Kuhr 2024-07-05 15:42:49 +02:00
parent f7d4557c57
commit ee7d4ba724
4 changed files with 50 additions and 8 deletions

View File

@ -33,11 +33,12 @@ package com.twelvemonkeys.imageio.plugins.tiff;
import com.twelvemonkeys.lang.Validate;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.color.*;
import java.awt.image.*;
import java.util.Objects;
import static java.awt.image.DataBuffer.getDataTypeSize;
import static com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReader.createOffsets;
import static java.awt.image.DataBuffer.*;
/**
* ExtraSamplesColorModel.
@ -55,11 +56,25 @@ final class ExtraSamplesColorModel extends ComponentColorModel {
private final int componentSize;
ExtraSamplesColorModel(ColorSpace cs, boolean hasAlpha, boolean isAlphaPremultiplied, int dataType, int extraComponents) {
super(cs, hasAlpha, isAlphaPremultiplied, Transparency.TRANSLUCENT, dataType);
this(cs, null, hasAlpha, isAlphaPremultiplied, dataType, extraComponents);
}
ExtraSamplesColorModel(ColorSpace cs, int[] bits, boolean hasAlpha, boolean isAlphaPremultiplied, int dataType, int extraComponents) {
super(cs, bits, hasAlpha, isAlphaPremultiplied, Transparency.TRANSLUCENT, dataType);
Validate.isTrue(extraComponents > 0, "Extra components must be > 0");
this.numComponents = cs.getNumComponents() + (hasAlpha ? 1 : 0) + extraComponents;
if (bits != null) {
Validate.isTrue(bits.length == numComponents, "bits.length must be == " + numComponents);
this.componentSize = bits[0];
for (int bit : bits) {
Validate.isTrue(bit == componentSize, "Variable bits per component not supported");
}
}
else {
this.componentSize = getDataTypeSize(dataType);
}
}
@Override
public int getNumComponents() {
@ -160,4 +175,21 @@ final class ExtraSamplesColorModel extends ComponentColorModel {
public int hashCode() {
return Objects.hash(super.hashCode(), numComponents, componentSize);
}
@Override
public SampleModel createCompatibleSampleModel(int w, int h) {
return new PixelInterleavedSampleModel(transferType, 1, 1, numComponents, numComponents, createOffsets(numComponents));
}
@Override
public WritableRaster createCompatibleWritableRaster(int w, int h) {
switch (transferType) {
case DataBuffer.TYPE_BYTE:
case DataBuffer.TYPE_USHORT:
return Raster.createInterleavedRaster(transferType, w, h, numComponents, null);
default:
SampleModel sampleModel = createCompatibleSampleModel(w, h);
return Raster.createWritableRaster(sampleModel, sampleModel.createDataBuffer(), null);
}
}
}

View File

@ -514,18 +514,27 @@ public final class TIFFImageReader extends ImageReaderBase {
cs = profile == null ? ColorSpace.getInstance(ColorSpace.CS_GRAY) : ColorSpaces.createColorSpace(profile);
if (cs == ColorSpace.getInstance(ColorSpace.CS_GRAY) && (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4 || bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32)) {
if (samplesPerPixel == significantSamples && cs == ColorSpace.getInstance(ColorSpace.CS_GRAY)
&& (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4 || bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32)) {
return ImageTypeSpecifiers.createGrayscale(bitsPerSample, dataType);
}
else if (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4) {
if (samplesPerPixel != significantSamples) {
throw new IIOException(String.format("ExtraSamples not supported for Bi-level/Gray TIFF with < 8 bitsPerSample: %d", bitsPerSample));
}
// Use packed format for 1/2/4 bits
return ImageTypeSpecifiers.createPackedGrayscale(cs, bitsPerSample, dataType);
}
else if (bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
return createImageTypeSpecifier(TIFFBaseline.PLANARCONFIG_CHUNKY, cs, dataType, significantSamples, samplesPerPixel, false, false);
return createImageTypeSpecifier(planarConfiguration, cs, dataType, significantSamples, samplesPerPixel, hasAlpha, isAlphaPremultiplied);
}
else if (bitsPerSample % 2 == 0) {
ColorModel colorModel = new ComponentColorModel(cs, new int[] {bitsPerSample}, false, false, Transparency.OPAQUE, dataType);
int[] bits = new int[samplesPerPixel];
Arrays.fill(bits, bitsPerSample);
ColorModel colorModel = samplesPerPixel > significantSamples
? new ExtraSamplesColorModel(cs, bits, false, false, dataType, samplesPerPixel - significantSamples)
: new ComponentColorModel(cs, bits, false, false, Transparency.OPAQUE, dataType);
return new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1));
}
@ -709,7 +718,7 @@ public final class TIFFImageReader extends ImageReaderBase {
}
}
private static int[] createOffsets(int samplesPerPixel) {
static int[] createOffsets(int samplesPerPixel) {
int[] offsets = new int[samplesPerPixel];
for (int i = 0; i < samplesPerPixel; i++) {
offsets[i] = i;

View File

@ -88,6 +88,7 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
new TestData(getClassLoaderResource("/tiff/quad-lzw.tif"), new Dimension(512, 384)), // RGB, Old spec (reversed) LZW compressed, tiled
new TestData(getClassLoaderResource("/tiff/bali.tif"), new Dimension(725, 489)), // Palette-based, LZW compressed
new TestData(getClassLoaderResource("/tiff/f14.tif"), new Dimension(640, 480)), // Gray, uncompressed
new TestData(getClassLoaderResource("/tiff/house.tif"), new Dimension(512, 512)), // Gray + extra sample, uncompressed
new TestData(getClassLoaderResource("/tiff/marbles.tif"), new Dimension(1419, 1001)), // RGB, LZW compressed w/predictor
new TestData(getClassLoaderResource("/tiff/lzw-full-12-bit-table.tif"), new Dimension(874, 1240)), // Gray, LZW compressed, w/predictor
new TestData(getClassLoaderResource("/tiff/chifley_logo.tif"), new Dimension(591, 177)), // CMYK, uncompressed

Binary file not shown.