[英]JavaFX - avoid blur when drawing with rect on Canvas with highdpi
I'd like to draw on a JavaFX Canvas.我想画一个 JavaFX Canvas。 Coordinates for the canvas are provided by double values, but I understand that in order to "snap" to the pixel grid, I need integer numbers to avoid drawing "inbetween" pixels, which will result in a blur/smoothing on edges.
canvas 的坐标由 double 值提供,但我知道为了“捕捉”到像素网格,我需要 integer 数字以避免绘制“中间”像素,这将导致边缘模糊/平滑。
Consider this minimal example:考虑这个最小的例子:
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();
}
}
This works and will result in a clear edge, consider this image:这有效并且会产生清晰的边缘,考虑这个图像:
Now suppose HighDPI is enabled, ie consider if we scale graphics in Windows 10 to 125 percent.现在假设启用了 HighDPI,即考虑我们是否将 Windows 中的图形缩放 10% 到 125%。 This will result in a scale factor of 1.25 (we can obtain this via getOutputScaleX/Y).
这将导致比例因子为 1.25(我们可以通过 getOutputScaleX/Y 获得)。
However then the whole canvas is scaled with that factor, and we get a blur.然而,然后整个 canvas 用该因子进行缩放,我们得到了模糊。 See the attached images (but you have to zoom, and maybe view them in a graphics program and not in a browser).
查看附加的图像(但您必须缩放,并且可能在图形程序而不是浏览器中查看它们)。
Then I thought that we can adjust the original coordinates such that the scaled coordinates will result in integers.然后我想我们可以调整原始坐标,使缩放后的坐标产生整数。 For example to make sure we hit an integer with offset 9 * 1.25 = 11.25 on the scaled image, let's try to target 11.0 instead, ie change this line
例如,为了确保我们在缩放后的图像上命中偏移量为 9 * 1.25 = 11.25 的 integer,让我们尝试以 11.0 为目标,即更改此行
gc.rect(9.0, 9.0, 50, 50);
to至
gc.rect(11.0/1.25, 11.0/1.25, 50, 50);
But this still results in a blur at the edge.但这仍然会导致边缘模糊。
How can we work around this?我们如何解决这个问题? Ideally I would like to turn off dpi scaling completely for the canvas, and do my own (pixel-perfect) calculations.
理想情况下,我想完全关闭 canvas 的 dpi 缩放,并进行自己的(像素完美)计算。 Is this possible or is there any other solution?
这是可能的还是有其他解决方案?
For drawImage there is a parameter setSmoothing, but there is nothing when drawing shapes directly (like rect).对于drawImage有一个参数setSmoothing,但是直接绘制形状的时候(比如rect)什么都没有。
You can solve this by eliminating the scaling used by scaling the Canvas to a smaller size.您可以通过消除将 Canvas 缩放到更小的尺寸所使用的缩放来解决此问题。 Here is a sample (note that lines are drawn "in between" pixels in JavaFX, so to get them sharp you need to add 0.5).
这是一个示例(请注意,线条是在 JavaFX 中的像素“中间”绘制的,因此要使它们变得清晰,您需要添加 0.5)。
This example also places a Group
around the scaled Canvas
.此示例还在缩放后的
Canvas
周围放置了一个Group
。 This is required to take the scale into account when doing layout, but also has some effect on rendering.这需要在做布局时考虑到比例,但对渲染也有一定的影响。 This example was tested at 100%, 125% and 150%.
此示例在 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()));
}
}
Here is how it looks on a 150% monitor (left side is scaled up 5 times so you can see there is no blur):这是它在 150% 显示器上的样子(左侧放大了 5 倍,因此您可以看到没有模糊):
And here is how it looks at 125% with the updated example:以下是更新后的示例显示为 125% 的样子:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.