What causes poor image quality in Java JLabel icons?

微笑、不失礼 提交于 2020-05-09 02:22:58

问题


Java JLabel icons are displaying with distorted pixels in JFrame. This is happening consistently with different png images (all 32x32). I am not scaling the images, they are displayed in the program 32x32, which I verified using getWidth and getHeight on the JLabel. The distortions appear in the same place each time the program is run, not randomly.

Screenshot using the example code provided below.

In this screenshot you can see an array of JLabel icons, each affected one differently.

When resizing the window from sideways, as the icon moves with the window, the distortions move across the icon like a vertical line.

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

public class FrameApp extends JFrame
{
    public static void main(String[] args) throws IOException
    {
        FrameApp frameApp = new FrameApp();
    }

    private FrameApp() throws IOException
    {
        BufferedImage image;

        URL url = new URL("http://i.stack.imgur.com/L5DGx.png");
        image = ImageIO.read(url);

        JLabel label = new JLabel(new ImageIcon(image));

        add(label);

        pack();
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }
}

Edit:
I am using JDK 11.0.3, Java SE Runtime Environment build 1.8.0_202, on Windows 8.1 64-bit.


回答1:


You may think you're displaying the images at 32x32 size, but your example of the tiled images says that's not so. You have a 9x2 grid of icons, which should be 288x64 pixels, but in your sample image the grid is 302x66.

If you carefully examine your tiled image, you can see that the individual tiles are being displayed 34px wide - see the magenta border that extends from 32px to 66px. (Note, some of the tiles are displayed 33px wide; it appears to be 33, 34, 34, 33, 34...)

In order to stretch the tiles to the wider width, certain columns are being doubled (red borders) and this creates the visual glitches you are seeing.

Have you tried fixing the size of the JLabel instead of allowing it to size based on its contents?




回答2:


Answer is from this link to generate high quality image : https://componenthouse.com/2008/02/08/high-quality-image-resize-with-java/

The appropriate class from the link :

public class ImageResize {

    public static void main(String[] args) {
        try {
            URL url = new URL("http://i.stack.imgur.com/L5DGx.png");
            BufferedImage image = ImageIO.read(url);
            ImageIO.write(resizeImage(image, 32, 32), "png", new File("D:/picture3.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static BufferedImage resize(BufferedImage image, int width, int height) {
        int type = image.getType() == 0? BufferedImage.TYPE_INT_ARGB : image.getType();
        BufferedImage resizedImage = new BufferedImage(width, height, type);
        Graphics2D g = resizedImage.createGraphics();
        g.setComposite(AlphaComposite.Src);
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.drawImage(image, 0, 0, width, height, null);
        g.dispose();
        return resizedImage;
    }

    private static BufferedImage resizeImage(BufferedImage image, int width, int height) {
        image = createCompatibleImage(image);
        image = resize(image, 100, 100);
        image = blurImage(image);
        return resize(image, width, height);
    }

    public static BufferedImage blurImage(BufferedImage image) {
        float ninth = 1.0f/9.0f;
        float[] blurKernel = {
                ninth, ninth, ninth,
                ninth, ninth, ninth,
                ninth, ninth, ninth
        };

        Map<RenderingHints.Key, Object> map = new HashMap<RenderingHints.Key, Object>();
        map.put(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        map.put(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
        map.put(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        RenderingHints hints = new RenderingHints(map);
        BufferedImageOp op = new ConvolveOp(new Kernel(3, 3, blurKernel), ConvolveOp.EDGE_NO_OP, hints);
        return op.filter(image, null);
    }

    private static BufferedImage createCompatibleImage(BufferedImage image) {
        GraphicsConfiguration gc = BufferedImageGraphicsConfig.getConfig(image);
        int w = image.getWidth();
        int h = image.getHeight();
        BufferedImage result = gc.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
        Graphics2D g2 = result.createGraphics();
        g2.drawRenderedImage(image, null);
        g2.dispose();
        return result;
    }

}



回答3:


First option: Instead of using ImageIcon, you can try to create your own icon class drawing the Image using graphics.drawImage(x,y,width,height,null) controlling rendering quality (https://docs.oracle.com/javase/tutorial/2d/advanced/quality.html)

an example would be something like this:

public class Icon32 extends ImageIcon {
    public Icon32(String f) {
      super(f);

      BufferedImage i= new BufferedImage(32, 32, 
           BufferedImage.TYPE_INT_RGB);


      Graphics2D g2d = (Graphics2D) i.getGraphics();
                g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

                g2d.setRenderingHint(
                    RenderingHints.KEY_INTERPOLATION,
                    RenderingHints.VALUE_INTERPOLATION_BILINEAR);

      g2d.drawImage(getImage(), 0, 0, 32, 32, null);
      setImage(i);
    }

    public int getIconHeight() {
      return 32;
    }

    public int getIconWidth() {
      return 32;
    }

    public void paintIcon(Component c, Graphics g, int x, int y) {
      g.drawImage(getImage(), x, y, c);
    }
  }

where the method:

getImage()

is loading your image/icon...

Second option: if you are not happy with the result you can try to use this library: https://github.com/mortennobel/java-image-scaling it claims to provides better image scaling options than the Java runtime provides.



来源:https://stackoverflow.com/questions/56501102/what-causes-poor-image-quality-in-java-jlabel-icons

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!