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);
}
}
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
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'.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With