Java - Требуется ли для точности субпиксельной линии AffineTransform? - PullRequest
9 голосов
/ 16 февраля 2011

Раньше я никогда не работал с методами рисования 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 минут. Это совершенно вертикально.

Sample image

1 Ответ

9 голосов
/ 16 февраля 2011

Методы Line2D.double () не могут рисовать с точностью до субпикселя.

Неправильно, при использовании RenderingHints.VALUE_STROKE_PURE объект Graphics2D может рисовать "субпиксельную" точность сshape Line2D.


Я предполагаю, что мог бы использовать AffineTransform только с вращением, в отличие от необходимости выполнять суперсэмплинг.Это единственный / лучший метод рисования с точностью до субпикселя?Или есть потенциально более быстрое решение?

Я думаю, что вам чего-то не хватает здесь.Объект Graphics2D уже содержит AffineTransform и использует его для всех действий рисования и его низкой производительности.


Но чтобы вернуться к вам, чего не хватает в вашемкод - это отсутствует:

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

Ниже приведен автономный пример, который создает эту картинку:

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