JavaFX - 在高 dpi 的 Canvas 上使用矩形绘制时避免模糊

标签 java javafx canvas blur highdpi

我想在 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();
    }

}

这有效并且会产生清晰的边缘,请考虑此图像:

enter image description here

现在假设启用了 HighDPI,即考虑我们是否将 Windows 10 中的图形缩放到 125%。这将导致比例因子为 1.25(我们可以通过 getOutputScaleX/Y 获得)。

然而,整个 Canvas 会根据该因子进行缩放,我们会得到模糊。查看所附图像(但您必须缩放,并且可能在图形程序中而不是在浏览器中查看它们)。

enter image description here

然后我想我们可以调整原始坐标,使得缩放后的坐标结果为整数。例如,为了确保我们在缩放图像上达到偏移量 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 倍,因此您可以看到没有模糊):

enter image description here

下面是更新后的示例中 125% 的情况:

enter image description here

关于JavaFX - 在高 dpi 的 Canvas 上使用矩形绘制时避免模糊,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62246642/

相关文章:

Android:如何在另一个位图上简单地绘制一个位图

java - 应用程序级别的安全远程密码协议(protocol)

java - Struts2不能使用验证?

java - 显示哪个月的降雨量最低 - arrays java

java - 线程中出现异常 "JavaFX Application Thread"java.lang.OutOfMemoryError : Java heap space

java - 如何更改 javafx tableview 中单元格的颜色?

java - 使用@DefaultProperty时为"Element does not define a default property"

javascript - 根据文档无法在 PaperJs 中创建随机点

java - 如何在2个不同类型的列表中查找元素匹配并使用java 8流连接它们?

javascript - 找到两点之间的中点