ADDITIONAL SYSTEM INFORMATION :
JavaFX 11 + JDK 11.0.6
A DESCRIPTION OF THE PROBLEM :
Suppose we draw on a JavaFX Canvas. Coordinates for the canvas are provided by double values, but in order to "snap" to the pixel grid, we need integer numbers to avoid drawing "inbetween" pixels, which will result in a blur/smoothing on edges.
The supplied example runs well with clear edges on 100% dpi scaling with Windows 10, as well as 150% dpi scaling. However with certain fractions - like 125% scaling, there is one line of blur around the rectangle.
cf. https://stackoverflow.com/questions/62246642/javafx-avoid-blur-when-drawing-with-rect-on-canvas-with-highdpi/62326515
This bug might be related: https://bugs.openjdk.java.net/browse/JDK-8220484
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the supplied source code. A dark grey rectangle upon a light gray rectangle will appear. In Windows 10 enable high dpi scaling (Windows Settings app -> System -> Display -> Scale and layout section -> Change the size of text, apps, and other icons) and run the proram each with 100%, 125% and 150%.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
There should be a clear edge between the dark and light gray box.
ACTUAL -
At 125% scaling, there is one pixel line of "blur" between the light and dark gray box. It is one pixel line with an interpolated color.
---------- BEGIN SOURCE ----------
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();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
There is no workaround. A naive way would be to "snap to a grid", i.e. get the ourputScale Factor via outputScaleX/Y, and divide the pixel size, i.e. consider change gc.rect(9.0, 9.0,....). Since 9.0 * 1.25 = 11.25. The fraction is probably the cause of the interpolation. We could try to "hit" an even pixel, i.e. 11.0 by chaning the line to gc.rect(11.0/1.25, 11.0/1.25, 50, 50); but that still results in a blur. The same hold if one tries to deactivate automatic scaling for the whole canvas, by i.e. canvas.scaleXProperty().bind(new SimpleDoubleProperty(1.0).divide(scene.getWindow().outputScaleXProperty()));
FREQUENCY : always
JavaFX 11 + JDK 11.0.6
A DESCRIPTION OF THE PROBLEM :
Suppose we draw on a JavaFX Canvas. Coordinates for the canvas are provided by double values, but in order to "snap" to the pixel grid, we need integer numbers to avoid drawing "inbetween" pixels, which will result in a blur/smoothing on edges.
The supplied example runs well with clear edges on 100% dpi scaling with Windows 10, as well as 150% dpi scaling. However with certain fractions - like 125% scaling, there is one line of blur around the rectangle.
cf. https://stackoverflow.com/questions/62246642/javafx-avoid-blur-when-drawing-with-rect-on-canvas-with-highdpi/62326515
This bug might be related: https://bugs.openjdk.java.net/browse/JDK-8220484
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the supplied source code. A dark grey rectangle upon a light gray rectangle will appear. In Windows 10 enable high dpi scaling (Windows Settings app -> System -> Display -> Scale and layout section -> Change the size of text, apps, and other icons) and run the proram each with 100%, 125% and 150%.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
There should be a clear edge between the dark and light gray box.
ACTUAL -
At 125% scaling, there is one pixel line of "blur" between the light and dark gray box. It is one pixel line with an interpolated color.
---------- BEGIN SOURCE ----------
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();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
There is no workaround. A naive way would be to "snap to a grid", i.e. get the ourputScale Factor via outputScaleX/Y, and divide the pixel size, i.e. consider change gc.rect(9.0, 9.0,....). Since 9.0 * 1.25 = 11.25. The fraction is probably the cause of the interpolation. We could try to "hit" an even pixel, i.e. 11.0 by chaning the line to gc.rect(11.0/1.25, 11.0/1.25, 50, 50); but that still results in a blur. The same hold if one tries to deactivate automatic scaling for the whole canvas, by i.e. canvas.scaleXProperty().bind(new SimpleDoubleProperty(1.0).divide(scene.getWindow().outputScaleXProperty()));
FREQUENCY : always
- relates to
-
JDK-8220484 JFXPanel renders a slanted image with a hidpi monitor scale of 125% or 175%
- Resolved