From 4513b0c16622e385dc735bbe063290d635dff8ff Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Fri, 22 Sep 2023 11:52:11 +0200 Subject: [PATCH] Better support for ImageTypeSpecifiers with IndexColorModel. --- .../imageio/util/ImageTypeSpecifiers.java | 20 +++---- .../util/IndexedImageTypeSpecifier.java | 4 +- .../imageio/util/ImageTypeSpecifiersTest.java | 37 ++++++++++++- .../imageio/plugins/tiff/TIFFImageWriter.java | 7 +-- .../plugins/tiff/TIFFImageWriterTest.java | 54 +++++++++++++++++++ 5 files changed, 106 insertions(+), 16 deletions(-) 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 1a4fef8a..f6d554e4 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 @@ -257,18 +257,18 @@ public final class ImageTypeSpecifiers { return createFromIndexColorModel(new IndexColorModel(bits, colors.length, colors, 0, hasAlpha, transIndex, dataType)); } - public static ImageTypeSpecifier createFromIndexColorModel(final IndexColorModel pColorModel) { - return new IndexedImageTypeSpecifier(pColorModel); + public static ImageTypeSpecifier createFromIndexColorModel(final IndexColorModel colorModel) { + return new IndexedImageTypeSpecifier(colorModel); } - public static ImageTypeSpecifier createDiscreteAlphaIndexedFromIndexColorModel(final IndexColorModel pColorModel) { - ColorModel colorModel = new DiscreteAlphaIndexColorModel(pColorModel); - return new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1)); + public static ImageTypeSpecifier createDiscreteAlphaIndexedFromIndexColorModel(final IndexColorModel colorModel) { + ColorModel discreteAlphaIndexColorModel = new DiscreteAlphaIndexColorModel(colorModel); + return new ImageTypeSpecifier(discreteAlphaIndexColorModel, discreteAlphaIndexColorModel.createCompatibleSampleModel(1, 1)); } - public static ImageTypeSpecifier createDiscreteExtraSamplesIndexedFromIndexColorModel(final IndexColorModel pColorModel, int extraSamples, boolean hasAlpha) { - ColorModel colorModel = new DiscreteAlphaIndexColorModel(pColorModel, extraSamples, hasAlpha); - return new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1)); + public static ImageTypeSpecifier createDiscreteExtraSamplesIndexedFromIndexColorModel(final IndexColorModel colorModel, int extraSamples, boolean hasAlpha) { + ColorModel discreteAlphaIndexColorModel = new DiscreteAlphaIndexColorModel(colorModel, extraSamples, hasAlpha); + return new ImageTypeSpecifier(discreteAlphaIndexColorModel, discreteAlphaIndexColorModel.createCompatibleSampleModel(1, 1)); } public static ImageTypeSpecifier createFromRenderedImage(RenderedImage image) { @@ -279,7 +279,9 @@ public final class ImageTypeSpecifiers { if (image instanceof BufferedImage) { int bufferedImageType = ((BufferedImage) image).getType(); - if (bufferedImageType != BufferedImage.TYPE_CUSTOM) { + if (bufferedImageType != BufferedImage.TYPE_CUSTOM && + // Need to retain the actual palette in the color model for IndexColorModel + bufferedImageType != BufferedImage.TYPE_BYTE_BINARY && bufferedImageType != BufferedImage.TYPE_BYTE_INDEXED) { return createFromBufferedImageType(bufferedImageType); } } diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/IndexedImageTypeSpecifier.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/IndexedImageTypeSpecifier.java index 07dfb018..284d8ebb 100755 --- a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/IndexedImageTypeSpecifier.java +++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/util/IndexedImageTypeSpecifier.java @@ -52,12 +52,12 @@ final class IndexedImageTypeSpecifier extends ImageTypeSpecifier { } @Override - public final BufferedImage createBufferedImage(final int pWidth, final int pHeight) { + public BufferedImage createBufferedImage(final int width, final int height) { try { // This is a fix for the super-method, that first creates a sample model, and then // creates a raster from it, using Raster.createWritableRaster. The problem with // that approach, is that it always creates a TYPE_CUSTOM BufferedImage for indexed images. - WritableRaster raster = colorModel.createCompatibleWritableRaster(pWidth, pHeight); + WritableRaster raster = colorModel.createCompatibleWritableRaster(width, height); return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null); } catch (NegativeArraySizeException e) { 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 812a953e..af57ef75 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 @@ -39,6 +39,7 @@ import java.awt.color.*; import java.awt.image.*; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; public class ImageTypeSpecifiersTest { @@ -736,6 +737,7 @@ public class ImageTypeSpecifiersTest { ImageTypeSpecifier fromType = ImageTypeSpecifiers.createFromBufferedImageType(type); assertEquals(fromConstructor.getColorModel(), fromType.getColorModel()); + assertEquals(fromConstructor.getSampleModel(), fromType.getSampleModel()); } } @@ -747,13 +749,43 @@ public class ImageTypeSpecifiersTest { ImageTypeSpecifier fromImage = ImageTypeSpecifiers.createFromRenderedImage(image); assertEquals(fromConstructor.getColorModel(), fromImage.getColorModel()); + assertEquals(fromConstructor.getSampleModel(), fromImage.getSampleModel()); } } + @Test + public void testCreateFromRenderedImageIndexedBinaryShouldRetainPalette() { + IndexColorModel whiteIsZero = new IndexColorModel(1, 2, new int[]{0xFFFFFFFF, 0xFF000000}, 0, false, -1, DataBuffer.TYPE_BYTE); + + BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY, whiteIsZero); + ImageTypeSpecifier fromImage = ImageTypeSpecifiers.createFromRenderedImage(image); + + assertEquals(whiteIsZero, fromImage.getColorModel()); + assertSame(whiteIsZero, fromImage.getColorModel()); // Note: This can be relaxed to asserting the LUTs are equal + assertEquals(image.getSampleModel(), fromImage.getSampleModel()); + } + + @Test + public void testCreateFromRenderedImageIndexedShouldRetainPalette() { + IndexColorModel palette = new IndexColorModel(4, 16, new int[]{ + 0xFFFFFFFF, 0xFF999999, 0xFF666666, 0xFF333333, + 0xFF000000, 0xFF00202E, 0xFF003F5C, 0xFF2C4875, + 0xFF8A508F, 0xFFBC5090, 0xFFFF6361, 0xFFFF8531, + 0xFFFFA600, 0xFFFFD380, 0xFF74A892, 0xFF008585 + }, 0, false, -1, DataBuffer.TYPE_BYTE); + + BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED, palette); + ImageTypeSpecifier fromImage = ImageTypeSpecifiers.createFromRenderedImage(image); + + assertEquals(palette, fromImage.getColorModel()); + assertSame(palette, fromImage.getColorModel()); // Note: This can be relaxed to asserting the LUTs are equal + assertEquals(image.getSampleModel(), fromImage.getSampleModel()); + } + private static byte[] createByteLut(final int count) { byte[] lut = new byte[count]; for (int i = 0; i < count; i++) { - lut[i] = (byte) count; + lut[i] = (byte) (i * 255 / count); } return lut; } @@ -762,7 +794,8 @@ public class ImageTypeSpecifiersTest { int[] lut = new int[count]; for (int i = 0; i < count; i++) { - lut[i] = 0xff000000 | count << 16 | count << 8 | count; + int val = (i * 255 / count); + lut[i] = 0xff000000 | val << 16 | val << 8 | val; } return lut; diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java index 08cfb2d2..41fc6e4a 100644 --- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java +++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java @@ -40,6 +40,7 @@ import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry; import com.twelvemonkeys.imageio.metadata.tiff.TIFFWriter; import com.twelvemonkeys.imageio.stream.SubImageOutputStream; import com.twelvemonkeys.imageio.util.IIOUtil; +import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers; import com.twelvemonkeys.imageio.util.ProgressListenerBase; import com.twelvemonkeys.io.enc.EncoderStream; import com.twelvemonkeys.io.enc.PackBitsEncoder; @@ -149,9 +150,9 @@ public final class TIFFImageWriter extends ImageWriterBase { RenderedImage renderedImage = image.getRenderedImage(); SampleModel sampleModel = renderedImage.getSampleModel(); - // Can't use createFromRenderedImage in this case, as it does not consider palette for TYPE_BYTE_BINARY... - // TODO: Consider writing workaround in ImageTypeSpecifiers - ImageTypeSpecifier spec = new ImageTypeSpecifier(renderedImage); + // Need ImageTypeSpecifiers.createFromRenderedImage in this case, as the JDK method does not consider + // palette for TYPE_BYTE_BINARY/TYPE_BYTE_INDEXED... + ImageTypeSpecifier spec = ImageTypeSpecifiers.createFromRenderedImage(renderedImage); // TODO: Handle case where convertImageMetadata returns null, due to unknown metadata format, or reconsider if that's a valid case... TIFFImageMetadata metadata = image.getMetadata() != null diff --git a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java index dd80369a..a74a9e70 100644 --- a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java +++ b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterTest.java @@ -1304,6 +1304,60 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTest