当前位置: 首页 > 知识库问答 >
问题:

我可以以更高的分辨率提供Java中的图像图标,以避免缩放后图标模糊吗?

上官兴昌
2023-03-14

我正在用Java Swing和AWT(Java 8)设计一个GUI,我正在努力使用我使用的图标。

我加载一个大的PNG图像并将其缩放到18x18px,然后在按钮或标签中使用它。当操作系统不放大时,它在所有分辨率下都能很好地工作。

然而,随着大屏幕分辨率(hidpi)的出现,使用操作系统设置放大用户交互界面控件是一种常见的做法,包括Java应用程序中的按钮等。例如,在Windows上,我使用具有4K分辨率的150%或200%的用户元素缩放来确保用户交互界面仍然可用。我想许多用户也会这样做。

然而,在这种情况下,图标只是在缩小到18x18px后才增加大小。也就是说,我首先缩小它们的比例,然后操作系统尝试使用图像中仍然保留的少量信息再次放大它们。

当使用操作系统的缩放功能以避免图像模糊时,有没有办法在Java中设计基于更高分辨率的图像图标?

这是一个工作示例:

import java.awt.Container;
import java.awt.Image;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

@SuppressWarnings("serial")
class Example extends JFrame {

    public static void main(String[] args) {
        new Example();
    }
    
    public Example() {
        Container c = getContentPane();
        JPanel panel = new JPanel();
        ImageIcon icon = new ImageIcon(new ImageIcon(getClass().getResource("tabler-icon-beach.png")).getImage().getScaledInstance(18, 18, Image.SCALE_SMOOTH));
        JButton button = new JButton("Test button", icon);
        panel.add(button);
        c.add(panel);
        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }
}

您可以在此处找到图标。所有图标都可以作为PNG或SVG文件使用。

为了说明这个问题,首先让我以正常的100%屏幕分辨率向您展示两个屏幕截图:

在100%缩放的Linux上:

在100%缩放的窗口上:

现在,当我将Windows 7设置为布局元素放大200%时,显然只是18x18px版本的扩展,变得模糊了:

当操作系统使用大于100%的缩放比例时,有没有办法提供更高分辨率的图像图标?此外,你可以看到,即使在100%的图像质量是不完美的;还有什么方法可以改进吗?

共有1个答案

公冶浩慨
2023-03-14

Java 8不支持高DPI,用户界面由Windows扩展。您应该使用支持每监视器高DPI设置的Java 11或更高版本。

如果您的目标是让图标看起来清晰,请使用BaseMultiResolutionImageMultiResolutionImage的基本实现)为不同分辨率准备一组图标,以提供更高分辨率的替代方案。(这些在Java8中不可用。)

您说您将原始图像(240×240)缩小到18×18px。根据系统设置,如果用户界面需要更高的分辨率,那么它现在只有你的小图标(18×18),它将被放大,从而导致质量较差。您应该使用多分辨率图像(MultiResolutionImage)或将原始图像绘制成所需大小,让图形为您缩小比例。

这是我想到的在不缩小原始图像的情况下制作图标18×18的最简单方法

private static final String IMAGE_URL =
        "https://tabler-icons.io/static/tabler-icons/icons-png/beach.png";

private static ImageIcon getIcon() {
    return new ImageIcon(Toolkit.getDefaultToolkit()
                                .getImage(new URL(IMAGE_URL))) {
        @Override
        public int getIconWidth() {
            return 18;
        }
        @Override
        public int getIconHeight() {
            return 18;
        }

        @Override
        public synchronized void paintIcon(Component c, Graphics g,
                                           int x, int y) {
            g.drawImage(getImage(), x, y, 18, 18, null);
        }
    };
}

我遗漏了可以从URL构造函数抛出的MalformedURLException的异常处理代码。

在这种情况下,绘制的图像每次绘制时都会缩小,这是无效的。然而,质量更好。嗯,对于标准分辨率的屏幕,它几乎与加载时缩小图像的比例相同。但在高DPI的情况下,它看起来更好。这是因为对于200%的用户界面比例,图像将被渲染为36×36像素,这些像素将从240×240的源创建,而不是放大缩小后的版本,因为缩小后的版本失去了质量。

为了获得更好的结果,我建议使用MultiResolutionImage。

下面的应用程序从base64编码的字符串加载图像(为了简单起见,没有外部依赖关系)。提供了三种变体:24×24(100%,96dpi)、36×36(150%,144dpi)、48×48(200%,192dpi)。

如果当前比例因子设置为任何提供的分辨率,则图像将按原样渲染。如果使用125%或175%,则会缩小较大的图像;如果比例大于200%,则将放大200%的图像。如果需要,可以添加更多分辨率。

该应用程序没有在Java 8中编译,因为那里没有MultiResolutionImage。要用JDK 11编译它,必须用常规字符串连接替换文本块。

import java.awt.Image;
import java.awt.image.BaseMultiResolutionImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Base64;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class BeachIconButton {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(BeachIconButton::new);
    }

    private BeachIconButton() {
        JPanel panel = new JPanel();
        ImageIcon icon = getIcon();
        JButton button = new JButton("Test button", icon);
        panel.add(button);

        JFrame frame = new JFrame("Beach Icon Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private static ImageIcon getIcon() {
        return new ImageIcon(
               new BaseMultiResolutionImage(
               Arrays.stream(new String[] { BEACH_100, BEACH_150, BEACH_200})
                     .map(BeachIconButton::loadImage)
                     .toArray(Image[]::new)));
    }

    private static Image loadImage(String base64) {
        try {
            return ImageIO.read(new ByteArrayInputStream(
                                Base64.getMimeDecoder().decode(base64)));
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private static final String BEACH_100 = """
            iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAA7DAAAO
            wwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAXxJ
            REFUSInl1E9LVVEUBfAfVJBIIoWGIDiRGtagN7Yg+gTZXPwICU4FyUGDahBRCTrK
            qUVNIxrkJHjWJMKZikIjSyHoYd0GZ186vOef7n2zWnB4e591WXudt/c5/G8Ywiw+
            4gd28QGLuNCt+C18RXHI+onJuuLjIVBgGWOYyPb2syI3q4qfzZw/ir1raMXeNBay
            k+zgfNUC25nTBWxGPhfffI78XfzOVD1FP+5lrgusoweXIv+C6xE3qxYocRFrWZHv
            eBPxA5yJeK9ugRJPdU7QGPoi3+1GfFBqZIFn+BVxC88jft9NgdL9q8gH8MSfUa3V
            5BKXQ6gl9aOdey1N2GBV4VO4ik/h8CWG67rMcQ53Hf5ErOBGXfEGtjKxVSxhHi/w
            LeMe4mQV8ROZ67fSZWrHadyW7kKBqaonuC89Bcc5uyKNZ+M4wTvS9d/AY/RmXK80
            iht/wa+HVgfKB61cTYzGaups8FH89kEFhjBSUzDnR0LrSFT5Sw7i/yH8BmQ0mnmX
            f2wqAAAAAElFTkSuQmCC""";

    private static final String BEACH_150 = """
            iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAACXBIWXMAABYlAAAW
            JQFJUiTwAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAoVJ
            REFUWIXt1kuoTVEYB/DfdZWiq7gykZBHHokyw0QGREpKLgMDGSojQ6+JARFi4JFn
            ySPyyoCJR8zcIUVJiPLopiMhLoO1d3ftc+7Z95xrnYHyr1V7feu/1/ff3/etby/+
            4x9DW8K9ZmE9FmTP7fiBp3iE03iS0F9dTMQN/G5gvMLMVopZhi8NisnHD6xohZiV
            2ea5o584h+WYgAclor5jaUoxE9ATOXiOednaUMUU9mKPkK5YVCXbJwkuKkZmYbS2
            t8rxpsz+QW2krqYSdF5tCnZjCX5F9h0Zf25k66nizEghaBSOV22cRyt/vhnxd0b2
            E7gQzXelEJRjnv6L96dQ9IR+9CxaWymcsnx+L6UgQnPtUizyfDwRaiiff8JwjIts
            71MLytGBy0I9xaLitG7LuCMi2+d4kyEJBVWwWkhPfz568UaI6Mho/XtCDTXYqO/L
            v+GF2jQ+wNZofrdVYjrwNnK0XYjOYSEt9bp20lMWY0/k5LVQJzlGCn2qur56Mb0V
            YiYLKcodddXhTcX1iJesU1cj/nc9NPA9axE2Cw02GUYITe6UYgoOCv+29pTOyjAH
            VxRT1N/4iP0Y2yohHTij9v810Khgiwavy43eqSfhGmZHtt/oxm2h4fUI0Zim75IW
            4wI24GuDPutiCF4q1sklAx/XJXisGK2zfysGhum7N3/F2ibebce+SNCdFIJgMQ4J
            F63BYBUOYEqzL87HLaEeKrivPCLrMk5lEPyezNf8MkHv9H9STioegDbF/vM3/Hdl
            grpLnBxFJ8bgWAmvWX53maBOId/jMRpHGnB8JOM2yx+f+eosE1SNNiH89TY/oTY1
            zfAHjS7hMv45G3exJiH/P/5d/AHE21JDZYKOHAAAAABJRU5ErkJggg==""";

    private static final String BEACH_200 = """
            iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAB2HAAAd
            hwGP5fFlAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAA5FJ
            REFUaIHt2ctrXFUcB/BPYoWGVGOwBZtYQQoqhUp9IFEoulC04spGExQEpYoiVBBc
            qVAVwYX/gEWqooKboItqN4oV0frWPgR3XRSl9mHT2GjaJh0X5w6ZOXPuzO08roL5
            wlnM+T3O93cev3vOb1jCEv7f6CtpnBswjg1Yh2HM4wx+wh58jC9K4lMIfXgIB1Ap
            2H7FC7jwX+Bbh2uwW3HiqUDWlE26io040YJgkXYKd5TM3Z34K0HmNKYwgavxgLD/
            WwUxi1vKIr8GfyRIfIAravTuxtmE3g+Jvkrm86pek+/XuOfP4elIbz1mIr15bBUm
            IG8lPtHjjLkpMeg+jNboDOKXSOcsJjP5bU0CqODBsgOoHsRnsRyvJ+RP1vh4PpId
            in7v7WUA/diRE0QFhxN92yMfeyL5MxoP+nW9DAJuxY85QdS2AxiosVsrnJmqfAGX
            4/3I7uVeBwAX4DEcaRLAFFbV2LwSyXdn/ZNR/86es6/BCmyTTpnVWX4Tl2pMv49k
            PtZF/QfLIl+L9fhN/mrEK3XE4vYajGQzZRKPcTuOyw+k2rbV2FwSyY7GTvt7yTjC
            UQwV0FspEKf+nMBcVxmdJz7VeEVYkF6F43gKD0f9H5bOOsN9Gkluws04lpBV21z0
            +7myiRO+wgcjInE6HE/opNpYOZTrEV8RzghX6hjLhWvHKWny35RBNsZogtCrLWxG
            8JrG8zHRO5r5eCci8bvFDNMKY/hMeNRsF77wpWJM/f2mgke7PciyLvtbKby4NuJe
            9Y+QGdyEv7FLSJX/CfThfqGmM691Jqlkep8L6bWs2lQSd+F7xUjnte+EYkCpGMQb
            HRKP247M73mhneVbhY9wY0J2TpjRnUKV4bBQIxrGalyPe4RSY+oe9q1who61wasQ
            +vC1xtlbwFu4sqCftXhb+i70lR6ei+HEgPtxbZv+NuDnhM/hjpnmoE+YoepAu7Sx
            byOsELZkWyvQzlINCTWaGbwnpMROsUx4/16Md3GyE2cDQqXsS0wLRPfhJfWFqjyM
            Zrr78WcX7KczLlvVVzJyMSU/1U1brKSlMJnp9Mp+qkgAs00cVL+imxN244p9iTux
            ny0SQLMVqLY5PCHs2SGhRHi6gF2n9oVWYABbhOLUZbgoGyx+3rUi+Hhm26n96ozL
            FgXPQB4262yLdGrfFUxo/vfRCc1fTp3adwUjeFEod5/M2l7h38WREuyXsIQl1OAf
            9zFZ1uiy3BkAAAAASUVORK5CYII=""";
}
 类似资料:
  • 我有一些. jpg,我正在面板中显示。不幸的是,它们都是大约1500x1125像素,这对我来说太大了。有没有一种编程方法来改变这些. jpg的分辨率?

  • 我有一个java的列表。awt。图像,每个分辨率为300 DPI。我想打印它们,当我开始打印这些图像时(使用javax.PrintService API),只打印一些图像的一部分,因为Java的打印/3D类默认使用72DPI(相对于我的图像的300 DPI)。但是,当我使用72 DPI的图像(与Java默认值具有相同的分辨率)时,所有图像都可以很好地打印(打印整个图像时,而不仅仅是其中的一部分)。

  • 然后,我根据页面大小与图像大小计算缩放的维度,返回:java.awt.dimension[width=562,height=792]我使用下面的代码来计算缩放的维度: 为了实际执行图像缩放,我使用image Scalr API: 我的问题是我做错了什么?当缩放到较小的尺寸时,大的图像不应该被模糊。这与PDF页面的分辨率/大小有关吗?

  • 我希望高质量的图像显示在中,并上传到服务器。我搜索了互联网、StackOverflow和GitHub,但我找不到答案,也许有人知道如何修复它? : 这里怎么了?

  • Android SDK提供了以下图标。 有没有办法给那些设置颜色...如果可能的话,怎么做? 使现代化 在对项目进行了完整的刷新之后,发现Xml中的属性起到了作用。 简而言之 ...这是为我工作的解决方案-将属性添加到ImageView xml: 来自@goldenb的答案是对解决这个问题的不同方法的全面介绍,所以我将其标记为答案。

  • 问题内容: 我需要使用Java程序减小图像的大小(而不是宽度和高度)。他们为此提供了任何好的API吗? 我需要将大小从1MB减少到大约50kb-100 kb。当然,分辨率会降低,但这并不重要。 问题答案: 这是工作代码 这段代码对我来说很棒。如果需要调整图像大小,则可以在此处更改x和y比例J;