#267: Reads monochrome images with gray ICC profile.

This commit is contained in:
Harald Kuhr 2016-06-23 16:15:22 +02:00
parent 9a6096664e
commit f4b61820ac
5 changed files with 67 additions and 17 deletions

View File

@ -28,12 +28,14 @@
package com.twelvemonkeys.imageio.util;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.ImageTypeSpecifier;
import java.awt.color.ColorSpace;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.*;
import static com.twelvemonkeys.lang.Validate.isTrue;
import static com.twelvemonkeys.lang.Validate.notNull;
/**
* Factory class for creating {@code ImageTypeSpecifier}s.
@ -59,17 +61,9 @@ public final class ImageTypeSpecifiers {
final int transferType, boolean isAlphaPremultiplied) {
if (transferType == DataBuffer.TYPE_BYTE || transferType == DataBuffer.TYPE_USHORT) {
// ImageTypeSpecifier unconditionally uses bits == 32, we'll use a workaround for BYTE/USHORT types
if (colorSpace == null) {
throw new IllegalArgumentException("colorSpace == null!");
}
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!");
}
notNull(colorSpace, "colorSpace");
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");
int bits = transferType == DataBuffer.TYPE_BYTE ? 8 : 16;
@ -131,6 +125,34 @@ public final class ImageTypeSpecifiers {
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,
final byte[] blueLUT, final byte[] alphaLUT,
final int bits, final int dataType) {

View File

@ -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
public void testCreateIndexedByteArrays1to8() {
for (int bits = 1; bits <= 8; bits <<= 1) {

View File

@ -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)) {
return ImageTypeSpecifiers.createGrayscale(bitsPerSample, dataType);
}
else if (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4 || bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
// TODO: Should use packed format for 1/2/4
else if (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 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);
}

View File

@ -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-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/scan-mono-iccgray.tif"), new Dimension(2408, 3436)), // B/W, PackBits w/gray ICC profile
// 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_fill.tif"), new Dimension(6, 4)), // B/W, CCITT T4 1D