Java - 子像素线精度是否需要使用 AffineTransform?

9
我以前从未使用过Java绘图方法,所以我决定尝试创建一个模拟时钟作为PoC。除了指针外,我还绘制了一个包括分钟/小时刻度的时钟表盘。我使用简单的sin/cos计算来确定圆周上线条的位置。
然而,我注意到由于分钟刻度非常短,线条的角度看起来不正确。我确信这是因为Graphics2D.drawLine()Line2D.double()方法都无法以亚像素精度绘制。
我知道我可以从中心绘制线条并用圆形遮罩它(以创建更长、更准确的线条),但这似乎是一种不优雅且昂贵的解决方案。我已经对如何做这个问题进行了一些研究,但我找到的最好的答案是使用AffineTransform。我认为我可以仅使用旋转的AffineTransform,而不必执行超采样。
这是使用亚像素精度绘制的唯一/最佳方法吗?还是有可能有更快的解决方案?
编辑:我已经将RenderingHint设置为Graphics2D对象。
根据请求,这里有一些代码(由于这只是一个PoC,代码没有完全优化):
diameter = Math.max(Math.min(pnlOuter.getSize().getWidth(),
                             pnlOuter.getSize().getHeight()) - 2, MIN_DIAMETER);

for (double radTick = 0d; radTick < 360d; radTick += 6d) {
   g2d.draw(new Line2D.Double(
      (diameter / 2) + (Math.cos(Math.toRadians(radTick))) * diameter / 2.1d,
      (diameter / 2) + (Math.sin(Math.toRadians(radTick))) * diameter / 2.1d,
      (diameter / 2) + (Math.cos(Math.toRadians(radTick))) * diameter / 2.05d,
      (diameter / 2) + (Math.sin(Math.toRadians(radTick))) * diameter / 2.05d));
} // End for(radTick)

这是绘图的截图。可能有些难以看清,但请注意59分钟的刻度线。它是完全垂直的。 示例图片

请提供时钟截图和计算/绘制时钟的代码,这与编程有关。 - typo.pl
1个回答

8

Line2D.double()方法无法以亚像素精度绘制。

错误,使用RenderingHints.VALUE_STROKE_PUREGraphics2D对象可以使用形状Line2D以“亚像素”精度绘制。


我认为我可以只使用旋转的AffineTransform,而不必执行超采样。这是实现亚像素精度绘图的唯一/最佳方法吗?还是有潜在更快的解决方案?

我认为你漏掉了某些内容。Graphics2D对象已经持有一个AffineTransform并且它对于所有的绘图操作都在使用它,性能上也很便宜。


但是回到你的问题,你的代码中缺少以下内容:

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

以下是一个自包含的示例,可以生成这张图片: 截图
public static void main(String[] args) throws Exception {

    final JFrame frame = new JFrame("Test");

    frame.add(new JComponent() {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g;

            System.out.println(g2d.getTransform());
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                 RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                                 RenderingHints.VALUE_STROKE_PURE);

            double dia = Math.min(getWidth(), getHeight()) - 2;

            for (int i = 0; i < 60 ; i++) {
                double angle = 2 * Math.PI * i / 60;
                g2d.draw(new Line2D.Double(
                        (dia / 2) + Math.cos(angle) * dia / 2.1d,
                        (dia / 2) + Math.sin(angle) * dia / 2.1d,
                        (dia / 2) + Math.cos(angle) * dia / 2.05d,
                        (dia / 2) + Math.sin(angle) * dia / 2.05d));
            }

            g2d.draw(new Ellipse2D.Double(1, 1, dia - 1, dia - 1));
        }
    });

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(400, 400);
    frame.setVisible(true);
}

嗯,看起来你正在使用一个 AffineTransform,它需要进行平移(即超采样)和旋转。我希望避免这种情况,因为我目前每50毫秒更新一次。 - D.N.
你不应该单独绘制时钟表盘,然后再将其与变化的指针合并吗? - typo.pl
@D.N.:我只创建离线镜像一次?每次更新都需要创建吗?(@typo.pl: 是的) - dacwe
好的观点,但是指针与刻度线有相同的问题,只是不那么突出。特别是在分针上可以看出来,它会从一个像素位置“跳跃”到下一个位置,而不是平滑过渡。我希望所有组件都能具备亚像素精度.. :( - D.N.
1
太美了!非常漂亮!这正是所缺少的,谢谢!感谢您对Graphics2DAffineTransform使用的澄清。我已经知道实现几乎不使用处理能力(50ms更新1小时只需要0.3秒的处理时间),所以我不想破坏效率。现在需要更多的处理时间,但仍然完全可以忽略不计(我测量了60秒内的0.025秒,大约是5倍的处理)。 - D.N.
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接