diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/color/ColorSpaces.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/color/ColorSpaces.java index 6720146e..7a408615 100644 --- a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/color/ColorSpaces.java +++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/color/ColorSpaces.java @@ -161,6 +161,11 @@ public final class ColorSpaces { if (cs == null) { cs = new ICC_ColorSpace(profile); + + // Validate the color space, to avoid caching bad color spaces + // Will throw IllegalArgumentException or CMMException if the profile is bad + cs.fromRGB(new float[] {1f, 0f, 0f}); + cache.put(key, cs); } @@ -195,7 +200,7 @@ public final class ColorSpaces { * @return {@code true} if known to be offending, {@code false} otherwise * @throws IllegalArgumentException if {@code profile} is {@code null} */ - public static boolean isOffendingColorProfile(final ICC_Profile profile) { + static boolean isOffendingColorProfile(final ICC_Profile profile) { Validate.notNull(profile, "profile"); // NOTE: @@ -213,6 +218,26 @@ public final class ColorSpaces { return data[ICC_Profile.icHdrRenderingIntent] != 0; } + /** + * Tests whether an ICC color profile is valid. + * Invalid profiles are known to cause problems for {@link java.awt.image.ColorConvertOp}. + *

+ * + * Note that this method only tests if a color conversion using this profile is known to fail. + * There's no guarantee that the color conversion will succeed even if this method returns {@code false}. + * + * + * @param profile the ICC color profile. May not be {@code null}. + * @return {@code profile} if valid. + * @throws IllegalArgumentException if {@code profile} is {@code null} + * @throws java.awt.color.CMMException if {@code profile} is invalid. + */ + public static ICC_Profile validateProfile(final ICC_Profile profile) { + createColorSpace(profile); // Creating a color space will fail if the profile is bad + + return profile; + } + /** * Returns the color space specified by the given color space constant. *

diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java index 2204e8af..16723bbb 100644 --- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java @@ -409,6 +409,7 @@ public class JPEGImageReader extends ImageReaderBase { if (DEBUG) { System.err.println("Converting from " + intendedCS + " to " + (image.getColorModel().getColorSpace().isCS_sRGB() ? "sRGB" : image.getColorModel().getColorSpace())); } + convert = new ColorConvertOp(intendedCS, image.getColorModel().getColorSpace(), null); } // Else, pass through with no conversion @@ -858,7 +859,7 @@ public class JPEGImageReader extends ImageReaderBase { return null; } - return readICCProfileSafe(stream); + return readICCProfileSafe(stream, allowBadIndexes); } else if (!segments.isEmpty()) { // NOTE: This is probably over-complicated, as I've never encountered ICC_PROFILE chunks out of order... @@ -905,15 +906,17 @@ public class JPEGImageReader extends ImageReaderBase { streams[badICC ? i : chunkNumber - 1] = stream; } - return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams)))); + return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams))), allowBadIndexes); } return null; } - private ICC_Profile readICCProfileSafe(final InputStream stream) throws IOException { + private ICC_Profile readICCProfileSafe(final InputStream stream, final boolean allowBadProfile) throws IOException { try { - return ICC_Profile.getInstance(stream); + ICC_Profile profile = ICC_Profile.getInstance(stream); + + return allowBadProfile ? profile : ColorSpaces.validateProfile(profile); } catch (RuntimeException e) { // NOTE: Throws either IllegalArgumentException or CMMException, depending on platform. @@ -1306,6 +1309,8 @@ public class JPEGImageReader extends ImageReaderBase { int subX = 1; int subY = 1; + int xOff = 0; + int yOff = 0; Rectangle roi = null; boolean metadata = false; boolean thumbnails = false; @@ -1318,8 +1323,16 @@ public class JPEGImageReader extends ImageReaderBase { String[] sub = args[++argIdx].split(","); try { - subX = Integer.parseInt(sub[0]); - subY = sub.length > 1 ? Integer.parseInt(sub[1]) : subX; + if (sub.length >= 4) { + subX = Integer.parseInt(sub[0]); + subY = Integer.parseInt(sub[1]); + xOff = Integer.parseInt(sub[2]); + yOff = Integer.parseInt(sub[3]); + } + else { + subX = Integer.parseInt(sub[0]); + subY = sub.length > 1 ? Integer.parseInt(sub[1]) : subX; + } } catch (NumberFormatException e) { System.err.println("Bad sub sampling (x,y): '" + args[argIdx] + "'"); @@ -1423,7 +1436,7 @@ public class JPEGImageReader extends ImageReaderBase { BufferedImage image; ImageReadParam param = reader.getDefaultReadParam(); if (subX > 1 || subY > 1 || roi != null) { - param.setSourceSubsampling(subX, subY, 0, 0); + param.setSourceSubsampling(subX, subY, xOff, yOff); param.setSourceRegion(roi); image = reader.getImageTypes(0).next().createBufferedImage((reader.getWidth(0) + subX - 1)/ subX, (reader.getHeight(0) + subY - 1) / subY); diff --git a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java index ee3dd3c6..63f982b3 100644 --- a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java +++ b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java @@ -86,6 +86,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest