在Windows中,如何处理图像的DPI缩放?

人工智能 2026-07-09

这个应用是一个Swing图形用户界面程序,用来显示一个JTable。数据从一个文件中加载。第一列是根据文件中的数据动态渲染的图片。这在总体上是可行的。这是一个老应用,最初为Java 7制作,我现在在更新它。

我遇到的问题是在启用Windows DPI缩放且倍率超过100% 的显示器上,渲染的图片看起来很糟糕。好像存在某种低质量的放大处理。

目标版本是Java 11,因此理论上它应该能够感知按显示器的DPI缩放。

图片是动态生成的。它是一张通过Indigo工具包渲染的化学结构图。不过这并不是问题的核心,只是对代码片段的解释。以下是当前的渲染代码:

    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;

        GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
        GraphicsConfiguration gc  = device.getDefaultConfiguration();
        AffineTransform transform = gc.getDefaultTransform();

        // get cell size and scale for DPI scaling
        // Issue: scaleX and scaleY are always 1
        int w = (int) ((c.getWidth() - x - insets.right) * transform.getScaleX());
        int h = (int) ((c.getHeight() - y - insets.bottom) * transform.getScaleY());
        logger.debug("Rendering Chemical Structure image: W: " + w + " H: " + h + " scaleX: " + transform.getScaleX()
                + " scaleY: " + transform.getScaleY());

        if (w != width || h != height) {
            if (w < 16 || h < 16) {
                // 16 pixels is minimum size supported by indigo
                return;
            }
            width = w;
            height = h;
            indigo.setOption("render-image-size", w, h);
            indigo.setOption("image-resolution", 300);
            image = renderImage();
            setImage(image);
            ImageObserver io = getImageObserver();
            g.drawImage(image, x, y, w, h, io == null ? c : io);
        }
    }

    private Image renderImage()  {

        try {
            Image image = null;
            if (structureData != null) {
                if (mol != null) {
                    if (!mol.hasCoord()) {
                        mol.layout();
                    }
                    byte[] imageData = renderer.renderToBuffer(mol);
                    ByteArrayInputStream bis = new ByteArrayInputStream(imageData);
                    image = ImageIO.read(bis);

                    //image = Toolkit.getDefaultToolkit().createImage(imageData);

                    if (image == null) {
                        return null;
                    }
                }
            }
            return image;
        } catch (IOException e) {
            logger.catching(e);
            return null;
        }
    }

最初的代码并没有这些Graphics相关的部分。这是我在研究后添加的,确实也算是AI的产物。也解释了我为何不喜欢AI——它根本不起作用,图像仍然以某种方式被放大。

我也试过:

-Dsun.java2d.uiScale=1

-Dsun.java2d.uiScale.enabled=true

但两者都无效,同时这些也是为了解决此问题而给出的建议。

我也看到过MultiResolutionImage,但我更愿意直接渲染正确的那个。

如何让代码在启用DPI缩放的显示器上正确缩放图像?

解决方案

根据 @Holger的评论,下面是能够修复此问题的代码:

    Graphics2D g2d = (Graphics2D) g;
    AffineTransform transform = g2d.getTransform();

    // Dimensions for table cell size for Graphics2d
    int wg = c.getWidth() - x - insets.right;
    int hg = c.getHeight() - y - insets.bottom;
    // Dimensions for indigo renderer taking into account dpi scaling
    // This is required so that images look sharp and are not getting manipulated by awt
    int w = (int) (wg * transform.getScaleX());
    int h = (int) (hg * transform.getScaleY());

    if (w != width || h != height) {
        if (w < 16 || h < 16) {
            // 16 pixels is minimum size supported by indigo
            return;
        }
        width = w;
        height = h;
    }

    indigo.setOption("render-image-size", w, h);
    image = renderImage();
    setImage(image);
    ImageObserver io = getImageObserver();
    g.drawImage(image, x, y, wg, hg, io == null ? c : io);

图像宽度必须与容器中表格单元格的大小不同,才能考虑DPI缩放。此外,关于从哪里获取变换的AI相关部分也确实错了。只有Graphics2D能给出按显示器正确的缩放值。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章