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 Effectinstances 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 theBoxBlur.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.inputvariable 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 aText 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 Reflectioneffect and is positioned in (faux) 3D using aPerspectiveTransform. 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 theDropShadow 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 aText, 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