-
Bug
-
Resolution: Fixed
-
P3
-
8, 9, 11, 17
-
b20
-
x86
-
other
FULL PRODUCT VERSION :
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
JDK1.8.0 (we have seen same issue on Windows/Linux and Mac releases of JKD
A DESCRIPTION OF THE PROBLEM :
We spotted a substantial slow down in our code when we updated to JDK1.8 we traced this to the ColorConvertOp filter() method which is much slower in new JDK and have written example to demonstrate this regression.
REGRESSION. Last worked in version 7u25
ADDITIONAL REGRESSION INFORMATION:
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run example code
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
typical Java7 run:
BufferedImage@5220c1b: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
With filter Takes 202 milliseconds
BufferedImage@77383942: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
In pure Java Takes 1450 milliseconds
typical Java8 run
BufferedImage@4459eb14: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
With filter Takes 672 milliseconds
BufferedImage@45283ce2: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
In pure Java Takes 650 milliseconds
ACTUAL -
filter() method is 3 times slower in JAva8 compared with Java7 on identical data
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
It requires 2 files which I can supply if you email markstephens@idrsolutions.com
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import org.jpedal.color.ColorSpaces;
import org.jpedal.color.DeviceCMYKColorSpace;
/*
* ---------------
* ShowFilterRegressionInJava8.java
* ---------------
*/
/**
*
*/
public class ShowFilterRegressionInJava8 {
/**
* main method to run the software as standalone application
*/
public static void main(String[] args) {
long start = System.currentTimeMillis();
convert(true);
System.out.println("With filter Takes " + ((System.currentTimeMillis() - start)) + " milliseconds");
start = System.currentTimeMillis();
convert(false);
System.out.println("In pure Java Takes " + ((System.currentTimeMillis() - start)) + " milliseconds");
/** typical Java7 run:
BufferedImage@5220c1b: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
With filter Takes 202 milliseconds
BufferedImage@77383942: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
In pure Java Takes 1450 milliseconds
/**/
/**typical Java8 run
BufferedImage@4459eb14: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
With filter Takes 672 milliseconds
BufferedImage@45283ce2: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
In pure Java Takes 650 milliseconds
/**/
}
/**
* open non-RGB jpeg and convert into rbg Image
* @param useFilter
*/
public static void convert(boolean useFilter) {
String file = "/Users/markee/Desktop/sample.jpg";
File f = new File(file);
byte[] data = new byte[(int) f.length()];
try {
java.io.FileInputStream a = new java.io.FileInputStream(file);
a.read(data);
a.close();
} catch (Exception e) {
e.printStackTrace();
}
ByteArrayInputStream in = null;
ImageReader iir = null;
ImageInputStream iin = null;
BufferedImage image;
try {
ICC_Profile p = ICC_Profile.getInstance("/Users/markee/Desktop/cmyk.icm");
ICC_ColorSpace cs = new ICC_ColorSpace(p);
ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorConvertOp CSToRGB = new ColorConvertOp(cs, rgbCS, ColorSpaces.hints);
in = new ByteArrayInputStream(data);
try {
Iterator iterator = ImageIO.getImageReadersByFormatName("JPEG");
while (iterator.hasNext()) {
Object o = iterator.next();
iir = (ImageReader) o;
if (iir.canReadRaster()) {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
//iir = (ImageReader)ImageIO.getImageReadersByFormatName("JPEG").next();
ImageIO.setUseCache(false);
iin = ImageIO.createImageInputStream((in));
iir.setInput(iin, true); //new MemoryCacheImageInputStream(in));
Raster ras = iir.readRaster(0, null);
int w = ras.getWidth();
int h = ras.getHeight();
if (useFilter) {
ColorModel rgbModel
= new ComponentColorModel(
rgbCS,
new int[]{8, 8, 8},
false,
false,
ColorModel.OPAQUE,
DataBuffer.TYPE_BYTE);
/**
* generate the rgb image
*/
WritableRaster rgbRaster = rgbModel.createCompatibleWritableRaster(w, h);
CSToRGB.filter(ras, rgbRaster);
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
image.setData(rgbRaster);
} else {
image = convertCMYKImageToRGB(((DataBufferByte) ras.getDataBuffer()).getData(), w, h);
}
if (image != null) {
System.out.println(image);
}
} catch (Exception ee) {
ee.printStackTrace();
} catch (Error err) {
err.printStackTrace();
} finally {
try {
in.close();
iir.dispose();
iin.close();
} catch (Exception ee) {
ee.printStackTrace();
}
}
}
/**
* convert manually for comparison
*/
public static BufferedImage convertCMYKImageToRGB(final byte[] buffer, final int w, final int h) throws IOException {
/**
* set colorspaces and color models using profiles if set
*/
final ColorSpace CMYK = DeviceCMYKColorSpace.getColorSpaceInstance();
final int pixelCount = w * h * 4;
int C, M, Y, K, lastC = -1, lastM = -1, lastY = -1, lastK = -1;
int j = 0;
float[] RGB = new float[]{0f, 0f, 0f};
//turn YCC in Buffer to CYM using profile
for (int i = 0; i < pixelCount; i = i + 4) {
C = (buffer[i] & 255);
M = (buffer[i + 1] & 255);
Y = (buffer[i + 2] & 255);
K = (buffer[i + 3] & 255);
// System.out.println(C+" "+M+" "+Y+" "+K);
if (C == lastC && M == lastM && Y == lastY && K == lastK) {
//no change so use last value
} else { //new value
RGB = CMYK.toRGB(new float[]{C / 255f, M / 255f, Y / 255f, K / 255f});
//flag so we can just reuse if next value the same
lastC = C;
lastM = M;
lastY = Y;
lastK = K;
}
//put back as CMY
buffer[j] = (byte) (RGB[0] * 255f);
buffer[j + 1] = (byte) (RGB[1] * 255f);
buffer[j + 2] = (byte) (RGB[2] * 255f);
j = j + 3;
}
/**
* create CMYK raster from buffer
*/
final Raster raster = Raster.createInterleavedRaster(new DataBufferByte(buffer, j), w, h, w * 3, 3, new int[]{0, 1, 2}, null);
//data now sRGB so create image
final BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
image.setData(raster);
return image;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
I have included code to convert by hand which is much faster on Java8
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
JDK1.8.0 (we have seen same issue on Windows/Linux and Mac releases of JKD
A DESCRIPTION OF THE PROBLEM :
We spotted a substantial slow down in our code when we updated to JDK1.8 we traced this to the ColorConvertOp filter() method which is much slower in new JDK and have written example to demonstrate this regression.
REGRESSION. Last worked in version 7u25
ADDITIONAL REGRESSION INFORMATION:
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run example code
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
typical Java7 run:
BufferedImage@5220c1b: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
With filter Takes 202 milliseconds
BufferedImage@77383942: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
In pure Java Takes 1450 milliseconds
typical Java8 run
BufferedImage@4459eb14: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
With filter Takes 672 milliseconds
BufferedImage@45283ce2: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
In pure Java Takes 650 milliseconds
ACTUAL -
filter() method is 3 times slower in JAva8 compared with Java7 on identical data
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
It requires 2 files which I can supply if you email markstephens@idrsolutions.com
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import org.jpedal.color.ColorSpaces;
import org.jpedal.color.DeviceCMYKColorSpace;
/*
* ---------------
* ShowFilterRegressionInJava8.java
* ---------------
*/
/**
*
*/
public class ShowFilterRegressionInJava8 {
/**
* main method to run the software as standalone application
*/
public static void main(String[] args) {
long start = System.currentTimeMillis();
convert(true);
System.out.println("With filter Takes " + ((System.currentTimeMillis() - start)) + " milliseconds");
start = System.currentTimeMillis();
convert(false);
System.out.println("In pure Java Takes " + ((System.currentTimeMillis() - start)) + " milliseconds");
/** typical Java7 run:
BufferedImage@5220c1b: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
With filter Takes 202 milliseconds
BufferedImage@77383942: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
In pure Java Takes 1450 milliseconds
/**/
/**typical Java8 run
BufferedImage@4459eb14: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
With filter Takes 672 milliseconds
BufferedImage@45283ce2: type = 1 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 IntegerInterleavedRaster: width = 625 height = 843 #Bands = 3 xOff = 0 yOff = 0 dataOffset[0] 0
In pure Java Takes 650 milliseconds
/**/
}
/**
* open non-RGB jpeg and convert into rbg Image
* @param useFilter
*/
public static void convert(boolean useFilter) {
String file = "/Users/markee/Desktop/sample.jpg";
File f = new File(file);
byte[] data = new byte[(int) f.length()];
try {
java.io.FileInputStream a = new java.io.FileInputStream(file);
a.read(data);
a.close();
} catch (Exception e) {
e.printStackTrace();
}
ByteArrayInputStream in = null;
ImageReader iir = null;
ImageInputStream iin = null;
BufferedImage image;
try {
ICC_Profile p = ICC_Profile.getInstance("/Users/markee/Desktop/cmyk.icm");
ICC_ColorSpace cs = new ICC_ColorSpace(p);
ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorConvertOp CSToRGB = new ColorConvertOp(cs, rgbCS, ColorSpaces.hints);
in = new ByteArrayInputStream(data);
try {
Iterator iterator = ImageIO.getImageReadersByFormatName("JPEG");
while (iterator.hasNext()) {
Object o = iterator.next();
iir = (ImageReader) o;
if (iir.canReadRaster()) {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
//iir = (ImageReader)ImageIO.getImageReadersByFormatName("JPEG").next();
ImageIO.setUseCache(false);
iin = ImageIO.createImageInputStream((in));
iir.setInput(iin, true); //new MemoryCacheImageInputStream(in));
Raster ras = iir.readRaster(0, null);
int w = ras.getWidth();
int h = ras.getHeight();
if (useFilter) {
ColorModel rgbModel
= new ComponentColorModel(
rgbCS,
new int[]{8, 8, 8},
false,
false,
ColorModel.OPAQUE,
DataBuffer.TYPE_BYTE);
/**
* generate the rgb image
*/
WritableRaster rgbRaster = rgbModel.createCompatibleWritableRaster(w, h);
CSToRGB.filter(ras, rgbRaster);
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
image.setData(rgbRaster);
} else {
image = convertCMYKImageToRGB(((DataBufferByte) ras.getDataBuffer()).getData(), w, h);
}
if (image != null) {
System.out.println(image);
}
} catch (Exception ee) {
ee.printStackTrace();
} catch (Error err) {
err.printStackTrace();
} finally {
try {
in.close();
iir.dispose();
iin.close();
} catch (Exception ee) {
ee.printStackTrace();
}
}
}
/**
* convert manually for comparison
*/
public static BufferedImage convertCMYKImageToRGB(final byte[] buffer, final int w, final int h) throws IOException {
/**
* set colorspaces and color models using profiles if set
*/
final ColorSpace CMYK = DeviceCMYKColorSpace.getColorSpaceInstance();
final int pixelCount = w * h * 4;
int C, M, Y, K, lastC = -1, lastM = -1, lastY = -1, lastK = -1;
int j = 0;
float[] RGB = new float[]{0f, 0f, 0f};
//turn YCC in Buffer to CYM using profile
for (int i = 0; i < pixelCount; i = i + 4) {
C = (buffer[i] & 255);
M = (buffer[i + 1] & 255);
Y = (buffer[i + 2] & 255);
K = (buffer[i + 3] & 255);
// System.out.println(C+" "+M+" "+Y+" "+K);
if (C == lastC && M == lastM && Y == lastY && K == lastK) {
//no change so use last value
} else { //new value
RGB = CMYK.toRGB(new float[]{C / 255f, M / 255f, Y / 255f, K / 255f});
//flag so we can just reuse if next value the same
lastC = C;
lastM = M;
lastY = Y;
lastK = K;
}
//put back as CMY
buffer[j] = (byte) (RGB[0] * 255f);
buffer[j + 1] = (byte) (RGB[1] * 255f);
buffer[j + 2] = (byte) (RGB[2] * 255f);
j = j + 3;
}
/**
* create CMYK raster from buffer
*/
final Raster raster = Raster.createInterleavedRaster(new DataBufferByte(buffer, j), w, h, w * 3, 3, new int[]{0, 1, 2}, null);
//data now sRGB so create image
final BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
image.setData(raster);
return image;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
I have included code to convert by hand which is much faster on Java8