如何解决Windows显示缩放导致JFrame截图显示模糊的问题?
问题
我做了个Java恶作剧程序,功能是截取屏幕截图后用JFrame展示,用户点击屏幕不会有任何响应(因为只是和图像交互)。但在Windows开启显示缩放(比如125%)时,原本1920x1080的BufferedImage会被显示成2400x1350的模糊像素化画面,恶作剧完全失效。我试过把图像按1/1.25比例缩小,但缩放后像素化还是很严重,请问怎么让程序不受系统显示缩放影响,按预期运行?
解决方案
核心是让Java Swing程序绕过系统的高DPI缩放适配,确保截图尺寸和窗口显示尺寸完全匹配,避免自动拉伸模糊。
1. 强制禁用Swing的UI缩放
在程序启动时强制设置UI缩放比例为1,让Swing忽略系统的DPI设置。有两种方式:
- JVM参数启动:运行程序时添加参数
java -Dsun.java2d.uiScale=1 Test - 代码内设置:在
main方法最开头添加一行,提前配置系统属性(推荐,无需修改启动命令):System.setProperty("sun.java2d.uiScale", "1");
2. 修正截图的缩放偏差
Windows高DPI环境下,GDI32Util.getScreenshot获取的截图是经过系统缩放后的尺寸,需要反向缩放回原窗口的实际像素尺寸,同时使用高质量缩放算法避免模糊:
- 通过JNA获取目标窗口的DPI值,计算缩放比例
- 使用
Graphics2D的双线性插值算法对截图进行缩放,保证清晰度
修改后的完整代码
import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinDef.RECT; import com.sun.jna.platform.win32.GDI32Util; public class Test { public static void main(String[] args) throws Exception { // 强制禁用UI缩放,忽略系统DPI设置 System.setProperty("sun.java2d.uiScale", "1"); BufferedImage capture = captureNotepadWindow(); int width = capture.getWidth(); int height = capture.getHeight(); System.out.println(width + "," + height); JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(new JLabel(new ImageIcon(capture))); frame.setPreferredSize(new Dimension(width, height)); frame.setUndecorated(true); frame.pack(); // 精准定位窗口到原记事本位置,增强恶作剧效果 HWND notepadHwnd = User32.INSTANCE.FindWindow("Notepad", null); RECT rect = new RECT(); User32.INSTANCE.GetWindowRect(notepadHwnd, rect); frame.setLocation(rect.left, rect.top); frame.setVisible(true); } public static BufferedImage captureNotepadWindow() throws Exception { HWND notepadHwnd = User32.INSTANCE.FindWindow("Notepad", null); if (notepadHwnd == null) { throw new Exception("未找到记事本窗口"); } // 获取目标窗口的DPI,计算系统缩放比例 int dpi = User32.INSTANCE.GetDpiForWindow(notepadHwnd); double scale = dpi / 96.0; // 96为Windows默认DPI值 BufferedImage originalCapture = GDI32Util.getScreenshot(notepadHwnd); // 计算原窗口的实际像素尺寸 int targetWidth = (int) (originalCapture.getWidth() / scale); int targetHeight = (int) (originalCapture.getHeight() / scale); // 使用高质量缩放算法生成适配尺寸的截图 BufferedImage scaledCapture = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = scaledCapture.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2d.drawImage(originalCapture, 0, 0, targetWidth, targetHeight, null); g2d.dispose(); return scaledCapture; } }
内容的提问来源于stack exchange,提问作者Benjamin Ing
相关产品推荐
相关产品推荐

