Fastest Way to Load an Image?

Well was experimenting to try find what would be the fastest way to load an image (in an applet for my purposes), as there are several ways to do this, firstly, i tried ImageIO using

image = ImageIO.read(Thread.currentThread().getContextClassLoader().getResource("filename"));

yup sorry about the ugly hack, its from java4k contest, but any how next i decided to try Toolkit using

DataInputStream datainputstream = new DataInputStream(getClass().getResourceAsStream(filename));
            byte abyte0[] = new byte[datainputstream.available()];
            datainputstream.readFully(abyte0);
            datainputstream.close();
            return Toolkit.getDefaultToolkit().createImage(abyte0);

after running a benchmark, found that Toolkit was almost 400x faster, surely this can’t be right, any idea’s?

Thought I’d try it also.

OS: Windows Vista
JAVA: jdk1.6

Testcase: loading 128x128 32 bit png 1k times.
(toolkit testcase also uses mediatracker and converts Image to a BufferedImage to make it a bit more fair ;))

Results:
Toolkit: 3631 ms
ImageIO: 6503 ms

And here’s my code for the test:


package star.client;

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

/**
 * User: tarmo
 * Date: 25.05.2007
 * Time: 21:28:19
 */
public class ImageLoadingTest {

    private static final String IMAGE = "/fonts/LucidaConsole12.png";

    private ImageLoader loader;
    private String name;

    private interface ImageLoader {
        BufferedImage load(String name) throws IOException, InterruptedException;
    }

    public ImageLoadingTest(String name, ImageLoader loader) {
        this.loader = loader;
        this.name = name;
    }

    private void start() throws IOException, InterruptedException {

        long start = System.currentTimeMillis();

        for (int i = 0; i < 1000; i++) {
            foo(loader.load(IMAGE));
        }

        System.out.println(String.format(
                "%s: %s", name, System.currentTimeMillis() - start));
    }

    public void foo(Image image) {
    }

    public static void main(String[] args) throws IOException, InterruptedException {

        final MediaTracker tracker = new MediaTracker(new JPanel());

        ImageLoadingTest test1 = new ImageLoadingTest("ImageIO", new ImageLoader() {
            public BufferedImage load(String name) throws IOException {
                return ImageIO.read(Main.class.getResource(name));
            }
        });

        ImageLoadingTest test2 = new ImageLoadingTest("Toolkit", new ImageLoader() {
            public BufferedImage load(String name) throws IOException, InterruptedException {
                DataInputStream datainputstream = new DataInputStream(
                        Main.class.getResourceAsStream(name));

                byte bytes[] = new byte[datainputstream.available()];

                datainputstream.readFully(bytes);
                datainputstream.close();

                Image image = Toolkit.getDefaultToolkit().createImage(bytes);

                tracker.addImage(image, 1);
                tracker.waitForID(1);
                tracker.removeImage(image);

                BufferedImage buffered = new BufferedImage(
                        image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);

                buffered.getGraphics().drawImage(image, 0, 0, null);

                return buffered;
            }
        });

        if (args.length != 1) {
            System.out.println("Usage: ImageLoadingTest <imageio|toolkit>");
            System.exit(1);
        }


        if (args[0].equalsIgnoreCase("imageio")) {
            test1.start();
        } else if (args[0].equalsIgnoreCase("toolkit")) {
            test2.start();
        } else {
            System.out.println("Invalid test.");
        }


    }
}


I hate to make a blanket statement, but the default ImageIO stuff is notoriously slow. It’s only there because it’s a lot more convenient and clean to use.

If you are just after loading PNG’s, Matthias posted up some custom code for loading those really quickly. Ask him about it.

  • Jon

thx, but want to really avoid extra code like a custom png loader as in this case its an applet, and start up time needs to be fast as well as small, so would be better if could rely on just existing jre classes. The thing here was just how much faster Toolkit was than ImageIO, surprising that the don’t use the same loader.

On another note: Toolkit also loads the image as managed image if possible (i.e. for .gif/.jpg format). The image is stored so it’s compatible to the local graphics environment and thus will be drawn faster as well!

The same is true for ImageIO. Also… doesn’t Toolkit use ImageIO under the hood anyways?

I said the same thing but it appears not looking at the figures.

Kev

The real frustration is that both loading mechanisms are bugged in different ways, so to ensure you can load all possible png files you have to try one, and then fall back on to the other in the case of a failure. ( I usually try ImageIO, and fall back onto Toolkit.createImage )

Who would have thought writing a png decoder would be so hard :o

If you are trying to load images faster and faster the best way I’ve found is to use the latest nio/JIIO FileChannelImageInputStream/OutputStreams. Include the JAI-core mlib and JIIO clib wrappers in your lib path, set ready your classpath with JAI and JIIO and you’re on to use Java Advanced Imaging acceleration for any image format. [quote]Thought I’d try it also.

OS: Windows Vista
JAVA: jdk1.6

Testcase: loading 128x128 32 bit png 1k times.
(toolkit testcase also uses mediatracker and converts Image to a BufferedImage to make it a bit more fair Wink)

Results:
Toolkit: 3631 ms
ImageIO: 6503 ms
[/quote]
These are really poor timings. :o I get 20 FPS with FileChannelImageInputStreams for loading images. For the moment I don’t know if it is worth to convert images before or after loading them, but it is obviously needed to load pictures before using them for painting as well as to decide whether to use BufferedImage or VolatileImage or even the JAI RenderableImage.
I do confirm that loading get really faster with NIO/JIIO Channels for base file loading.It is also true that GraphicsJAI are recommended instead of classical Graphics instances when you want to improve rendering speed and do not pay much attention to Quality.

Finally you can read the Java 2D AWT/SWING improvements sheet for the platform, there you can get more information about painting. ::slight_smile:

BB, you may want to reconsider this. Surely, the figures stated by tarmo are overall timings. This means the decoder works at ~6ms per frame, i.e. neglecting I/O this is well over 150fps. I/O included (say we can do 20MB/sec), assuming the image is 32K large (2:1 compression), and no fancy overlapping I/O, adds another 1…2ms. In other words, worst case should be around 10ms which is a good 100fps.

sorry I’ve made a mistake it was 1000 times the image !!! :wink: By the way I’ve got to apologize for this “poor” statement, but it is also important to be able to cut-off threads asynchronously, so I decided to use the FileChannels for that. Is that correct?

Certainly not a bad idea! :slight_smile: Although, personally I wouldn’t worry about a 10ms lag in stopping a thread! Might be a different story though if you’re loading larger images…

[quote]The same is true for ImageIO. Also… doesn’t Toolkit use ImageIO under the hood anyways?
[/quote]
I thought so too, but you are wrong: http://weblogs.java.net/blog/chet/archive/2003/08/bufferedimage_a_1.html

[quote]Images that you get from other key means, such as ImageIO-created images or any image created explicitly through calling new BufferedImage() are not managed, and thus will not benefit from under-the-hood acceleration possibilities.
[/quote]
I tried and I can confirm the statement…

2003/08…

Managed by default (with ImageIO) exists since 1.5 iirc. With 1.4 you have to copy it over manually.

[quote]Managed by default (with ImageIO) exists since 1.5 iirc
[/quote]
not with my 1.5… you also got any references to confirm your claim?

you also got any references to confirm your claim?

http://www.java-gaming.org/forums/index.php?topic=4583.msg42005#msg42005 (He’s from the Java2D team)

[quote]Testcase: loading 128x128 32 bit png 1k times.
Results:
Toolkit: 3631 ms
ImageIO: 6503 ms
[/quote]
So this makes 3 ms vs 6 ms per Image. Even for 100 images the difference will be less than a half second. Does this really matter? If you load images only once here and there, I wouldnt care about some ms more or less. When you load images on the fly during a game where fps matter, I would use a stream and read only a limited amount of data every frame, and create the image when all data is loaded. I dont know if you can do this with ImageIO.

-JAW

Images returned by ImageIO should be managed in 1.5 except for
a few cases due to known bugs.

However, remember that if your images are translucent (as many pngs are)
they won’t be accelerated by the default pipelines.

Also, only BufferedImages created with your own raster/data buffer will
not be managed, the rest will be (so images
create with “new BufferedImage(w, h, type)” will be managed).

Thanks,
Dmitri

seen the 3ms 6ms average, is that a matter of raster or something else to improve Image Memory Storage? I mean to rasterize the image data in buffers px by px in addition to reference them softly is even faster, isn’t it?
For the moment I only use softRef 'ed images in my cache/buffers but I don’t manage to get the 3-6ms per image since they are quite bigger than usual icons-like pic’s, say 150x150 is the pics size I have to load up. What you mean about?