BufferedImage as Good as Butter
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'
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
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
// new, cool representation of that image, created by one
// of the means above, with width/height equal to img's
// 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.