8274735: javax.imageio.IIOException: Unsupported Image Type while processing a valid JPEG image
Reviewed-by: kizune, serb
This commit is contained in:
parent
70648a6a15
commit
f8a164915f
src/java.desktop/share/classes/com/sun/imageio/plugins
test/jdk/javax/imageio/plugins/jpeg/CMYK
@ -63,7 +63,7 @@ public final class SimpleCMYKColorSpace extends ColorSpace {
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return theInstance.hashCode();
|
||||
return System.identityHashCode(theInstance);
|
||||
}
|
||||
|
||||
public float[] toRGB(float[] colorvalue) {
|
||||
|
@ -35,6 +35,7 @@ import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.plugins.jpeg.JPEGImageReadParam;
|
||||
import javax.imageio.plugins.jpeg.JPEGQTable;
|
||||
import javax.imageio.plugins.jpeg.JPEGHuffmanTable;
|
||||
import com.sun.imageio.plugins.common.SimpleCMYKColorSpace;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
@ -164,6 +165,12 @@ public class JPEGImageReader extends ImageReader {
|
||||
/** If we need to post-convert in Java, convert with this op */
|
||||
private ColorConvertOp convert = null;
|
||||
|
||||
/** If reading CMYK as an Image, flip the bytes */
|
||||
private boolean invertCMYK = false;
|
||||
|
||||
/** Whether to read as a raster */
|
||||
private boolean readAsRaster = false;
|
||||
|
||||
/** The image we are going to fill */
|
||||
private BufferedImage image = null;
|
||||
|
||||
@ -938,6 +945,32 @@ public class JPEGImageReader extends ImageReader {
|
||||
ArrayList<ImageTypeProducer> list = new ArrayList<ImageTypeProducer>(1);
|
||||
|
||||
switch (colorSpaceCode) {
|
||||
case JPEG.JCS_YCCK:
|
||||
case JPEG.JCS_CMYK:
|
||||
// There's no standard CMYK ColorSpace in JDK so raw.getType()
|
||||
// will return null so skip that.
|
||||
// And we can't add RGB because the number of bands is different.
|
||||
// So need to create our own special that is 4 channels and uses
|
||||
// the iccCS ColorSpace based on profile data in the image, and
|
||||
// if there is none, on the internal CMYKColorSpace class
|
||||
if (iccCS == null) {
|
||||
iccCS = SimpleCMYKColorSpace.getInstance();
|
||||
}
|
||||
if (iccCS != null) {
|
||||
list.add(new ImageTypeProducer(colorSpaceCode) {
|
||||
@Override
|
||||
protected ImageTypeSpecifier produce() {
|
||||
int [] bands = {0, 1, 2, 3};
|
||||
return ImageTypeSpecifier.createInterleaved
|
||||
(iccCS,
|
||||
bands,
|
||||
DataBuffer.TYPE_BYTE,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
case JPEG.JCS_GRAYSCALE:
|
||||
list.add(raw);
|
||||
list.add(getImageType(JPEG.JCS_RGB));
|
||||
@ -1019,6 +1052,8 @@ public class JPEGImageReader extends ImageReader {
|
||||
int csType = cs.getType();
|
||||
convert = null;
|
||||
switch (outColorSpaceCode) {
|
||||
case JPEG.JCS_CMYK: // Its CMYK in the file
|
||||
break;
|
||||
case JPEG.JCS_GRAYSCALE: // Its gray in the file
|
||||
if (csType == ColorSpace.TYPE_RGB) { // We want RGB
|
||||
// IJG can do this for us more efficiently
|
||||
@ -1144,6 +1179,8 @@ public class JPEGImageReader extends ImageReader {
|
||||
private Raster readInternal(int imageIndex,
|
||||
ImageReadParam param,
|
||||
boolean wantRaster) throws IOException {
|
||||
|
||||
readAsRaster = wantRaster;
|
||||
readHeader(imageIndex, false);
|
||||
|
||||
WritableRaster imRas = null;
|
||||
@ -1186,6 +1223,16 @@ public class JPEGImageReader extends ImageReader {
|
||||
image = null;
|
||||
}
|
||||
|
||||
// Adobe seems to have decided that the bytes in CMYK JPEGs
|
||||
// should be stored inverted. So we need some extra logic to
|
||||
// flip them in that case. Don't flip for the raster case
|
||||
// so code that is reading these as rasters today won't
|
||||
// see a change in behaviour.
|
||||
invertCMYK =
|
||||
(!wantRaster &&
|
||||
((colorSpaceCode == JPEG.JCS_YCCK) ||
|
||||
(colorSpaceCode == JPEG.JCS_CMYK)));
|
||||
|
||||
// Create an intermediate 1-line Raster that will hold the decoded,
|
||||
// subsampled, clipped, band-selected image data in a single
|
||||
// byte-interleaved buffer. The above transformations
|
||||
@ -1363,6 +1410,21 @@ public class JPEGImageReader extends ImageReader {
|
||||
* After the copy, we notify update listeners.
|
||||
*/
|
||||
private void acceptPixels(int y, boolean progressive) {
|
||||
|
||||
/*
|
||||
* CMYK JPEGs seems to be universally inverted at the byte level.
|
||||
* Fix this here before storing.
|
||||
* For "compatibility" don't do this if the target is a raster.
|
||||
* Need to do this here in case the application is listening
|
||||
* for line-by-line updates to the image.
|
||||
*/
|
||||
if (invertCMYK) {
|
||||
byte[] data = ((DataBufferByte)raster.getDataBuffer()).getData();
|
||||
for (int i = 0, len = data.length; i < len; i++) {
|
||||
data[i] = (byte)(0x0ff - (data[i] & 0xff));
|
||||
}
|
||||
}
|
||||
|
||||
if (convert != null) {
|
||||
convert.filter(raster, raster);
|
||||
}
|
||||
|
@ -131,6 +131,7 @@ public class JPEGImageWriter extends ImageWriter {
|
||||
private int newAdobeTransform = JPEG.ADOBE_IMPOSSIBLE; // Change if needed
|
||||
private boolean writeDefaultJFIF = false;
|
||||
private boolean writeAdobe = false;
|
||||
private boolean invertCMYK = false;
|
||||
private JPEGMetadata metadata = null;
|
||||
|
||||
private boolean sequencePrepared = false;
|
||||
@ -654,6 +655,7 @@ public class JPEGImageWriter extends ImageWriter {
|
||||
newAdobeTransform = JPEG.ADOBE_IMPOSSIBLE; // Change if needed
|
||||
writeDefaultJFIF = false;
|
||||
writeAdobe = false;
|
||||
invertCMYK = false;
|
||||
|
||||
// By default we'll do no conversion:
|
||||
int inCsType = JPEG.JCS_UNKNOWN;
|
||||
@ -807,6 +809,14 @@ public class JPEGImageWriter extends ImageWriter {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ColorSpace.TYPE_CMYK:
|
||||
outCsType = JPEG.JCS_CMYK;
|
||||
if (jfif != null) {
|
||||
ignoreJFIF = true;
|
||||
warningOccurred
|
||||
(WARNING_IMAGE_METADATA_JFIF_MISMATCH);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // else no dest, metadata, not an image. Defaults ok
|
||||
@ -1013,6 +1023,11 @@ public class JPEGImageWriter extends ImageWriter {
|
||||
System.out.println("outCsType: " + outCsType);
|
||||
}
|
||||
|
||||
invertCMYK =
|
||||
(!rasterOnly &&
|
||||
((outCsType == JPEG.JCS_YCCK) ||
|
||||
(outCsType == JPEG.JCS_CMYK)));
|
||||
|
||||
// Note that getData disables acceleration on buffer, but it is
|
||||
// just a 1-line intermediate data transfer buffer that does not
|
||||
// affect the acceleration of the source image.
|
||||
@ -1721,6 +1736,12 @@ public class JPEGImageWriter extends ImageWriter {
|
||||
srcBands);
|
||||
}
|
||||
raster.setRect(sourceLine);
|
||||
if (invertCMYK) {
|
||||
byte[] data = ((DataBufferByte)raster.getDataBuffer()).getData();
|
||||
for (int i = 0, len = data.length; i < len; i++) {
|
||||
data[i] = (byte)(0x0ff - (data[i] & 0xff));
|
||||
}
|
||||
}
|
||||
if ((y > 7) && (y%8 == 0)) { // Every 8 scanlines
|
||||
cbLock.lock();
|
||||
try {
|
||||
|
200
test/jdk/javax/imageio/plugins/jpeg/CMYK/CMYKJPEGTest.java
Normal file
200
test/jdk/javax/imageio/plugins/jpeg/CMYK/CMYKJPEGTest.java
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* This test verifies that using the built-in ImageI/O JPEG plugin that JPEG images
|
||||
* that are in a CMYK ColorSpace can be read into a BufferedImage using the convemience
|
||||
* APIS and that and the colours are properly interpreted.
|
||||
* Since there is no standard JDK CMYK ColorSpace, this requires that either the image
|
||||
* contain an ICC_Profile which can be used by the plugin to create an ICC_ColorSpace
|
||||
* or that the plugin provides a suitable default CMYK ColorSpace instance by some other means.
|
||||
*
|
||||
* The test further verifies that the resultant BufferedImage will be re-written as a CMYK
|
||||
* BufferedImage. It can do this so long as the BufferedImage has that CMYK ColorSpace
|
||||
* used by its ColorModel.
|
||||
*
|
||||
* The verification requires re-reading again the re-written image and checking the
|
||||
* re-read image still has a CMYK ColorSpace and the same colours.
|
||||
*
|
||||
* Optionally - not for use in the test harness - the test can be passed a parameter
|
||||
* -display to create a UI which renders all the images the test is
|
||||
* verifying so it can be manually verified
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8274735
|
||||
* @summary Verify CMYK JPEGs can be read and written
|
||||
*/
|
||||
|
||||
import java.awt.Color;
|
||||
import static java.awt.Color.*;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
public class CMYKJPEGTest {
|
||||
|
||||
static String[] fileNames = {
|
||||
"black_cmyk.jpg",
|
||||
"white_cmyk.jpg",
|
||||
"gray_cmyk.jpg",
|
||||
"red_cmyk.jpg",
|
||||
"blue_cmyk.jpg",
|
||||
"green_cmyk.jpg",
|
||||
"cyan_cmyk.jpg",
|
||||
"magenta_cmyk.jpg",
|
||||
"yellow_cmyk.jpg",
|
||||
};
|
||||
|
||||
static Color[] colors = {
|
||||
black,
|
||||
white,
|
||||
gray,
|
||||
red,
|
||||
blue,
|
||||
green,
|
||||
cyan,
|
||||
magenta,
|
||||
yellow,
|
||||
};
|
||||
|
||||
static boolean display;
|
||||
|
||||
static BufferedImage[] readImages;
|
||||
static BufferedImage[] writtenImages;
|
||||
static int imageIndex = 0;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
if (args.length > 0) {
|
||||
display = "-display".equals(args[0]);
|
||||
}
|
||||
|
||||
String sep = System.getProperty("file.separator");
|
||||
String dir = System.getProperty("test.src", ".");
|
||||
String prefix = dir+sep;
|
||||
|
||||
readImages = new BufferedImage[fileNames.length];
|
||||
writtenImages = new BufferedImage[fileNames.length];
|
||||
|
||||
for (String fileName : fileNames) {
|
||||
String color = fileName.replace("_cmyk.jpg", "");
|
||||
test(prefix+fileName, color, imageIndex++);
|
||||
}
|
||||
if (display) {
|
||||
SwingUtilities.invokeAndWait(() -> createUI());
|
||||
}
|
||||
}
|
||||
|
||||
static void test(String fileName, String color, int index)
|
||||
throws IOException {
|
||||
|
||||
readImages[index] = ImageIO.read(new File(fileName));
|
||||
verify(readImages[index], color, colors[index]);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(readImages[index], "jpg", baos);
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
writtenImages[index] = ImageIO.read(bais);
|
||||
verify(writtenImages[index], color, colors[index]);
|
||||
}
|
||||
|
||||
static void verify(BufferedImage img, String colorName, Color c) {
|
||||
ColorModel cm = img.getColorModel();
|
||||
int tc = cm.getNumComponents();
|
||||
int cc = cm.getNumColorComponents();
|
||||
if (cc != 4 || tc != 4) {
|
||||
throw new RuntimeException("Unexpected num comp for " + img);
|
||||
}
|
||||
|
||||
int rgb = img.getRGB(0,0);
|
||||
int c_red = c.getRed();
|
||||
int c_green = c.getGreen();
|
||||
int c_blue = c.getBlue();
|
||||
int i_red = (rgb & 0x0ff0000) >> 16;
|
||||
int i_green = (rgb & 0x000ff00) >> 8;
|
||||
int i_blue = (rgb & 0x00000ff);
|
||||
int tol = 16;
|
||||
if ((Math.abs(i_red - c_red) > tol) ||
|
||||
(Math.abs(i_green - c_green) > tol) ||
|
||||
(Math.abs(i_blue - c_blue) > tol))
|
||||
{
|
||||
System.err.println("red="+i_red+" green="+i_green+" blue="+i_blue);
|
||||
throw new RuntimeException("Too different " + img + " " + colorName + " " + c);
|
||||
}
|
||||
}
|
||||
|
||||
static class ImageComp extends JComponent {
|
||||
|
||||
BufferedImage img;
|
||||
|
||||
ImageComp(BufferedImage img) {
|
||||
this.img = img;
|
||||
}
|
||||
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension(img.getWidth(), img.getHeight());
|
||||
}
|
||||
|
||||
public Dimension getMinimumSize() {
|
||||
return getPreferredSize();
|
||||
}
|
||||
|
||||
public void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
g.drawImage(img, 0, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
static void createUI() {
|
||||
JFrame f = new JFrame("CMYK JPEG Test");
|
||||
JPanel p = new JPanel();
|
||||
p.setLayout(new GridLayout(3, colors.length, 10, 10));
|
||||
for (String s : fileNames) {
|
||||
p.add(new JLabel(s.replace("_cmyk.jpg", "")));
|
||||
}
|
||||
for (BufferedImage i : readImages) {
|
||||
p.add(new ImageComp(i));
|
||||
}
|
||||
for (BufferedImage i : writtenImages) {
|
||||
p.add(new ImageComp(i));
|
||||
}
|
||||
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
f.add(p);
|
||||
f.pack();
|
||||
f.setVisible(true);
|
||||
}
|
||||
}
|
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/black_cmyk.jpg
Normal file
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/black_cmyk.jpg
Normal file
Binary file not shown.
After ![]() (image error) Size: 220 B |
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/blue_cmyk.jpg
Normal file
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/blue_cmyk.jpg
Normal file
Binary file not shown.
After ![]() (image error) Size: 220 B |
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/cyan_cmyk.jpg
Normal file
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/cyan_cmyk.jpg
Normal file
Binary file not shown.
After ![]() (image error) Size: 220 B |
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/gray_cmyk.jpg
Normal file
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/gray_cmyk.jpg
Normal file
Binary file not shown.
After ![]() (image error) Size: 214 B |
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/green_cmyk.jpg
Normal file
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/green_cmyk.jpg
Normal file
Binary file not shown.
After ![]() (image error) Size: 220 B |
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/magenta_cmyk.jpg
Normal file
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/magenta_cmyk.jpg
Normal file
Binary file not shown.
After ![]() (image error) Size: 220 B |
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/red_cmyk.jpg
Normal file
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/red_cmyk.jpg
Normal file
Binary file not shown.
After ![]() (image error) Size: 220 B |
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/white_cmyk.jpg
Normal file
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/white_cmyk.jpg
Normal file
Binary file not shown.
After ![]() (image error) Size: 220 B |
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/yellow_cmyk.jpg
Normal file
BIN
test/jdk/javax/imageio/plugins/jpeg/CMYK/yellow_cmyk.jpg
Normal file
Binary file not shown.
After ![]() (image error) Size: 220 B |
Loading…
x
Reference in New Issue
Block a user