Main Menu

Effects in JavaFX: Chaining

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:

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: