Suppose I want 25% or 31% gray color in Java?
The following code shows
BufferedImage image = new BufferedImage(2, 2, BufferedImage.TYPE_BYTE_GRAY);
image.setRGB(0, 0, new Color(0,0,0).getRGB());
image.setRGB(1, 0, new Color(50, 50, 50).getRGB());
image.setRGB(0, 1, new Color(100,100,100).getRGB());
image.setRGB(1, 1, new Color(255,255,255).getRGB());
Raster raster = image.getData();
double[] data = raster.getPixels(0, 0, raster.getWidth(), raster.getHeight(), (double[]) null);
System.out.println(Arrays.toString(data));
obvious fact, that RGC relates with density (?) non linear
[0.0, 8.0, 32.0, 255.0]
So, how to create color of a given density?
UPDATE
I have tried methods, proposed by @icza and @hlg and also one more found by me:
double[] data;
Raster raster;
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY);
float[] grays = {0, 0.25f, 0.5f, 0.75f, 1};
ColorSpace linearRGB = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
ColorSpace GRAY = ColorSpace.getInstance(ColorSpace.CS_GRAY);
Color color;
int[] rgb;
for(int i=0; i<grays.length; ++i) {
System.out.println("\n\nShould be " + (grays[i]*100) + "% gray");
color = new Color(linearRGB, new float[] {grays[i], grays[i], grays[i]}, 1f);
image.setRGB(0, 0, color.getRGB());
raster = image.getData();
data = raster.getPixels(0, 0, 1, 1, (double[]) null);
System.out.println("data by CS_LINEAR_RGB (hlg method) = " + Arrays.toString(data));
color = new Color(GRAY, new float[] {grays[i]}, 1f);
image.setRGB(0, 0, color.getRGB());
raster = image.getData();
data = raster.getPixels(0, 0, 1, 1, (double[]) null);
System.out.println("data by CS_GRAY = " + Arrays.toString(data));
rgb = getRGB(Math.round(grays[i]*255));
color = new Color(rgb[0], rgb[1], rgb[2]);
image.setRGB(0, 0, color.getRGB());
raster = image.getData();
data = raster.getPixels(0, 0, 1, 1, (double[]) null);
System.out.println("data by icza method = " + Arrays.toString(data));
}
and all gave different results!
Should be 0.0% gray
data by CS_LINEAR_RGB (hlg method) = [0.0]
data by CS_GRAY = [0.0]
data by icza method = [0.0]
Should be 25.0% gray
data by CS_LINEAR_RGB (hlg method) = [63.0]
data by CS_GRAY = [64.0]
data by icza method = [36.0]
Should be 50.0% gray
data by CS_LINEAR_RGB (hlg method) = [127.0]
data by CS_GRAY = [128.0]
data by icza method = [72.0]
Should be 75.0% gray
data by CS_LINEAR_RGB (hlg method) = [190.0]
data by CS_GRAY = [192.0]
data by icza method = [154.0]
Should be 100.0% gray
data by CS_LINEAR_RGB (hlg method) = [254.0]
data by CS_GRAY = [254.0]
data by icza method = [255.0]
Now I wonder which one is correct?
UPDATE 2
Sorry, gray/white percentage should be, of course, reversed.
The huge differences are due to the gamma encoding in sRGB (Wikipedia). sRGB is the default color space used in the Color
constructor. If you set your colors using a linear RGB color space instead, the grey values are not distorted:
ColorSpace linearRGB = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
Color grey50 = new Color(linearRGB, new float[]{50f/255,50f/255,50f/255}, 1f);
Color grey100 = new Color(linearRGB, new float[]{100f/255,100f/255,100f/255}, 1f);
Color grey255 = new Color(linearRGB, new float[]{1f,1f,1f}, 1f);
However, when setting the pixel by using Color.getRGB
and ImageBuffer.setRGB
, the linear grey scale values are converted to sRGB and back. Thus they are gamma encoded and decoded, yielding rounding errors depending on the chosen color space.
These errors can be avoided by setting the raw pixel data behind the gray scale color model directly:
WritableRaster writable = image.getRaster();
writable.setPixel(0,0, new int[]{64});
Note, that you have to round the percentage values, e.g. for 25% you can not store 63.75
. If you need more precision, use TYPE_USHORT_GRAY
instead of TYPE_BYTE_GRAY
.