Недавно я принял решение перейти на более новую версию JDK с той, которую использовал. (В частности, с jdk1.8.0_261
по jdk-14.0.2
). Это обновление прошло довольно гладко, так как большинство функций работают так, как я ожидал, и я смог найти, как приспособиться ко всему, что было не так.
Однако, как показано в заголовке, я столкнулся с проблемой в конкретном приложении, которое я пишу и которое использует Swing. Мой основной монитор — это монитор 4k, и я установил масштабирование системы Windows для этого монитора на 200%. В то время как любое другое приложение Swing, масштабируемое в соответствии с этим параметром DPI, является хорошим, для этого конкретного приложения я хочу отключить это масштабирование и отрисовывать пиксели 1: 1, особенно когда это приложение распространяется.
Есть ли способ отключить это автоматическое масштабирование с помощью аргумента виртуальной машины? Или есть способ отключить масштабирование во время выполнения до того, как будут сделаны какие-либо вызовы в библиотеку свинга? (Подобно тому, как любые изменения внешнего вида должны быть выполнены до любых других вызовов библиотеки.)
Для справки:
Вот код, который я использую для создания своего 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, хотя и приветствуется, на самом деле не поможет решить эту проблему, поскольку мне нужно иметь возможность отображать исходное разрешение любого монитора и уже широко использовать AffineTransforms для объекта Graphics.
EDIT: я безуспешно пробовал System.setProperty("sun.java2d.dpiaware", "false");
в качестве первой строки в main. Свойство неправильное?
EDIT 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. Это означает, что рендеринг работает так, как хотелось бы, но положение мыши - нет. Когда мышь находится в правом нижнем углу моего экрана, она дает положение, равное половине исходного разрешения моего монитора. Это решение не работает для меня. Время обработки является ценным, и тем больше вычислений необходимо выполнить, чтобы ОТМЕНИТЬ добавление этой функции. Вот ссылка, в которой обсуждается, как это будет реализовано. Я ссылаюсь на это, чтобы указать на раздел, где упоминается, что разработчик может это сделать, но это потребует много работы со стороны разработчиков.
Я все еще хотел бы знать, есть ли способ:
- Отключить функцию с помощью аргумента виртуальной машины командной строки.
- Отключить функцию с помощью фрагмента кода
getRenderGraphics()
? - person weisj   schedule 29.07.2020Graphics2D
в начале, вы получите собственное разрешение. Вам нужно только извлечь исходные значения масштабирования, чтобы узнать, какой фактический размер холста у вас есть в абсолютных числах. Когда вы говорите, что уже используете преобразования, это не усложняет ваш код. Так почему бы не сделать это вместо того, чтобы запрашивать конкретный вариант реализации, который не будет работать в других JRE? - person Holger   schedule 29.07.2020JFrame
, а не толькоFrame
илиWindow
? - person Holger   schedule 29.07.2020