RFR: 8364583: ColorConvertOp fails for CMYK → RGB conversion

Sergey Bylokhov serb at openjdk.org
Mon Nov 3 19:03:55 UTC 2025


On Fri, 24 Oct 2025 21:47:31 GMT, Phil Race <prr at openjdk.org> wrote:

>>> It can also be over-written at line 835.
>> 
>> Interesting, is it possible that that line has the same bug?
>> `color = dstColorSpace.fromCIEXYZ(dstColor);`
>> Does dstColor always have the same number of components as CIEXYZ? 
>> 
>> Is the logic of using CIEXYZ for mix of non-/ICC source and non-/ICC destination actually correct?
>
> fromCIEXYZ is defined on ColorSpace, not ICC_ColorSpace.
> 
> It requires 3 (or more) components, and then dstColorSpace will return an array of colors in its own colorspace.
> The dstColor parameter is always created with at least 3 components based on the dstNumComp
> And if there's no bug in dstColorSpace it should return an array of the right length for itself.
> So if there's a bug it isn't obvious to me.

Seems the destination handles it properly; I tested it with the following example:

import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.WritableRaster;

import static java.awt.color.ColorSpace.TYPE_2CLR;
import static java.awt.color.ColorSpace.TYPE_FCLR;
import static java.awt.color.ColorSpace.TYPE_GRAY;

public final class TestCCP {

    private static final int WIDTH = 10;
    private static final int HEIGHT = 10;

    private static class CustomColorSpace extends ColorSpace {
        private final int numComponents;

        private CustomColorSpace(int type, int numComponents) {
            super(type, numComponents);
            this.numComponents = numComponents;
        }

        @Override
        public float[] toRGB(float[] colorvalue) {
            return new float[3];
        }

        @Override
        public float[] fromRGB(float[] rgbvalue) {
            return new float[numComponents];
        }

        @Override
        public float[] toCIEXYZ(float[] colorvalue) {
            return new float[3];
        }

        @Override
        public float[] fromCIEXYZ(float[] colorvalue) {
            return new float[numComponents];
        }
    }

    private static final ColorSpace[] CS = {
            ColorSpace.getInstance(ColorSpace.CS_CIEXYZ),
            ColorSpace.getInstance(ColorSpace.CS_GRAY),
            ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB),
            ColorSpace.getInstance(ColorSpace.CS_PYCC),
            ColorSpace.getInstance(ColorSpace.CS_sRGB),
            new CustomColorSpace(TYPE_GRAY, 1),
            new CustomColorSpace(TYPE_2CLR, 2),
            new CustomColorSpace(TYPE_FCLR, 15)
    };

    public static void main(String[] args) {
        for (ColorSpace srcCS : CS) {
            for (ColorSpace fromCS : CS) {
                for (ColorSpace toCS : CS) {
                    for (ColorSpace dstCS : CS) {
                        BufferedImage src = createTestImage(srcCS);
                        BufferedImage dst = createTestImage(dstCS);

                        new ColorConvertOp(fromCS, toCS, null).filter(src, dst);
                    }
                }
            }
        }
    }

    private static BufferedImage createTestImage(ColorSpace cs) {
        ComponentColorModel cm = new ComponentColorModel(cs, false, false,
                                                         Transparency.OPAQUE,
                                                         DataBuffer.TYPE_BYTE);
        WritableRaster raster = cm.createCompatibleWritableRaster(WIDTH,
                                                                  HEIGHT);
        return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
    }
}

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/27785#discussion_r2487547644


More information about the client-libs-dev mailing list