3

When using JColorChooser, entered CMYK values translate to a specific RGB color. When that color is entered manually on the RGB side, the CMYK values are not the same as before.

The following program can be used to demonstrate the behavior I am encountering.

import java.awt.*;
import javax.swing.*;

public class ColorChooserProblem {
    JFrame f = new JFrame("Testing Color Chooser");

    public static void main(String[] args) {
        new ColorChooserProblem().start();
    }

    public void start() {
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JColorChooser jc1 = new JColorChooser();
        JColorChooser jc2 = new JColorChooser();
        f.add(jc1, BorderLayout.NORTH);
        f.add(jc2, BorderLayout.SOUTH);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}
  1. In both panels, select CMYK and type in any valid numbers for CMYK. Both panels must have the same values.
  2. Now compare the RGB values for each panel. They should be the same.
  3. Select a single panel and reset the sliders to 0.
  4. Now re-enter the RGB values into that same panel.
  5. Switch to CMYK for both panels. The values in the panels that I see are different.

Note that when going the other way (i.e. selecting RGB first and re-entering the CMYK values), all works as one might expect. Am I missing something in what's expected of the conversion process, or is this a bug?

I am running Java 10 on Windows 10 and my IDE is Eclipse.

Also posted at http://www.javaprogrammingforums.com/java-theory-questions/41836-possible-bug-jcolorchooser.html

WJS
  • 36,363
  • 4
  • 24
  • 39

3 Answers3

5

The conversion from one (discrete) color model to another (discrete) color model can never be perfect.

The reason why CMYK to RGB to CMYK will never work perfectly in JColorChooser is simply because JColorChooser displays integers instead of floating numbers. For example choose yellow=255 in the CMYK model and go back to RGB. You will see this yellow color is mixed from red=255 and green=255. Now go back to CMYK, lower yellow to be 254 and check RGB values - it's still red=255 and green=255 !

Now change yellow to 253 in CMYK and go back to RGB. Red and green are still 255 and blue is added with value 1. The correct value for a CMYK yellow=254 (previous case) might be blue=0.4, but to make usage simpler only integers are display in JColorChooser, so blue is showing as 0.

This numeric issue is made even worse by the fact that the 'color sensitivity' of these integer color models is different. While CMYK has 4 dimensions (cyan,magenta,yellow,key) and can therefore represent 256^4 = 4294967296 different colors RGB has 3 dimensions and can only represent 256^3 = 16777216 colors. So you will always loose quite information when transforming CMYK to this type of RGB.

In other words, on average, 256 points in the CMYK color space are represented by only 1 point in the RGB color space. When you convert back one color from RGB to CMYK, on average, 255 colors in the CMYK space can never be 'reached'.

Axel Podehl
  • 4,034
  • 29
  • 41
  • 1
    Your answer inspired me to write my own answer using the 255..254..253 steps as a guideline. However I don't agree with your explaination of the 'larger' colorspace. In Java's (oversimplified) CMYK, adding K just means subtracting equivalent amounts of C,M,Y. This just means that there are a lot of ambiguous color points, and the amount of unique color points is still equal to 2^24. – Mark Jeronimus Apr 19 '19 at 09:00
  • Hi Mark, congrats to your find in the source code, but I'm not totally agreeing on your comment here. With CMYK there could be many colors combinations that are redundant but there are definitely more than 256^3 for CMYK. Think for example about the case of dark yellow: r=1,g=1,b=0 this should be mapped to CMYK with y=1 and k=0. But then we have y=1 and k=1,2,3,..,255 which are even darker than r=1,g=1,b=0. So a discrete CMYK can express r=0.5,g=0.5 with y=1 and k=128. – Axel Podehl Apr 24 '19 at 08:15
  • Just FYI, another interesting note is that, in theory, RGB and CMYK are actually different, not totally overlapping color spaces. Check out this quote from the Gamut entry on wikipedia: https://en.wikipedia.org/wiki/Gamut" .... "For example, while pure red can be expressed in the RGB color space, it cannot be expressed in the CMYK color space; pure red is out of gamut in the CMYK color space." - so stuff get's quickly complicated and also non-linear when you look into this color spaces. – Axel Podehl Apr 24 '19 at 08:18
  • You're right about the non-ambiguous frational colors. I know about the non-overlappyness. That's why in I called it 'oversimplified' in parentheses. The easiest clue is that paint-cyan is more like deep sky blue than addtive-cyan. The 'true' conversion (that printers should use) is also non-linear as RGB is in sRGB and CMYK obviously isn't. – Mark Jeronimus Apr 24 '19 at 13:22
4

I did some debugging inside the color models used by JColorChooser, in particular ColorModelCMYK (package-private class).

The calculation is mostly straightforward, except that all values 0..255 are converted to float 0.0..1.0 by scaling by 255.0f. This introduces roundoff errors in the least significant bit (of the IEEE754 float representation).

Here C=254 is converted to ~R=1 (note that both arrays are the same object and it's updated in-place, so the CMYK values are lost in the conversion. rgb[0]*255f = 0.99999994

With proper half-up rounding while converting back to integer values for displaying, this shouldn't be any issue. However, digging onto ColorModel itself, I found this function is used by the routine that converts the float array to a packed 32-bit RGB value:

private static int to8bit(float value) {
    return (int) (255.0f * value);
}

It's truncated! I don't know if this is a bug, but it certainly is a usability issue.

Mark Jeronimus
  • 9,278
  • 3
  • 37
  • 50
2

I use Java 8 with Windows on Eclipse too, and it gives me the same result, but that's not the problem. It's working fine for you, but converting RGB to CMYK doesn't work the same way as CMYK to RGB. You can see it in this converter online:

https://www.rapidtables.com/convert/color/rgb-to-cmyk.html

CMYK works with cyan, magenta and yellow percentages. On the other hand RGB with values from 0 to 255 of red, green and blue. In the web I've passed you puts formulas of that transformation and does not work the same bidirectional way.

Francesc Recio
  • 2,187
  • 2
  • 13
  • 26