Java - Требуется ли для точности субпиксельной линии AffineTransform?
Я никогда раньше не работал с методами рисования 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 минут. Это совершенно вертикально.
1 ответ
Методы Line2D.double() не могут рисовать с субпиксельной точностью.
Неправильно, используя RenderingHints.VALUE_STROKE_PURE
объект Graphics2D может рисовать "субпиксельную" точность с формой 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);
}