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

Aba*_*dir 2 java swing bufferedimage cmyk graphics2d

我想使用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();
Run Code Online (Sandbox Code Playgroud)

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

CMYK值

我的问题是:

  1. 我可以使用BufferedImage来写入带有CMYK色彩空间的图像吗?
  2. 如果组件值为0,0,0,1.0且alpha为1,如何使用CMYK颜色空间写入图像

请注意,我已经尝试过EPS并且工作正常,但是,在我的项目的这个阶段,我觉得使用EPS感觉不舒服.

har*_*ldK 5

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

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

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

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

关于使用ImageIO读取CMYK JPEG图像的注意事项

上面的代码会写一个JPEG图像标准JPEGImageReader将无法读取(除了通过该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);
Run Code Online (Sandbox Code Playgroud)

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

/** IJG can handle up to 4-channel JPEGs */
Run Code Online (Sandbox Code Playgroud)

所以,不幸的是,没有简单的解决方法.