0

I'm doing a basic window that's just filled with an image. My current approach is to create an undecorated JFrame filled with a JLabel to which I then apply the image, and then set the frame visible. This performs very well on Windows PC, basically being instantaneous. However, on OS X it performs quite poorly. I get a gray box where the image should be for roughly 1.5-2 seconds, then the image fills in. The Mac I'm using is actually quite a bit faster than my Windows test machine based on profiling the code. The profiler also reveals that it's not my code that's running while we're waiting on the gray screen, so presumably part of the framework or even OS which is unhappy about how/what I'm asking it to do.

So far I've experimented with a variety of different image formats (both source and how I pre-load them in the image) in the hopes of making it render faster, but no change. I've also tried adding pack and repaints in various places to try and hint it into a faster refresh, but no change with that either.

In short, the constructor does this with (BufferedImage image):

final URL imageUrl=new URL(*custom URL here*);  
image=ImageIO.read(imageUrl);

Then in setVisible (to avoid the override warnings you get if you do these things in the constructor) we do:

final JLabel bgImage=new JLabel(new ImageIcon(image)); 
setContentPane(bgImage);
super.setVisible(true);

Not a whole lot to optimize in this process (unless I'm missing a step). Perhaps I'm approaching the problem the wrong way entirely and there is a better/more consistent performing sequence?

MRE

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

public class FrameDisplayTime {

    private JComponent ui = null;

    FrameDisplayTime() {
        try {
            initUI();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public final void initUI() throws IOException {
        if (ui != null) {
            return;
        }

        ui = new JPanel(new BorderLayout(4, 4));
        ui.setBorder(new EmptyBorder(4, 4, 4, 4));
        long tStart = System.currentTimeMillis();

        BufferedImage image = ImageIO.read(
                new URL("https://i.stack.imgur.com/aH5zB.jpg"));
        long tLoad = System.currentTimeMillis();

        ImageIcon icon = new ImageIcon(image);
        JLabel label = new JLabel(icon);
        long tIcon = System.currentTimeMillis();

        JFrame f = new JFrame(this.getClass().getSimpleName());
        f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        f.setLocationByPlatform(true);

        f.setContentPane(ui);
        ui.add(label);
        f.pack();
        f.setMinimumSize(f.getSize());

        f.setVisible(true);
        long tDisp = System.currentTimeMillis();

        printProperty("os.name");
        printProperty("os.version");
        printProperty("java.vendor");
        printProperty("java.version");
        System.out.println("_____________________");
        System.out.println("Image load: \t" + (tLoad - tStart));
        System.out.println("Icon create: \t" + (tIcon - tLoad));
        System.out.println("Frame display: \t" + (tDisp - tIcon));
        System.out.println("TOTAL time: \t" + (tDisp - tStart));
    }

    private static void printProperty(String name) {
        System.out.println(String.format(
                "%1s:\t%1s", name, System.getProperty(name)));
    }

    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = () -> {
            FrameDisplayTime o = new FrameDisplayTime();
        };
        SwingUtilities.invokeLater(r);
    }
}
Community
  • 1
  • 1
Brian Knoblauch
  • 20,639
  • 15
  • 57
  • 92
  • You might have some luck with creating an image optimized for the video hardware, using [createCompatibleImage](https://docs.oracle.com/en/java/javase/12/docs/api/java.desktop/java/awt/GraphicsConfiguration.html#createCompatibleImage%28int,int%29), then drawing your loaded image onto that. Pass the compatible image to the ImageIcon constructor instead of the loaded one. – VGR Jul 26 '19 at 01:48
  • 1) For better help sooner, [edit] to add a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). In this case the code should report the Java version details as well as the various times taken, to the system output stream. 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 Jul 26 '19 at 02:52
  • 1
    What output does the above code (in the edit) provide? – Andrew Thompson Jul 26 '19 at 03:30
  • On the above MRE, Windows has content in the window immediately. OS X has just a slight lag where it is gray first (much better than my code). os.name: Windows 10 os.version: 10.0 java.vendor: Oracle Corporation java.version: 1.8.0_202 Image load: 1471 Icon create: 0 Frame display: 131 TOTAL time: 1602 os.name: Mac OS X os.version: 10.14.6 java.vendor: Oracle Corporation java.version: 1.8.0_202 Image load: 605 Icon create: 4 Frame display: 97 TOTAL time: 706 – Brian Knoblauch Jul 26 '19 at 12:03
  • Tested with createCompatibleImage. No notable change in performance. Extra bonus note: I've done some insertion of print statements further in the code and they are printing out before the drawing is done. So, appears this slow drawing is happening async while my code continues to run. – Brian Knoblauch Jul 26 '19 at 12:17
  • *"On the above MRE"* Tip: Add @VGR (or whoever, the `@` is important) to *notify* the person of a new comment. *"appears this slow drawing is happening async while my code continues to run."* Aah.. interesting. – Andrew Thompson Jul 27 '19 at 06:58

0 Answers0