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

发布于 2024-10-17 20:06:49 字数 1317 浏览 7 评论 0原文

我以前从未使用过 Java 绘图方法,因此我决定深入研究并创建一个模拟时钟作为 PoC。除了指针之外,我还画了一个钟面,其中包括分钟/小时的刻度线。我使用简单的正弦/余弦计算来确定圆周围直线的位置。

然而,我注意到由于分钟刻度线非常短,所以线条的角度看起来不对。我确信这是因为 Graphics2D.drawLine()Line2D.double() 方法都无法以亚像素精度进行绘制。

我知道我可以绘制从中心开始的线条,然后用一个圆圈将其遮盖(以创建更长、更准确的线条),但这似乎是一个不优雅且成本高昂的解决方案。我已经对如何执行此操作进行了一些研究,但我遇到的最佳答案是使用 AffineTransform。我假设我可以使用仅旋转的 AffineTransform,而不是必须执行超级采样。

这是唯一/最好的亚像素精度绘图方法吗?或者有可能更快的解决方案吗?

编辑:我已经为 Graphics2D 对象设置了 RenderingHint

根据要求,这里是一些代码(没有完全优化,因为这只是一个 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 分钟的刻度线。它是完全垂直的。

示例图片

I've never worked with Java drawing methods before, so I decided to dive in and create an analog clock as a PoC. In addition to the hands, I draw a clock face that includes tick marks for minutes/hours. I use simple sin/cos calculations to determine the position of the lines around the circle.

However, I've noticed that since the minute tick-marks are very short, the angle of the lines looks wrong. I'm certain this is because both Graphics2D.drawLine() and Line2D.double() methods cannot draw with subpixel accuracy.

I know I can draw lines originating from the center and masking it out with a circle (to create longer, more accurate lines), but that seems like such an inelegant and costly solution. I've done some research on how to do this, but the best answer I've come across is to use an AffineTransform. I assume I could use an AffineTransform with rotation only, as opposed to having to perform a supersampling.

Is this the only/best method of drawing with sub-pixel accuracy? Or is there a potentially faster solution?

Edit: I am already setting a RenderingHint to the Graphics2D object.

As requested, here is a little bit of the code (not fully optimized as this was just a 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)

Here's a screenshot of the drawing. It may be somewhat difficult to see, but look at the tick mark for 59 minutes. It is perfectly vertical.

Sample image

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

撑一把青伞 2024-10-24 20:06:49

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);
}

Line2D.double() methods cannot draw
with subpixel accuracy.

Wrong, using RenderingHints.VALUE_STROKE_PURE the Graphics2D object can draw "subpixel" accuracy with the shape Line2D.


I assume I could use an
AffineTransform with rotation only, as
opposed to having to perform a
supersampling. Is this the only/best
method of drawing with sub-pixel
accuracy? Or is there a potentially
faster solution?

I think you are missing somthing here. The Graphics2D object already holds a AffineTransform and it is using it for all drawing actions and its cheap performance wise.


But to get back to you what is missing from your code - this is missing:

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

Below is a self contained example that produces this picture:

screenshot

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文