2

I am coding a program which has an entire JPanel zoomed in with a JScrollPane as a map, but I also want to give users a scaled & minimized view of the whole map in the corner of the JScrollPane. I am using BlueJ, & (in BlueJ, at least) I have basically gotten what I want. To scale down the map, I create a single BufferedImage, bi, that is the size of the mini-map, then take its Graphics object, g, convert it to a Graphics2D object, g2, then give g2 a single set of RenderingHints, through:

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

From there, I use the Graphics.scale(double sx, double sy) method on g2 to set the scale so that upon calling the paint(Graphics g) method of the large map with g2 as its parameter, it will draw a scaled version of the large map on bi that is the exact size of the mini-map. This happens 20 times per second (somewhat arbitrary, at that speed I think it looks best. The mini-map reflects what the user is doing in real-time & I think it looks laggy if done fewer than 20 times per second.)

On my Macbook Pro, The resulting look of this code is:

enter image description here
There is no noticeable lag with this code when I run the program, which has led me to keep the picture re-sampling at 20 times per second, since there appears to be plenty of CPU for this. However, upon exporting my project to a JAR file, the resulting look is this:

enter image description here
There still is little to no lag with the program running, but what the heck happened? So long as I run the code in BlueJ, it works - but in a JAR file, the mini-map looks horrible.

For clarification, this is the map to be scaled (which, ironically, is being scaled when I'm placing it in this post): enter image description here And here are the two BlueJ & JAR file mini-maps, side-by-side:

enter image description hereenter image description here

What could be the cause of this? When I run "java -version", this is the output:

java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

Tl;dr:
When I run my code in BlueJ, the small picture looks good. After exporting to a JAR file, it looks bad. What could be causing this?


EDIT: I am now including an SSCCE to give a better idea of what I mean.

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);
    }
}

In BlueJ the resulting scaled image is this:
enter image description here
Yet, in the JAR file that is produced from BlueJ, running the JAR produces this image:
enter image description here
What could be going on?

Scott Hetrick
  • 161
  • 1
  • 10
  • I'ts possible that BlueJ has established different rendering hints for the components. You could try playing around with the `RenderingHints` – MadProgrammer Jan 22 '15 at 02:34
  • I have messed around with the `RenderingHints` a bit already, which has led me to finding out that it is only the hint with anti-aliasing that gives such a smooth result in BlueJ. Because of that, I set the `RenderingHints` of `g2` to that value, to make sure that the hints will be the same between the BlueJ IDE & the JAR file. – Scott Hetrick Jan 22 '15 at 02:41
  • Consider providing a [runnable example](https://stackoverflow.com/help/mcve) which demonstrates your problem. This will result in less confusion and better responses – MadProgrammer Jan 22 '15 at 02:51
  • Sounds good. I'm currently busy but I should be able to have one up in the next 3 hours – Scott Hetrick Jan 22 '15 at 02:53
  • An SSCCE has been added. – Scott Hetrick Jan 22 '15 at 04:47

2 Answers2

2

I would guess that your BlueJ using JDK1.6.0.

  • java version "1.6.0_45"

enter image description here

  • java version "1.8.0_31"

enter image description here

aterai
  • 9,658
  • 4
  • 35
  • 44
  • Although I don't know how to check, that looks like it's it. Do you know how I can check that/change the JDK version BlueJ is using? Also, it's strange that the image scaling seems to have gotten worse with the Java update. – Scott Hetrick Jan 22 '15 at 07:18
  • 1
    Well that's annoying if so. You can check the version of java being used by executing: String version = System.getProperty("java.version"); – Julian Wright Jan 22 '15 at 07:33
  • Yep, the Java my BlueJ is using is 1.6.0_65. How do I change that? – Scott Hetrick Jan 22 '15 at 08:53
1

I found this answer on SE: https://stackoverflow.com/a/24746194/1695856 . The upshot of it is that one way to improve the quality of the scaled down image is to blur it and scale it down in stages. In the following example I scaled the image down by 50% three times, and then by 80%, to get a final scale of 10% of the original.

The file referred to (map.png) was the large image you posted earlier. Setting the Antialiasing rendering hint is of no use when drawing scaled images as it only has an effect on lines, ovals, rectangles etc, but not images, from what I can tell. The Interpolation rendering hint helps a little, but relying on any rendering hint is not always going to work as they may not be implemented the same (or at all!) on different platforms.

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;
   }

  });
 }

}

I hope this helps.

Community
  • 1
  • 1
Julian Wright
  • 705
  • 4
  • 13
  • Well the image in question is actually a JPanel, not a PNG file. That's why, I believe, anti-aliasing works. The confusing part for me is why with the rendering hints given on BlueJ the image turns out so well, but on my same computer running a JAR file it looks different. This isn't even a cross-platform issue per sé because both images are produced from the same computer. – Scott Hetrick Jan 22 '15 at 06:04
  • Ah, that makes more sense. For the sake of anyone else who is looking for ways to downscale images with minimal loss in quality, I should also mention there are third party libraries that apparently do a much better job than Graphics2D does, such as imgscalr (at http://www.thebuzzmedia.com/software/imgscalr-java-image-scaling-library/ ) – Julian Wright Jan 22 '15 at 07:54