Skip to main content

Effects in JavaFX: Chaining

Posted by campbell on June 11, 2009 at 2:04 AM PDT

Picking up where I left off six (cough, cough) months ago, in my series on the effects framework in JavaFX...

In JavaFX, you can chain effects together, contrary to a couple blog entries I've come across recently. This feature may not be entirely obvious from the API documentation, so let's look at chaining in a bit more detail here.

Just like Nodes in the JavaFX scene graph, Effects can be linked together in a tree-like structure. Most Effect subclasses expose one or more "input" variables that allow you to chain Effect instances together. By default, the input variable is set to null, indicating that the rendered contents of the Node to which the Effect is attached will be used as the input to the effect. For example, in the following code snippet, since we do not set the BoxBlur.input variable, the blur effect will be applied to the Text node content:

    Text {
        effect: BoxBlur {}
        content: "Hello!"
        ...
    }

If we instead explicitly set the BoxBlur.input variable to, say, a SepiaTone instance, the sepia effect will first be applied to the node content, and the result of that operation will then be blurred:

    Text {
        effect: BoxBlur {
            input: SepiaTone {}
        }
        content: "Hello!"
        ...
    }

The following is a simple applet that demonstrates what a Text node looks like when one or more effects are chained to it:


(The source bundle for this demo is available here.)

Note that in some situations, the order of the chained effects can be quite significant. One such scenario is in "cover flow" style components, where each node has a Reflection effect and is positioned in (faux) 3D using a PerspectiveTransform. In this case, it is important to apply the reflection effect first, and then transform the result last. If you try it the other way around, the result will be goofily [goofily, really?] non-realistic. The applet above demonstrates the difference in behavior of the two orderings.

Unfortunately, due to a minor technical limitation, we have not yet exposed an input variable on the DropShadow and InnerShadow classes as of the 1.2 release. This will certainly be resolved in an upcoming release. In the meantime, you can achieve the same result (albeit with a bit more code) by using nested Groups. For example, we can apply a Lighting effect to a Text, and then wrap that in a Group that has a DropShadow applied to it:

    Group {
        effect: DropShadow {
            radius: 14
            offsetX: 4
            offsetY: 4
        }
        content: Text {
            effect: Lighting {
                light: DistantLight { azimuth: -135 }
                surfaceScale: 5
            }
        }
    }

In the next few installments, I'll look at individual effects such as Lighting and Reflection in more detail. (Let's hope this time I get to it sooner than six months from now!)


In my ears: Charles Mingus, The Black Saint And The Sinner Lady


In my eyes: Ricky Gervais and Karl Pilkington, The World of Karl Pilkington

Related Topics >>

Comments

Its cool, but it would be infinitely more impressive if you bound the content of the Text object to an input box.