【问题标题】:Is there a way to disable native DPI scaling in swing via code or command line (JDK 9+)?有没有办法通过代码或命令行(JDK 9+)禁用原生 DPI 缩放?
【发布时间】:2020-07-28 23:10:12
【问题描述】:

我最近决定从我正在使用的 JDK 切换到更新版本的 JDK。 (具体从jdk1.8.0_261jdk-14.0.2)。这次升级非常顺利,因为大多数功能都按照我预期的方式工作,并且我能够找到如何适应任何不一样的东西。

但是,如标题所示,我在编写使用 Swing 的特定应用程序时遇到了问题。我的主显示器是 4k 显示器,我已将该显示器的 Windows 系统缩放比例设置为 200%。虽然缩放以匹配 DPI 设置的所有其他 Swing 应用程序都很好,但对于这个特定的应用程序,我想禁用该缩放并以 1:1 绘制像素,尤其是在分发此应用程序时。

有没有办法通过 VM 参数禁用这种自动缩放?或者有没有办法在对swing库进行任何调用之前在运行时禁用缩放? (类似于必须在对库进行任何其他调用之前对外观进行任何更改。)

供参考:

这是我用来创建 JFrame 和创建图形对象的代码,我将所有内容都绘制到该对象上,这发生在此类之外的定时循环和本问题的范围内。

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
}

使用 AffineTransforms 虽然是受欢迎的建议,但并不能真正帮助解决这个问题,因为我需要能够以任何显示器的原始分辨率显示,并且已经在 Graphics 对象上广泛使用 AffineTransforms。

编辑:我尝试将 System.setProperty("sun.java2d.dpiaware", "false"); 作为 main 的第一行,但没有成功。属性不对吗?

编辑 2:增加清晰度:

我的渲染方法如下所示:

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);
    }
}

并且这里调用了这个方法:

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

我已打印出第一次创建图形对象时位于图形对象上的 AffineTransform,并打印出像 AffineTransform[[2.0, 0.0, 0.0], [0.0, 2.0, 0.0]] 这样的结果,这确实使我得出结论,缩放是在代码中完成的。然而,问题是,使用我的自定义转换 ScreenManager.getInstance().getCamera().getCurrentTransform(); 渲染的东西也被放大了 2,即使我没有联系我的转换并将其设置为我的。(其实没关系,我意识到我在我的变换上做 g.transform 而不是 g.setTransform。不过我仍然在文章中包含这个)。

我的渲染问题的解决方法是 g.setTransform(AffineTransform.getScaleInstance(1, 1); 但是,这比我想做的要多,它(在我的代码上尝试之后)不能解决 awt 坐标空间的问题。意思是,渲染按需要工作,但鼠标位置没有。当鼠标位于我屏幕的右下角时,它的位置将是我显示器原始分辨率的一半。这个解决方案对我不起作用。处理时间很宝贵,并且必须进行更多计算才能UNDO 添加此功能。 Here is a link discussing how this would be implemented.我链接这个是为了指出它提到开发人员可以做到的部分,但开发人员需要做很多工作。

我仍然想知道是否有办法:

  • 禁用使用命令行虚拟机参数的功能
  • 禁用使用 sn-p 代码的功能

【问题讨论】:

  • 因为我需要能够以原始分辨率显示 - 我相信 Swing 通过设置 AffineTransform 的比例来处理缩放。因此,您可以尝试用您自己的不进行缩放的缩放替换。 并且已经在 Graphics 对象上广泛使用 AffineTransforms。 - 您不应该更改传递给绘画方法的 Graphics 对象的变换。相反,您创建一个 Graphics 对象的副本并将您的转换“连接()”到复制的 Graphics 实例的默认转换。
  • 我查看了你所说的关于使用 AffineTransforms 完成缩放的摇摆,你是对的,有一个比例仿射变换。但是,除了在图形对象上使用仿射变换之外,除了所讨论的 JFrame 之外,没有使用任何摆动组件。唯一使用的其他摆动元素是各种输入的均匀监听器。所以它不会导致其他组件出现问题。此外,此帧忽略重绘,因为渲染背后的引擎位于代码库的单独部分中并且独立于 Swing。我仍然更喜欢完全关闭它的选项
  • 您是否尝试设置您在getRenderGraphics() 中创建的图形对象的变换?
  • 当您不使用任何其他 Swing 组件时,即完全不依赖逻辑坐标系时,我不明白您的问题。只需在开始时在Graphics2D 上设置一个身份转换即可为您提供原始分辨率。您只需提取原始缩放值即可找出您拥有的实际画布大小的绝对数字。当您说您已经在使用转换时,这在您的代码中并不复杂。那么为什么不这样做,而不是要求在其他 JRE 中会失败的特定于实现的选项呢?
  • 当你不使用任何 Swing 功能时,你为什么要使用 JFrame 而不仅仅是 FrameWindow

标签: java swing java-9 java-14


【解决方案1】:

在做了一段时间的其他工作后回到这个问题后,我遇到了我正在寻找的解决方案,主要是偶然的。

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

this answersimilar question 中讨论了此解决方案。为了完整起见,我将在这里重述。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    • 2020-10-16
    • 1970-01-01
    • 1970-01-01
    • 2019-08-31
    相关资源
    最近更新 更多