There is no direct translation needed for the code-related question you provided.

huangapple go评论126阅读模式
英文:

Is there a way to disable native DPI scaling in swing via code or command line (JDK 9+)?

问题

I recently made the decision to switch over to a newer version of the JDK from the one I was using. (Specifically from jdk1.8.0_261 to jdk-14.0.2). This upgrade when pretty smoothly, as most features work the way I expected them and I was able to find how to adjust to anything that was not the same.

However, as shown in the title, I came across an issue with a specific application I am writing that uses Swing. My main monitor is a 4k monitor, and I have set the windows system scaling for that monitor to 200%. While every other Swing application being scaled to match that DPI setting is nice, for this specific application I want to disable that scaling and have pixels be drawn 1:1, especially when this Application is distributed.

Is there a way to disable this automatic scaling with a VM argument? Or is there a way to disable the scaling at runtime before any calls are made to the swing library? (Similar to how any changes to the look and feel must be done before any other calls to the library.)

英文:

I recently made the decision to switch over to a newer version of the JDK from the one I was using. (Specifically from jdk1.8.0_261 to jdk-14.0.2). This upgrade when pretty smoothly, as most features work the way I expected them and I was able to find how to adjust to anything that was not the same.

However, as shown in the title, I came across an issue with a specific application I am writing that uses Swing. My main monitor is a 4k monitor, and I have set the windows system scaling for that monitor to 200%. While every other Swing application being scaled to match that DPI setting is nice, for this specific application I want to disable that scaling and have pixels be drawn 1:1, especially when this Application is distributed.

Is there a way to disable this automatic scaling with a VM argument? Or is there a way to disable the scaling at runtime before any calls are made to the swing library? (Similar to how any changes to the look and feel must be done before any other calls to the library.)

For reference:

Here is the code I use to create my JFrame and to create the graphics object I draw everything onto, which happens in a timed loop outside of this class and the scope of this question.

public class ScreenManager {
	private GraphicsDevice device;
	private final JFrame frame;
	
	private static ScreenManager instance;
	
	public ScreenManager() {
		instance = this;
		
		GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
		this.device = env.getDefaultScreenDevice();
		
		this.frame = new JFrame(game.getGame().gameName());
		this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.frame.setUndecorated(true);
		this.frame.setIgnoreRepaint(true);
		this.frame.setResizable(false);
		
		this.device.setFullScreenWindow(frame);
		this.device.setDisplayMode(device.getDisplayMode());
		
		this.frame.createBufferStrategy(2);
	}

    public Graphics2D getRenderGraphics() {
		Window window = device.getFullScreenWindow();
		if (window != null) {
			BufferStrategy strategy = window.getBufferStrategy();
			Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
			g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
			g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			return g;
		}
		return null;
	}

    public void updateDisplay() {
		Window window = device.getFullScreenWindow();
		if (window != null) {
			BufferStrategy strategy = window.getBufferStrategy();
			if (!strategy.contentsLost()) {
				strategy.show();
			}
		}
		Toolkit.getDefaultToolkit().sync();
	}
    // other methods go here
}

Using AffineTransforms would, while welcome advice, not truly help solve this issue, as I need to be able to display at the native resolution of any monitor and already extensively use AffineTransforms on the Graphics object.

EDIT: I have tried System.setProperty("sun.java2d.dpiaware", "false"); as the first line in main with no success. Is the property wrong?

EDIT 2: Addition of more clarity:

My render method looks like this:

private void render(Graphics2D g) {
    // Black out the screen to prevent old stuff from showing
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, ScreenManager.getScreenWidth(), ScreenManager.getScreenHeight());
    
    // Set the transform for zoom to draw the zoomed stuffs
    AffineTransform saveState = g.getTransform();
    AffineTransform cameraTransform = ScreenManager.getInstance().getCamera().getCurrentTransform();
    g.transform(cameraTransform);
    
    // RENDER UPDATEABLE
    this.game.getController().renderGame(g);
    
    // REDNER STATIC GUI
    g.setTransform(saveState);
    // draw mouse cords
    Point mouse = this.mouseHandler.getFrameMousePoint();
    if (mouse != null) {
        Font f = new Font("Consolas", 0, 40);
        Point2D scaledPos;
        try {
            scaledPos = cameraTransform.inverseTransform(mouse, null);
        } catch (NoninvertibleTransformException e) {
            scaledPos = mouse;
            e.printStackTrace();
        }
        String s1 = mouse.toString();
        String s2 = scaledPos.toString();
        Rectangle2D r = f.getMaxCharBounds(g.getFontRenderContext());
        int yOff = (int) r.getHeight();
        Color c = new Color(100, 100, 100, 191);
        g.setColor(c);
        g.fillRect(mouse.x, mouse.y, (int) (r.getWidth() * (s1.length() > s2.length() ? s1.length() : s2.length())), yOff + yOff + yOff);
        g.setColor(Color.WHITE);
        g.setFont(f);
        g.drawString(s1, mouse.x, mouse.y + yOff);
        g.drawString(s2, mouse.x, mouse.y + yOff + yOff);
    }
}

And this method is called here:

Graphics2D g = screenManager.getRenderGraphics();
render(g);
g.dispose();
screenManager.updateDisplay(); // SYNCS SCREEN WITH VSync

I have printed out the AffineTransform that is on the graphics object when it is first created, and that prints like this AffineTransform[[2.0, 0.0, 0.0], [0.0, 2.0, 0.0]] which does lead me to conclude that the scaling is done in code. However, the issue is, the things that are rendered with my custom transform ScreenManager.getInstance().getCamera().getCurrentTransform(); are also scaled up by 2, <strike>even though I do not contact my transform and set it to mine.</strike> (Actually never mind, I realized that I do g.transform on my transform not g.setTransform. I am still including this in the write-up though).

A workaround solution to my rendering problem is to do g.setTransform(AffineTransform.getScaleInstance(1, 1); However, this is more than I want to do and it (after trying it on my code) does not fix the issue with the awt coordinate space. Meaning, The rendering works as desired, but the mouse position does not. When the mouse is in the bottom right corner of my screen, it will give a position that is half of the native resolution of my monitor. This solution does not work for me. Processing time is valuable and the more calculations that must be done to UNDO this feature addition. Here is a link discussing how this would be implemented. I link this to point out the section where it mentions that the developer could do it, but it would take a lot of work on the developers part.

I would still like to know if there is a way to either:

  • Disable the feature with a command-line VM argument
  • Disable the feature with a snippet of code

答案1

得分: 7

在一段时间的工作后,我又回到这个问题,并且差不多是偶然间找到了我所寻找的解决方案。

我寻找的解决方案是一个Java系统属性或JVM参数,可以禁用Swing组件的DPI感知,这意味着在具有200%缩放的系统上,用户输入空间和组件渲染将与屏幕的本机分辨率匹配,而不是200%缩放的分辨率(例如,期望从3840x2160屏幕读取鼠标输入,而不是发生的情况,鼠标输入被限制在1920x1080)。

这个解决方案在这个答案中讨论过,是针对类似问题的。为了完整起见,我将在这里重申。

解决方案是将-Dsun.java2d.uiScale=1作为VM参数传递给VM。这会强制UI比例为1,忽略任何系统缩放,并以本机分辨率收集鼠标输入。我还可以确认,在调用任何Swing类之前调用System.setProperty("sun.java2d.uiScale", "1");也会产生我所期望的结果。

英文:

After coming back to this question after a while of working on something else, I have come across the solution I was looking for, mostly by accident.

The solution I was looking for was a Java system property or JVM argument that would disable the DPI awareness of Swing components, meaning that on a system with 200% scaling, the user input space and component rendering would match the native resolution of the screen and not the 200% scaled resolution (eg expecting to read mouse input from and render to a 3840x2160 screen rather than what was happening, where mouse input was capped at 1920x1080).

This solution was discussed in this answer to a similar question. I will restate it here for sake of being complete.

The solution is to pass -Dsun.java2d.uiScale=1 into the VM as a VM argument. This forces the UI scale to be 1, ignoring any system scaling and rendering and gathering mouse input at native resolution. I can also confirm that calling System.setProperty(&quot;sun.java2d.uiScale&quot;, &quot;1&quot;); before calling any swing class will also produce the result I was looking for.

huangapple
  • 本文由 发表于 2020年7月29日 07:10:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/63144081.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定