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.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.

IamJohnGalt
  • 93
  • 1
  • 6
  • 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. – markspace Jun 07 '19 at 20:56
  • 2
    1) For better help sooner, [edit] to add a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). 2) One way to get image(s) for an example is to hot link to images seen in [this Q&A](http://stackoverflow.com/q/19209650/418556). E.G. [This answer](https://stackoverflow.com/a/10862262/418556) hot links to an image embedded in [this question](https://stackoverflow.com/q/10861852/418556). – Andrew Thompson Jun 08 '19 at 01:08
  • @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? Thanks – IamJohnGalt Jun 11 '19 at 00:32
  • Which version of java are you using? I tried to reproduce using your example, but the image looks perfectly fine (openjdk 11.0.1). – dpr Jun 11 '19 at 11:23
  • `Java(TM) SE Runtime Environment (build 1.8.0_77-b03)` on Windows 7 gives no problem either. – bracco23 Jun 11 '19 at 12:59
  • 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. – matt Jun 12 '19 at 07:29
  • @matt 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.3 – IamJohnGalt Jun 13 '19 at 21:30
  • I had the same problem but I tried my app on another screen, using my laptop the images are displayed correctly, with no blurring. – bashizip Feb 09 '22 at 11:12

3 Answers3

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?

DavidW
  • 1,413
  • 10
  • 17
  • 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 https://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. – IamJohnGalt Jun 15 '19 at 02:43
  • 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.) – DavidW Jun 15 '19 at 02:50
  • 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. – Andrew Thompson Jun 19 '19 at 22:20
  • 1
    @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... – DavidW Jun 19 '19 at 22:26
  • Not every question *has* a viable answer. (shrugs) Maybe it was down to 'Gremlins'. – Andrew Thompson Jun 19 '19 at 22:29
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.

navy1978
  • 1,411
  • 1
  • 15
  • 35
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.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;
    }

}
Anish B.
  • 9,111
  • 3
  • 21
  • 41
  • 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. – matt Jun 12 '19 at 16:36