【发布时间】:2020-07-28 23:10:12
【问题描述】:
我最近决定从我正在使用的 JDK 切换到更新版本的 JDK。 (具体从jdk1.8.0_261 到jdk-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而不仅仅是Frame或Window?