diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/color/DiscreteAlphaIndexColorModel.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/color/DiscreteAlphaIndexColorModel.java new file mode 100644 index 00000000..db3a1bd7 --- /dev/null +++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/color/DiscreteAlphaIndexColorModel.java @@ -0,0 +1,134 @@ +package com.twelvemonkeys.imageio.color; + +import java.awt.*; +import java.awt.image.*; + +import static com.twelvemonkeys.lang.Validate.notNull; + +/** + * This class represents a hybrid between an {@link IndexColorModel} and a {@link ComponentColorModel}, + * having both a color map and a full, discrete alpha channel. + * The color map entries are assumed to be fully opaque and should have no transparent index. + *
+ * ColorSpace will always be the default sRGB color space (as with {@code IndexColorModel}). + *
+ * Component order is always P, A, where P is a palette index, and A is the alpha value.
+ *
+ * @see IndexColorModel
+ * @see ComponentColorModel
+ */
+public final class DiscreteAlphaIndexColorModel extends ColorModel {
+ // Our IndexColorModel delegate
+ private final IndexColorModel icm;
+
+ /**
+ * Creates a {@code DiscreteAlphaIndexColorModel}, delegating color map look-ups
+ * to the given {@code IndexColorModel}.
+ *
+ * @param icm The {@code IndexColorModel} delegate. Color map entries are assumed to be
+ * fully opaque, any transparency or transparent index will be ignored.
+ */
+ public DiscreteAlphaIndexColorModel(final IndexColorModel icm) {
+ super(
+ notNull(icm, "IndexColorModel").getPixelSize() * 2,
+ new int[] {icm.getPixelSize(), icm.getPixelSize(), icm.getPixelSize(), icm.getPixelSize()},
+ icm.getColorSpace(), true, false, Transparency.TRANSLUCENT, icm.getTransferType()
+ );
+
+ this.icm = icm;
+ }
+
+ @Override
+ public final int getRed(final int pixel) {
+ return icm.getRed(pixel);
+ }
+
+ @Override
+ public final int getGreen(final int pixel) {
+ return icm.getGreen(pixel);
+ }
+
+ @Override
+ public final int getBlue(final int pixel) {
+ return icm.getBlue(pixel);
+ }
+
+ @Override
+ public final int getAlpha(final int pixel) {
+ return (int) ((((float) pixel) / ((1 << getComponentSize(3))-1)) * 255.0f + 0.5f);
+ }
+
+ private int getSample(final Object inData, final int index) {
+ int pixel;
+
+ switch (transferType) {
+ case DataBuffer.TYPE_BYTE:
+ byte bdata[] = (byte[]) inData;
+ pixel = bdata[index] & 0xff;
+ break;
+ case DataBuffer.TYPE_USHORT:
+ short sdata[] = (short[]) inData;
+ pixel = sdata[index] & 0xffff;
+ break;
+ case DataBuffer.TYPE_INT:
+ int idata[] = (int[]) inData;
+ pixel = idata[index];
+ break;
+ default:
+ throw new UnsupportedOperationException("This method has not been implemented for transferType " + transferType);
+ }
+
+ return pixel;
+ }
+
+ @Override
+ public final int getRed(final Object inData) {
+ return getRed(getSample(inData, 0));
+ }
+
+ @Override
+ public final int getGreen(final Object inData) {
+ return getGreen(getSample(inData, 0));
+ }
+
+ @Override
+ public final int getBlue(final Object inData) {
+ return getBlue(getSample(inData, 0));
+ }
+
+ @Override
+ public final int getAlpha(final Object inData) {
+ return getAlpha(getSample(inData, 1));
+ }
+
+ @Override
+ public final SampleModel createCompatibleSampleModel(final int w, final int h) {
+ return new PixelInterleavedSampleModel(transferType, w, h, 2, w * 2, new int[] {0, 1});
+ }
+
+ @Override
+ public final boolean isCompatibleSampleModel(final SampleModel sm) {
+ return sm instanceof PixelInterleavedSampleModel && sm.getNumBands() == 2;
+ }
+
+ @Override
+ public final WritableRaster createCompatibleWritableRaster(final int w, final int h) {
+ return Raster.createWritableRaster(createCompatibleSampleModel(w, h), new Point(0, 0));
+ }
+
+ @Override
+ public final boolean isCompatibleRaster(final Raster raster) {
+ int size = raster.getSampleModel().getSampleSize(0);
+ return ((raster.getTransferType() == transferType) &&
+ (raster.getNumBands() == 2) && ((1 << size) >= icm.getMapSize()));
+ }
+
+ public String toString() {
+ return "DiscreteAlphaIndexColorModel: #pixelBits = " + pixel_bits
+ + " numComponents = " + getNumComponents()
+ + " color space = " + getColorSpace()
+ + " transparency = " + getTransparency()
+ + " has alpha = " + hasAlpha()
+ + " isAlphaPre = " + isAlphaPremultiplied();
+ }
+}
diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/ImageTypeSpecifiers.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/ImageTypeSpecifiers.java
index 05cf4468..4c1d9308 100644
--- a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/ImageTypeSpecifiers.java
+++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/ImageTypeSpecifiers.java
@@ -28,6 +28,7 @@
package com.twelvemonkeys.imageio.util;
+import com.twelvemonkeys.imageio.color.DiscreteAlphaIndexColorModel;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.ImageTypeSpecifier;
@@ -167,4 +168,9 @@ public final class ImageTypeSpecifiers {
public static ImageTypeSpecifier createFromIndexColorModel(final IndexColorModel pColorModel) {
return new IndexedImageTypeSpecifier(pColorModel);
}
+
+ public static ImageTypeSpecifier createDiscreteAlphaIndexedFromIndexColorModel(final IndexColorModel pColorModel) {
+ ColorModel colorModel = new DiscreteAlphaIndexColorModel(pColorModel);
+ return new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1));
+ }
}
diff --git a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/color/DiscreteAlphaIndexColorModelTest.java b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/color/DiscreteAlphaIndexColorModelTest.java
new file mode 100644
index 00000000..72d42186
--- /dev/null
+++ b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/color/DiscreteAlphaIndexColorModelTest.java
@@ -0,0 +1,184 @@
+package com.twelvemonkeys.imageio.color;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+
+import java.awt.image.*;
+
+import static org.junit.Assert.*;
+
+public class DiscreteAlphaIndexColorModelTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateNull() {
+ new DiscreteAlphaIndexColorModel(null);
+ }
+
+ @Test
+ public void testCreateByte() {
+ int[] colors = createIntLut(1 << 8);
+ IndexColorModel colorModel = new IndexColorModel(8, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
+
+ new DiscreteAlphaIndexColorModel(colorModel);
+ }
+
+ @Test
+ public void testCreateUShort() {
+ int[] colors = createIntLut(1 << 16);
+ IndexColorModel colorModel = new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT);
+
+ new DiscreteAlphaIndexColorModel(colorModel);
+ }
+
+ @Test
+ public void testGetRed() {
+ int[] colors = createIntLut(1 << 8);
+ colors[0] = 0x336699;
+ IndexColorModel icm = new IndexColorModel(8, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
+
+ DiscreteAlphaIndexColorModel colorModel = new DiscreteAlphaIndexColorModel(icm);
+
+ assertEquals(0x33, colorModel.getRed(0));
+ assertEquals(0x33, colorModel.getRed(new byte[] {0x00, 0x45}));
+
+ for (int i = 1; i < colors.length; i++) {
+ assertEquals(i, colorModel.getRed(i));
+ assertEquals(i, colorModel.getRed(new byte[] {(byte) i, (byte) 0xff}));
+ }
+ }
+
+ @Test
+ public void testGetGreen() {
+ int[] colors = createIntLut(1 << 8);
+ colors[0] = 0x336699;
+ IndexColorModel icm = new IndexColorModel(8, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
+
+ DiscreteAlphaIndexColorModel colorModel = new DiscreteAlphaIndexColorModel(icm);
+
+ assertEquals(0x66, colorModel.getGreen(0));
+ assertEquals(0x66, colorModel.getGreen(new byte[] {0x00, 0x45}));
+
+ for (int i = 1; i < colors.length; i++) {
+ assertEquals(i, colorModel.getGreen(i));
+ assertEquals(i, colorModel.getGreen(new byte[] {(byte) i, (byte) 0xff}));
+ }
+ }
+
+ @Test
+ public void testGetBlue() {
+ int[] colors = createIntLut(1 << 8);
+ colors[0] = 0x336699;
+ IndexColorModel icm = new IndexColorModel(8, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
+
+ DiscreteAlphaIndexColorModel colorModel = new DiscreteAlphaIndexColorModel(icm);
+
+ assertEquals(0x99, colorModel.getBlue(0));
+ assertEquals(0x99, colorModel.getBlue(new byte[] {0x00, 0x45}));
+
+ for (int i = 1; i < colors.length; i++) {
+ assertEquals(i, colorModel.getBlue(i));
+ assertEquals(i, colorModel.getBlue(new byte[] {(byte) i, (byte) 0xff}));
+ }
+ }
+
+ @Test
+ public void testGetAlpha() {
+ int[] colors = createIntLut(1 << 8);
+ IndexColorModel icm = new IndexColorModel(8, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
+
+ DiscreteAlphaIndexColorModel colorModel = new DiscreteAlphaIndexColorModel(icm);
+
+ assertEquals(0x45, colorModel.getAlpha(0x45));
+ assertEquals(0x45, colorModel.getAlpha(new byte[] {0x01, 0x45}));
+
+ for (int i = 1; i < colors.length; i++) {
+ assertEquals(i, colorModel.getAlpha(i));
+ assertEquals(i, colorModel.getAlpha(new byte[]{(byte) 0xff, (byte) i}));
+ }
+ }
+
+ @Test
+ public void testGetAlphaUShort() {
+ int[] colors = createIntLut(1 << 16);
+ colors[1] = 0x336699;
+ IndexColorModel icm = new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT);
+
+ DiscreteAlphaIndexColorModel colorModel = new DiscreteAlphaIndexColorModel(icm);
+
+ assertEquals(0x45, colorModel.getAlpha(0x4500));
+ assertEquals(0x45, colorModel.getAlpha(0x457F));
+
+ assertEquals(0x46, colorModel.getAlpha(0x45C6)); // Hmm.. This seems rather odd.. I would assume the limit should be 0x4580
+ assertEquals(0x46, colorModel.getAlpha(0x45FF));
+
+ assertEquals(0x45, colorModel.getAlpha(new short[] {0x01, 0x4500}));
+ assertEquals(0x45, colorModel.getAlpha(new short[] {0x02, 0x457F}));
+
+ assertEquals(0x46, colorModel.getAlpha(new short[] {0x03, 0x45C6}));
+ assertEquals(0x46, colorModel.getAlpha(new short[] {0x04, 0x45FF}));
+ }
+
+ @Test
+ public void testCreateCompatibleSampleModel() {
+ int[] colors = createIntLut(1 << 8);
+ IndexColorModel icm = new IndexColorModel(8, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
+
+ ColorModel colorModel = new DiscreteAlphaIndexColorModel(icm);
+ SampleModel sampleModel = colorModel.createCompatibleSampleModel(3, 2);
+
+ assertNotNull(sampleModel);
+
+ assertEquals(3, sampleModel.getWidth());
+ assertEquals(2, sampleModel.getHeight());
+
+ assertTrue(colorModel.isCompatibleSampleModel(sampleModel));
+ assertThat(sampleModel, CoreMatchers.is(PixelInterleavedSampleModel.class));
+ assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_BYTE));
+ }
+
+ @Test
+ public void testCreateCompatibleSampleModelUShort() {
+ int[] colors = createIntLut(1 << 8);
+ IndexColorModel icm = new IndexColorModel(8, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT);
+
+ ColorModel colorModel = new DiscreteAlphaIndexColorModel(icm);
+ SampleModel sampleModel = colorModel.createCompatibleSampleModel(3, 2);
+
+ assertNotNull(sampleModel);
+
+ assertEquals(3, sampleModel.getWidth());
+ assertEquals(2, sampleModel.getHeight());
+
+ assertTrue(colorModel.isCompatibleSampleModel(sampleModel));
+ assertThat(sampleModel, CoreMatchers.is(PixelInterleavedSampleModel.class));
+ assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_USHORT));
+ }
+
+ @Test
+ public void testCreateCompatibleRaster() {
+ int[] colors = createIntLut(1 << 8);
+ IndexColorModel icm = new IndexColorModel(8, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
+
+ ColorModel colorModel = new DiscreteAlphaIndexColorModel(icm);
+ WritableRaster raster = colorModel.createCompatibleWritableRaster(3, 2);
+
+ assertNotNull(raster);
+
+ assertEquals(3, raster.getWidth());
+ assertEquals(2, raster.getHeight());
+
+ assertTrue(colorModel.isCompatibleRaster(raster));
+ assertThat(raster, CoreMatchers.is(WritableRaster.class)); // Specific subclasses are in sun.awt package
+ assertThat(raster.getTransferType(), CoreMatchers.equalTo(DataBuffer.TYPE_BYTE));
+ }
+
+ private static int[] createIntLut(final int count) {
+ int[] lut = new int[count];
+
+ for (int i = 0; i < count; i++) {
+ lut[i] = 0xff000000 | i << 16 | i << 8 | i;
+ }
+
+ return lut;
+ }
+}
\ No newline at end of file
diff --git a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/util/ImageTypeSpecifiersTest.java b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/util/ImageTypeSpecifiersTest.java
index 9a286bbf..d22e79ca 100644
--- a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/util/ImageTypeSpecifiersTest.java
+++ b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/util/ImageTypeSpecifiersTest.java
@@ -609,7 +609,26 @@ public class ImageTypeSpecifiersTest {
new IndexedImageTypeSpecifier(colorModel),
ImageTypeSpecifiers.createFromIndexColorModel(colorModel)
);
+ }
+ @Test
+ public void testCreateDiscreteAlphaIndexedFromIndexColorModel8() {
+ int[] colors = createIntLut(1 << 8);
+ IndexColorModel colorModel = new IndexColorModel(8, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
+ assertEquals(
+ new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1)),
+ ImageTypeSpecifiers.createFromIndexColorModel(colorModel)
+ );
+ }
+
+ @Test
+ public void testCreateDiscreteAlphaIndexedFromIndexColorModel16() {
+ int[] colors = createIntLut(1 << 16);
+ IndexColorModel colorModel = new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT);
+ assertEquals(
+ new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1)),
+ ImageTypeSpecifiers.createFromIndexColorModel(colorModel)
+ );
}
private static byte[] createByteLut(final int count) {
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 5961f6e5..068fd319 100755
--- 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
@@ -52,7 +52,6 @@ import com.twelvemonkeys.io.FastByteArrayOutputStream;
import com.twelvemonkeys.io.LittleEndianDataInputStream;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.io.enc.PackBitsDecoder;
-import com.twelvemonkeys.xml.XMLSerializer;
import org.w3c.dom.NodeList;
import javax.imageio.*;
@@ -459,7 +458,7 @@ public class TIFFImageReader extends ImageReaderBase {
}
case TIFFBaseline.PHOTOMETRIC_PALETTE:
// Palette
- if (samplesPerPixel != 1) {
+ if (samplesPerPixel != 1 && !(samplesPerPixel == 2 && extraSamples != null && extraSamples.length == 1)) {
throw new IIOException("Bad SamplesPerPixel value for Palette TIFF (expected 1): " + samplesPerPixel);
}
else if (bitsPerSample <= 0 || bitsPerSample > 16) {
@@ -474,6 +473,12 @@ public class TIFFImageReader extends ImageReaderBase {
IndexColorModel icm = createIndexColorModel(bitsPerSample, dataType, (int[]) colorMap.getValue());
+ if (extraSamples != null && extraSamples.length > 0
+ && (extraSamples[0] == TIFFBaseline.EXTRASAMPLE_ASSOCIATED_ALPHA
+ || extraSamples[0] == TIFFBaseline.EXTRASAMPLE_UNASSOCIATED_ALPHA)) {
+ return ImageTypeSpecifiers.createDiscreteAlphaIndexedFromIndexColorModel(icm);
+ }
+
return ImageTypeSpecifiers.createFromIndexColorModel(icm);
case TIFFExtension.PHOTOMETRIC_SEPARATED:
@@ -808,8 +813,11 @@ public class TIFFImageReader extends ImageReaderBase {
int tilesAcross = (width + stripTileWidth - 1) / stripTileWidth;
int tilesDown = (height + stripTileHeight - 1) / stripTileHeight;
+
+ // TODO: Get number of extra samples not part of the rawType spec...
// TODO: If extrasamples, we might need to create a raster with more samples...
WritableRaster rowRaster = rawType.createBufferedImage(stripTileWidth, 1).getRaster();
+// WritableRaster rowRaster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, stripTileWidth, 1, 2, null).createWritableChild(0, 0, stripTileWidth, 1, 0, 0, new int[]{0});
Rectangle clip = new Rectangle(srcRegion);
int row = 0;
Boolean needsCSConversion = null;
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 08bbf63d..095c0ddb 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
@@ -93,6 +93,7 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest