Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8247541

Blur when drawing on Canvas with HighDPI enabled

XMLWordPrintable

    • x86_64
    • windows_10

      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


            arapte Ambarish Rapte
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: