TMI-34: Handling of problematic Corbis RGB ICC profiles.

This commit is contained in:
Harald Kuhr 2014-02-06 22:45:35 +01:00
parent 14e12eb2c1
commit db259bff10
7 changed files with 71 additions and 2 deletions

View File

@ -86,6 +86,9 @@ public final class ColorSpaces {
/** A best-effort "generic" CMYK color space. Either read from disk or built-in. */ /** A best-effort "generic" CMYK color space. Either read from disk or built-in. */
public static final int CS_GENERIC_CMYK = 5001; public static final int CS_GENERIC_CMYK = 5001;
/** Value used instead of 'XYZ ' in problematic Corbis RGB Profiles */
private static final byte[] CORBIS_RGB_ALTERNATE_XYZ = new byte[] {0x17, (byte) 0xA5, 0x05, (byte) 0xB8};
// Weak references to hold the color spaces while cached // Weak references to hold the color spaces while cached
private static WeakReference<ICC_Profile> adobeRGB1998 = new WeakReference<ICC_Profile>(null); private static WeakReference<ICC_Profile> adobeRGB1998 = new WeakReference<ICC_Profile>(null);
private static WeakReference<ICC_Profile> genericCMYK = new WeakReference<ICC_Profile>(null); private static WeakReference<ICC_Profile> genericCMYK = new WeakReference<ICC_Profile>(null);
@ -135,9 +138,42 @@ public final class ColorSpaces {
} }
} }
// Special handling to detect problematic Corbis RGB ICC Profile.
// This makes sure tags that are expected to be of type 'XYZ ' really have this expected type.
// Should leave other ICC profiles unchanged.
if (fixProfileXYZTag(profile, ICC_Profile.icSigMediaWhitePointTag)) {
fixProfileXYZTag(profile, ICC_Profile.icSigRedColorantTag);
fixProfileXYZTag(profile, ICC_Profile.icSigGreenColorantTag);
fixProfileXYZTag(profile, ICC_Profile.icSigBlueColorantTag);
}
return getCachedOrCreateCS(profile, profileHeader); return getCachedOrCreateCS(profile, profileHeader);
} }
/**
* Fixes problematic 'XYZ ' tags in Corbis RGB profile.
*
* @return {@code true} if found and fixed, otherwise {@code false} for short-circuiting
* to avoid unnecessary array copying.
*/
private static boolean fixProfileXYZTag(ICC_Profile profile, final int tagSignature) {
byte[] data = profile.getData(tagSignature);
// The CMM expects 0x64 65 73 63 ('XYZ ') but is 0x17 A5 05 B8..?
if (data != null && Arrays.equals(Arrays.copyOfRange(data, 0, 4), CORBIS_RGB_ALTERNATE_XYZ)) {
data[0] = 'X';
data[1] = 'Y';
data[2] = 'Z';
data[3] = ' ';
profile.setData(tagSignature, data);
return true;
}
return false;
}
private static ICC_ColorSpace getInternalCS(final int profileCSType, final byte[] profileHeader) { private static ICC_ColorSpace getInternalCS(final int profileCSType, final byte[] profileHeader) {
if (profileCSType == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, sRGB.header)) { if (profileCSType == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, sRGB.header)) {
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_sRGB); return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_sRGB);

View File

@ -33,6 +33,7 @@ import org.junit.Test;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile; import java.awt.color.ICC_Profile;
import java.io.IOException;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -184,4 +185,15 @@ public class ColorSpacesTest {
public void testIsCS_sRGBNull() { public void testIsCS_sRGBNull() {
ColorSpaces.isCS_sRGB(null); ColorSpaces.isCS_sRGB(null);
} }
@Test
public void testCorbisRGBSpecialHandling() throws IOException {
ICC_Profile corbisRGB = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/Corbis RGB.icc"));
ICC_Profile corbisRGBFixed = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/Corbis RGB_fixed.icc"));
ICC_ColorSpace colorSpace = ColorSpaces.createColorSpace(corbisRGB);
assertNotNull(colorSpace);
assertArrayEquals(colorSpace.getProfile().getData(), corbisRGBFixed.getData());
}
} }

View File

@ -632,6 +632,8 @@ public class JPEGImageReader extends ImageReaderBase {
byte[] profileData = profile.getData(); // Need to clone entire profile, due to a JDK 7 bug byte[] profileData = profile.getData(); // Need to clone entire profile, due to a JDK 7 bug
if (profileData[ICC_Profile.icHdrRenderingIntent] == ICC_Profile.icPerceptual) { if (profileData[ICC_Profile.icHdrRenderingIntent] == ICC_Profile.icPerceptual) {
processWarningOccurred("ICC profile is Perceptual but Display class, treating as Display class");
intToBigEndian(ICC_Profile.icSigDisplayClass, profileData, ICC_Profile.icHdrDeviceClass); // Header is first intToBigEndian(ICC_Profile.icSigDisplayClass, profileData, ICC_Profile.icHdrDeviceClass); // Header is first
return ICC_Profile.getInstance(profileData); return ICC_Profile.getInstance(profileData);
@ -1374,10 +1376,10 @@ public class JPEGImageReader extends ImageReaderBase {
// start = System.currentTimeMillis(); // start = System.currentTimeMillis();
float aspect = reader.getAspectRatio(0); float aspect = reader.getAspectRatio(0);
if (aspect >= 1f) { if (aspect >= 1f) {
image = ImageUtil.createResampled(image, maxW, Math.round(maxW / aspect), Image.SCALE_DEFAULT); image = ImageUtil.createResampled(image, maxW, Math.round(maxW / aspect), Image.SCALE_SMOOTH);
} }
else { else {
image = ImageUtil.createResampled(image, Math.round(maxH * aspect), maxH, Image.SCALE_DEFAULT); image = ImageUtil.createResampled(image, Math.round(maxH * aspect), maxH, Image.SCALE_SMOOTH);
} }
// System.err.println("Scale time: " + (System.currentTimeMillis() - start) + " ms"); // System.err.println("Scale time: " + (System.currentTimeMillis() - start) + " ms");
} }

View File

@ -329,6 +329,25 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
verify(warningListener).warningOccurred(eq(reader), anyString()); verify(warningListener).warningOccurred(eq(reader), anyString());
} }
@Test
public void testCorbisRGB() throws IOException {
// Special case, throws exception below without special treatment
// java.awt.color.CMMException: General CMM error517
JPEGImageReader reader = createReader();
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/cmm-exception-corbis-rgb.jpg")));
assertEquals(512, reader.getWidth(0));
assertEquals(384, reader.getHeight(0));
BufferedImage image = reader.read(0);
assertNotNull(image);
assertEquals(512, image.getWidth());
assertEquals(384, image.getHeight());
reader.dispose();
}
@Test @Test
public void testHasThumbnailNoIFD1() throws IOException { public void testHasThumbnailNoIFD1() throws IOException {
JPEGImageReader reader = createReader(); JPEGImageReader reader = createReader();

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB