亚像素渲染
Sub-pixel rendering
我正在尝试使用子像素定位在 Java 中渲染 2d 形状,但失败了。下面是一次失败的尝试(暂时忽略 scale
和 AffineTransform
):
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));
}
}
}
这会产生以下输出:
线条应该均匀分布,但是,如您所见,它们不是。我的猜测是起点和终点被四舍五入到最近的像素。
关于 SO 的建议解决方案提到使用 Line2D.Double
(我已经在做但显然行不通),或使用 AffineTransform
缩小规模。如果您将代码示例中的 scale
变量更改为 100,那么这会以更高的比例绘制线条,然后根据建议使用变换将其缩小。这对生成的图像也没有影响。
我做错了什么?
解决方法是设置渲染提示之一:
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
引用Javadocs:
Stroke normalization control hint value -- geometry should be left unmodified and rendered with sub-pixel accuracy.
我正在尝试使用子像素定位在 Java 中渲染 2d 形状,但失败了。下面是一次失败的尝试(暂时忽略 scale
和 AffineTransform
):
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));
}
}
}
这会产生以下输出:
线条应该均匀分布,但是,如您所见,它们不是。我的猜测是起点和终点被四舍五入到最近的像素。
关于 SO 的建议解决方案提到使用 Line2D.Double
(我已经在做但显然行不通),或使用 AffineTransform
缩小规模。如果您将代码示例中的 scale
变量更改为 100,那么这会以更高的比例绘制线条,然后根据建议使用变换将其缩小。这对生成的图像也没有影响。
我做错了什么?
解决方法是设置渲染提示之一:
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
引用Javadocs:
Stroke normalization control hint value -- geometry should be left unmodified and rendered with sub-pixel accuracy.