Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8041125

ColorConvertOp filter much slower in JDK 8 compared to JDK7

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: P3
    • Resolution: Fixed
    • Affects Version/s: 8, 9
    • Fix Version/s: 18
    • Component/s: client-libs
    • Labels:
    • Subcomponent:
    • Resolved In Build:
      b20
    • CPU:
      x86
    • OS:
      other

      Description

      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

        Attachments

        1. cmyk.icm
          708 kB
        2. sample.jpg
          sample.jpg
          63 kB
        3. ShowFilterRegressionInJava8.java
          8 kB

          Issue Links

            Activity

              People

              Assignee:
              serb Sergey Bylokhov
              Reporter:
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              10 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: