mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-10-04 11:26:44 -04:00
#267: Reads monochrome images with gray ICC profile.
This commit is contained in:
@@ -28,12 +28,14 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.util;
|
package com.twelvemonkeys.imageio.util;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.ColorSpace;
|
||||||
import java.awt.image.ColorModel;
|
import java.awt.image.*;
|
||||||
import java.awt.image.DataBuffer;
|
|
||||||
import java.awt.image.DirectColorModel;
|
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||||
import java.awt.image.IndexColorModel;
|
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory class for creating {@code ImageTypeSpecifier}s.
|
* Factory class for creating {@code ImageTypeSpecifier}s.
|
||||||
@@ -59,17 +61,9 @@ public final class ImageTypeSpecifiers {
|
|||||||
final int transferType, boolean isAlphaPremultiplied) {
|
final int transferType, boolean isAlphaPremultiplied) {
|
||||||
if (transferType == DataBuffer.TYPE_BYTE || transferType == DataBuffer.TYPE_USHORT) {
|
if (transferType == DataBuffer.TYPE_BYTE || transferType == DataBuffer.TYPE_USHORT) {
|
||||||
// ImageTypeSpecifier unconditionally uses bits == 32, we'll use a workaround for BYTE/USHORT types
|
// ImageTypeSpecifier unconditionally uses bits == 32, we'll use a workaround for BYTE/USHORT types
|
||||||
if (colorSpace == null) {
|
notNull(colorSpace, "colorSpace");
|
||||||
throw new IllegalArgumentException("colorSpace == null!");
|
isTrue(colorSpace.getType() == ColorSpace.TYPE_RGB, colorSpace, "ColorSpace must be TYPE_RGB");
|
||||||
}
|
isTrue(redMask != 0 || greenMask != 0 || blueMask != 0 || alphaMask != 0, "No mask has at least 1 bit set");
|
||||||
|
|
||||||
if (colorSpace.getType() != ColorSpace.TYPE_RGB) {
|
|
||||||
throw new IllegalArgumentException("colorSpace is not of type TYPE_RGB!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redMask == 0 && greenMask == 0 && blueMask == 0 && alphaMask == 0) {
|
|
||||||
throw new IllegalArgumentException("No mask has at least 1 bit set!");
|
|
||||||
}
|
|
||||||
|
|
||||||
int bits = transferType == DataBuffer.TYPE_BYTE ? 8 : 16;
|
int bits = transferType == DataBuffer.TYPE_BYTE ? 8 : 16;
|
||||||
|
|
||||||
@@ -131,6 +125,34 @@ public final class ImageTypeSpecifiers {
|
|||||||
return ImageTypeSpecifier.createGrayscale(bits, dataType, false, isAlphaPremultiplied);
|
return ImageTypeSpecifier.createGrayscale(bits, dataType, false, isAlphaPremultiplied);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ImageTypeSpecifier createPackedGrayscale(final ColorSpace colorSpace, final int bits, final int dataType) {
|
||||||
|
notNull(colorSpace, "colorSpace");
|
||||||
|
isTrue(colorSpace.getType() == ColorSpace.TYPE_GRAY, colorSpace, "ColorSpace must be TYPE_GRAY");
|
||||||
|
isTrue(bits == 1 || bits == 2 || bits == 4, bits, "bits must be 1, 2, or 4: %s");
|
||||||
|
isTrue(dataType == DataBuffer.TYPE_BYTE, dataType, "dataType must be TYPE_BYTE: %s");
|
||||||
|
|
||||||
|
int numEntries = 1 << bits;
|
||||||
|
|
||||||
|
byte[] arr = new byte[numEntries];
|
||||||
|
byte[] arg = new byte[numEntries];
|
||||||
|
byte[] arb = new byte[numEntries];
|
||||||
|
|
||||||
|
// Scale array values according to color profile..
|
||||||
|
for (int i = 0; i < numEntries; i++) {
|
||||||
|
float[] gray = new float[]{i / (float) (numEntries - 1)};
|
||||||
|
float[] rgb = colorSpace.toRGB(gray);
|
||||||
|
|
||||||
|
arr[i] = (byte) (rgb[0] * 255);
|
||||||
|
arg[i] = (byte) (rgb[1] * 255);
|
||||||
|
arb[i] = (byte) (rgb[2]* 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorModel colorModel = new IndexColorModel(bits, numEntries, arr, arg, arb);
|
||||||
|
SampleModel sampleModel = new MultiPixelPackedSampleModel(dataType, 1, 1, bits);
|
||||||
|
|
||||||
|
return new ImageTypeSpecifier(colorModel, sampleModel);
|
||||||
|
}
|
||||||
|
|
||||||
public static ImageTypeSpecifier createIndexed(final byte[] redLUT, final byte[] greenLUT,
|
public static ImageTypeSpecifier createIndexed(final byte[] redLUT, final byte[] greenLUT,
|
||||||
final byte[] blueLUT, final byte[] alphaLUT,
|
final byte[] blueLUT, final byte[] alphaLUT,
|
||||||
final int bits, final int dataType) {
|
final int bits, final int dataType) {
|
||||||
|
@@ -463,6 +463,30 @@ public class ImageTypeSpecifiersTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatePackedGrayscale1() {
|
||||||
|
assertEquals(
|
||||||
|
ImageTypeSpecifier.createGrayscale(1, DataBuffer.TYPE_BYTE, false),
|
||||||
|
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 1, DataBuffer.TYPE_BYTE)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatePackedGrayscale2() {
|
||||||
|
assertEquals(
|
||||||
|
ImageTypeSpecifier.createGrayscale(2, DataBuffer.TYPE_BYTE, false),
|
||||||
|
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 2, DataBuffer.TYPE_BYTE)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatePackedGrayscale4() {
|
||||||
|
assertEquals(
|
||||||
|
ImageTypeSpecifier.createGrayscale(4, DataBuffer.TYPE_BYTE, false),
|
||||||
|
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 4, DataBuffer.TYPE_BYTE)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateIndexedByteArrays1to8() {
|
public void testCreateIndexedByteArrays1to8() {
|
||||||
for (int bits = 1; bits <= 8; bits <<= 1) {
|
for (int bits = 1; bits <= 8; bits <<= 1) {
|
||||||
|
@@ -364,8 +364,11 @@ public class TIFFImageReader extends ImageReaderBase {
|
|||||||
if (cs == ColorSpace.getInstance(ColorSpace.CS_GRAY) && (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4 || bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32)) {
|
if (cs == ColorSpace.getInstance(ColorSpace.CS_GRAY) && (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4 || bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32)) {
|
||||||
return ImageTypeSpecifiers.createGrayscale(bitsPerSample, dataType);
|
return ImageTypeSpecifiers.createGrayscale(bitsPerSample, dataType);
|
||||||
}
|
}
|
||||||
else if (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4 || bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
|
else if (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4 ) {
|
||||||
// TODO: Should use packed format for 1/2/4
|
// Use packed format for 1/2/4 bits
|
||||||
|
return ImageTypeSpecifiers.createPackedGrayscale(cs, bitsPerSample, dataType);
|
||||||
|
}
|
||||||
|
else if (bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
|
||||||
return ImageTypeSpecifiers.createInterleaved(cs, new int[] {0}, dataType, false, false);
|
return ImageTypeSpecifiers.createInterleaved(cs, new int[] {0}, dataType, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -90,6 +90,7 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
|||||||
new TestData(getClassLoaderResource("/tiff/lzw-rgba-padded-icc.tif"), new Dimension(19, 11)), // RGBA, LZW compression with padded ICC profile
|
new TestData(getClassLoaderResource("/tiff/lzw-rgba-padded-icc.tif"), new Dimension(19, 11)), // RGBA, LZW compression with padded ICC profile
|
||||||
new TestData(getClassLoaderResource("/tiff/lzw-rgba-4444.tif"), new Dimension(64, 64)), // RGBA, LZW compression with UINT 4/4/4/4 + gray 2/2
|
new TestData(getClassLoaderResource("/tiff/lzw-rgba-4444.tif"), new Dimension(64, 64)), // RGBA, LZW compression with UINT 4/4/4/4 + gray 2/2
|
||||||
new TestData(getClassLoaderResource("/tiff/lzw-buffer-overflow.tif"), new Dimension(5, 49)), // RGBA, LZW compression, will throw IOOBE if small buffer
|
new TestData(getClassLoaderResource("/tiff/lzw-buffer-overflow.tif"), new Dimension(5, 49)), // RGBA, LZW compression, will throw IOOBE if small buffer
|
||||||
|
new TestData(getClassLoaderResource("/tiff/scan-mono-iccgray.tif"), new Dimension(2408, 3436)), // B/W, PackBits w/gray ICC profile
|
||||||
// CCITT
|
// CCITT
|
||||||
new TestData(getClassLoaderResource("/tiff/ccitt/group3_1d.tif"), new Dimension(6, 4)), // B/W, CCITT T4 1D
|
new TestData(getClassLoaderResource("/tiff/ccitt/group3_1d.tif"), new Dimension(6, 4)), // B/W, CCITT T4 1D
|
||||||
new TestData(getClassLoaderResource("/tiff/ccitt/group3_1d_fill.tif"), new Dimension(6, 4)), // B/W, CCITT T4 1D
|
new TestData(getClassLoaderResource("/tiff/ccitt/group3_1d_fill.tif"), new Dimension(6, 4)), // B/W, CCITT T4 1D
|
||||||
|
BIN
imageio/imageio-tiff/src/test/resources/tiff/scan-mono-iccgray.tif
Executable file
BIN
imageio/imageio-tiff/src/test/resources/tiff/scan-mono-iccgray.tif
Executable file
Binary file not shown.
Reference in New Issue
Block a user