jmonkeyengine
jmonkeyengine copied to clipboard
Canvas embedded in Swing application renders at wrong scale on HiDPI
I am trying to embed JMonkeyEngine into a Swing application and noticed the Canvas is rendering at a different scale on HiDPI displays, the screenshot below is on a 200% scale display. I set a custom background color on the canvas to make sure it has the correct dimensions.
I combined the TetsGltfLoading class with some sample code for embedding the canvas:
package nl.rp.ddd.jme;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext;
public class JmeTests {
public JmeTests() {
final JFrame frame = new JFrame();
frame.setTitle("JME 3 Tests");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
AppSettings settings = new AppSettings(true);
settings.setWidth(800);
settings.setHeight(600);
settings.setVSync(true);
TestGltfLoading canvasApplication = new TestGltfLoading();
canvasApplication.setSettings(settings);
canvasApplication.setDisplayStatView(false);
canvasApplication.setDisplayFps(false);
canvasApplication.createCanvas();
JmeCanvasContext ctx = (JmeCanvasContext) canvasApplication.getContext();
ctx.setSystemListener(canvasApplication);
Dimension dim = new Dimension(800, 600);
ctx.getCanvas().setPreferredSize(dim);
ctx.getCanvas().setBackground(new Color(50, 50, 100));
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(ctx.getCanvas(), BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
System.setProperty("org.lwjgl.opengl.Display.enableHighDPI", "true");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new JmeTests();
}
});
}
}
I tested this with 3.3.2-stable, jme3-lwjgl (embedding a canvas failed with jme3-lwjgl3), java 10 and java 11 runtimes.
Hi @remcopoelstra , Have you resolved this problem ?
I managed to work around it for now by resizing the canvas myself (zooming in on the scene in the upper left corner), for this I override doLayout() of the parent container:
@Override
public void doLayout() {
if (canvas != null && scale != null) {
canvas.setBounds(0, 0, (int)(getWidth() * scale), (int)(getHeight() * scale));
} else {
super.doLayout();
}
}
This does have some issues though, overlay text is rendered at wrong scale for example. I wouldn't consider this workaround a solution to the problem.
I also did some experimenting with embedding a canvas with lwjgl3 (because I would prefer to use this instead of lwjgl2). I was able to embed a canvas on Windows with glfwAttachWin32Window() but I still have to try to get keyboard/mouse input working correctly. See this issue for more info on attaching glfw to an existing handle: https://github.com/glfw/glfw/issues/25
I was facing similar issues , so my approach was to :
Create a JPanel instance & initialize it -> add the JPanel to the JFrame content pane -> attach the jme3 canvas on top of the JPanel not the Jframe.
Check this :
https://hub.jmonkeyengine.org/t/integrate-jmonkey-canvas-into-swing-frame/44018/4?u=pavl_g
This example resembles AWTPanel from paul's SPiX library , but.you can use javax.swing.JPanel as well.
Extending on @remcopoelstra response, this is an alright workaround for Windows.
// Custom JPanel to correct for Windows OS display scaling.
// If you have scaling set to something different from 100% in Windows display settings, the canvas will render at the incorrect size.
// This fixes this issue, by scaling the canvas bounds by the Windows scaling amount.
JPanel panel = new JPanel(new BorderLayout()) {
@Override
public void doLayout() {
if (JmeSystem.getPlatform().getOs() == Platform.Os.Windows) {
GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
AffineTransform displayTransform = graphicsConfiguration.getDefaultTransform();
double scaleX = displayTransform.getScaleX(); // Windows OS Display X scaling.
double scaleY = displayTransform.getScaleY(); // Windows OS Display Y scaling.
canvas.setBounds(0, 0, (int)(getWidth() * scaleX), (int)(getHeight() * scaleY));
}
}
};
panel.add(canvas, BorderLayout.CENTER);
This seems related... solution looks good. https://forum.jogamp.org/GLcanvas-vs-NEWT-on-Hi-DPI-Screens-tp4041191p4041642.html
I think returning scaled sizes from the getHeight() and getWidth() methods of the canvas is better and seems to break less things. Modified LwjglCanvas.java
Not submitting this as change though, because I'm unsure if this workaround breaks anything else...
Why isn't this a bug in AWT? Seems that for 'Canvas' ought to expand in size to fix the parent JPanel. I think Canvas is using physical pixel for its size, as opposed to scaled, logical pixels. Perhaps it really can't, since Canvas would have to resample any image it contains. But then, I'd want Canvas to increase its size the way your code does. Do images in Canvas always display in the current screen resolution?