Frosted Glass on the Java™ Desktop
More and more operating systems use a border resembling frosted glass for their windows, like, e.g., the Aero Glass® decoration known from Windows Vista® and Windows 7®. Providing this ’special effect’ on the Java™ platform is still not easy to realize. Most Look and Feels use opaque borders, which do not visually match the surrounding designs of these operating systems.
This article describes a pragmatic approach to solve to this problem.
The desired result of a Java implementation is illustrated in the following figure:
The whole procedure of rendering such a border can be divided into three steps:
- Blurring the background that is covered by the border area
- Overlaying the ‘milky’ surface with respect to the rounded corners
- Painting outline decoration and drop shadow
How to blur the covered background
Blurring a region of an image is quite complicated in Java 2D since it is not a simple composite operation – instead, it is a more complicated convolution operation. Additionally, the fact that a JComponent and its UI only receive a reference to a Graphics instance on which painting occurs makes it even more complicated.
One option to actually access the pixel data is to override JDesktopPane’s paint(Graphics) method and invoke super.paint(Graphics) with a Graphics instance created from an offscreen image, which finally contains the necessary data and can be painted on the original Graphics. To ensure that JDesktopPane’s paint method is in charge of painting the child components ‘optimized drawing’ has to be disabled.
public class FrostedGlassDesktopPane extends JDesktopPane {
public boolean isOptimizedDrawingEnabled() {
// we want to be in charge of painting the children
return false;
}
public void paint(Graphics g) {
// we only have to care about
// what's inside clipping bounds
Rectangle clipBounds = g.getClipBounds();
// request a (reused) offscreen buffer
BufferedImage bufferImage =
getBufferImage(clipBounds.width, clipBounds.height);
Graphics bufferGraphics = bufferImage.createGraphics();
// translate and clip according to our given graphics g
bufferGraphics.translate(-clipBounds.x, -clipBounds.y);
bufferGraphics.setClip(clipBounds);
try {
// let children paint
super.paint(this.bufferGraphics);
// blit offscreen buffer to given graphics g
g.drawImage(bufferImage, clipBounds.x, clipBounds.y,
null);
} catch (RuntimeException ex) {
throw ex;
} finally {
bufferGraphics.dispose();
}
}
}
Since a BufferedImage is used for offscreen rendering it would be possible to use a ConvolveOp for the blur effect. Unfortunately, even with a small sized kernel of 5 by 5 pixels, the operation seems to be too slow for a real-world situation where the CPU is also busy with drawing additional GUI elements and executing application logic.
A good alternative seems to be using a one dimensional box blur (also called motion blur), which can be computed very fast using a moving average algorithm. Applying it horizontally leads to the following result:
Doing it in a first pass horizontally and in a second one vertically results in a two dimensional box blur:
Since execution is really fast but visually imperfect, this filter operation can be done multiple times to get increasingly better results with every iteration. Just doing it twice already looks a lot smoother and less artificial:
This solution delivers acceptable results regarding visual quality and performance. Benchmarking a small demo application with four and a second one with eight internal frames shows good frame rates compared to the aforementioned ConvolveOp version:
‘Milky’ overlay and rounded corners
To get a naturally looking frosted glass effect, a ‘milky’ overlay is applied after doing the blur. This can be done in a simple fashion by filling an area with a translucent color. However the rounded corners are more difficult to achieve.
The first choice for realizing this non-rectangular outline would be a clipping shape. But because the blur is done directly in a pixel raster by computing RGB values in contrast to operating on a Graphics instance, a clipping shape is not applicable. Another drawback of using a clipping shape is missing anti-aliasing resulting in an unaesthetic staircase effect. Although these artifacts can be avoided by overlaying an image with a thin anti-aliased outline (like we did for the leather texture in Cezanne’s showcase application Icon Book), there are more efficient options available.
A reasonable compromise between effort, performance and visual quality is
- saving the corner area to a buffer (
cornerBuffer) before painting the border - painting the border with rectangular shape
- cutting out the piece of the corner buffer that should not be obscured by rounded corners
- copying that piece back into the
Graphics
For step 3, a masking image (of which only the alpha channel is necessary) can be used together with an AlphaComposite of type DST_OUT:
protected void restoreCorner(Graphics g, int x, int y) {
Graphics2D g2d = cornerBuffer.createGraphics();
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.DST_OUT));
g2d.drawImage(maskingImage, 0, 0, null);
g2d.dispose();
g.drawImage(cornerBuffer, x, y, null);
}
The following images illustrate the process step by step:
Painting outline decoration and drop shadow
The last and certainly most easy step is painting the border outline and the drop shadow. This can be done by assembling picture elements using the 9-Slice-Scaling method.
The result
A sample application demonstrating this new frosted glass desktop can be started via Web Start:

Interestingly the signed version of the demo application is noticeably faster (about 40% on some machines):

Conclusion
Although creating a frosted glass border decoration in Java is not an easy task, it is possible to achieve great results with a few tricks.
The implementation I did for this article is not a full-featured replacement for JDesktopPane and JInternalFrame’s UI. It lacks some essential aspects of the original Java counterparts, like, e.g., the system menu, proper decoration of full size frames and iconified frames and correct location of drag-resize area. Nevertheless, it serves as a good example on how the visual appearance can be achieved.
Feel free to download the Jar and give it a try in your application (as long as it is non-commercial):
Using it is pretty simple: You can either assign the JInternalFrame UI on a per instance basis …
import de.centigrade.frostedglass.FrostedGlassDesktopFactory; ... JDesktopPane desktopPane = FrostedGlassDesktopFactory .getInstance().createDesktopPane(); JInternalFrame internalFrame = new JInternalFrame(null, true, true, true, false); internalFrame.setUI(FrostedGlassDesktopFactory.getInstance() .createInternalFrameUI()); internalFrame.setBounds(100, 100, 200, 150); internalFrame.setVisible(true); desktopPane.add(internalFrame);
… or set it as global UI for all JInternalFrames …
import de.centigrade.frostedglass.FrostedGlassDesktopFactory;
...
UIManager.put("InternalFrameUI",
FrostedGlassDesktopFactory.class.getName());
JDesktopPane desktopPane = FrostedGlassDesktopFactory
.getInstance().createDesktopPane();
JInternalFrame internalFrame = new JInternalFrame(null, true,
true, true, false);
internalFrame.setBounds(100, 100, 200, 150);
internalFrame.setVisible(true);
desktopPane.add(internalFrame);
You can vary the frost effect by using different color overlays. These can be created with static factory methods in class de.centigrade.frostedglass.FrostEffect. Either you can use one of the predefined colors or you choose createColored(Color) which takes an arbitrary color (but has slightly lower performance).
import de.centigrade.frostedglass.FrostEffect; import de.centigrade.frostedglass.FrostedGlassDesktopFactory; ... JInternalFrame internalFrame = new JInternalFrame(null, true, true, true, false); internalFrame.setUI(FrostedGlassDesktopFactory.getInstance() .createInternalFrameUI()); FrostEffect darkFrostEffect = FrostEffect.createDark(); darkFrostEffect.apply(internalFrame);
Microsoft, Windows Vista, Windows 7, Aero and Aero Glass are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.




