JDK-8222209 : JavaFX is rendered blurry on systems with monitors in different configuration
  • Type: Bug
  • Component: javafx
  • Sub-Component: swing
  • Affected Version: jfx11
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_10
  • CPU: x86_64
  • Submitted: 2019-03-19
  • Updated: 2024-08-20
  • Resolved: 2023-08-21
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
jfx22 b06Fixed
Related Reports
Duplicate :  
Duplicate :  
Description
ADDITIONAL SYSTEM INFORMATION :
Windows 10, OpenJDK 11.01, OpenJFX11

A DESCRIPTION OF THE PROBLEM :
Error receipe:
* Two monitors in different scales (175% 125% (Main-Monitor)) 
* Use the JavaFX Panel

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Error receipe:
* Two monitors in different scales (175% 125% (Main-Monitor)) 
* Use the JavaFX Panel inside a JFrame
* Move the JFrame from one monitor to the other -> the rendering inside the JFXPanel becomes blurry

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Clean rendering like in Swing
ACTUAL -
The Label in the JFXPanel is rendered blurry

---------- BEGIN SOURCE ----------
package test;

import java.awt.FlowLayout;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.Label;

public class UnsharpJFXPanel
{
   public static void main(String... args)
   {
      JFrame frame = new JFrame("Unsharp JFXPanel");
      frame.setSize(550, 100);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      JFXPanel jfxPanel = new JFXPanel();
      jfxPanel.setSize(500, 30);
      Label label1 = new Label("JavaFX: An preost wes on leoden, Laȝamon was ihoten- He wes Leovenaðes sone");
      label1.setPrefWidth(500);

      JPanel jPanel = new JPanel(new FlowLayout());
      jPanel.add(jfxPanel);
      jPanel.add(new JLabel("Swing: An preost wes on leoden, Laȝamon was ihoten- He wes Leovenaðes sone"));
      frame.setContentPane(jPanel);

      frame.setVisible(true);
      Platform.runLater(() -> {
         jfxPanel.setScene(new Scene(label1));
      });
   }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The newScaleFactorX and newScaleFactorY in updateComponentSize() must not be the one of the DefaultScreenDevice, but the one that is currently used for display.

We use for updateComponentSize() and paintComponent(Graphics g): 
AffineTransform defaultTransform = GUIHelper.getGraphicsConfiguration(this, false).getDefaultTransform();
final double newScaleFactorX = defaultTransform.getScaleX();
final double newScaleFactorY = defaultTransform.getScaleY();

With GUIHelper
/**
    * Nudges a point onto a given screen.
    *
    * The given point is translated by at most 1 px per direction if that makes it
    * lie inside the bounds of the given screen. This is necessary if a highly scaled
    * screen (>= 200%) is used and a particular correct logical bound of that screen
    * differs from a part of the location of a component that is completely on that screen.
    *
    * @param point a point
    * @param configuration a screen
    * @return the same point again, but potentially modified
    */
   public static Point nudgeLocationOntoPreferredDevice(Point point, GraphicsConfiguration configuration)
   {
      if ((point == null) || (configuration == null))
      {
         return point;
      }

      Rectangle bounds = configuration.getBounds();

      int x = point.x;
      if (x == bounds.x - 1)
      {
         x++;
      }
      else if (x == bounds.x + bounds.width)
      {
         x--;
      }

      int y = point.y;
      if (y == bounds.y - 1)
      {
         y++;
      }
      else if (y == bounds.y + bounds.height)
      {
         y--;
      }

      if (bounds.contains(x, y))
      {
         point.move(x, y);
      }

      return point;
   }

   /**
    * Determines a screen for a given component, or the primary screen if there is no proper one.
    *
    * If {@code checkBounds} is {@code true}, the screen's bounds are checked against the
    * location of the component. Other than usual, do not start with screen 0 but consider the
    * associated screen first. This is necessary if multiple screen have different scalings, because
    * the downscaled logical pixels can overlap, leading to the impression that a pixel actually
    * belongs to another screen.
    *
    * @param component the component to consider
    * @param checkBounds whether we need to check whether the location on screen is part of
    * the returned screen
    */
   @NotNull
   public static GraphicsConfiguration getGraphicsConfiguration(Component component, boolean checkBounds)
   {
      if (component != null)
      {
         GraphicsConfiguration configuration = component.getGraphicsConfiguration();
         if ((configuration != null) && !checkBounds)
         {
            return configuration;
         }
         else if (checkBounds && component.isShowing())
         {
            Point location = nudgeLocationOntoPreferredDevice(component.getLocationOnScreen(), configuration);
            if ((configuration != null) && configuration.getBounds().contains(location))
            {
               // Prefer the associated device
               return configuration;
            }
            else
            {
               for (GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices())
               {
                  if (device.getType() == GraphicsDevice.TYPE_RASTER_SCREEN)
                  {
                     configuration = device.getDefaultConfiguration();
                     if (configuration.getBounds().contains(location))
                     {
                        return configuration;
                     }
                  }
               }
            }
         }
      }
      return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
   }


Comments
Changeset: 6a7c7437 Author: Prasanta Sadhukhan <psadhukhan@openjdk.org> Date: 2023-08-21 09:52:48 +0000 URL: https://git.openjdk.org/jfx/commit/6a7c743765f50d469ab72be907c45005202be6b5
21-08-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jfx/pull/1171 Date: 2023-07-06 13:10:44 +0000
02-08-2023

Are these two issues related? JDK-8222209 (fx) and JDK-8312470 (swing)?
28-07-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jfx/pull/1189 Date: 2023-07-28 09:26:35 +0000
28-07-2023