While working with an old PC1, I found something that I thought to be a strange rendering error.
The problem occurs when the color depth is set to 16bit (32768 colors).
Particularly, it shows up when painting colors with a low alpha value with a Graphics2D
. The following is a MVCE to reproduce the issue. It just paints a few sets of lines, with a white color whose alpha value ranges from 4 to 12. The result of running this program is shown in the following screenshot:
One can see that some of the colors are actually green for some alpha values, although there clearly should be 50 9 shades of grey.
(Again, it only appears when the color depth is set to 16bit!)
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class _WTFPaintTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JPanel()
{
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(Color.BLACK);
g.fillRect(0,0,getWidth(),getHeight());
for (int a=4; a<=12; a++)
{
int x = (a-3) * 50;
g.setColor(Color.WHITE);
g.drawString(String.valueOf(a), x, 25);
g.setColor(new Color(255,255,255,a));
for (int j=0; j<3; j++)
{
for (int i=0; i<50; i++)
{
g.drawLine(x,50+i,x+25,75);
}
}
}
}
});
f.setSize(550,200);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
(I post it here with its original name, to emphasize how irritating I found this...)
What is the reason for the odd color rendering in this case?
1: WinXP, 32 bit, NVIDIA GeForce 2 graphics card
The reason for this behavior is that the color components (Red, Green and Blue) are represented with a different number of bits in 16-bit color mode.
The 16 bits are distributed among the color components as follows:
This means that a 16 bit color has 25 = 32 different shades of red and blue, but 26 = 64 shades of green.
(Green receives the additional bit, because the sensitivity of the human eye is greater for shades of green than for shades of red or blue)
Now, when a standard 24 bit RGB color is converted to a 16 bit color, sampling errors will occur. In a 24 bit color, each color component has 8 bits. These are converted to the corresponding component for the 16 bit color by taking the highest-order bits of the color component.
In the example screenshot, one can see the lines that are painted with an alpha value of 7. The RGB representation of this color is (7,7,7). The binary representation of this color is (00000111b,00000111b,00000111b). When taking the highest-order bits for each color component, the results are
The green component is thus the only component with a non-zero value. The green component of the actual color that is then displayed on the screen can be imagined as the 6-bit green component, left-shifted by 2 bits:
This is confirmed by examining the corresponding pixels in the screenshot, which are stored as 0x000C00
, or 24 bit RGB values (0,12,0): A dark shade of green.