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();
}
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();
}
- duplicates
-
JDK-8338378 JFXPanel: distorted rendering when using display scale > 100%
- Closed
-
JDK-8261777 JFXPanel applies wrong screen scaling
- Closed