What causes poor image quality in Java JLabel icons?
Asked Answered
A

3

9

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.
example screenshot (1)

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

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("https://i.sstatic.net/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.

Andi answered 7/6, 2019 at 20:53 Comment(8)
I'm guessing this has to do with image scaling, and you'll need to turn on some sort of anti-aliasing. Just a guess though, but gives you something to Google.Cuyler
1) For better help sooner, edit to add a minimal reproducible example or Short, Self Contained, Correct Example. 2) One way to get image(s) for an example is to hot link to images seen in this Q&A. E.G. This answer hot links to an image embedded in this question.Revivify
@AndrewThompson I think I correctly edited my question as you suggested, but still don't have an answer and it has since been down-voted. Is the question somehow ill-formed and do you think I should give up on an answer? ThanksAndi
Which version of java are you using? I tried to reproduce using your example, but the image looks perfectly fine (openjdk 11.0.1).Lucienlucienne
Java(TM) SE Runtime Environment (build 1.8.0_77-b03) on Windows 7 gives no problem either.Satyriasis
It's not clear which java you're running your app with. When you say the jre is build 1.8, are you building with java 11 then running with java 8? I tried with both 8 and 11 and they both show up fine on Centos 7.Wilek
@Wilek I got that information from IntelliJ Help -> about. Here is the whole thing: IntelliJ IDEA 2019.1.3 (Community Edition) Build #IC-191.7479.19, built on May 28, 2019 JRE: 1.8.0_202-release-1483-b58 amd64 JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o Windows 8.1 6.3Andi
I had the same problem but I tried my app on another screen, using my laptop the images are displayed correctly, with no blurring.Dosia
S
4

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.

marked up portion of provided sample image

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

Status answered 14/6, 2019 at 17:26 Comment(5)
Good job noticing that. I experimented with explicitly sizing the JLabels to the desired 32x32, as well as larger and smaller, but to no avail. Even when sized to something smaller like 28x28, the same visual glitches exist, which would seem to disprove your idea of it being caused by the icon expanding. However, I visually checked it, like you did, at 28x28 and it is still larger than it should be (see here i.imgur.com/xyrGpUJ.png). Calls to label.getWidth() return 28, while calls to label.getIcon().getIconWidth() return 32. Interesting, but I still don't know what to make of it.Andi
So even in your new example it looks like the doubled columns are happening at regular intervals; 20 or 21 pixels start to start. In the previous example with "32x32" icons they were 22 or 23. I'm not sure what that tells us... Is there a discrepancy between device pixels and logical pixels? (I'm grasping at straws.)Status
Thanks for replying David. After prompting the OP to tweak and expand the question over time, I was dismayed it had fallen off the main page & received no replies once it became an excellent question. Adding the bounty (I feel I have far more rep. than I need or deserve) was then a no-brainer.Revivify
@AndrewThompson It was an interesting question; I'm sorry I didn't see it resolved. Unfortunately I couldn't reproduce it myself, so I was limited to pointing out things I could see...Status
Not every question has a viable answer. (shrugs) Maybe it was down to 'Gremlins'.Revivify
B
2

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.

Bursarial answered 12/6, 2019 at 7:4 Comment(0)
K
1

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("https://i.sstatic.net/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;
    }

}
Kentigerma answered 11/6, 2019 at 7:47 Comment(1)
Why do you feel the question has to do with resizing? They say the image is 32x32, and it displays as 32x32. There is a distortion in the image. When they resize, they are only resizing the JFrame, the image is still 32x32, but it causes the distortion to move.Wilek

© 2022 - 2024 — McMap. All rights reserved.