Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4799903
Votes 11
Synopsis RFE:Decoding of JPEGs with more colorspaces, e.g., CMYK or externally-specified
Category jai:codec_imageio
Reported Against 1.1.1
Release Fixed
State 3-Accepted, request for enhancement
Priority: 5-Very Low
Related Bugs 6302282 , 6383709 , 5100094 , 6217565
Submit Date 08-JAN-2003
Description


DESCRIPTION OF THE PROBLEM :
Two types of JPEG that I have observed in PDFs cannot be created by JDK1.4.1 / Image I/O / JAI Image I/O Tools now in 1.0 beta.

In the first case, the color space is stored externally and is different from the one stored or not stored-but-assumed.   I'm not a JPEG expert but it appears that a RGB colorspace is imputed when the PDF wants, say, Lab with its control
over gamma correction.  This type of JPEG can be created but the colors are slightly off.

In the second case, the JPEG has 4-component ICC color profile that can be created with java.awt.color.ICC_ColorSpace(ICC_Profile.getInstance(InputStream))
and an "alternate" color space of CMYK.  QuarkXPress creates such JPEGs.  This kind of JPEG cannot be created at all.


This RFE requests:

1. supporting passed external color spaces (which may be user sublcasses of ColorSpace) for decoding and, secondarily, encoding of JPEGs, most logically through javax.imageio.plugins.jpeg.JPEGImageReadParam

2. decoding of 4-component color spaces



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I can supply sample data, but I believe the limitations are known.

As the "JPEG Metadata Format Specification and Usage Notes"
(.../j2sdk1.4.1/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html)

  states, "CMYK and YCCK images are currently not supported."

(Review ID: 179692) 
======================================================================
Posted Date : 2005-10-28 00:55:41.0
Work Around
Use code such as in this class to read the image data:

/*
 * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that Software is not designed,licensed or intended for use in
 * the design, construction, operation or maintenance of any nuclear facility.
 */

import java.awt.image.*;
import java.io.*;
import java.util.*;
import javax.imageio.*;
import javax.imageio.stream.*;
import javax.swing.*;

/**
 * Example of how to read JPEG images as Rasters.
 */
public class ReadJPEGRaster extends JFrame {
    public static void main(String[] args) throws Throwable {
        // Find a JPEG reader which supports reading Rasters.
        Iterator readers = ImageIO.getImageReadersByFormatName("JPEG");
        ImageReader reader = null;
        while(readers.hasNext()) {
            reader = (ImageReader)readers.next();
            if(reader.canReadRaster()) {
                break;
            }
        }

        // Set the input.
        ImageInputStream input =
            ImageIO.createImageInputStream(new File(args[0]));
        reader.setInput(input);

        // Create the image.
        BufferedImage image;
        try {
            // Try reading an image (including color conversion).
            image = reader.read(0);
        } catch(IIOException e) {
            // Try reading a Raster (no color conversion).
            Raster raster = reader.readRaster(0, null);

            // Arbitrarily select a BufferedImage type.
            int imageType;
            switch(raster.getNumBands()) {
            case 1:
                imageType = BufferedImage.TYPE_BYTE_GRAY;
                break;
            case 3:
                imageType = BufferedImage.TYPE_3BYTE_BGR;
                break;
            case 4:
                imageType = BufferedImage.TYPE_4BYTE_ABGR;
                break;
            default:
                throw new UnsupportedOperationException();
            }

            // Create a BufferedImage.
            image = new BufferedImage(raster.getWidth(),
                                      raster.getHeight(),
                                      imageType);

            // Set the image data.
            image.getRaster().setRect(raster);
        }

        // Display the image.
        new ReadJPEGRaster(image);
    }

    ReadJPEGRaster(RenderedImage image) {
        super();

        getContentPane().add(new com.sun.media.jai.widget.DisplayJAI(image));
        pack();
        show();
    }
}
Evaluation
See also 5100094, which is a similar RFE on the core JDK JPEGImageReader.
  xxxxx@xxxxx   2004-09-10
Comments
  
  Include a link with my name & email   

Submitted On 03-OCT-2003
phelps
An easy way to support CMYK and YCCK is to convert the raster to 
RGB.
The code below does this and has been tested on Adobe YCCK,
Adobe CMYK, and non-Adobe CMYK (there is no non-Adobe YCCK).
Sun is welcome to use this code in any way.

If the conversion were done by IIO, it would be
more convenient obviously,
be faster and use less memory since samples would be directly 
accessible,
and be easier to obtain transform field of Adobe APP14 marker
   as opposed to inspecting metadata or reparsing the JPEG data 
format.


  /**                                                                                                                                           
    Java's ImageIO can't process 4-component images                                                                                             
    and Java2D can't apply AffineTransformOp either,                                                                                            
    so convert raster data to RGB.                                                                                                              
    Technique due to MArk Stephens.                                                                                                             
    Free for any use.                                                                                                                           
  */
  private static BufferedImage createJPEG4(Raster r, int xform) {
    int w = r.getWidth(), h = r.getHeight();
    byte[] rgb = new byte[w*h*3];

    // if (Adobe_APP14 and transform==2) then YCCK else CMYK                                                                                    
    if (xform==2) {    // YCCK -- Adobe                                                                                                         
        float[] Y = r.getSamples(0,0,w,h, 0, (float[])null), Cb = 
r.getSamples(0,0,w,h, 1, (float[])null), Cr = r.getSamples(0,0,w,h, 
2, (float$
        for (int i=0,imax=Y.length, base=0; i<imax; i++, base+=3) {
            // faster to track last cmyk and save computations on 
stretches of same color?                                                      
            // better to use ColorConvertOp?                                                                                                    
            float k=K[i], y = Y[i], cb=Cb[i], cr=Cr[i];
            double val = y + 1.402*(cr-128) - k;
            rgb[base] = val<0.0? (byte)0: val>255.0? (byte)0xff: 
(byte)(val+0.5);
            val = y - 0.34414*(cb-128) - 0.71414*(cr-128) - k;
            rgb[base+1] = val<0.0? (byte)0: val>255.0? (byte)0xff: 
(byte)(val+0.5);
            val = y + 1.772 * (cb-128) - k;
            rgb[base+2] = val<0.0? (byte)0: val>255.0? (byte)0xff: 
(byte)(val+0.5);
        }

    } else { assert xform==0: xform;    // CMYK                                                                                                 
        int[] C = r.getSamples(0,0,w,h, 0, (int[])null), M = 
r.getSamples(0,0,w,h, 1, (int[])null), Y = r.getSamples(0,0,w,h, 2, 
(int[])null), $
        for (int i=0,imax=C.length, base=0; i<imax; i++, base+=3) {
            int k = K[i];
            rgb[base] = (byte)(255 - Math.min(255, C[i] + k));
            rgb[base+1] = (byte)(255 - Math.min(255, M[i] + k));
            rgb[base+2] = (byte)(255 - Math.min(255, Y[i] + k));
        }
    }

    // from other image types we know InterleavedRaster's can be 
manipulated by AffineTransformOp, so create one of those.                      
    r = Raster.createInterleavedRaster(new DataBufferByte(rgb, 
rgb.length), w, h, w*3,3, new int[] {0,1,2}, null);
    ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
    ColorModel cm = new ComponentColorModel(cs, false, true, 
Transparency.OPAQUE, DataBuffer.TYPE_BYTE);


    return new BufferedImage(cm, (WritableRaster)r, true, null);
  }


Submitted On 13-JAN-2007
bteichmann
Recode a sRGB jpeg to a CMYK jpeg with a CMYK color profile: 
				JPEGImageDecoder decoder = JPEGCodec
						.createJPEGDecoder(new ByteArrayInputStream(tmp));
				BufferedImage rgbImage = decoder.decodeAsBufferedImage();

				// Create a CMYK Color Space from a CMYK- Profile,  
				// there exists lots of, look at: http://www.eci.org/eci/de/060_downloads.php
				// if you don't know take: ISOcoated.icc  ...
				String cmykProfile = "c:\\temp\\ECI_Offset_2004\\ISOcoated.icc";
				ICC_Profile p = ICC_Profile.getInstance(new FileInputStream(cmykProfile));
			    ColorSpace cmykCS = new ICC_ColorSpace(p);
				ByteArrayOutputStream out = new ByteArrayOutputStream();
				// OutputStream out = new FileOutputStream(cmykJPEGFile);

				// ColorConvert rgbImage to CMYK
				ColorSpace rgbCS = rgbImage.getColorModel().getColorSpace();
				ColorConvertOp rgbToCmyk = new ColorConvertOp(rgbCS, cmykCS,
						null);

				// Create a ColorModel for destination CMYK image
				int[] bits = null;
				ColorModel cmykModel = new ComponentColorModel(cmykCS, bits,
						false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
				WritableRaster cmykRaster = cmykModel
						.createCompatibleWritableRaster(rgbImage.getWidth(),
								rgbImage.getHeight());

				// Convert to CMYK now
				rgbToCmyk.filter(rgbImage.getRaster(), cmykRaster);
				// encode to jpeg
				JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
				JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(
						cmykRaster, JPEGDecodeParam.COLOR_ID_CMYK);
				param.setQuality(0.75f, false);
				encoder.encode(cmykRaster, param);

				out.close();
				eo.setContent(out.toByteArray());
				
Read a jpeg that is a CMYK with Color Profile an convert it to Buffered Image:
                        inputStream = ImageIO.createImageInputStream(instr);
				reader = findReader(inputStream);
				reader.setInput(inputStream);
				try {
					// Try reading an image (including color conversion).
					jpegBufferedImage = reader.read(0);
				} catch (IIOException e) {
					// Try reading a Raster (no color conversion)....
					Raster raster = reader.readRaster(0, null);
					// ...and hope thats a CMYK, take a CMYK-Profile, 
					//there exists lots of, look at: http://www.eci.org/eci/de/060_downloads.php
					// if you don't know take: ISOcoated.icc  ...
					String cmykProfile = "c:\\temp\\ECI_Offset_2004\\ISOcoated.icc";
					ICC_Profile p = ICC_Profile
							.getInstance(new FileInputStream(cmykProfile));
					//... and convert it to sRGB
					jpegBufferedImage = cmykRasterToSRGB(raster, p);
				}
Using : 
      /**
	 * Java's ImageIO can't process 4-component images and Java2D can't apply
	 * AffineTransformOp either, so convert raster data to sRGB.
	 */
	public static BufferedImage cmykRasterToSRGB(Raster raster,
			ICC_Profile cmykProfile) {
		int w = raster.getWidth();
		int h = raster.getHeight();

		ColorSpace cmykCS = new ICC_ColorSpace(cmykProfile);
		BufferedImage rgbImage = new BufferedImage(w, h,
				BufferedImage.TYPE_INT_RGB);
		WritableRaster rgbRaster = rgbImage.getRaster();
		ColorSpace rgbCS = rgbImage.getColorModel().getColorSpace();
		ColorConvertOp cmykToRgb = new ColorConvertOp(cmykCS, rgbCS, null);
		cmykToRgb.filter(raster, rgbRaster);
		return rgbImage;

	}



PLEASE NOTE: JDK6 is formerly known as Project Mustang