我想在 JavaFX Canvas 上绘图。 Canvas 的坐标由 double 值提供,但我知道为了“捕捉”到像素网格,我需要整数以避免绘制“中间”像素,这将导致边缘模糊/平滑。
考虑这个最小的例子:
public class App extends Application {
@Override
public void start(Stage stage) {
var vbox = new VBox();
var canvas = new Canvas(600, 400);
var gc = canvas.getGraphicsContext2D();
// draw a background
gc.beginPath();
gc.setFill(Color.rgb(200,200,200));
gc.rect(0, 0, 600, 400);
gc.fill();
// draw a smaller square
gc.beginPath();
gc.setFill(Color.rgb(100,100,100));
gc.rect(9.0, 9.0, 50, 50); // snap to grid
gc.fill();
vbox.getChildren().addAll(canvas);
var scene = new Scene(vbox, 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
这有效并且会产生清晰的边缘,请考虑此图像:
现在假设启用了 HighDPI,即考虑我们是否将 Windows 10 中的图形缩放到 125%。这将导致比例因子为 1.25(我们可以通过 getOutputScaleX/Y 获得)。
然而,整个 Canvas 会根据该因子进行缩放,我们会得到模糊。查看所附图像(但您必须缩放,并且可能在图形程序中而不是在浏览器中查看它们)。
然后我想我们可以调整原始坐标,使得缩放后的坐标结果为整数。例如,为了确保我们在缩放图像上达到偏移量 9 * 1.25 = 11.25 的整数,让我们尝试以 11.0 为目标,即更改此行
gc.rect(9.0, 9.0, 50, 50);
到
gc.rect(11.0/1.25, 11.0/1.25, 50, 50);
但这仍然会导致边缘模糊。
我们如何解决这个问题?理想情况下,我想完全关闭 Canvas 的 dpi 缩放,并进行我自己的(像素完美)计算。这可能吗?还有其他解决方案吗?
对于drawImage,有一个参数setSmoothing,但是直接绘制形状(如矩形)时没有任何参数。
最佳答案
您可以通过将 Canvas 缩放到较小尺寸来消除缩放来解决此问题。这是一个示例(请注意,在 JavaFX 中,线条是在“像素之间”绘制的,因此为了使它们变得清晰,您需要添加 0.5)。
此示例还在缩放后的 Canvas
周围放置了一个 Group
。这在布局时需要考虑比例,但对渲染也有一定影响。此示例在 100%、125% 和 150% 下进行了测试。
import javafx.application.Application;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class CanvasTest extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) throws Exception {
HBox hbox = new HBox();
Canvas canvas = new Canvas(100,100);
GraphicsContext g2d = canvas.getGraphicsContext2D();
g2d.fillRect(15, 15, 60, 10);
g2d.fillRect(15.5, 30, 60, 10); // supposed to be blurry
g2d.fillRect(16, 45, 60, 10);
g2d.fillRect(16.5, 60, 60, 10); // supposed to be blurry
g2d.setStroke(Color.RED);
g2d.strokeLine(13.5, 13.5, 13.5, 93.5);
g2d.strokeLine(13.5, 13.5, 93.5, 93.5);
hbox.getChildren().add(new Group(canvas));
Scene scene = new Scene(hbox);
stage.setScene(scene);
stage.show();
canvas.scaleXProperty().bind(new SimpleDoubleProperty(1.0).divide(scene.getWindow().outputScaleXProperty()));
canvas.scaleYProperty().bind(new SimpleDoubleProperty(1.0).divide(scene.getWindow().outputScaleYProperty()));
}
}
这是它在 150% 显示器上的外观(左侧放大了 5 倍,因此您可以看到没有模糊):
下面是更新后的示例中 125% 的情况:
关于JavaFX - 在高 dpi 的 Canvas 上使用矩形绘制时避免模糊,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62246642/