JDK-8230007 : HiDPI scaling (125%) makes WebView in JFXPanel have a skewed/tilted rendering
  • Type: Bug
  • Component: javafx
  • Sub-Component: swing
  • Affected Version: openjfx11
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_10
  • CPU: x86_64
  • Submitted: 2019-08-20
  • Updated: 2020-06-12
  • Resolved: 2020-06-12
Related Reports
Duplicate :  
Description
ADDITIONAL SYSTEM INFORMATION :
Windows 10 Pro (Version	10.0.17134 Build 17134),
openjdk version "11.0.3-redhat" 2019-04-16 LTS
OpenJDK Runtime Environment 18.9 (build 11.0.3-redhat+7-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.3-redhat+7-LTS, mixed mode)
VM options : --add-modules javafx.base,javafx.graphics,javafx.controls,javafx.web,javafx.media,javafx.swing and a "-p" to directory/directories containing the jars.
Screen resolution: 3840x2160
DPI scaling: 125%

A DESCRIPTION OF THE PROBLEM :
Run the supplied code on a monitor with native resolution 3840x2160 and set to scale (Windows settings) at 125%.
The supplied code uses Robot to try and mimic a user resizing the JFrame. If this does not work (I suspect that the error will manifest at different "resizings" depending on screen resolution and maybe other things), please try and resize the JFrame manually.
The problem is reproducible at 175% scaling too, but at different sizes of the JFrame. The problem is NOT reproducible at 100% or 250% scaling. I have not tried other scaling settings.
Setting the size of the JFrame via code does also NOT show the bug. This makes me believe that the bug is
a rounding error somewhere (you can't set the size of JFrame to a non-integer so I can't test this).
It is possible that the bug happens without JFXPanel but I have not been able to reproduce it without it.

NOTE 1: The supplied code will connect to http://www.google.com as an example site. The problem occurs with any type of content supplied to the WebView (not just google). Just fiddle with the JFrame size manually if the Robot code doesn't show the problem on other sites.

NOTE 2: The bug has nothing to do with the usage of Robot. That part of the code is only there to show the problem without having to tell you exactly how much to resize the JFrame. That is, this problem occurs in our application even though we never use Robot. I can't send you our entire app though so this was the best solution I could think of to show the problem with as little code as possible. I suspect that the error has nothing to do with resize in general, but that either events get lost between Swing and JavaFX or that there is some wonky rounding error when converting sizes/locations between Swing and JavaFX.

Note 3: This is running on RedHat's OpenJDK distribution. It is an OpenJDK or OpenJFX bug though so if you believe this is the cause of the bug, I would be happy to be directed to a place to file bugs against OpenJFX/OpenJDK rather than Oracle's JDK.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the supplied code. Make sure not to move the mouse while Robot is running or the bug will probably only been seen as a flickering.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
A straight, non-skewed rendition of http://www.google.com.
ACTUAL -
The rendition is skewed at what seems to be 45 degrees. I would supply a screenshot but not sure how to do that.

---------- BEGIN SOURCE ----------
package your.package.structure;

import java.awt.BorderLayout;
import javafx.application.Platform;
import javafx.concurrent.Worker;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.AnchorPane;
import javafx.scene.robot.Robot;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class App {
    private final JFrame frame;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new App();
        });
    }

    public App() {
        frame = new JFrame("Test");
        JFXPanel jfx = new JFXPanel();
        Platform.runLater(() -> {
            jfx.setScene(createScene());
            SwingUtilities.invokeLater(() -> {
                frame.setLayout(new BorderLayout());
                frame.add(jfx, BorderLayout.CENTER);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            });
        });

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public Scene createScene() {
        AnchorPane ap = new AnchorPane();
        WebView browser = new WebView();
        browser.setContextMenuEnabled(false);

        WebEngine webkit = browser.getEngine();

        webkit.load("http://www.google.com");

        webkit.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
            if(newValue.equals(Worker.State.SUCCEEDED)) {
                Platform.runLater(() -> {
                    Robot rr = new Robot();
                    double start = frame.getX() + frame.getWidth() - 4;
                    double end = frame.getX() + frame.getWidth() - 6;
                    double x = start;
                    double y = frame.getY() + 100;
                    rr.mouseMove(x, y);
                    rr.mousePress(MouseButton.PRIMARY);
                    for(; x >= end; x = x - 0.05) {
                        double finalX = x;
                        Platform.runLater(() -> {
                            rr.mouseMove(finalX, y);
                        });
                    }
                    Platform.runLater(() -> {
                        rr.mouseRelease(MouseButton.PRIMARY);
                    });
                });
            }
        });

        AnchorPane.setTopAnchor(browser, 0d);
        AnchorPane.setBottomAnchor(browser, 0d);
        AnchorPane.setLeftAnchor(browser, 0d);
        AnchorPane.setRightAnchor(browser, 0d);
        ap.getChildren().add(browser);

        Scene scene = new Scene(ap);

        return scene;
    }
}
---------- END SOURCE ----------

FREQUENCY : always



Comments
As noted in https://github.com/openjdk/jfx/pull/246 this is a duplicate of JDK-8220484.
12-06-2020

I tried with monitor of 1920x1080 with scaling of 125 (both with Windows setting and with command line option of -Dsun.java2d.uiScale=1.25 -Dglass.win.uiScale=125%, I observe google.com is rendered normally and not slanted. I will see if I can get monitor of greater resolution. But it seems to be a duplicate of JDK-8220484
27-08-2019

The following is from the reporter of this bug: First of all, sorry if this is not the place but I've been trying to find a way to comment on a bug that I reported a few days ago, since Kevin Rushforth has commented on it in a way that makes me believe that the bug may receive a harder than needed triage and that you (possibly) will spend unnecessary time looking for a solution. So this is my attempt at reaching the devs. Secondly, I think a P4 priority is fine, it's probably not a common bug, nothing crashes and I've found a workable workaround (see below). As the report states, this has nothing to do with the use of Robot. We get this error without using either AWT Robot or JFX Robot. The reason Robot is used is so that I could send a minimal amount of code showing the problem without having to make further instructions about resizing, how many pixels and so on. Just a couple lines of code to force the bug to show on screen. If you resize manually, one needs a fairly steady hand to see the bug as more than a flickering. So I wanted the code to show that the skewed rendering is persistent at certain pixels, not just a flickering that corrects itself. You may remove the entire loadWorker-listener and the bug will still be reproducible always, but then you need to manually resize the JFrame (with a steady hand to hit the correct pixels and release the mouse button without moving the mouse away from that position). It's not hard to do manually, just harder than to show the bug programmatically. (As an aside: Kevin Rushforth's comment hints at it being more natural to use AWT Robot in a JFrame. This is true but I needed to wait for the WebEngine to load the site before resizing the window so it made more sense to me to use JFX's Robot since I was on the JFX-thread.) Any way, the bug is reproducible without Robots of any kind so the bug is either a problem with JFXPanel or JavaFX/Swing/AWT's HiDPI support or more likely, that the HiDPI support of the different frameworks don't play nice with each other. The following workaround may give more insight. If the JFXPanel's setBounds(int,int,int,int) is overriden with code to "skip" every pixel resize that, when multiplied with the DPI-scaling of the screen, results in a number that ends with .25 or .75, everything works without a skewed rendering. This obviously means that the JFXPanel will sometimes be smaller than it should be and thus not rendering all the way out to the parent's bounds, but that is a tradeoff I'm willing to make. A few missed pixels on high resolution monitors isn't as eye jarring as a 45 degree x-shearing of the entire rendering surface. Here is example code to show how the "skipping" is done: JFXPanel jfx = new JFXPanel() { private int skipPixels(int number, double scale) { double scaled = number * scale; while((scaled - Math.floor(scaled) == 0.25) || (scaled - Math.floor(scaled) == 0.75)) { number--; scaled = number * scale; } return number; } @Override public void setBounds(int x, int y, int width, int height) { double scaleX = getGraphicsConfiguration().getDefaultTransform().getScaleX(); double scaleY = getGraphicsConfiguration().getDefaultTransform().getScaleX(); int w = skipPixels(width, scaleX); int h = skipPixels(height, scaleY); super.setBounds(x, y, w, h); } };
23-08-2019

If the top-level frame is a Swing JPanel, it would seem more natural to use an AWT Robot rather than a JavaFX Robot, but it still suggests that there is a bug either in JFXPanel or in javafx.scene.Robot. [~psadhukhan] If the bug turns out to be in Robot rather than JFXPanel then you can reassign it (to javafx/window-toolkit).
22-08-2019