mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 04:25:29 -04:00
TMI-140: JPEG with corrupted ICC profile (new kind) can now be read.
This commit is contained in:
parent
c97ebae303
commit
bbaa3e1186
@ -161,6 +161,11 @@ public final class ColorSpaces {
|
|||||||
|
|
||||||
if (cs == null) {
|
if (cs == null) {
|
||||||
cs = new ICC_ColorSpace(profile);
|
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);
|
cache.put(key, cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +200,7 @@ public final class ColorSpaces {
|
|||||||
* @return {@code true} if known to be offending, {@code false} otherwise
|
* @return {@code true} if known to be offending, {@code false} otherwise
|
||||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
* @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");
|
Validate.notNull(profile, "profile");
|
||||||
|
|
||||||
// NOTE:
|
// NOTE:
|
||||||
@ -213,6 +218,26 @@ public final class ColorSpaces {
|
|||||||
return data[ICC_Profile.icHdrRenderingIntent] != 0;
|
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}.
|
||||||
|
* <p />
|
||||||
|
* <em>
|
||||||
|
* 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}.
|
||||||
|
* </em>
|
||||||
|
*
|
||||||
|
* @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.
|
* Returns the color space specified by the given color space constant.
|
||||||
* <p />
|
* <p />
|
||||||
|
@ -409,6 +409,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.err.println("Converting from " + intendedCS + " to " + (image.getColorModel().getColorSpace().isCS_sRGB() ? "sRGB" : image.getColorModel().getColorSpace()));
|
System.err.println("Converting from " + intendedCS + " to " + (image.getColorModel().getColorSpace().isCS_sRGB() ? "sRGB" : image.getColorModel().getColorSpace()));
|
||||||
}
|
}
|
||||||
|
|
||||||
convert = new ColorConvertOp(intendedCS, image.getColorModel().getColorSpace(), null);
|
convert = new ColorConvertOp(intendedCS, image.getColorModel().getColorSpace(), null);
|
||||||
}
|
}
|
||||||
// Else, pass through with no conversion
|
// Else, pass through with no conversion
|
||||||
@ -858,7 +859,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return readICCProfileSafe(stream);
|
return readICCProfileSafe(stream, allowBadIndexes);
|
||||||
}
|
}
|
||||||
else if (!segments.isEmpty()) {
|
else if (!segments.isEmpty()) {
|
||||||
// NOTE: This is probably over-complicated, as I've never encountered ICC_PROFILE chunks out of order...
|
// 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;
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ICC_Profile readICCProfileSafe(final InputStream stream) throws IOException {
|
private ICC_Profile readICCProfileSafe(final InputStream stream, final boolean allowBadProfile) throws IOException {
|
||||||
try {
|
try {
|
||||||
return ICC_Profile.getInstance(stream);
|
ICC_Profile profile = ICC_Profile.getInstance(stream);
|
||||||
|
|
||||||
|
return allowBadProfile ? profile : ColorSpaces.validateProfile(profile);
|
||||||
}
|
}
|
||||||
catch (RuntimeException e) {
|
catch (RuntimeException e) {
|
||||||
// NOTE: Throws either IllegalArgumentException or CMMException, depending on platform.
|
// NOTE: Throws either IllegalArgumentException or CMMException, depending on platform.
|
||||||
@ -1306,6 +1309,8 @@ public class JPEGImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
int subX = 1;
|
int subX = 1;
|
||||||
int subY = 1;
|
int subY = 1;
|
||||||
|
int xOff = 0;
|
||||||
|
int yOff = 0;
|
||||||
Rectangle roi = null;
|
Rectangle roi = null;
|
||||||
boolean metadata = false;
|
boolean metadata = false;
|
||||||
boolean thumbnails = false;
|
boolean thumbnails = false;
|
||||||
@ -1318,8 +1323,16 @@ public class JPEGImageReader extends ImageReaderBase {
|
|||||||
String[] sub = args[++argIdx].split(",");
|
String[] sub = args[++argIdx].split(",");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
subX = Integer.parseInt(sub[0]);
|
if (sub.length >= 4) {
|
||||||
subY = sub.length > 1 ? Integer.parseInt(sub[1]) : subX;
|
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) {
|
catch (NumberFormatException e) {
|
||||||
System.err.println("Bad sub sampling (x,y): '" + args[argIdx] + "'");
|
System.err.println("Bad sub sampling (x,y): '" + args[argIdx] + "'");
|
||||||
@ -1423,7 +1436,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
|||||||
BufferedImage image;
|
BufferedImage image;
|
||||||
ImageReadParam param = reader.getDefaultReadParam();
|
ImageReadParam param = reader.getDefaultReadParam();
|
||||||
if (subX > 1 || subY > 1 || roi != null) {
|
if (subX > 1 || subY > 1 || roi != null) {
|
||||||
param.setSourceSubsampling(subX, subY, 0, 0);
|
param.setSourceSubsampling(subX, subY, xOff, yOff);
|
||||||
param.setSourceRegion(roi);
|
param.setSourceRegion(roi);
|
||||||
|
|
||||||
image = reader.getImageTypes(0).next().createBufferedImage((reader.getWidth(0) + subX - 1)/ subX, (reader.getHeight(0) + subY - 1) / subY);
|
image = reader.getImageTypes(0).next().createBufferedImage((reader.getWidth(0) + subX - 1)/ subX, (reader.getHeight(0) + subY - 1) / subY);
|
||||||
|
@ -86,6 +86,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
|||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
new TestData(getClassLoaderResource("/jpeg/cmm-exception-adobe-rgb.jpg"), new Dimension(626, 76)),
|
new TestData(getClassLoaderResource("/jpeg/cmm-exception-adobe-rgb.jpg"), new Dimension(626, 76)),
|
||||||
new TestData(getClassLoaderResource("/jpeg/cmm-exception-srgb.jpg"), new Dimension(1800, 1200)),
|
new TestData(getClassLoaderResource("/jpeg/cmm-exception-srgb.jpg"), new Dimension(1800, 1200)),
|
||||||
|
new TestData(getClassLoaderResource("/jpeg/corrupted-icc-srgb.jpg"), new Dimension(1024, 685)),
|
||||||
new TestData(getClassLoaderResource("/jpeg/gray-sample.jpg"), new Dimension(386, 396)),
|
new TestData(getClassLoaderResource("/jpeg/gray-sample.jpg"), new Dimension(386, 396)),
|
||||||
new TestData(getClassLoaderResource("/jpeg/cmyk-sample.jpg"), new Dimension(160, 227)),
|
new TestData(getClassLoaderResource("/jpeg/cmyk-sample.jpg"), new Dimension(160, 227)),
|
||||||
new TestData(getClassLoaderResource("/jpeg/cmyk-sample-multiple-chunk-icc.jpg"), new Dimension(2707, 3804)),
|
new TestData(getClassLoaderResource("/jpeg/cmyk-sample-multiple-chunk-icc.jpg"), new Dimension(2707, 3804)),
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 242 KiB |
Loading…
x
Reference in New Issue
Block a user