I have this program:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class TestLine {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TestLine().start();
}
});
}
private static void start() {
JFrame frame = new JFrame();
frame.setContentPane(new CarthPanel());
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private static class CarthPanel extends JComponent {
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D gg = (Graphics2D) g;
int w = gg.getClipBounds().width;
int h = gg.getClipBounds().height;
System.out.println("(w,h)=(" + w + "," + h + ")");
gg.translate(w - 1, 0); // <<< when uncommenting both lines, mirroring applies
gg.scale(-1.0, 1.0); //
paintTest(gg, w, h);
}
private static void paintTest(Graphics2D g, int w, int h) {
// black background
g.setColor(Color.black);
g.fillRect(0, 0, w, h);
// colored corners
g.setColor(Color.RED);
g.drawLine(0, 0, 10, 0);
g.drawLine(0, 0, 0, 10);
g.setColor(Color.RED);
g.drawLine(0, 199, 10, 199);
g.drawLine(0, 199, 0, 189);
g.setColor(Color.CYAN);
g.drawLine(189, 0, 199, 0);
g.drawLine(199, 0, 199, 10);
g.setColor(Color.CYAN);
g.drawLine(189, 199, 199, 199);
g.drawLine(199, 199, 199, 189);
// yellow squares
g.setColor(Color.yellow);
g.drawRect(3, 3, 10, 10);
g.fillRect(186, 3, 11, 11);
g.fillRect(3, 186, 11, 11);
g.drawRect(186, 186, 10, 10);
String chars = "ABC";
g.setFont(Font.decode("Arial 72"));
Rectangle2D rect = g.getFontMetrics().getStringBounds(chars, g);
g.drawString(chars, (int) (200 - rect.getWidth()) / 2, (int) (200 - rect.getHeight()));
}
}
}
If you run this program once with two particular lines commented in and then once with the same lines commented out (see the code), then you get to see this:
[
If you didn't spot the problem, here's a zoomed pic:
One would expect the image to be perfectly mirrored. This is true for all strokes and hollow shapes (= drawXxx() methods). All filled shapes however (= fillXxx() methods) are drawn exactly one pixel to the left of where I expect them; e.g. the filled yellow rectangles, and you can also notice that the black background has shifted, as seen by the white line at the right border.
Is this a bug or is this intended? I suspect that it has something to do with the difference how "width" and "height" are being handled in drawXxx() and fillXxx() methods:
What am I missing?
It is a bug, it is not a feature :) Surely it is not intended, the mirrored image should be perfect, there's no reason why it shouldn't.
There is a way to use BufferedImage to render on it normally, and then drawing this BufferedImage flipped, the only drawback is a little performance influence and not well looking text drawn using LCD subpixeling.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class TestLine {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TestLine().start();
}
});
}
private static void start() {
JFrame frame = new JFrame();
frame.setContentPane(new CarthPanel());
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private static class CarthPanel extends JComponent {
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D gg = (Graphics2D) g;
// System.out.println("(w,h)=(" + w + "," + h + ")");
gg.translate(getWidth(), 0); // <<< when uncommenting both lines, mirroring applies
gg.scale(-1.0, 1.0); //
BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
try {
paintTest(img.createGraphics());
} catch (Exception e) {
e.printStackTrace();
}
g.drawImage(img, 0, 0, null);
}
private void paintTest(Graphics2D g) {
// black background
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
// colored corners
g.setColor(Color.RED);
g.drawLine(0, 0, 10, 0);
g.drawLine(0, 0, 0, 10);
g.setColor(Color.RED);
g.drawLine(0, 199, 10, 199);
g.drawLine(0, 199, 0, 189);
g.setColor(Color.CYAN);
g.drawLine(189, 0, 199, 0);
g.drawLine(199, 0, 199, 10);
g.setColor(Color.CYAN);
g.drawLine(189, 199, 199, 199);
g.drawLine(199, 199, 199, 189);
// yellow squares
g.setColor(Color.yellow);
g.drawRect(3, 3, 10, 10);
g.fillRect(186, 3, 11, 11);
g.fillRect(3, 186, 11, 11);
g.drawRect(186, 186, 10, 10);
String chars = "ABC";
g.setFont(Font.decode("Arial 72"));
Rectangle2D rect = g.getFontMetrics().getStringBounds(chars, g);
g.drawString(chars, (int) (200 - rect.getWidth()) / 2, (int) (200 - rect.getHeight()));
}
}
}