You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Windows DPI缩放对Swing动态渲染图片的影响?

解决Swing JTable高DPI下化学结构图片模糊问题

问题根源

你当前代码获取的是默认屏幕的GraphicsConfiguration变换,这无法反映组件所在显示器的实际DPI缩放比例——Java 11的每显示器DPI缩放是基于组件所在的绘图上下文,而非全局屏幕设置。另外,-Dsun.java2d.uiScale=1参数会强制禁用系统DPI缩放,这是导致scale值始终为1的关键原因。

解决方案

  • 从绘图上下文获取真实缩放比例:直接从paintIcon传入的Graphics对象转换为Graphics2D,获取当前组件的实际缩放变换。
  • 按物理像素渲染图片:用逻辑尺寸乘以缩放比例得到物理像素尺寸,让Indigo渲染对应大小的高分辨率图片,避免Swing自动缩放导致模糊。
  • 修正JVM参数:移除-Dsun.java2d.uiScale=1,保留-Dsun.java2d.uiScale.enabled=true,或使用Java 9+支持的-Dsun.java2d.dpiaware=true确保DPI感知开启。
  • 缓存对应缩放的图片:根据缩放比例和逻辑尺寸缓存渲染结果,避免重复计算。

修改后的代码

public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
    Image image = getImage();
    if (image == null) {
        return;
    }
    Insets insets = ((Container) c).getInsets();
    x = insets.left;
    y = insets.top;

    // 从当前绘图上下文获取真实缩放比例
    Graphics2D g2d = (Graphics2D) g;
    AffineTransform transform = g2d.getTransform();
    float scaleX = (float) transform.getScaleX();
    float scaleY = (float) transform.getScaleY();

    // 计算组件的逻辑尺寸(用户看到的尺寸)
    int logicalWidth = c.getWidth() - x - insets.right;
    int logicalHeight = c.getHeight() - y - insets.bottom;

    // 计算需要渲染的物理像素尺寸(避免Swing缩放)
    int renderWidth = Math.max(16, (int) (logicalWidth * scaleX));
    int renderHeight = Math.max(16, (int) (logicalHeight * scaleY));

    logger.debug("Rendering Chemical Structure image: Logical W: " + logicalWidth + " H: " + logicalHeight 
            + " ScaleX: " + scaleX + " ScaleY: " + scaleY + " Render W: " + renderWidth + " H: " + renderHeight);

    // 仅当渲染尺寸变化时重新生成图片
    if (renderWidth != width || renderHeight != height) {
        width = renderWidth;
        height = renderHeight;
        // 按物理像素尺寸渲染
        indigo.setOption("render-image-size", renderWidth, renderHeight);
        // 根据缩放比例调整DPI(Windows默认96DPI,缩放后为96*scale)
        indigo.setOption("image-resolution", (int) (96 * Math.max(scaleX, scaleY)));
        image = renderImage();
        setImage(image);
    }

    // 按逻辑尺寸绘制高分辨率图片,Swing会自动适配DPI
    ImageObserver io = getImageObserver();
    g.drawImage(image, x, y, logicalWidth, logicalHeight, io == null ? c : io);
}

private Image renderImage() {
    try {
        if (structureData == null || mol == null) {
            return null;
        }
        if (!mol.hasCoord()) {
            mol.layout();
        }
        byte[] imageData = renderer.renderToBuffer(mol);
        ByteArrayInputStream bis = new ByteArrayInputStream(imageData);
        return ImageIO.read(bis);
    } catch (IOException e) {
        logger.catching(e);
        return null;
    }
}

额外注意事项

  • 确保你的JTable和所在容器没有被强制设置setScaleX(1)或类似禁用缩放的代码。
  • 测试多显示器场景:如果组件在不同DPI的显示器间拖动,paintIcon会自动获取新的缩放比例并重新渲染图片。

内容的提问来源于stack exchange,提问作者beginner_

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 19:04:52