HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Palette animation is a method for doing simple animation that requires a minimum of memory for pixel data. Doing palette animation in Java is certainly a possibility, but you're not going to figure out how unless you do a lot of experimentation yourself. So, in this month's Tips 'n Tricks column we'll see how to do palette animation in Java using your own IndexColorModel objects.

Basically, the way it works is you have a single copy of an image's pixel data. Each pixel's value is actually an index into a look-up table of N colors. (In Java 1.0, N is always 256.) To achieve animation, instead of changing the value of the pixels in the image, why not just change the N colors in the lookup table? This way you will only ever need one copy of an image. You also keep some color tables, one table for each frame of your animation.

To animate your figure you rapidly repaint the same image, but with each repainting you substitute in another color table of N colors. By setting up your color table cleverly, you can achieve some very sophisticated animation.

For the purposes of this column, which has limited space, we are going to take a look at a slightly simpler version of palette animation. We are going to show palette animation by showing how to "fade-out" and "fade-up" images by just manipulating color model tables.

For example, the way you "fade-out" an image is as follows: First, decode how quickly you want the fade to occur. Say you want a ten-step fade to take place over one second. This means you will need ten color tables, one for each of the ten steps of fading. Next, you build your color tables. Finally, you just paint the image ten times, waiting about 1/10th of a second between each repainting. Each time the image is painted you use a slightly darker color table. When the second is up, you've completely faded-out the image to black.

You probably can see now how you might use the same technique to "fade-up" an image (just follow the same steps you used above, but use the color tables in the opposite order). You can also use the same technique to perform a "fade-to-white." Do this by making each of your ten color tables successively brighter until the tenth color table has a "white" value (0x00FFFFFF) for each of the 256 indexes.

The Benefits and Deficits of Palette Animation
Whenever you are considering where and when to use a particular technique, you need to be aware of the advantages and disadvantages. With palette animation in Java there are two distinct benefits. First, you only need to keep around a single copy of an image. By scaling that image and using different color tables, you can greatly modify how that image is rendered. For example, you can generate image data for an image of a six-sided die. Pixels corresponding to different sides of the die would have different pixel values. The background pixels would also have a different pixel value. By rendering the image with scaling, and substituting different color tables, you could get very different images from this same set of image data.

The second major advantage is that palette animation in Java is fast. Given an image's pixel data and a color table, the Java Graphics class and MemoryImageSource classes, working together, can render that image to the screen just about as fast as Java can render an Image generated from a .GIF file or a .JPG file.

The Java IndexColorModel
We're not going to go into what ColorModels in general are in Java. That's a more complicated subject, and a little off the point of palette animation. Suffice it to say that there is a type of ColorModel class in Java called an IndexColorModel, and objects of that class are the Java equivalent to color tables as they are described in the introduction to this column.

Each IndexColorModel object internally has a list of 256 packed RGB integers. Each RGB value has a bit layout of 0x00rrggbb (where rr shows where an 8-bit red value is, gg shows where an 8-bit green value is, and bb shows where an 8-bit blue value is). As you'll see in just a second, it's pretty easy in Java to create a new image from another image's pixel data, substituting in your own IndexColorModel. So to achieve palette animation in Java, we're going to paint images using different IndexColorModel objects that we create ourselves.

Using the Color Model
Let's take a second to review how you use an IndexColorModel and how you create a new one with the colors you want. An IndexColorModel object has as part of its instance data an array of 256 integer values. Each value specifies the red, green and blue components of a single color. So, if you want to know the red component of color number 181, you would use the IndexColorModel like this:

IndexColorModel cm;
// Assume at some point cm gets set to a valid IndexColorModel
int red = cm.getRed(181);

The IndexColorModel methods getRed, getGreen and getBlue can each be used this way to get the primary color components of one of the colors in the 256-color table.

To create a new IndexColorModel, you must assemble three separate arrays of 8-bit values to represent the primary color components of every color in your color table. Each of the three arrays will have 256 members. One array holds the red color component values, one holds green and another holds blue. The index into each array is the identifying number of a color.

Here's a quick example of creating a 256 color grayscale color table in an IndexColorModel:

byte[] reds = new byte[256];
byte[] greens = new byte[256];
byte[] blues = new byte[256];

for(int ii=0 ; ii<256 ; ii++)
reds[ii] = greens[ii] = blues[ii] = (byte)ii;

IndexColorModel cm = new IndexColorModel(8, 256, reds, greens, blues);

Building One IndexColorModel from Another
The IndexColorModel class has a few methods that make it very easy to construct variant color tables from an original. For example, you can make a color table where each color is slightly dimmer, or where each color is slightly brighter, than in the original color table. You would make multiple "dimmer" or "brighter" color tables to effect the "fade-out" or "fade-in" effects mentioned in the introduction.

The IndexColorModel class methods, getReds, getGreens and getBlues, each return arrays of byte values. These are the same arrays (or duplicates of those) used to construct the IndexColorModel. By just copying the arrays and modifying the values you can create a new, modified IndexColorModel object based on the original. This example method takes an IndexColorModel and constructs a new one from it that is some factor dimmer or brighter for each color in the table.

public IndexColorModel dimModel(double factor, IndexColorModel orig) {
byte[] origReds = orig.getReds();
byte[] origGreens = orig.getGreens();
byte[] origBlues = orig.getBlues();

byte[] newReds = new byte[256];
byte[] newGreens = new byte[256];
byte[] newBlues = new byte[256];

for(int ii=0 ; ii<256 ; ii++) {
newReds[ii] = (byte)(factor * (double)origReds[ii]);
newGreens[ii] = (byte)(factor * (double)origGreens[ii]);
newBlues[ii] = (byte)(factor * (double)origBlues[ii]);
}

IndexColorModel cm = new IndexColorModel(8, 256, newReds,
NewGreens, newBlues);
}

Applying Color Models to Images
Image data is comprised of two things in Java. First, there's the actual pixel data itself. Each pixel has data indicating its color. Next, there's the color model. The color model describes the format of the pixel data. For images that use an IndexColorModel, each pixel's data is just an index into the IndexColorModel's color table. There also are other types of color models. In general, the job of a ColorModel object is to translate a pixel's data value into a 32-bit RGB value.

So, once you have a new color table (for example, a "dimmed" version of a color table), how do you apply it to an image? That is, how do you render the image using the new color table as opposed to the old color table?

Java provides you with a method for defining your own Image objects using just pixel data and a color model. (Remember, those are the two informational pieces of an image in Java, so that's all you need to create a new Image object of your own). To do so, you use the MemoryImageSource class to construct a new Image for you. This code is basically what you would use:

Image img = Toolkit.getDefaultToolkit().createImage(
new MemoryImageSource(width, height,
color_model, pixel_data_array, 0, width));

(Note that this code uses the Toolkit class' createImage method.) In this article, we've already seen how to create IndexColorModels. To accomplish palette animation, all we have left to do is gather the pixel data from an original input image. This data must be placed in a single array (the pixel_data_array in the code above).

Getting the pixel data from individual pixels is a little annoying in Java. You'd think there was some way to just say, "Hey, Java, fill my array with the pixel data from this image." Actually, the PixelGrabber class can do this for you, but you can not use a PixelGrabber object to grab indexed color model pixel data. Instead, the PixelGrabber will automatically convert your pixel values into raw RGB values. Since we want to do palette animation, which requires the use of indexed color model pixel data, we can't use PixelGrabbers to get pixel data from an image for us.

There's not enough room in this column to show you how to get an image's pixel data. I'll just give a quick verbal description and let you look through the Java documentation for further information. Every Image object has a method called getSource, which returns an ImageProducer object. This ImageProducer can deliver an image's pixel data and color model object to another object that implements the ImageConsumer interface. Use of ImageProducers to give data to ImageConsumers is not trivial, so if you want to pursue doing palette animation in Java you will have to read up on how to use these two interfaces.

Putting it All Together
Once you have successfully used an Image object's ImageProducer and received pixel data and an original color model using an ImageConsumer interface, you are ready to do palette animation. You need to:

  1. Construct a different IndexColorModel for each frame in your animation sequence.
  2. Create a new Image object for each frame of your animation sequence. Construct the new image using pixel data from an original image, and put it together with one of your own IndexColorModel objects. Use a MemoryImageSource to put these two items together.
The two steps here just describe generally what you must do. There are, of course, many different ways to actually implement palette animation. For example, you might construct all of your IndexColorModel objects and constructed Image objects before even starting to display the sequence. In that case, it would only be necessary for you to keep around references to the constructed Image objects. Once you have used an IndexColorModel to generate an Image, there's no reason to keep the IndexColorModel around any longer. Its purpose has been served.

A completely different situation in which you want to use palette animation might be where you want to render images slightly darker or slightly brighter. The Sun sample MoleculeViewer applet uses a palette animation technique to shade the different images of the atoms which render the molecules. That is, atoms that are further away from the viewer are rendered using a slightly darker color table, while atoms that are closer to the viewer are rendered using a brighter one.

To accomplish this, the MoleculeViewer applet keeps references to IndexColorModel objects instead of individual Image objects. Whenever an atom needs to be rendered, the applet chooses the appropriate IndexColorModel based on the shading the individual atom is supposed to get. The same array of pixel data is used to draw each atom. Only the size of the output rectangle and the IndexColorModel are changed to make larger atoms different from smaller ones, and closer atoms brighter than ones that are further away from the viewer.

Here's some simplified code to demonstrate how this is done. The m_data reference is a reference to an array of bytes which constitute an image. The m_cmArray reference is a reference to an array of IndexColorModel objects. This method is supposed to be run in a background thread. On every pass through the loop, the thread will create a new Image using a different color model with the same image data. That image will then be displayed on the screen.

public void run() {
try {
for(int ii=0 ; true ; ii = (++ii % m_cmArray.length)) {
Image img = Toolkit.getDefaultToolkit().createImage(
new MemoryImageSource(m_width, m_height,
m_cmArray[ii], m_data, 0, m_width));
Graphcs g = getGraphics();
g.drawImage(img, 0, 0, this);

Thread.currentThread().sleep(50);
}
} catch (InterruptedException ie) {
// Must be time to quit...
} catch (Exception e) {
// Some error. Must quit...
}
}
Summary
Consider using palette animation to achieve visual effects in your applets and applications. Here are some of the effects you can achieve: "fading-up," "fading-down," modifying tint, shade, brightness, hue. Just about any global transform on an image can be achieved using palette animation and modifying IndexColorModel objects.

Use of this technique involves generating pixel data, whether gathering data from another Image object or by generating the pixel data yourself and applying an IndexColorModel to the pixel data. The MemoryImageSource class is specifically made to join pixel data with any ColorModel (including all IndexColorModels) to create new Image objects. Those images can be rendered quickly, even if scaled, to a Graphics surface in Java.

About the Author
Brian Maso is a programming consultant working out of Portland, OR. He is the co-author of The Waite Group Press's upcoming release, "The Java API SuperBible." Before Java, he spent five years corralled in the MS Windows branch of programming, working for such notables as the Hearst Corp., first DataBank, and Intel. Readers are encouraged to contact Brian via e-mail with any comments or questions at [email protected]

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.