diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/ExtraSamplesColorModel.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/ExtraSamplesColorModel.java index d5c8ab7b..ac3daa45 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/ExtraSamplesColorModel.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/ExtraSamplesColorModel.java @@ -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,10 +56,24 @@ 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; - this.componentSize = getDataTypeSize(dataType); + + 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 @@ -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); + } + } } diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java index 21879b90..3f305887 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java @@ -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; diff --git a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java index 38f4f7ca..73cf75a7 100644 --- a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java +++ b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java @@ -88,6 +88,7 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest