# Optimized thumbnail generation

Here is a snippet of code that generates a thumbnail from a given file. It works well, but is much slower than desired. Without using an external library (which I'm trying to avoid for learning purposes), is there anything that can be done to increase the speed?

I know the fastest way would be to retrieve the stored thumbnail. I've looked into reading for any EXIF thumbnail data by parsing out the embedded information, but gave up since it was taking me a lot of effort with no finished end result. Again, I'd prefer not to rely on a library specifically because I want to learn more about the format itself.

I know many will state right away I shouldn't be messing with EXIF data unless I use an external library, so as an alternative, what can be done specifically with this snippet to make things (significantly) faster?

Feel free to give me feedback about my coding style as well, except for the use of JavaDoc comments.

class ThumbIcon extends ImageIcon implements Runnable {
public int    thumbWidth;
public int    thumbHeight;

Image         image;
Graphics2D    g2dBuffer;
ThumbButton   parent;
BufferedImage scaledImage;

public ThumbIcon(final ThumbButton theParent) {
super();
parent = theParent;
} // End ThumbIcon(ThumbButton, String)

public void run() {
image   = Toolkit.getDefaultToolkit().getImage(filePath);
final MediaTracker tracker = new MediaTracker(new Container());
try {
tracker.waitForID(0);
} catch (InterruptedException e) {
System.out.println("Interrupted getting thumb");
} // End try

// Get dimensions from the now-loaded image
final int width   = image.getWidth(null);
final int height  = image.getHeight(null);

// Use the width to determine if the image is legitimate and loaded
// Limit importable images to <54MiB or <51.5MP (9000px x 6000px)
if ((width > 0) && (width * height < 54000000)) {
// This is an actual image, so display on screen

// Set the thumbnail size depending on image orientation
// We are assuming a square thumbnail
if (width > height) { // Landscape
thumbWidth  = THUMBS_SIZE;
thumbHeight = (int)((double)height / (double)width * THUMBS_SIZE);
} else {              // Portrait
thumbWidth  = (int)((double)width / (double)height * THUMBS_SIZE);
thumbHeight = THUMBS_SIZE;
} // End if

scaledImage = new BufferedImage(thumbWidth, thumbHeight,
BufferedImage.TYPE_INT_RGB);

// Simultaneously load image into g2d buffer and scale to desired size
g2dBuffer = scaledImage.createGraphics();
g2dBuffer.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);

//g2dBuffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
//                           RenderingHints.VALUE_ANTIALIAS_ON);

// Size image (no matter initial size) to destWidth x destHeight
g2dBuffer.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);
...
// Create the thumbnail
setImage(Toolkit.getDefaultToolkit().createImage(scaledImage.getSource()));
scaledImage.flush();
image.flush();
//g2dBuffer.finalize();
g2dBuffer = null;
} else {
...
} // End if
} // End run()
...
} // End ThumbIcon


I shaved off some time by disabling anti-aliasing. It looks nasty, but it serves its purpose.

• Have you tried image.getScaledInstance(thumbWidth, thumbHeight, Image.SCALE_FAST);? – Peter Taylor Feb 22 '11 at 13:07
• If I recall correctly, that's what I started out with. I switched away from it because this version was MUCH faster. – D.N. Feb 23 '11 at 18:41
• are the images all jpeg? – Ron Mar 14 '11 at 7:08
• They aren't necessarily, but in general, they are. I wouldn't be too concerned with thumbnail generation of non-JPG images, as the program would primarily dictate the use of JPG, but not require it. – D.N. Mar 14 '11 at 13:13

You probably reached the maximum speed you can achieve with the API you are using. You could try the following extra hints, just in case they’re not the default values:

g2dBuffer.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_SPEED);
g2dBuffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_OFF);
g2dBuffer.setRenderingHint(RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_DISABLE);
g2dBuffer.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_SPEED);


Also, try replacing TYPE_INT_RGB with TYPE_INT_BGR or TYPE_3BYTE_BGR to experiment with how efficiently the Java graphics libraries do their byte packing. But it’s pretty unlikely you’ll see a noticeable difference.

• Hmm, great suggestions. I'll give 'em a go and do some benchmarks. – D.N. Mar 13 '11 at 16:29