6
\$\begingroup\$

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());
 tracker.addImage(image, 0);
 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.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Feb 21, 2011 at 22:11
\$\endgroup\$
4
  • \$\begingroup\$ Have you tried image.getScaledInstance(thumbWidth, thumbHeight, Image.SCALE_FAST);? \$\endgroup\$ Commented Feb 22, 2011 at 13:07
  • \$\begingroup\$ If I recall correctly, that's what I started out with. I switched away from it because this version was MUCH faster. \$\endgroup\$ Commented Feb 23, 2011 at 18:41
  • \$\begingroup\$ are the images all jpeg? \$\endgroup\$ Commented Mar 14, 2011 at 7:08
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Mar 14, 2011 at 13:13

1 Answer 1

1
\$\begingroup\$

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.

answered Mar 13, 2011 at 2:06
\$\endgroup\$
1
  • \$\begingroup\$ Hmm, great suggestions. I'll give 'em a go and do some benchmarks. \$\endgroup\$ Commented Mar 13, 2011 at 16:29

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.