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

Mismatch in constants between JPEGImageWriter and jpeglib.h causes exception

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P4 P4
    • None
    • 7u67, 8
    • client-libs

      FULL PRODUCT VERSION :
      JDK 7 and JDK 8, all updates

      ADDITIONAL OS VERSION INFORMATION :
      Windows 7 32 and 64 bit, Linux, etc

      A DESCRIPTION OF THE PROBLEM :
      JPEGImageWriter fails writing CMYK / YCCK files because of a mismatch in constant definitions between Java code and native C code.

      com.sun.imageio.plugins.jpeg.JPEG defines a set of constants for JPEG color spaces:

          // IJG Color codes.
          public static final int JCS_UNKNOWN = 0; // error/unspecified
          public static final int JCS_GRAYSCALE = 1; // monochrome
          public static final int JCS_RGB = 2; // red/green/blue
          public static final int JCS_YCbCr = 3; // Y/Cb/Cr (also known as YUV)
          public static final int JCS_CMYK = 4; // C/M/Y/K
          public static final int JCS_YCC = 5; // PhotoYCC
          public static final int JCS_RGBA = 6; // RGB-Alpha
          public static final int JCS_YCbCrA = 7; // Y/Cb/Cr/Alpha
          // 8 and 9 were old "Legacy" codes which the old code never identified
          // on reading anyway. Support for writing them is being dropped, too.
          public static final int JCS_YCCA = 10; // PhotoYCC-Alpha
          public static final int JCS_YCCK = 11; // Y/Cb/Cr/K

      On the native code, these constants are defined in jpeglib.h as:

      typedef enum {
              JCS_UNKNOWN, /* error/unspecified */
              JCS_GRAYSCALE, /* monochrome */
              JCS_RGB, /* red/green/blue */
              JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */
              JCS_CMYK, /* C/M/Y/K */
              JCS_YCCK /* Y/Cb/Cr/K */
      } J_COLOR_SPACE;

      The values of these constants in C is implied by the order of the enum declaration, as 0, 1, 2, 3, 4 and 5.

      0 to 4 match with the Java constracts, but JCS_YCCK == 11 in Java and == 5 in the native code.

      When the native code is about to compress and write a JPEG file, it checks the color space to make sure that it is in range, and it does so by checking for:

      inCs > JCS_YCCK or outCs > JCS_YCCK

      If the Java code passes Java's JCS_YCCK (a valid value), this fails validation in the native code, even though it should be fine. i.e. Java passes 11, which is not < 5, and therefore an exception is thrown.

      The validation and exception in the C code happens in imageioJPEG.c ->Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage().

      Resolution is simple (and good programming practice), the enum definition in the native code should use the option to hard code the constant values, to make them match the constant values in the JPEG code:

      typedef enum {
              JCS_UNKNOWN = 0, /* error/unspecified */
              JCS_GRAYSCALE = 1, /* monochrome */
              JCS_RGB = 2, /* red/green/blue */
              JCS_YCbCr = 3, /* Y/Cb/Cr (also known as YUV) */
              JCS_CMYK = 4, /* C/M/Y/K */
              JCS_YCCK = 11 /* Y/Cb/Cr/K */
      } J_COLOR_SPACE;



      ADDITIONAL REGRESSION INFORMATION:
      This was working correctly in Java 6. The reason is that Java 6 native code had additional entries in the enum definition, and therefore the constant values matched those in Java.

      The values were still implied, but because they were the same number of constants in the enum, they happened to match.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Create a CMYK image in Java and try to write it as a JPEG using ImageIO. We can provide source code if necessary that works in Java 6, but not in Java 7 or Java 8.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The JPEG image should be written out without errors.
      ACTUAL -
      Exception is thrown by native method.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      IIOException: Invalid argument to native writeImage

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      When a BufferedImage is passed to this method of type CMYK:

          public static void saveCMYKJPEG(OutputStream outStream, BufferedImage image, int xdpi, int ydpi, float quality) throws PDFException, IOException
          {
              // get a JPEG writer
              Iterator encoders = ImageIO.getImageWritersByFormatName("JPEG");
              if (encoders.hasNext() == false)
              {
                  throw new PDFException ("No JPEG Writers Available.");
              }
              
              // Get the first writer
              ImageWriter writer = (ImageWriter)encoders.next();
              ImageWriteParam param = writer.getDefaultWriteParam();
              
              // Set compression quality
              param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
              param.setCompressionQuality(quality);

              ImageTypeSpecifier imageType = ImageTypeSpecifier.createFromRenderedImage(image);
              param.setDestinationType(imageType);
              
             // Write the image
              writer.setOutput(new MemoryCacheImageOutputStream (outStream));
              writer.write(null, new IIOImage(image.getRaster(), null, null), param);
          }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      None found.

            prr Philip Race
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: