Introducing Painters II: filters, shapes, and the builder

Introduction

Welcome back. Last week I introduced a cool new technology we've been working on in SwingLabs, Painters, and described how they work. If you missed the first blog you should go read it now. Don't worry. We'll wait.

Back now? Great. So lets dive a bit deeper to Painters. This week I'm going to cover Filters, Shapes Effects, and the visual builder.

Filters

The Painter interface only defines the single paint method, but all of the built in painters actually subclass AbstractPainter which provides a bit more. AbstractPainter has several methods for controlling rendering options like antialiasing and interpolation as well as caching support.

When you turn on caching the drawing operations will be stored in an intermediate buffer, which will in turn be used for all screen painting. This way your code doesn't have to be called on every repaint if nothing has changed. If your painting is slow, or if you are painting a bunch of times quickly (as with an animation) then using the cache will greatly speed up your application's drawing performance. In a second you see why this is so important.

One addition feature of AbstractPainter is support for Effects. An Effect is an object which transforms an image in some way. Currently there is only one concrete implementation of the Effect interface: ImageEffect. ImageEffect will apply a BufferedImageOp to your image. Some common examples of BufferedImageOps are things like Gaussian Blurs, brightness and constrast adjustments, and drop shadows. Perhaps an example is in order:

Suppose we want a drop shadow on some text. As before we will put a text painter on top of a matte painter, but this time we will add a shadow filter using the setEffects method.

    [prettify]
TextPainter textPainter = new TextPainter("Painters Rock!",
    new Font("SansSerif",Font.BOLD, 36),Color.RED);
textPainter.setEffects(new ShadowFilter());
MattePainter matte = new MattePainter(Color.WHITE);

JXPanel panel4 = new JXPanel();
panel4.setBackgroundPainter(
    new CompoundPainter(matte, textPainter));
[/prettify]

The setEffects method actually accepts multiple filters using the varargs notation in Java 5, so you can apply multiple filters at the same time. The code above would look like this:

Picture 8.png

If you change the effect to a CrystallizeFilter and set the text to green it would look like this:

crystalizeexample.png

The two examples above use filters that from JH Labs released under the Apache License. To see a more sophisticated use of painter effects take a look at Romain's blog entry which shows more shadows and a generated wood texture

Shape Effects

Filters are great but they can be slow because they must operate on every pixel, even the ones you don't see. The drop shadow, for example, calculates the pixels in the middle of each letter, not just the edges, even though that will always evaluate to black. Because of this Filters look cool but in practice are usually too slow for realtime work. To address this problem we've come up with ShapeEffects.

A ShapeEffect is a vector based effect that can be applied to any Shape class. Because it uses Java2D vector code rather than pixel level manipulation it can be many times faster than an equivalent Filter. Shape effects are modeled after the Layer Effects in Photoshop. Currently you can use inner and outer glows and shadows. They are all the same effect but you can configure them by changing offsets and drawing colors.

Here's an example of using an inner glow to create a mild embossing effect.

shapeeffectsdemo.png

The ShapeEffect API is still under active design and bug fixing. (note the clipping on the right edge of the 'S'). There is only one concrete implementation right now, but in the future we will expand this to a general interface for all sort of shape effects.

The Painter Visual Builder

This is the part of painters I am most excited about. The only thing better than reusing your own code is not having to write code at all! The Painter Visual Builder (needs a better name) is a graphical tool that lets you build sets of painters by setting properties and dragging around treenodes. The finished painter set can be saved to XML and then loaded into your application using the URLPainter. Lets try an example:

This is what the builder looks like when you start it up.

painterbuilderstartup.png

First delete the ShapePainter by selecting it in the tree and pressing the Delete button. Next, select the TextPainter and use the property sheet to change the paint property. This will open a dialog to choose a color or gradient. Create a gradient that looks like this:

gradientpickerexample.png

Close the gradient dialog and check the snapPaint property. Without this the gradient would be a fixed size, regardless of the component the painter is attached to. With snappaint turned on the gradient will always stretch across the component at runtime, even if it's a resizable component. snapPaint will also snap to the eight compass points so you don't have to worry about drawing your gradients perfectly straight. Now the painter preview should look like this:

visualbuilderpreview1.png

Now change the background color of the MattePainter to be black.

Now that your painter set is done you can save it as an XML file with "Save" menu item. With the painter set stored as XML you can load it into your application using a URLPainter like this:

    [prettify]
JXLabel label5 = new JXLabel("Painters Rule");        
URLPainter painter = new URLPainter(
     new File("/Users/joshy/Desktop/test.xml"));
label5.setBackgroundPainter(painter);
[/prettify]

finaljxlabelexample.png

You can try out the painter builder yourself with this webstartable version.

The Painter Visual Builder is still very alpha software, and as such is very buggy. We hope to improve to support for more of the core painters as well as shake out bugs in the new JXPropertySheet, the Outline view (a possible tree table replacement), and the JXGradientPicker.

Get Involved

If Painter Technology is something that interests you then please join the SwingLabs mailing list and get involved. The API and implementation are very much in flux and we could really use your feedback. Please tell us what you think!

Talk to me about it on Twitter

Posted September 26th, 2006

Tagged: java.net