4

I was writing a program with some custom rendering, and needed to render a rectangle with a border. I decided to simply call graphics2D.fillRect(), switch to the border color, and call graphics2D.drawRect(). However, even though I make these calls back to back with the same coordinates and sizes, fillRect() does not always fill in the entire area contained by drawRect when the color I'm drawing with is translucent (has alpha). Furthermore, the area painted by fillRect() is sometimes outside of the area contained by drawRect(). Why do these two methods draw things in different places when given different colors?

Here is an example to demonstrate the problem. Clicking the mouse in the window will switch between drawing the fill with alpha and without. Notice that there is a row of pixels at the bottom of the rectangle that is white when drawing with alpha, but that row of pixels is not there when drawing without alpha.

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class ColorWithAlpha extends JPanel {

private boolean hasAlpha = true;

private static final long serialVersionUID = 1L;

/**
 * @param args
 */
public static void main(String[] args) {
    // setup a basic frame with a ColorWithAlpha in it
    JFrame frame = new JFrame();
    JPanel panel = new ColorWithAlpha();
    panel.setPreferredSize(new Dimension(500, 500));
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(panel);
    frame.pack();
    frame.show();
}

public ColorWithAlpha() {
    super();
    setBackground(Color.WHITE);

    this.addMouseListener(new MouseListener() {
        @Override
        public void mouseClicked(MouseEvent arg0) {
            // when the user clicks their mouse, toggle whether we are drawing a color with alhpa or without.
            hasAlpha = !hasAlpha;
            ColorWithAlpha.this.repaint();
        }
        @Override
        public void mouseEntered(MouseEvent arg0) {}

        @Override
        public void mouseExited(MouseEvent arg0) {}

        @Override
        public void mousePressed(MouseEvent arg0) {}

        @Override
        public void mouseReleased(MouseEvent arg0) {}
    });
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Color color = new Color(100, 100, 250);// this color doesnt have an alpha component

    // some coordinates that demonstrate the bug. Not all combinations of x,y,width,height will show the bug
    int x = -900;
    int y = 1557;
    int height = 503;
    int width = 502;
    if (hasAlpha) { // toggle between drawing with alpha and without
        color = new Color(200, 100, 250, 100);
    }
    Graphics2D g2 = (Graphics2D) g;
    // this is the transform I was using when I found the bug.
    g2.setTransform(new AffineTransform(0.160642570281124, 0.0, 0.0, -0.160642570281124, 250.0, 488.0));


    g2.setColor(color);
    g2.fillRect(x, y, width, height);
    g2.setColor(Color.DARK_GRAY);
    g2.setStroke(new BasicStroke(8f));
    g2.drawRect(x, y, width, height);

}
}
Eric Fitting
  • 160
  • 1
  • 7

1 Answers1

4

Scrap that answer, I reread your question and copied your code and found what your talking about. The small white line is due to a round-off error in the painting. Very interesting little problem. Add this after creating your Graphics2D

g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);

Rendering hints tell the painting class how you want certain procedures to work. I have no idea why adding transparency to the color would make the roundoff different. I figure it must have to do with multiple rendering hints combining together like antialiasing.

ug_
  • 11,267
  • 2
  • 35
  • 52
  • This is useful information about alpha compositing, but unfortunately it does not solve the issue for me. – Eric Fitting May 02 '13 at 13:11
  • @EricFitting I guess the idea was to use alpha compositing instead of colors with alpha to achieve transparency. It did not work or this would not be a feasible workaround for you? – lbalazscs May 02 '13 at 15:32
  • @lbalazscs It did not work. I see the same line of unfilled pixels at the bottom of the rectangle when hasAlpha is true. – Eric Fitting May 02 '13 at 16:10
  • Sort of. It fixed the issue of unfilled pixels, but it had the negative side effect of making the left side of the border 1 pixel thinner than the other 3 sides. I suspect this is because the stroke I am using doesnt align with the pixels nicely. I think I can tweak the width of the stroke to align with the pixels, so I will see if I can get it working like that. – Eric Fitting May 02 '13 at 19:25