我想使用JavaFX Canvas。画布的坐标是由双精度值提供的,但是我知道为了“捕捉”到像素网格,我需要整数以避免在像素之间绘制“中间”,这会导致边缘模糊/平滑。
考虑这个最小的例子:
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获得它)。
然而,整个画布以此比例缩放,我们得到了模糊效果。查看附带的图像(但是您必须缩放,并且可能在图形程序中而不是在浏览器中查看它们)。
然后,我认为我们可以调整原始坐标,以便缩放后的坐标将产生整数。例如,要确保在缩放后的图像上命中偏移量为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);
但是此静止导致边缘模糊。
我们如何解决这个问题?理想情况下,我想完全关闭画布的dpi缩放比例,并进行自己的(像素完美)计算。这可能还是有其他解决方案?
对于drawImage,有一个参数setSmoothing,但是当直接绘制形状时(如rect)没有任何东西。
我不知道这是否是您想要的,但似乎DPI和Java存在一些问题。
好像Java报告自己是DPI感知的,因此操作系统无法扩展它,但是它本身不能正确地扩展。您可以使用以下类似的jvm属性来关闭此行为并强制系统缩放Java GUI:
-Dsun.java2d.dpiaware=false
我在SuperUser论坛上找到了一个有趣的讨论,您可能会找到有用的东西,请take a look。