Many applications have buttons containing icons where the icons are changed as the button is selected or the mouse moved over the button. An example of this is with toolbar buttons in a Web browser, such as Microsoft Internet Explorer, where the buttons use gray colors for the graphic that becomes colored as the mouse moves over the button. To achieve this effect, two sets of graphics can be created; one for the normal state and one for the active state when the mouse is over the button. Creating two sets of graphics, however, is time consuming for designers and means that dual maintenance must be done on both sets if the graphic needs to change. A more optimal approach would be to have a single icon and the graphic effect done programmatically to avoid the overhead of creating and maintaining a separate icon. The problem described in this article is for a set of buttons in a wizard page shown in Figure 1, where as the user hovers over each button, the graphic changes to show them it is active.
Figure 1. The Web Module button is active with the mouse over it and the graphic has been changed.

In this article we show how we achieved this effect by using the Java2D API to create a class that is able to take an image and create the desired effect. This involves understanding how images are constructed and can be manipulated using AWT composites.
The class javax.swing.JButton has a boolean property rolloverEnabled and a property rolloverIcon that is typed to javax.swing.Icon. If rolloverEnabled is true, then the value of the rolloverIcon is used as the button's graphic when the mouse is over it. We decided that a good solution would be to have a new class called RolloverImageIcon that was given an image in its constructor and would then manipulate the icon before drawing it.
The code to create a rolloverButton would then be as follows:
JButton button = new JButton(regularIcon); button.setRolloverEnabled(true); button.setRolloverIcon(new RolloverIcon(regularIcon)); |
The next step is to create the RolloverIcon class that is able to wrap an original icon and paint it with the desired graphics effect shown in Figure 1.
The RolloverIcon class will implement the interface javax.swing.Icon that allows it to be a valid value for a button's rolloverIcon property and store the original icon in an instance variable called fIcon:
public class RolloverIcon implements Icon {
protected Icon fIcon; {
An alternative solution could be to subclass JButton and encapsulate the rollover effect in the subclass, but by placing the logic in the RolloverIcon class, the graphic effect can be used in other scenarios such as for checkboxes or menu items. Should a custom subclass be desired, it is straightforward to use and delegate the logic to create the graphic effect to an instance of RolloverIcon.
The javax.swing.Icon interface has three methods: getIconWidth(), getIconHeight() and paintIcon(Component,Graphics,x,y).
The first of the two methods can be delegated to the icon that the RolloverIcon is creating the graphic effect for, as the rollover image is going to
be the same size as the original.
public int getIconHeight() {
return fIcon.getIconHeight();
}
public int getIconWidth() {
return fIcon.getIconWidth();
}
|
The paint method is the one that is actually going to draw onto the graphics context. The Component argument is the control for which the icon is being drawn such as the instance of the javax.swing.JButton. This allows us to access details such as the control's font, insets, etc. and other properties we might want to take into account when we paint the icon. The x and y arguments are the position that the graphic is being asked to be rendered on the drawing surface. These positions are absolute to the drawing surface, unlike the values returned from getLocation() on the component which will be the position of the button relative to its parent container. The drawing APIs need to use absolute values so having them passed into the paintIcon method avoids having to compute them by traversing all the parents of the component.
The graphics argument is the object that represents the drawing surface. Although it is typed to be java.awt.Graphics it will be an instance of java.awt.Graphics2D. The abstract Graphics2D class is part of the Java2D API that was introduced as part of the Java2 platform and for backward compatibility, the argument of the paint method was not retyped to be Graphics2D, although as long as the JRE being used is Java2 or higher then it can be guaranteed to be an instance of java.awt.Graphics2D.
The full signature of the paintIcon method is as follows:
public void paintIcon(Component c, Graphics g, int x, int y);
The RolloverIcon instance is wrapping the original icon we want to paint with the graphics effect shown in Figure 1. To do this we can make use of the composite property on the Graphics2D object. The composite property is typed to the interface java.awt.Composite and all primitive drawing on the graphics surface done by the Graphics2D object is directed through its composite. A number of pre-existing composite classes exist such as one for creating an XOR effect. The XORComposite is the class sun.java2d.loops.XORComposite and its constructor takes an argument of the color for the drawing being rendered to be XOR'd against. Each color is made up of a red, green, and blue value, and if color is XOR'd against black ( which is r,g,b of 0,0,0 ) then it is effectively inverted. To see this, the paint method could be written as follows:
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2D = (Graphics2D)g;
Composite oldComposite = g2D.getComposite();
g2D.setComposite(new sun.java2d.loops.XORComposite(Color.black));
fIcon.paintIcon(c,g,x,y);
g2D.setComposite(oldComposite);
}
|
To render the original icon, which is held in the instance variable fIcon, we just defer to its paintIcon(Component,Graphics,int,int) method having previously set the graphic's composite to be the XORComposite object. It is good practice when manipulating properties of the graphics object to restore them to the original values when finished. This is shown in the method above where the original composite is stored before changing it and then restored afterwards. If you do not do this, then the XORComposite will be left in the graphics object and it will affect all subsequent drawing.
Figure 2 shows the effect of the XORComposite. The top row of buttons have the original buttons with the RolloverIcon set as their rolloverIcon property. The bottom row of buttons are permanently set to show the result of the RolloverIcon which is set as their icon property. The XORComposite has taken the icon and inverted it by XORing each of the pixel values against 0.
Figure 2. An XORComposite can be used to control how the icon is rendered.

The XOR effect is not the result shown in Figure 1, but it does show how the composite is responsible for rendering the icon onto the drawing surface. If we create our own composite class that gives us access to the precise rendering of the icon, we should be able to achieve our desired
rollover effect. Our class will be called RolloverComposite and the paintIcon method can set this into the graphics object before rendering the
original icon.
Before implementing the RolloverComposite class, we need to understand more about how colors are represented in the Java language and how they are
painted onto a drawing surface.
The RolloverComposite must implement the interface java.awt.Composite. This has a single
method Entry Helpers that returns an instance of java.awt.CompositeContext. Rather than the
composite being responsible for doing the actual primitive manipulation of the pixels on the
graphics surface, it delegates this to the composite context object that is able to maintain state and
work in a multithreaded environment as several composite context objects can exist for a single
composite.
public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints) ; |
The abstract class ColorModel has the API to translate between color components and primitive
red, green, blue, and alpha components. The RenderingHints are the rendering hints from the
Graphics2D that can be used by the composite if required, such as whether antialias should be on
for text rendering or what kind of join style for lines should be used. The CompositeContext is the
thread safe object that actually manipulates the raw pixels and we will create an anonymous inner
class for this purpose, as follows:
return new CompositeContext(){
public void dispose(){
}
public void compose(Raster src, Raster dstIn, WritableRaster dstOut){
|
The CompositeContext interface has two methods, dispose() and
compose(Raster,Raster,WritableRaster). The dispose method lets us clean up any resources that
may have been allocated and the compose method is the one that is responsible for the primitive
drawing. We do not have any objects that need to be cleaned up so we can have a no-op
implementation of dispose(), and the compose(...) method is where we will do the pixel
manipulation.
The three arguments of the compose(...) method are the Raster for the source image, the Raster
for the destination image, and the WritableRaster that represents the output that will be rendered
on the graphics surface. To access the raw pixel for a given location the method getPixel(int x, int
y, pixel int[]) can be used. The int[] argument is a four argument array that represents the red,
green, blue, and alpha value for the pixel. The src argument contains the raster for the source
argument and the dstOut argument is a WritableRaster that has the method setPixel(int x, int y,
pixel int[]). By iterating over each pixel element from the source, we can create a new pixel with
the manipulated values and set it into the dstOut that will be drawn on the graphics surface. The
code for the compose method is as follows:
// Get the source pixels
int[] srcPixels = new int[4];
src.getPixel(x,y,srcPixels);
// Ignore transparent pixels
if (srcPixels[3] != 0){
// Lighten each color by 1/2, and increasing the blue
srcPixels[0] = srcPixels[0] / 2;
srcPixels[1] = srcPixels[1] / 2;
srcPixels[2] = srcPixels[2] / 2 + 68;
dstOut.setPixel(x,y,srcPixels);
}
}
};
}
|
Note that the fourth pixel (srcPixels[3]) is checked to see whether or not it is transparent or not. If it is transparent on the
source then we don't want to write anything to the dstOut as we want the existing pixel of the drawing surface to remain.
Having created the RolloverComposite class that returns a CompositeContext that sets the desired pixel values into the
raster that is drawn on the graphics surface, we need to complete the code in the RolloverIcon class. Earlier when we
were testing the XOR composite we created a new instance before setting it into the graphic's composite in the
paintIcon(...) method. Creating objects is expensive in Java language, as memory must be allocated and the resulting object then
garbage collected when it is no longer required, so we will instead have a singleton instance of the RolloverComposite class
that can be reused as required. We do not have to worry about multiple icons or graphics objects having access to the
same object, as all of the state is held by the CompositeContext that is re-created as required and provides thread-safe
access.
The RolloverComposite will contain its single instance in a public static field called DEFAULT. To enforce people to use
this singleton rather than creating a new object each time the constructor is made private so that only the
RolloverComposite class is able to create instances of itself.
public class RolloverComposite implements Composite {
public static RolloverComposite DEFAULT = new RolloverComposite();
private RolloverComposite(){ }
|
The paintIcon method in RolloverIcon needs to set the composite of the graphics argument to be the singleton
RolloverComposite before painting the original icon that it wrappers. The original composite needs to be restored
afterwards to ensure that any subsequent drawing is not unnecessarily affected by leaving the custom composite instance
there for longer than is required.
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2D = (Graphics2D)g;
Composite oldComposite = g2D.getComposite();
g2D.setComposite(RolloverComposite.DEFAULT);
fIcon.paintIcon(c,g,x,y);
g2D.setComposite(oldComposite);
}
|
Having coded the RolloverIcon class, we now need to test it. For this, a test harness was written that created two rows of
four buttons, the top of which had the rollover effect and the bottom of which had the button's icon permanently set to be
the rollover icon. You can download the code for the test harness, as well as all of the code for the RolloverIcon and the
RolloverComposite class from the Resources section below.
Figure 3. The test harness shows the effect of the RolloverIcon.

The figure above shows finished result of the RolloverIcon. The original graphics in the top row are darkened by 50% and
the intensity of the blue has been increased slightly to give the graphic effect we originally desired.
The RolloverIcon is an example of how extensible the Java2D API is, so that we can access very low level components in
the graphics subsystem to create our own extensions. The one we used to good affect for our graphics effect was to
control the composite used by the graphics object, but there are many other custom effects that can be achieved by
understanding more about how Java2D works. More information on the complete API can be found in one of the many
good books on Java2D (see Resources).
| Description | Name | Size | Download method |
|---|---|---|---|
| Code sample for this article | us-j2d/rollover.zip | 9 KB | HTTP |
Information about download methods
- Download the code for the test harness described earlier in this article.
- Read Java 2D API Graphics by Vincent J. Hardy, Prentice Hall PTR, ISBN 0130142662. This is an excellent book to learn about the Java 2D. It is well written and has many good examples.
- Read Pure Jfc 2d Graphics and Imaging by Satyaraj, Ph.D Pantham, Sams, ISBN 0672316692. This book is for the more experienced developer and it contains code samples for some advanced graphics effects.
- See this excellent on-line Java2D API tutorial that shows different effects possible with the Java2D API and includes sample code.
- Download some examples with accompanying code that show the different effects possible with the Java2D API. These samples can be run in a browser plugin.
Joe Winchester is a lead developer for WebSphere Application Developer (previously WebSphere Studio). He works in the Hursley, UK Labs as a senior software engineer. You can contact Joe at <a href="mailto:winchest@uk.ibm.com">winchest@uk.ibm.com</a>.
Renee Schwartz works as a visual designer for IBM at the Research Triangle Park Lab in Raleigh, N.C. Primarily focused on WebSphere products, she applies her traditional visual design skills and knowledge to Web and Java application development. You can contact Renee at <a href="mailto:rsch@us.ibm.com">rsch@us.ibm.com</a>.




