Recently there was a brief discussion on the java2d interest list. We were talking about some of uses of BufferedImage and someone replied to the group with their epiphany on these objects with just a brief: "BufferedImage as good as butter!"

The Java2D team has spent the last few days trying to understand and come to terms with this phrase. Was it praise? Irony? Sarcasm? An epigram? A typo? Does it mean that BufferedImage is yellow? Or creamy? Or that it spreads well if left out of the fridge, but melts if it is too hot outside? Is it a condiment? Does it go well with bread? Is it a substitute for vegetable oil?

I have decided it was actually one of those opaque philosophical phrases that you might see in a fortune cookie (albeit one that you got from a very geeky restaurant). I do not yet understand its true meaning, but I hope to walk far enough on the path to enlightenment to one day glean its essence.

In the meantime, I will arbitrarily choose one possible meaning for the purposes of this article: BufferedImages are slick.

One of the things that we and our developers run up against again and again (and again) is the vast array of possibilities when dealing with images and image operations in our APIs. In particular, confusion appears to reign when developers first learned Java in the 1.0/1.1 days (whose APIs are still working and prevalent in some browsers' implementations of Java) but they are now trying to apply that knowledge in the current API and implementation.

So, for example, someone could easily assume that ImageProducer/ImageConsumer is still the way to deal with creating and using images. Or that PixelGrabber is a great way to get at the actual pixel data in an image. Or that MediaTracker is a really cool way to ensure that an image is loaded before you use it.

Then these developers run into the new (as of 1.4) ImageIO APIs and wonder how they get from their Image to a BufferedImage.

Might we suggest, instead, trying to use BufferedImage for most, if not all, of your condiment image needs. There are very few situations where you really want or need to use the old Image APIs and thus translating between that world and the new world of BufferedImage is really unnecessary. ImageProducer/Consumer is an interesting way of dealing with Images that are coming over the net and won't arrive until some nondeterministic time in the future, but if you have the image on disk and simply want to load it in, that's a pile of work to go through (and some pretty non-obvious API calls) just to get your hands on the data. Or if you want an image that you can access the pixel data on, you can sure use PixelGrabber, but wouldn't it make more sense to simply call get/setRGB on a BufferedImage? Or if you want an offscreen image, wouldn't it be nice to simply be able to create an image of a specific type (or one compatible with the display) and simply give it the width and height to use?

In particular, check out the simple commands like:

new BufferedImage(int width, int height, int type)
that give you an image of the width/height/type you require, synchronously (that is, after the method returns, that image does exist; no need to send it through MediaTracker to await its imminent arrival). You can then get at the data directly (getRGB, setRGB), grab the Raster object (which allows more ways of getting at the pixel data), get the ColorModel, and all sorts of other complex and interesting stuff. But you don't have to do anything complicated with it to take advantage of this API; you can just create that image, get the Graphics object, render to it, and away you go.

 

Or how about:

GraphicsConfiguration.createCompatibleImage(int width, int height)
This retuns an opaque BufferedImage (there is another variation in which you can specify a non-opaque type) of the given width/height that should be in the same format as the screen. This means that operations to/from the screen or other compatible images should be optimized (and sometimes hardware accelerated).

 

Then there's always the entire ImageIO package, which deals exclusively in BufferedImage objects. Instead of using the old Toolkit APIs to load an Image, you can use ImageIO to read images of more varied types (such as PNG) which will then return a BufferedImage.

And remember too that converting from one image type to another is usually as easy as a simple copy command. So, for example, if you happen to have one of these old Image objects lying around and you really want to get at the pixels, or use ImageIO, or just revel in the power and glory of having a BufferedImage representation of that image, you can always do something like this:

// your other image type
Image img;

// new, cool representation of that image, created by one
// of the means above, with width/height equal to img's
BufferedImage bImg;

// Get the Graphics object for the BufferedImage
Graphics g = bImg.getGraphics();

// Copy the old image into the new BufferedImage object
g.drawImage(img, 0, 0, null);


 

So store it in the fridge, keep it out on the counter, spread it on your favorite wheat products and applications; BufferedImage is as good as butter ... and maybe even better.

ps: Did you notice I didn't touch on that hot "performance" topic anywhere in this article? Stay tuned for the next article, in which I'll talk about some of the performance implications and implementation details of BufferedImages.