使用CMYK颜色空间在java中写入图像

时间:2017-09-07 19:49:14

标签: java swing bufferedimage graphics2d cmyk

我想使用CMYK颜色空间在java中编写一个图像:

BufferedImage image= new BufferedImage(path.getBounds().width, 
path.getBounds().height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
color c = new Color(ColorSpaces.getDeviceCMYKColorSpace(), new float[] 
{1.0f,1.0f,1.0f,1.0f}, 1.0f)
g2d.setPaint(c);
g2d.fill(path);     
g2d.draw(path);
g2d.dispose();

然后我使用imageIO将图像写入JPEG,但是,当转换为PDF时,生成的图像没有我在代码中提供的CMYK颜色,而是具有以下内容

CMYK Values

我的问题是:

  1. 我可以使用BufferedImage写入带有CMYK色彩空间的图像吗?
  2. 如果组件值为0,0,0,1.0且alpha为1,如何使用CMYK颜色空间写入图像
  3. 请注意我已经尝试过EPS并且工作正常,但是,在我的项目的这个阶段,我不习惯使用EPS。

1 个答案:

答案 0 :(得分:5)

  1. 是的,你可以。您需要在CMYK颜色空间中创建BufferedImage。然后画上那个。您不能使用任何标准BufferedImage.TYPE_*类型,因为它们都是灰色或RGB。请参阅以下代码。

  2. 您可以使用ImageIO编写CMYK JPEG。但是,您需要在元数据中添加一些额外的细节并稍微按下像素数据,否则图像将被写为RGBA或反转CMYK。再次,请参阅下面的代码。

  3. 这是一个完整的,可运行的,概念验证代码示例:

    public class CMYKTest {
    
        public static final String JAVAX_IMAGEIO_JPEG_IMAGE_1_0 = "javax_imageio_jpeg_image_1.0";
    
        public static void main(String[] args) throws IOException {
            // I'm using my own TwelveMonkeys ImageIO library for this, 
            // but I think you can use the one you used above, like:
            // ColorSpace cmykCS = ColorSpaces.getDeviceCMYKColorSpace()
            ColorSpace cmykCS = ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK);
    
            // Create CMYK color model, raster and image
            ColorModel colorModel = new ComponentColorModel(cmykCS, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
            BufferedImage image = new BufferedImage(colorModel, colorModel.createCompatibleWritableRaster(100, 100), colorModel.isAlphaPremultiplied(), null);
    
            // Paint some sample rectangles on it
            Graphics2D g = image.createGraphics();
            try {
                g.setColor(new Color(cmykCS, new float[] {0, 0, 0, 0}, 1.0f)); // All 0 (White)
                g.fillRect(0, 0, 25, 50);
                g.setColor(new Color(cmykCS, new float[] {0, 0, 0, 1}, 1.0f)); // Key (Black)
                g.fillRect(25, 0, 25, 50);
                g.setColor(new Color(cmykCS, new float[] {1, 0, 0, 0}, 1.0f)); // Cyan
                g.fillRect(50, 0, 50, 50);
                g.setColor(new Color(cmykCS, new float[] {0, 1, 0, 0}, 1.0f)); // Magenta
                g.fillRect(0, 50, 50, 50);
                g.setColor(new Color(cmykCS, new float[] {0, 0, 1, 0}, 1.0f)); // Yellow
                g.fillRect(50, 50, 50, 50);
            }
            finally {
                g.dispose();
            }
    
            // Write it as a JPEG, using ImageIO    
            try (ImageOutputStream stream = ImageIO.createImageOutputStream(new File("cmyk.jpg"))) {
                ImageWriter writer = ImageIO.getImageWritersByFormatName("JPEG").next();
                writer.setOutput(stream);
    
                // We need to massage the image metadata a little to be able to write CMYK
                ImageWriteParam param = writer.getDefaultWriteParam();
                IIOMetadata metadata = writer.getDefaultImageMetadata(ImageTypeSpecifier.createFromRenderedImage(image), param);
    
                IIOMetadataNode jpegMeta = new IIOMetadataNode(JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
                jpegMeta.appendChild(new IIOMetadataNode("JPEGVariety")); // Just leave as default
    
                IIOMetadataNode markerSequence = new IIOMetadataNode("markerSequence");
                jpegMeta.appendChild(markerSequence);
    
                // The APP14 "Adobe" marker acts as a trigger for decoders, to
                // specify 4 channels as CMYK or YCCK (instead of RGBA or YCCA).
                IIOMetadataNode app14Adobe = new IIOMetadataNode("app14Adobe");
                app14Adobe.setAttribute("transform", "0"); // 0 means "unknown"
                markerSequence.appendChild(app14Adobe);
    
                // You could also append an ICC profile as part of the JPEG metadata
                // if you feel adventurous...
    
                // Merge with metadata from the writer
                metadata.mergeTree(JAVAX_IMAGEIO_JPEG_IMAGE_1_0, jpegMeta);
    
                // Also, we need to massage the raster a little, as CMYK data is
                // written in "inverse" form. 
                // We could use image.getRaster() here to get better performance
                // if you don't mind the image being inverted in memory too. 
                // image.getData() creates a copy, and is safe from this side effect.
                Raster raster = image.getData(); 
                byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
                // Inverse the pixel data
                for (int i = 0; i < data.length; i++) {
                    data[i] = (byte) (255 - data[i]);
                }
    
                // Finally, write it all
                writer.write(null, new IIOImage(raster, null, metadata), param);
            }
        }
    }
    

    使用ImageIO阅读CMYK JPEG图像的注意事项

    以上代码将编写标准JPEGImageReader无法读取的JPEG图像(通过readRaster()方法除外)。

    为了更好/更容易的CMYK JPEG支持,我建议使用TwelveMonkeys ImageIO JPEG插件(我是这个插件的作者)。

    关于CMYK和透明度的小字

    您还可以创建具有透明度(Alpha通道)的CMYK颜色模型/图像,如下所示:

    ColorModel colorModel = new ComponentColorModel(cmykCS, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
    BufferedImage image = new BufferedImage(colorModel, colorModel.createCompatibleWritableRaster(100, 100), colorModel.isAlphaPremultiplied(), null);
    

    但遗憾的是,JPEGImageWriter无法处理4个以上的数据通道。我在一个包含注释的数组中得到一个IndexArrayOutOfBoundsException(IJG是开发libjpeg的Independent JPEG Group):

    /** IJG can handle up to 4-channel JPEGs */
    

    所以,不幸的是,没有简单的解决办法。