Ocean, Gradients and Image Caching - oh my
One of the biggest features we did in 1.5 was revamping the java look and feel by providing a new theme (OceanTheme). Love it or hate it, Ocean requires a bit more graphics support than the line primitives of yore. In order to make Ocean the default we needed to make sure performance was in line with the old theme (Steel). All of our internal tests show that we succeeded, Ocean performs as well and better in some cases, than Steel. How did we do this?
Before I get into the solution let's first look at how the gradients that Ocean makes such extensive use of are painted. The gradient is not a single gradient, but is made up of four distinct areas:
1. Gradient from color1 - color2
2. Solid rectangle of color2
3. Gradient from color2 back to color1
4. Gradient from color1 to color3
Here's a monsterously large button showing the distinct regions:
This gives a nice radial gradient and is the effect the designers were after. From the description it's pretty obvious this is way more expensive than a simple fillRect. So, how did we speed this up? Image caching! Chet wrote up a great article that in effect describes the approach we took. In a nutshell, we cache the expensive images (gradients, slider thumbs ...) in an offscreen buffer. To avoid taking up excessive amounts of memory we reference these images via SoftReferences. A SoftReference is a step up from a WeakReference in that the referenced object will be released when no one is referencing the object and memory is low. In other words, if memory is tight the image will be chucked. Perfect for this scenario. Additionally we directly use VolatileImages rather than BufferedImages. In this case a VolatileImage is good because it'll most likely live in video memory, and if the image is lost we can trivially recreate it.
We placed all this functionality into the package private abstract class javax.swing.plaf.metal.CachedPainter (in 1.6 this has been moved to sun.swing), take a look if you're curious. This class hides all the image management, and SoftReference trickyness from subclasses, a subclass only need override a single method to do the actual painting. CachedPainter makes use of varags to allow for seamless passing of arguments to the method responsible for the actual painting. It results in lossing some type safety, but it works.
One last thing, for compatability Ocean still uses bold fonts, but this can easily be turned off with the system property swing.boldMetal=false. Oh ya!
Special thank to Romain for helping with the image.