2

I'm attempting, and failing, to render 2d shapes in Java using sub-pixel positioning. A failed attempt is below (ignore the scale and the AffineTransform for the moment) :

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

public class TestFrame extends Frame {
    private static final int D = 64;

    public static void main(String args[]) {
        new TestFrame(D, D);
    }

    private final Insets ins;
    private final double scale = 1;

    TestFrame(int w, int h) {
        ins = getInsets();
        final Dimension dim = new Dimension(w + ins.left + ins.right, h + ins.top + ins.bottom);

        pack();

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent we) {
                dispose();
            }
        });

        setSize(dim);
        setLocationRelativeTo(null);
        setBackground(Color.BLACK);
        setVisible(true);
        repaint();
    }

    @Override
    public void paint(Graphics gfx) {
        super.paint(gfx);
        final Graphics2D g2d = (Graphics2D)gfx;
        g2d.setTransform(new AffineTransform(1.0/scale, 0.0, 0.0, 1.0/scale, ins.left, ins.top));
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setStroke(new BasicStroke((float)scale));
        g2d.setColor(Color.WHITE);

        for (double deg = 0; deg < 90.0; deg += 2.0) {
            final double rad = deg / 180.0 * Math.PI;
            final double x1 = Math.cos(rad) * D * scale;
            final double y1 = Math.sin(rad) * D * scale;
            final double x2 = Math.cos(rad) * (D - 4.0) * scale;
            final double y2 = Math.sin(rad) * (D - 4.0) * scale;
            g2d.draw(new Line2D.Double(x1, y1, x2, y2));
        }
    }
}

This gives rise to the following output:

Rendering

The lines should be evenly spaced, but, as you can see, they are not. My guess is that the start and end points are being rounded to the nearest pixel.

Suggested solutions on SO mention using Line2D.Double (which I'm already doing and apparently doesn't work), or using an AffineTransform to scale down. If you change the scale variable in the code sample to say 100, then this plots the lines at a much higher scale, and then uses the transform to scale it back down as per the suggestions. This too has no effect on the resultant image.

What am I doing wrong?

jon hanson
  • 8,722
  • 2
  • 37
  • 61
  • Nothing, seems to be due to the aliasing algorithm of java mixed with rounding... I tried changing many parameters, the best results is by changing line width, but alas luminosity drops down... – Jean-Baptiste Yunès Sep 22 '16 at 19:01
  • 2
    Actually, just tried `g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);` and that seems to fix it. Slightly cryptic. – jon hanson Sep 22 '16 at 19:04
  • Didn't tried that one! "Stroke normalization control hint value -- geometry should be left unmodified and rendered with sub-pixel accuracy." Exactly what you need or the other VALUE_STROKE_NORMALIZE ? – Jean-Baptiste Yunès Sep 22 '16 at 19:07

1 Answers1

1

The solution is set one of the rendering hints:

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

Quoting the Javadocs:

Stroke normalization control hint value -- geometry should be left unmodified and rendered with sub-pixel accuracy.

jon hanson
  • 8,722
  • 2
  • 37
  • 61