This article discusses how to tastefully format/decorate a thumbnail. Previous articles discuss how to scale images efficiently, including bmps, jpgs and pngs, and how to extract existing thumbnails from large jpg images, but this article focuses on the question: and then what?
Why do we need to decorate thumbnails? We don't need to, I guess. This is a matter of taste. For comparison, here are a few examples of what other people are doing:
Mac OS Finder
This is a set of thumbnails when I browse a folder of images in the Finder on Mac 10.7. This is one of the most complicated formatting I've found: it includes a shadow, a white border, and then a thin dark border.
In iTunes: the thumbnails only have a light shadow.
Here you might be able to make more assumptions about the content of the thumbnail: it is probably a very brightly colored image with lots of eye-catching patterns and texture. (That is: an artist specifically composed this thumbnail to fill every pixel.)
Woot.com doesn't bother with shadows, but they still add some very distinct borders around certain thumbnails.
In this case: the borders help identify what is the "clickable area" for these thumbnails.
These non-rectangular thumbnails from Amazon.com have no shadows or borders at all.
These are also the largest thumbnails in this set of examples: they occupy so much space that they are visually distinct without any help from formatting.
Microsoft.com also appears to follow this model (using large, undecorated thumbnails).
The decision regarding when and how to decorate a thumbnail might be better left to graphic designers than to software engineers, but for now my point is simply: this is an established convention that you may sometimes want to follow.
My most immediate goal is to create a file browsing UI, so I want to emulate the first example shown above: photo thumbnails in the Mac Finder. There are three basic elements in this image: the drop shadow, the border, and the thumbnail itself.
This applet demonstrates these 3 layers:
This jar (including source code) is available here.
This applet demonstrates the BasicThumbnail class and its ability to format a thumbnail image. The default options displayed are the preset options that resemble the Mac OS thumbnail (simply referred to by the static field
Each layer of the thumbnail (shadow, border, image) is a separate object with its own concept of padding (insets). They are painted in the order shown in the list (from left to right), so that each layer overlaps all the previous layers. To modify a layer: click that layer in the list and use the controls in the inspector.
All layers support varying amounts of curvature for the corners. A curvature of zero is a sharp (rectangular) corner, and a high curvature is a very rounded corner.
In this model a shadow is just a series of strokes of increasing thickness. (That is: a shadow of 1 pixel, then 3 pixels, then 5 pixels, etc.) Each stroke is solid black with a very light opacity (usually less than 20 out of 255).
Since this can be several pixels thick: you have control of both the inner and outer curvature.
A border is a solid-color frame. Technically this can use any color, but the UI here restricts it to a shade of gray for simplicity's sake. Like the shadow: this supports an inner and outer curvature.
This layer contains the actual thumbnail. It also contains an optional background color, and one curvature control to apply rounded edges.
In the applet above: selecting the image layer lets you control the rotation. The rotation is technically a property of the entire
BasicThumbnail (it applies to all layers), however it was placed in the image's inspector for convenience when modeling the GUI.
The top row of the applet contains a set of buttons to add, remove, and reorder layers. Also the "<>" button displays the source code used to represent the thumbnail in the editor.
Usage & Implementation
BasicThumbnail is constructed, you can simply call:
BufferedImage img = thumbnail.create( sourceImage, maxSize);
Where "sourceImage" is an
File, and "maxSize" is a
java.awt.Dimension object. The returned thumbnail will be either the width or height of the argument dimensions, with the other dimension scaled proportionately as needed.
The actual scaling is handled abstract by the parent class Thumbnail: this is where the logic lives that accepts a file, URL or image and scales it. The subclasses (such as the
BasicThumbnail) are responsible for calculating exactly what dimensions to scale the source to, and then how to render that scaled image.
If a rotation has been supplied: then a binary search is used to calculate the exact scaling factor required to fit the source image inside the thumbnail with all the specified decorations.
My final thumbnail formatting is not a perfect replica of Apple's formatting, but it is sufficiently close for my needs. (Also Apple can modify this formatting at any time, so maintaining an exact copy is a very ambitious ideal.) What I have now is a strong improvement over a plain (undecorated) thumbnail, so I am happy with the result.
I encountered a few thorny issues along the way; the main two both had to do with adding rotation, and subtle rendering bugs I had to hunt down. In hindsight the rotation feature may have been more trouble than it was worth, but I'm happy with the finished result.
This applet has a more carefully crafted UI than usual. This has the unfortunate side-effect of bloating the jar's file size (because it includes the UI for the angle dial, the grayscale slider, and the pixel-magnification-panel, among other things)... but I think this bloat is worth it: this applet generates copy-and-paste-able code! On a personal/selfish level: practicing UI design here is very useful, even if it comes at a cost of inconveniencing other developers with a larger jar than necessary.
Most aesthetic layouts probably only contain only 1 or 2 layers. Maybe 3. So I have mixed feelings about this applet starting out with a 5-layer demo. This complicated demo is probably the exception rather than the rule, but I wanted to demonstrate what this architecture is capable of. In most cases, though: you probably only need about 2 layers to make a subtle-but-significant improvement in appearance.