I'd like to draw on a 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.
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, i.e. consider if we scale graphics in Windows 10 to 125 percent. This will result in a scale factor of 1.25 (we can obtain this via getOutputScaleX/Y).
However then the whole canvas is scaled with that factor, and we get a blur. 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, i.e. change this line
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. 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).



