DrawRect () does not work properly on certain colors

I always created rectangles with such contours (using Graphics ( 2D )):

 g.setColor(aColor); g.fillRect(x, y, width, height); g.setColor(anotherColor); g.drawRect(x, y, width, height); 

This works great, except for some colors such as Color.BLUE . There are lines that do not have the same thickness:

enter image description here

It may be difficult to see at a glance, but if you look carefully, you will realize that the left line is too thick and the right line is too thin. This happens with other colors, but not so obvious: (I'm still not sure if this happens with blue, I can’t say for sure)

enter image description here

I can’t understand this, because the black line is simply drawn on the inner blue rectangle, the inner rectangle should not affect it. (without fillRect() lines are even thick)

I gave an example below, which will probably help you better understand the difference. My question is: why does this happen with certain RGB colors and how to fix it?

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.HashMap; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JToggleButton; import javax.swing.WindowConstants; public class LineExample { Color colors[] = new Color[] { Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.WHITE, Color.YELLOW }; String colorNames[] = new String[] { "Black", "Blue", "Cyan", "Dark Gray", "Gray", "Green", "Light Gray", "Magenta", "Orange", "Pink", "Red", "White", "Yellow" }; HashMap<String, Color> hashMap = new HashMap<String, Color>(); Color currentColor = colors[2]; public LineExample() { fillHashMap(hashMap); JFrame frame = new JFrame(); JPanel mainPanel = new JPanel(new BorderLayout()); JPanel northPanel = new JPanel(new FlowLayout()); JPanel centerPanel = new JPanel(new GridLayout(1, 2)); CustomPanel customPanel = new CustomPanel(); BluePanel bluePanel = new BluePanel(); JComboBox<String> comboBox = new JComboBox<String>(); addItems(comboBox); comboBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { currentColor = hashMap.get(comboBox.getSelectedItem()); centerPanel.repaint(); } }); JToggleButton toggleButton = new JToggleButton("Switch"); toggleButton.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { centerPanel.removeAll(); if (e.getStateChange() == ItemEvent.SELECTED) { centerPanel.add(bluePanel); centerPanel.add(customPanel); } else if (e.getStateChange() == ItemEvent.DESELECTED) { centerPanel.add(customPanel); centerPanel.add(bluePanel); } centerPanel.revalidate(); centerPanel.repaint(); } }); northPanel.add(comboBox); northPanel.add(toggleButton); centerPanel.add(customPanel); centerPanel.add(bluePanel); mainPanel.add(northPanel, BorderLayout.NORTH); mainPanel.add(centerPanel, BorderLayout.CENTER); frame.setContentPane(mainPanel); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.setSize(250, 250); frame.setVisible(true); } public void addItems(JComboBox<String> comboBox) { for (int i = 0; i < colors.length; i++) { comboBox.addItem(colorNames[i]); } comboBox.setSelectedIndex(2); } public void fillHashMap(HashMap<String, Color> hashmap) { for (int i = 0; i < colors.length; i++) { hashMap.put(colorNames[i], colors[i]); } } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { new LineExample(); } }); } public class BluePanel extends JPanel { @Override public void paintComponent(Graphics g) { super.paintComponent(g); int width = 100; int height = 100; int x = ((this.getWidth() - width) / 2); int y = ((this.getHeight() - height) / 2); g.setColor(Color.BLUE); g.fillRect(x, y, width, height); g.setColor(Color.BLACK); g.drawRect(x, y, width, height); } } public class CustomPanel extends JPanel { @Override public void paintComponent(Graphics g) { super.paintComponent(g); int width = 100; int height = 100; int x = ((this.getWidth() - width) / 2); int y = ((this.getHeight() - height) / 2); g.setColor(currentColor); g.fillRect(x, y, width, height); g.setColor(Color.BLACK); g.drawRect(x, y, width, height); } } } 
+5
source share
2 answers

This is an interesting problem arising from subpixel devices on LCDs. I took some pictures of my monitor with my phone to explain this effect.

First we look at the left side. The line has a thickness of about 2 pixels. This is due to the fact that the transition from left to right on the line is the transition from black to blue. Since the red and green pixels cannot be highlighted for blue, we need to wait until the next blue sub-pixel after the end of the line ends.

enter image description here

Then we look at the right side. This line has a thickness of approximately 1 pixel. This time it is a transition from black to white, so red and green pixels can be highlighted immediately after the line.

enter image description here

This problem occurs because the Java graphics library is not sub-pixel (at least for forms). You will always have this problem for some color combination on each monitor. This will be most obvious for the red-green blue primary colors, so it is difficult to say in your blue example.

+8
source

Could be related to anti-aliasing ? When I encounter such problems, I usually do something like ...

 Graphics2D g2 = (Graphics2D)g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // draw stuff g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_DEFAULT); 
0
source

All Articles