Java JAR 文件中图像缩放质量丢失

Java Image Scaling Quality Lost in JAR File

我正在编写一个程序,该程序将整个 JPanel 放大并使用 JScrollPane 作为地图,但我也想为用户提供整个地图的缩放视图和最小化视图JScrollPane 的一角。我正在使用 BlueJ,&(至少在 BlueJ 中)我基本上已经得到了我想要的东西。为了缩小地图,我创建了一个 BufferedImagebi,即迷你地图的大小,然后获取它的 Graphics 对象,g,将其转换Graphics2D 对象 g2,然后给 g2 一组 RenderingHints,通过:

g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON));

从那里开始,我在 g2 上使用 Graphics.scale(double sx, double sy) 方法来设置比例,以便在调用以 g2 作为其大地图的 paint(Graphics g) 方法时参数,它将在 bi 上绘制大地图的缩放版本,即小地图的确切大小。这种情况每秒发生 20 次(有点武断,以这种速度我认为它看起来最好。迷你地图实时反映了用户正在做的事情并且我认为如果每秒完成少于 20 次它看起来会很滞后。)

在我的 Macbook Pro 上,这段代码的结果如下:


当我 运行 程序时,此代码没有明显的延迟,这导致我将图片重新采样保持在每秒 20 次,因为似乎有很多 CPU 为此。但是,将我的项目导出到 JAR 文件后,结果如下:


运行ning 程序几乎没有延迟,但到底发生了什么?只要我 运行 BlueJ 中的代码,它就可以工作 - 但在 JAR 文件中,迷你地图看起来很糟糕。

为了澄清,这是要缩放的地图(具有讽刺意味的是,当我将其放置在此 post 中时正在缩放): 这里是两个 BlueJ 和 JAR 文件迷你地图,并排显示:

这可能是什么原因造成的? 当我 运行 "java -version" 时,这是输出:

java版本“1.8.0_31”
Java(TM) SE 运行时环境(构建 1.8。0_31-b13)
Java HotSpot(TM) 64 位服务器 VM(内部版本 25.31-b07,混合模式)

Tl;博士:
当我 运行 我的代码在 BlueJ 中时,小图片看起来不错。导出为 JAR 文件后,它看起来很糟糕。可能是什么原因造成的?


编辑:我现在包括一个 SSCCE,以便更好地理解我的意思。

import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import javax.swing.border.*;

public class ScaleTest
{
    int scrollSpeed = 16, imageSize = 200;
    int rows = 40, columns = 40;
    JFrame f1 = new JFrame("ScaleTest: Full Size");
        JScrollPane scrollPane = new JScrollPane();
            JPanel f1Panel = new JPanel(new GridLayout(rows,columns,1,1));
    JFrame f2 = new JFrame("ScaleTest: Scaled");
        JPanel f2Panel = new JPanel()
        {
            public void paintComponent(Graphics g)
            {
                Graphics2D g2 = (Graphics2D)g;
                g2.drawImage(bi,0,0,null);
            }
        };
            BufferedImage bi = new BufferedImage(imageSize,imageSize,BufferedImage.TYPE_INT_ARGB);

    public ScaleTest()
    {
        f1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f1.add(scrollPane);
            scrollPane.setPreferredSize(new Dimension(600,600));
            scrollPane.getHorizontalScrollBar().setUnitIncrement(scrollSpeed);
            scrollPane.getVerticalScrollBar().setUnitIncrement(scrollSpeed);
            scrollPane.setViewportView(f1Panel);
                for (int i = 0; i < rows*columns; i++)
                {
                    JPanel p = new JPanel();
                    p.setBackground(Color.WHITE);
                    p.setBorder(BorderFactory.createLineBorder(Color.BLACK,1));
                    p.setPreferredSize(new Dimension(100,100));
                    f1Panel.add(p);
                }
        f1.pack();

        f2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f2.setResizable(false);
        f2.add(f2Panel);
            f2Panel.setPreferredSize(new Dimension(imageSize,imageSize));
        f2.pack();

        Graphics2D g2 = (Graphics2D)bi.getGraphics();
        g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON));
        g2.scale(((double)imageSize)/f1Panel.getWidth(),((double)imageSize)/f1Panel.getHeight());
        f1Panel.paint(g2);
    }
    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
    public static void createAndShowGUI()
    {
        ScaleTest s = new ScaleTest();
        s.f1.setVisible(true);
        s.f2.setVisible(true);
    }
}

在 BlueJ 中生成的缩放图像是这样的:

然而,在从 BlueJ 生成的 JAR 文件中,运行使用 JAR 生成此图像:

可能发生了什么?

我在 SE 上找到了这个答案:。 它的结果是提高缩小图像质量的一种方法是模糊它并分阶段缩小它。在下面的示例中,我将图像缩小了 50% 三次,然后缩小了 80%,最终得到原始图像的 10%。

引用的文件 (map.png) 是您之前发布的大图片。据我所知,设置抗锯齿渲染提示在绘制缩放图像时没有用,因为它只对线条、椭圆、矩形等有影响,但对图像没有影响。插值渲染提示有一点帮助,但依赖任何渲染提示并不总是有效,因为它们在不同平台上的实现可能不同(或根本不同!)。

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class Rescale {

 public static void main(String[] args) {
  SwingUtilities.invokeLater(new Runnable() {
   private ConvolveOp cop;

   @Override
   public void run() {
    JFrame frame = new JFrame("Map");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    try {
     BufferedImage im = ImageIO.read(new File("map.png"));
     cop = new ConvolveOp(new Kernel(3, 3, new float[] { 0f, 1f / 5f, 0f, 1f / 5f, 1f / 5f, 1f / 5f, 0f, 1f / 5f, 0f }),
       ConvolveOp.EDGE_NO_OP, null);

     for (int i = 0; i < 3; i++) {
      im = getScaled(im, 0.5);
     }

     im = getScaled(im, 0.8f);

     JLabel lab = new JLabel(new ImageIcon(im));
     frame.setContentPane(lab);
     frame.pack();
     frame.setLocationRelativeTo(null);
     frame.setVisible(true);
    } catch (IOException e) {
     e.printStackTrace();
    }
   }

   private BufferedImage getScaled(BufferedImage im, double scale) {
    BufferedImage nim;
    Dimension dim = new Dimension((int) (im.getWidth() * scale), (int) (im.getHeight() * scale));
    nim = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_RGB);
    Graphics2D g = (Graphics2D) nim.getGraphics();
    g.scale(scale, scale);
    g.drawImage(im, cop, 0, 0);
    g.dispose();
    return nim;
   }

  });
 }

}

希望对您有所帮助。

我猜你的 BlueJ 使用 JDK1.6.0。

  • java版本“1.6.0_45”

  • java版本“1.8.0_31”