© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
K. Sharan, P. SpäthLearn JavaFX 17https://doi.org/10.1007/978-1-4842-7848-2_17

17. Applying Effects

Kishori Sharan1   and Peter Späth2
(1)
Montgomery, AL, USA
(2)
Leipzig, Sachsen, Germany
 
In this chapter, you will learn:
  • What an effect is

  • How to chain effects

  • What different types of effects are

  • How to use perspective transformation effects

The examples of this chapter lie in the com.jdojo.effect package. In order for them to work, you must add a corresponding line to the module-info.java file:
...
opens com.jdojo.effect to javafx.graphics, javafx.base;
...

What Is an Effect?

An effect is a filter that accepts one or more graphical inputs, applies an algorithm on the inputs, and produces an output. Typically, effects are applied to nodes to create visually appealing user interfaces. Examples of effects are shadow, blur, warp, glow, reflection, blending, and different types of lighting, among others. The JavaFX library provides several effect-related classes. Effects are conditional features. They are applied to nodes and will be ignored if they are not available on a platform. Figure 17-1 shows four Text nodes using the drop shadow, blur, glow, and bloom effects.
Figure 17-1

Text nodes with different effects

The Node class contains an effect property that specifies the effect applied to the node. By default, it is null. The following snippet of code applies a drop shadow effect to a Text node:
Text t1 = new Text("Drop Shadow");
t1.setFont(Font.font(24));
t1.setEffect(new DropShadow());

An instance of the Effect class represents an effect. The Effect class is the abstract base for all effect classes. All effect classes are included in the javafx.scene.effect package.

The program in Listing 17-1 creates Text nodes and applies effects to them. These nodes are the ones shown in Figure 17-1. I will explain the different types of effects and their usages in subsequent sections.
// EffectTest.java
package com.jdojo.effect;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.effect.Bloom;
import javafx.scene.effect.BoxBlur;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Glow;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class EffectTest extends Application {
        public static void main(String[] args) {
                Application.launch(args);
        }
        @Override
        public void start(Stage stage) {
                Text t1 = new Text("Drop Shadow!");
                t1.setFont(Font.font(24));
                t1.setEffect(new DropShadow());
                Text t2 = new Text("Blur!");
                t2.setFont(Font.font(24));
                t2.setEffect(new BoxBlur());
                Text t3 = new Text("Glow!");
                t3.setFont(Font.font(24));
                t3.setEffect(new Glow());
                Text t4 = new Text("Bloom!");
                t4.setFont(Font.font("Arial", FontWeight.BOLD, 24));
                t4.setFill(Color.WHITE);
                t4.setEffect(new Bloom(0.10));
                // Stack the Text node with bloom effect over a
                     // Reactangle
                Rectangle rect = new Rectangle(100, 30, Color.GREEN);
                StackPane spane = new StackPane(rect, t4);
                HBox root = new HBox(t1, t2, t3, spane);
                root.setSpacing(20);
                root.setStyle("""
                         -fx-padding: 10;
                   -fx-border-style: solid inside;
                   -fx-border-width: 2;
                   -fx-border-insets: 5;
                   -fx-border-radius: 5;
                   -fx-border-color: blue;""");
                Scene scene = new Scene(root);
                stage.setScene(scene);
                stage.setTitle("Applying Effects");
                stage.show();
        }
}
Listing 17-1

Applying Effects to Nodes

Tip

An effect applied to a Group is applied to all its children. It is also possible to chain multiple effects where the output of one effect becomes the input for the next effect in the chain. The layout bounds of a node are not affected by the effects applied to it. However, the local bounds and bounds in the parent are affected by the effects.

Chaining Effects

Some effects can be chained with other effects when they are applied in sequence. The output of the first effect becomes the input for the second effect and so on, as shown in Figure 17-2.
Figure 17-2

A chain of effects applied on a node

Effect classes that allow chaining contain an input property to specify the effect that precedes it. If the input is null, the effect is applied to the node on which this effect is set instead of being applied to the preceding input effect. By default, the input is null. The following snippet of code creates two chains of effects on Text nodes, as shown in Figure 17-3:
// Effect Chain: Text >> Reflection >> Shadow
DropShadow dsEffect = new DropShadow();
dsEffect.setInput(new Reflection());
Text t1 = new Text("Reflection and Shadow");
t1.setEffect(dsEffect);
// Effect Chain: Text >> Shadow >> Reflection
Reflection reflection = new Reflection();
reflection.setInput(new DropShadow());
Text t2 = new Text("Shadow and Reflection");
t2.setEffect(reflection);
Figure 17-3

Chaining a DropShadow effect with a Reflection effect

In Figure 17-3, a Reflection effect followed by a DropShadow is applied to the text on the left; a DropShadow followed by a Reflection effect is applied to the text on the right. Notice the sequence of effects makes a difference in the output. The second chain of effects produces a taller output as the reflection also includes the shadow.

If an effect allows chaining, it will have an input property. In subsequent sections, I will list the input property for the effect classes, but not discuss it.

Shadowing Effects

A shadowing effect draws a shadow and applies it to an input. JavaFX supports three types of shadowing effects:
  • DropShadow

  • InnerShadow

  • Shadow

The DropShadow Effect

The DropShadow effect draws a shadow (a blurred image) behind the input, so the input seems to be raised. It gives the input a 3D look. The input can be a node or an effect in a chain of effects.

An instance of the DropShadow class represents a DropShadow effect. The size, location, color, and quality of the effect are controlled by several properties of the DropShadow class :
  • offsetX

  • offsetY

  • color

  • blurType

  • radius

  • spread

  • width

  • height

  • input

The DropShadow class contains several constructors that let you specify the initial values for the properties:
  • DropShadow()

  • DropShadow(BlurType blurType, Color color, double radius, double spread, double offsetX, double offsetY)

  • DropShadow(double radius, Color color)

  • DropShadow(double radius, double offsetX, double offsetY, Color color)

The offsetX and offsetY properties control the position of the shadow in pixels relative to the input. By default, their values are zero. The positive values of offsetX and offsetY move the shadow in the positive x-axis and y-axis directions, respectively. The negative values move the shadow in the reverse directions.

The following snippet of code creates a DropShadow object with the offsetX and offsetY of 10px. The third rectangle from the left in Figure 17-4 shows the rectangle with the effect using the same rectangle with a DropShadow effect and different x and y offsets. For the fourth from the left rectangle, the shadow is positioned at the lower-right corner of the rectangle as the rectangle size (50, 25) matches the offsets (50, 25).
DropShadow dsEffect = new DropShadow();
dsEffect.setOffsetX(10);
dsEffect.setOffsetY(10);
Rectangle rect = new Rectangle(50, 25, Color.LIGHTGRAY);
rect.setEffect(dsEffect);
Figure 17-4

Effects of the offsetX and offsetY properties on a DropShadow effect

The color property specifies the color of the shadow. By default, it is Color.BLACK. The following code would set the color to red:
DropShadow dsEffect = new DropShadow();
dsEffect.setColor(Color.RED);
The blurring in the shadow can be achieved using different algorithms. The blurType property specifies the type of blurring algorithm for the shadow. Its value is one of the following constants of the BlurType enum:
  • ONE_PASS_BOX

  • TWO_PASS_BOX

  • THREE_PASS_BOX

  • GAUSSIAN

The ONE_PASS_BOX uses a single pass of the box filter to blur the shadow. The TWO_PASS_BOX uses two passes of the box filter to blur the shadow. The THREE_PASS_BOX uses three passes of the box filter to blur the shadow. The GAUSSIAN uses a Gaussian blur kernel to blur the shadow. The blur quality of the shadow is the least in ONE_PASS_BOX and the best in GAUSSIAN. The default is THREE_PASS_BOX, which is very close to GAUSSIAN in quality. The following snippet of code sets the GAUSSIAN blur type:
DropShadow dsEffect = new DropShadow();
dsEffect.setBlurType(BlurType.GAUSSIAN);

The radius property specifies the distance the shadow is spread on each side of the source pixel. If the radius is zero, the shadow has sharp edges. Its value can be between 0 and 127. The default value is 10. The blurring outside the shadow region is achieved by blending the shadow color and the background color. The blur color fades out over the radius distance from the edges.

Figure 17-5 shows a rectangle twice with a DropShadow effect. The one on the left uses the radius of 0.0, which results in sharp edges of the shadow. The one on the right uses the default radius of 10.0 that spreads the shadow 10px around the edges. The following snippet of code produces the first rectangle in the figure that has sharp edges of the shadow:
DropShadow dsEffect = new DropShadow();
dsEffect.setOffsetX(10);
dsEffect.setOffsetY(10);
dsEffect.setRadius(0);
Rectangle rect = new Rectangle(50, 25, Color.LIGHTGRAY);
rect.setEffect(dsEffect);
Figure 17-5

Effects of the radius property of a DropShadow effect

The spread property specifies the portion of the radius, which has the same color as the shadow. The color for the remaining portion of the radius is determined by the blur algorithm. Its value is between 0.0 and 1.0. The default is 0.0.

Suppose you have a DropShadow with a radius 10.0 and a spread value of 0.60 and the shadow color is black. In this case, the blur color will be black up to 6px around the source pixel. It will start fading out from the seventh pixel to the tenth pixel. If you specify the spread value as 1.0, there would be no blurring of the shadow. Figure 17-6 shows three rectangles with a DropShadow using a radius of 10.0. The three DropShadow effects use different spread values. The spread of 0.0 blurs fully along the radius. The spread of 0.50 spreads the shadow color in the first half of the radius and blurs the second half. The spread of 1.0 spreads the shadow color fully along the radius, and there is no blurring. The following snippet of code produces the middle rectangle in Figure 17-6:
DropShadow dsEfefct = new DropShadow();
dsEfefct.setOffsetX(10);
dsEfefct.setOffsetY(10);
dsEfefct.setRadius(10);
dsEfefct.setSpread(.50);
Rectangle rect = new Rectangle(50, 25, Color.LIGHTGRAY);
rect.setEffect(dsEfefct);
Figure 17-6

Effects of the spread property of a DropShadow effect

The width and height properties specify the horizontal and vertical distances, respectively, from the source pixel up to where the shadow color is spread. Their values are between 0 and 255. Setting their values is equivalent to setting the radius property, so they are equal to (2 * radius + 1). Their default value is 21.0. When you change the radius, the width and height properties are adjusted using the formula if they are not bound. However, setting the width and height changes the radius value, so the average of the width and height is equal to (2 * radius + 1). Figure 17-7 shows four rectangles with DropShadow effects. Their width and height properties were set as shown under each rectangle. Their radius properties were adjusted automatically. The fourth from the left rectangle was produced using the following snippet of code:
DropShadow dsEffect = new DropShadow();
dsEffect.setOffsetX(10);
dsEffect.setOffsetY(10);
dsEffect.setWidth(20);
dsEffect.setHeight(20);
Rectangle rect = new Rectangle(50, 25, Color.LIGHTGRAY);
rect.setEffect(dsEffect);
Figure 17-7

Effects of setting the width and height of a DropShadow

The program in Listing 17-2 lets you experiment with properties of the DropShadow effect. It displays a window as shown in Figure 17-8. Change the properties to see their effects in action.
// DropShadowTest.java
// ...find in the book's download area.
Listing 17-2

Experimenting with DropShadow Properties

Figure 17-8

A window that allows you to change the properties of a DropShadow effect at runtime

The InnerShadow Effect

The InnerShadow effect works very similar to the DropShadow effect. It draws a shadow (a blurred image) of an input inside the edges of the input, so the input seems to have depth or a 3D look. The input can be a node or an effect in a chain of effects.

An instance of the InnerShadow class represents an InnerShadow effect. The size, location, color, and quality of the effect are controlled by several properties of the InnerShadow class:
  • offsetX

  • offsetY

  • color

  • blurType

  • radius

  • choke

  • width

  • height

  • input

The number of properties of the InnerShadow class is equal to that for the DropShadow class. The spread property in the DropShadow class is replaced by the choke property in the InnerShadow class, which works similar to the spread property in the DropShadow class. Please refer to the previous section “The DropShadow Effect” for a detailed description and examples of these properties.

The DropShadow class contains several constructors that let you specify the initial values for the properties:
  • InnerShadow()

  • InnerShadow(BlurType blurType, Color color, double radius, double choke, double offsetX, double offsetY)

  • InnerShadow(double radius, Color color)

  • InnerShadow(double radius, double offsetX, double offsetY, Color color)

The program in Listing 17-3 creates a Text node and two Rectangle nodes . An InnerShadow is applied to all three nodes. Figure 17-9 shows the results for these nodes. Notice that the shadow is not spread outside the edges of the nodes. You need to set the offsetX and offsetY properties to see a noticeable effect.
// InnerShadowTest.java
// ...find in the book's download area.
Listing 17-3

Using the InnerShadow Class

Figure 17-9

A Text and two Rectangle nodes using InnerShadow effects

The Shadow Effect

The Shadow effect creates a shadow with blurry edges of its input. Unlike DropShadow and InnerShadow, it modifies the original input itself to convert it into a shadow. Typically, a Shadow effect is combined with the original input to create a higher-level shadowing effect:
  • You can apply a Shadow effect with a light color to a node and superimpose it on a duplicate of the original node to create a glow effect.

  • You can create a Shadow effect with a dark color and place it behind the original node to create a DropShadow effect.

An instance of the Shadow class represents a Shadow effect. The size, color, and quality of the effect are controlled by several properties of the Shadow class:
  • color

  • blurType

  • radius

  • width

  • height

  • input

These properties work the same way they work in the DropShadow. Please refer to the section “The DropShadow Effect” for a detailed description and examples of these properties.

The Shadow class contains several constructors that let you specify the initial values for the properties:
  • Shadow()

  • Shadow(BlurType blurType, Color color, double radius)

  • Shadow(double radius, Color color)

The program in Listing 17-4 demonstrates how to use the Shadow effect. It creates three Text nodes . A shadow is applied to all three nodes. The output of the first shadow is displayed. The output of the second shadow is superimposed on the original node to achieve a glow effect. The output of the third shadow is placed behind its original node to achieve a DropShadow effect. Figure 17-10 shows these three nodes.
// ShadowTest.java
// ...find in the book's download area.
Listing 17-4

Using a Shadow Effect and Creating High-Level Effects

Figure 17-10

Applying a shadow to a Text node and creating Glow and DropShadow effects

Blurring Effects

A blurring effect produces a blurred version of an input. JavaFX lets you apply different types of blurring effects, which differ in the algorithms used to create these effects.

The BoxBlur Effect

The BoxBlur effect uses a box filter kernel to produce a blurring effect. An instance of the BoxBlur class represents a BoxBlur effect. The size and quality of the effect can be configured using these properties of the class:
  • width

  • height

  • iterations

  • input

The width and height properties specify the horizontal and vertical size of the effect, respectively. Imagine a box defined by the width and height centered on a pixel of the input. The color information of the pixel is spread within the box during the blurring process. The values of these properties are between 0.0 and 255.0. The default values are 5.0. A value of less than or equal to 1.0 does not produce the blurring effect in the corresponding direction.

The iterations property specifies the number of times the blurring effect is applied. A higher value produces a better quality blur. Its value can be between zero and three. The default is one. The value of three produces the blur quality comparable to the Gaussian blur, discussed in the next section. The value of zero produces no blur at all.

The BoxBlur class contains two constructors:
  • BoxBlur()

  • BoxBlur(double width, double height, int iterations)

The no-args constructor creates a BoxBlur object with the width and height of 5.0 pixels and iterations of 1. The other constructor lets you specify the initial value for the width, height, and iterations properties, as in the following section of code:
// Create a BoxBlur with defaults: width=5.0, height=5.0, iterations=1
BoxBlur bb1 = new BoxBlur();
// Create a BoxBlur with width=10.0, height=10.0, iterations=3
BoxBlur bb2 = new BoxBlur(10, 10, 3);
The following snippet of code creates four Text nodes and applies BoxBlur effects of various qualities. Figure 17-11 shows the results of these Text nodes. Notice that the last Text node does not have any blur effect as the iterations property is set to zero.
Text t1 = new Text("Box Blur");
t1.setFont(Font.font(24));
t1.setEffect(new BoxBlur(5, 10, 1));
Text t2 = new Text("Box Blur");
t2.setFont(Font.font(24));
t2.setEffect(new BoxBlur(10, 5, 2));
Text t3 = new Text("Box Blur");
t3.setFont(Font.font(24));
t3.setEffect(new BoxBlur(5, 5, 3));
Text t4 = new Text("Box Blur");
t4.setFont(Font.font(24));
t4.setEffect(new BoxBlur(5, 5, 0)); // Zero iterations = No blurring
Figure 17-11

Text nodes with BoxBlur effects of varying qualities

The GaussianBlur Effect

The GaussianBlur effect uses a Gaussian convolution kernel to produce a blurring effect. An instance of the GaussianBlur class represents a GaussianBlur effect. The effect can be configured using two properties of the class:
  • radius

  • input

The radius property controls the distribution of the blur in pixels from the source pixel. The greater this value, the more the blur effect. Its value can be between 0.0 and 63.0. The default value is 10.0. A radius of 0 pixels produces no blur effect.

The GaussianBlur class contains two constructors:
  • GaussianBlur()

  • GaussianBlur(double radius)

The no-args constructor creates a GaussianBlur object with a default radius of 10.0px. The other constructor lets you specify the initial value for the radius, as in the following code:
// Create a GaussianBlur with a 10.0 pixels radius
GaussianBlur gb1 = new GaussianBlur();
// Create a GaussianBlur with a 20.0 pixels radius
GaussianBlur gb2 = new GaussianBlur(20);
The following snippet of code creates four Text nodes and applies GaussianBlur effects of different radius values. Figure 17-12 shows the results of these Text nodes. Notice that the last Text node does not have any blur effect as the radius property is set to zero.
Text t1 = new Text("Gaussian Blur");
t1.setFont(Font.font(24));
t1.setEffect(new GaussianBlur(5));
Text t2 = new Text("Gaussian Blur");
t2.setFont(Font.font(24));
t2.setEffect(new GaussianBlur(10));
Text t3 = new Text("Gaussian Blur");
t3.setFont(Font.font(24));
t3.setEffect(new GaussianBlur(15));
Text t4 = new Text("Gaussian Blur");
t4.setFont(Font.font(24));
t4.setEffect(new GaussianBlur(0)); // radius = 0 means no blur
Figure 17-12

Text nodes with GaussianBlur effects of varying sizes

The MotionBlur Effect

The MotionBlur effect produces a blurring effect by motion. The input looks as if you are seeing it while it is moving. A Gaussian convolution kernel is used with a specified angle to produce the effect. An instance of the MotionBlur class represents a MotionBlur effect. The effect can be configured using the three properties of the class:
  • radius

  • angle

  • input

The radius and input properties work the same as respective properties for the GaussianBlur class, as described in the previous section. The angle property specifies the angle of the motion in degrees. By default, the angle is zero.

The MotionBlur class contains two constructors:
  • MotionBlur()

  • MotionBlur(double angle, double radius)

The no-args constructor creates a MotionBlur object with a default radius of 10.0px and an angle of 0.0 degrees. The other constructor lets you specify the initial value for the angle and radius, as shown in the following code:
// Create a MotionBlur with a 0.0 degrees angle and a 10.0 pixels radius
MotionBlur mb1 = new MotionBlur();
// Create a MotionBlur with a 30.0 degrees angle and a 20.0 pixels radius
MotionBlur mb1 = new MotionBlur(30.0, 20.0);
The program in Listing 17-5 shows how to use the MotionBlur effect on a Text node, with the results shown in Figure 17-13. The two sliders let you change the radius and angle properties.
// MotionBlurTest.java
// ...find in the book's download area.
Listing 17-5

Using the MotionBlur Effect on a Text Node

Figure 17-13

Text nodes with GaussianBlur effects of varying sizes

The Bloom Effect

The Bloom effect adds a glow to the pixels of its input that have a luminosity greater than or equal to a specified limit. Note that not all pixels in a Bloom effect are made to glow.

An instance of the Bloom class represents a Bloom effect. It contains two properties:
  • threshold

  • input

The threshold property is a number between 0.0 and 1.0. Its default value is 0.30. All pixels in the input having a luminosity greater than or equal to the threshold property are made to glow. The brightness of a pixel is determined by its luminosity. A pixel with a luminosity of 0.0 is not bright at all. A pixel with a luminosity of 1.0 is 100% bright. By default, all pixels having a luminosity greater than or equal to 0.3 are made to glow. A threshold of 0.0 makes all of the pixels glow. A threshold of 1.0 makes almost no pixels glow.

The Bloom class contains two constructors:
  • Bloom()

  • Bloom(double threshold)

The no-args constructor creates a Bloom object with a default threshold of 0.30. The other constructor lets you specify the threshold value, as shown in the following code:
// Create a Bloom with threshold 0.30
Bloom b1 = new Bloom();
// Create a Bloom with threshold 0.10 - more pixels will glow.
Bloom b2 = new Bloom(0.10);
Figure 17-14 shows four Text nodes with Bloom effects that have different threshold values. A Text node is laid over a rectangle using a StackPane. Notice that the lower the threshold value, the higher the blooming effect. The following snippet of code creates the first Text node and Rectangle pair from the left in Figure 17-14:
Text t1 = new Text("Bloom");
t1.setFill(Color.YELLOW);
t1.setFont(Font.font(null, FontWeight.BOLD, 24));
t1.setEffect(new Bloom(0.10));
Rectangle r1 = new Rectangle(100, 50, Color.GREEN);
StackPane sp1 = new StackPane(r1, t1);
Figure 17-14

Text nodes with Bloom effects

The Glow Effect

The Glow effect makes the bright pixels of the input brighter. An instance of the Glow class represents a Glow effect. It contains two properties:
  • level

  • input

The level property specifies the intensity of the Glow effect. It is a number between 0.0 and 1.0, and its default value is 0.30. A level of 0.0 adds no glow, and a level of 1.0 adds the maximum glow.

The Glow class contains two constructors:
  • Glow()

  • Glow(double level)

The no-args constructor creates a Glow object with a default level of 0.30. The other constructor lets you specify the level value, as shown in the following code:
// Create a Glow with level 0.30
Glow g1 = new Glow();
// Create a Glow with level 0.90 - more glow.
Glow g2 = new Glow(0.90);
Figure 17-15 shows four Text nodes with Glow effects with different level values. A Text node is laid over a rectangle using a StackPane. Notice that the higher the level value, the higher the glowing effect. The following snippet of code creates the first Text node and Rectangle pair from the left in Figure 17-15:
Text t1 = new Text("Glow");
t1.setFill(Color.YELLOW);
t1.setFont(Font.font(null, FontWeight.BOLD, 24));
t1.setEffect(new Glow(0.10));
Rectangle r1 = new Rectangle(100, 50, Color.GREEN);
StackPane sp1 = new StackPane(r1, t1);
Figure 17-15

Text nodes with Glow effects

The Reflection Effect

The Reflection effect adds a reflection of the input below the input. An instance of the Reflection class represents a reflection effect. The position, size, and opacity of the reflection are controlled by various properties:
  • topOffset

  • fraction

  • topOpacity

  • bottomOpacity

  • input

The topOffset specifies the distance in pixels between the bottom of the input and the top of the reflection. By default, it is 0.0. The fraction property specifies the fraction of the input height that is visible in the reflection. It is measured from the bottom. Its value can be between 0.0 and 1.0. A value of 0.0 means no reflection. A value of 1.0 means the entire input is visible in the reflection. A value of 0.25 means 25% of the input from the bottom is visible in the reflection. The default value is 0.75. The topOpacity and bottomOpacity properties specify the opacity of the reflection at its top and bottom extremes. Their values can be between 0.0 and 1.0. The default value is 0.50 for the topOpacity and 0.0 for the bottomOpacity.

The Reflection class contains two constructors:
  • Reflection()

  • Reflection(double topOffset, double fraction, double topOpacity, double bottomOpacity)

The no-args constructor creates a Reflection object with the default initial values for its properties. The other constructor lets you specify the initial values for the properties, as shown in the following code:
// Create a Reflection with default values
Reflection g1 = new Reflection();
// Create a Reflection with topOffset=2.0, fraction=0.90,
// topOpacity=1.0, and bottomOpacity=1.0
Reflection g2 = new Reflection(2.0, 0.90, 1.0, 1.0);
Figure 17-16 shows four Text nodes with Reflection effects configured differently. The following snippet of code creates the second Text node from the left, which shows the full input as the reflection:
Text t2 = new Text("Chatar");
t2.setFont(Font.font(null, FontWeight.BOLD, 24));
t2.setEffect(new Reflection(0.0, 1.0, 1.0, 1.0));
Figure 17-16

Text nodes with Reflection effects

The SepiaTone Effect

Sepia is a reddish-brown color. Sepia toning is performed on black-and-white photographic prints to give them a warmer tone. An instance of the SepiaTone class represents a SepiaTone effect . It contains two properties:
  • level

  • input

The level property specifies the intensity of the SepiaTone effect. It is a number between 0.0 and 1.0. Its default value is 1.0. A level of 0.0 adds no sepia toning, and a level of 1.0 adds the maximum sepia toning.

The SepiaTone class contains two constructors:
  • SepiaTone ()

  • SepiaTone (double level)

The no-args constructor creates a SepiaTone object with a default level of 1.0. The other constructor lets you specify the level value, as shown in the following code:
// Create a SepiaTone with level 1.0
SepiaTone g1 = new SepiaTone ();
// Create a SepiaTone with level 0.50
SepiaTone g2 = new SepiaTone(0.50);
The following snippet of code creates two Text nodes with the results shown in Figure 17-17. Notice that the higher the level value, the higher the sepia toning effect:
Text t1 = new Text("SepiaTone");
t1.setFill(Color.WHITE);
t1.setFont(Font.font(null, FontWeight.BOLD, 24));
1.setEffect(new SepiaTone(0.50));
Rectangle r1 = new Rectangle(150, 50, Color.BLACK);
r1.setOpacity(0.50);
StackPane sp1 = new StackPane(r1, t1);
Text t2 = new Text("SepiaTone");
t2.setFill(Color.WHITE);
t2.setFont(Font.font(null, FontWeight.BOLD, 24));
t2.setEffect(new SepiaTone(1.0));
Rectangle r2 = new Rectangle(150, 50, Color.BLACK);
r2.setOpacity(0.50);
StackPane sp2 = new StackPane(r2, t2);
Figure 17-17

Text nodes with SepiaTone effects

The DisplacementMap Effect

The DisplacementMap effect shifts each pixel in the input to produce an output. The name has two parts: “Displacement” and “Map.” The first part implies that the effect displaces the pixels in the input. The second part implies that the displacement is based on a map that provides a displacement factor for each pixel in the output.

An instance of the DisplacementMap class represents a DisplacementMap. The class contains several properties to configure the effect:
  • mapData

  • scaleX

  • scaleY

  • offsetX

  • offsetY

  • wrap

  • input

The mapData property is an instance of the FloatMap class. A FloatMap is a data structure that stores up to four values for each point in a rectangular area represented by its width and height properties. For example, you can use a FloatMap to store four components of the color (red, green, blue, and alpha) for each pixel in a two-dimensional rectangle. Each of the four values associated with a pair of numbers in the FloatMap is said to be in a band numbered 0, 1, 2, and 3. The actual meaning of the values in each band is context dependent. The following code provides an example of setting the FloatMap width and height:
// Create a FloatMap (width = 100, height = 50)
FloatMap map = new FloatMap(100, 50);
Now you need to populate the FloatMap with band values for each pair of numbers. You can use one of the following methods of the FloatMap class to populate it with the data:
  • setSample(int x, int y, int band, float value)

  • setSamples(int x, int y, float s0)

  • setSamples(int x, int y, float s0, float s1)

  • setSamples(int x, int y, float s0, float s1, float s2)

  • setSamples(int x, int y, float s0, float s1, float s2, float s3)

The setSample() method sets the specified value in the specified band for the specified (x, y) location. The setSamples() methods sets the specified values in the bands determined by the positions of the values in the method call. That is, the first value is set for band 0, the second value for band 1, and so forth:
// Set 0,50f for band 0 and band 1 for each point in the map
for (int i = 0; i < 100; i++) {
        for (int j = 0; j < 50; j++) {
                map.setSamples(i, j, 0.50f, 0.50f);
        }
}

The DisplacementMap class requires that you set the mapData property to a FloatMap that contains values for band 0 and band 1 for each pixel in the output.

The scaleX, scaleY, offsetX, and offsetY are double properties. They are used in the equation (described shortly) to compute the displacement of the pixels. The scaleX and scaleY properties have 1.0 as their default values. The offsetX and offsetY properties have 0.0 as their default values.

The following equation is used to compute the pixel at (x, y) coordinates in the output. The abbreviations dst and src in the equation represent the destination and source, respectively:
dst[x,y] = src[x + (offsetX + scaleX * mapData[x,y][0]) * srcWidth,
               y + (offsetY + scaleY * mapData[x,y][1]) * srcHeight]

If the preceding equation looks very complex, don’t be intimidated. In fact, the equation is very simple once you read the explanation that follows. The mapData[x,y][0] and mapData[x,y][1] parts in the equation refer to the values at band 0 and band 1, respectively, in the FloatMap for the location at (x, y).

Suppose you want to get the pixel for the (x, y) coordinates in the output, that is, you want to know which pixel from the input will be moved to (x, y) in the output. First, make sure you get the starting point right. To repeat, the equation starts with a point (x, y) in the output and finds the pixel at (x1, y1) in the input that will move to (x, y) in the output.

Tip

Many will get the equation wrong by thinking that you start with a pixel in the input and then find its location in the output. This is not true. The equation works the other way around. It picks a point (x, y) in the output and then finds which pixel in the input will move to this point.

The following are the steps to fully explain the equation:
  • You want to find the pixel in the input that will be moved to the point (x, y) in the output.

  • Get the values (band 0 and band 1) from the mapData for (x, y).

  • Multiply the mapData values by the scale (scaleX for x coordinate and scaleY for y coordinate).

  • Add the corresponding offset values to the values computed in the previous step.

  • Multiply the previous step values with the corresponding dimensions of the input. This gives you the offset values along the x and y coordinate axes from the output (x, y) from where the pixels in the input will be moving to the (x, y) in the output.

  • Add the values in the previous step to the x and y coordinates of the point in the output. Suppose these values are (x1, y1). The pixel at (x1, y1) in the input moves to the point (x, y) in the output.

If you still have problem understanding the pixel-shifting logic, you can break the preceding equation into two parts:
x1 = x + (offsetX + scaleX * mapData[x,y][0]) * srcWidth
y1 = y + (offsetY + scaleY * mapData[x,y][1]) * srcHeight

You can read these equations as “The pixel at (x, y) in the output is obtained by moving the pixel at (x1, y1) in the input to (x, y).”

If you leave the scale and offset values to their default
  • Use a positive value in band 0 to move the input pixels to the left.

  • Use a negative value in band 0 to move the input pixels to the right.

  • Use a positive value in band 1 to move the input pixels up.

  • Use a negative value in band 1 to move the input pixels down.

The program in Listing 17-6 creates a Text node and adds a DisplacementMap effect to the node. In the mapData, it sets values, so all pixels in the top half of the input are moved to the right by 1 pixel, and all pixels in the bottom half of the input are moved to the left by 1 pixel. The Text node will look like the one shown in Figure 17-18.
// DisplacementmapTest.java
// ...find in the book's download area.
Listing 17-6

Using the DisplacementMap Effect

Figure 17-18

A Text node with a DisplacementMap effect

The DisplacementMap class contains a wrap property , which is set to false by default. A pixel in the output is a pixel in the input that is moved to a new location. The location of the pixel in the input that needs to move to a new location is computed by the equation. It is possible that for some locations in the output, you do not have available pixels in the input. Suppose you have a 100px wide by 50px tall rectangle, and you apply a DisplacementMap effect to move all pixels to the left by 50px. The points at x = 75 in the output will get the pixel at x = 125 in the input. The input is only 100px wide. Therefore, for all points x > 50 in the output, you will not have available pixels in the input. If the wrap property is set to true, when the locations of the pixels in the input to be moved are outside the input bounds, the locations are computed by taking their modulus with the corresponding dimension (width along the x-axis and height along the y-axis) of the input. In the example, x = 125 will be reduced to 125 % 100, which is 25, and the pixels at x = 25 in the input will be moved to x = 75 in the output. If the wrap property is false, the pixels in the output are left transparent.

Figure 17-19 shows two Text nodes with DisplacementMap effects. Pixels in both nodes are moved 100px to the left. The Text node at the top has the wrap property set to false, whereas the Text node at the bottom has the wrap property set to true. Notice that the output for the bottom node is filled by wrapping the input. The program in Listing 17-7 is used to apply the wrapping effects.
Figure 17-19

Effects of using the wrap property in DisplacementMap

// DisplacementMapWrap.java
// ...find in the book's download area.
Listing 17-7

Using the wrap Property in the DisplacementMap Effect

The ColorInput Effect

The ColorInput effect is a simple effect that fills (floods) a rectangular region with a specified paint. Typically, it is used as an input to another effect.

An instance of the ColorInput class represents the ColorInput effect. The class contains five properties that define the location, size, and the paint for the rectangular region:
  • x

  • y

  • width

  • height

  • paint

Creating a ColorInput object is similar to creating a rectangle filled with the paint of the ColorInput. The x and y properties specify the location of the upper-left corner of the rectangular region in the local coordinate system. The width and height properties specify the size of the rectangular region. The default value for x, y, width, and height is 0.0. The paint property specifies the fill paint. The default value for paint is Color.RED.

You can use the following constructors to create an object of the ColorInput class :
  • ColorInput()

  • ColorInput(double x, double y, double width, double height, Paint paint)

The following snippet of code creates a ColorInput effect and applies it to a rectangle. The rectangle with the effect applied is shown in Figure 17-20. Note that when you apply the ColorInput effect to a node, all you see is the rectangular area generated by the ColorInput effect. As stated earlier, the ColorInput effect is not applied directly on nodes. Rather, it is used as an input to another effect.
ColorInput effect = new ColorInput();
effect.setWidth(100);
effect.setHeight(50);
effect.setPaint(Color.LIGHTGRAY);
// Size of the Rectangle does not matter to the rectangular area
// of the ColorInput
Rectangle r1 = new Rectangle(100, 50);
r1.setEffect(effect);
Figure 17-20

A ColorInput effect applied to a rectangle

The ColorAdjust Effect

The ColorAdjust effect adjusts the hue, saturation, brightness, and contrast of pixels by the specified delta amount. Typically, the effect is used on an ImageView node to adjust the color of an image.

An instance of the ColorAdjust class represents the ColorAdjust effect. The class contains five properties that define the location, size, and the paint for the rectangular region:
  • hue

  • saturation

  • brightness

  • contrast

  • input

The hue, saturation, brightness, and contrast properties specify the delta amount by which these components are adjusted for all pixels. They range from –1.0 to 1.0. Their default values are 0.0.

The program in Listing 17-8 shows how to use the ColorAdjust effect on an image. It displays an image and four sliders to change the properties of the ColorAdjust effect. Adjust their values using the sliders to see the effects. If the program does not find the image, it prints a message and displays a Text node overlaying a rectangle in a StackPane, and the effect is applied to the StackPane.
// ColorAdjustTest.java
// ...find in the book's download area.
Listing 17-8

Using the ColorAdjust Effect to Adjust the Color of Pixels in an Image

The ImageInput Effect

The ImageInput effect works like the ColorInput effect. It passes the given image as an input to another effect. The given image is not modified by this effect. Typically, it is used as an input to another effect, not as an effect directly applied to a node.

An instance of the ImageInput class represents the ImageInput effect. The class contains three properties that define the location and the source of the image:
  • x

  • y

  • source

The x and y properties specify the location of the upper-left corner of the image in the local coordinate system of the content node on which the effect is finally applied. Their default values are 0.0. The source property specifies the Image object to be used.

You can use the following constructors to create an object of the ColorInput class:
  • ImageInput()

  • ImageInput(Image source)

  • ImageInput(Image source, double x, double y)

The program in Listing 17-9 shows how to use the ImageInput effect. It passes an ImageInput as an input to a DropShadow effect, which is applied on a rectangle, as shown in Figure 17-21.
// ImageInputTest.java
// ...find in the book's download area.
Listing 17-9

Using an ImageInput Effect As an Input to a DropShadow Effect

Figure 17-21

An ImageInput effect with a DropShadow effect applied to a rectangle

The Blend Effect

Blending combines two pixels at the same location from two inputs to produce one composite pixel in the output. The Blend effect takes two input effects and blends the overlapping pixels of the inputs to produce an output. The blending of two inputs is controlled by a blending mode.

An instance of the Blend class represents the Blend effect. The class contains properties to specify the
  • topInput

  • bottomInput

  • mode

  • opacity

The topInput and bottomInput properties specify the top and bottom effects, respectively. They are null by default. The mode property specifies the blending mode, which is one of the constants defined in the BlendMode enum. The default is BlendMode.SRC_OVER. JavaFX provides 17 predefined blending modes. Table 17-1 lists all of the constants in the BlendMode enum with a brief description of each. All blending modes use the SRC_OVER rules to blend the alpha components. The opacity property specifies the opacity to be applied to the top input before the blending is applied. The opacity is 1.0 by default.
Table 17-1

The Constants in the BlendMode Enum with Their Descriptions

BlendMode Enum Constant

Description

ADD

It adds the color (red, green, and blue) and alpha values for the pixels in the top and bottom inputs to get the new component value.

MULTIPLY

It multiplies the color components from two inputs.

DIFFERENCE

It subtracts the darker color components from any inputs from the lighter color components of the other input to get the resulting color components.

RED

It replaces the red component of the bottom input with the red component of the top input, leaving all other color components unaffected.

BLUE

It replaces the blue component of the bottom input with the blue component of the top input, leaving all other color components unaffected.

GREEN

It replaces the green component of the bottom input with the green component of the top input, leaving all other color components unaffected.

EXCLUSION

It multiplies the color components of the two inputs and doubles the result. The value thus obtained is subtracted from the sum of the color components of the bottom input to get the resulting color component.

COLOR_BURN

It divides the inverse of the bottom input color components by the top input color components and inverts the result.

COLOR_DODGE

It divides the bottom input color components by the inverse of the top input color.

LIGHTEN

It uses the lighter of the color components from the two inputs.

DARKEN

It uses the darker of the color components from the two inputs.

SCREEN

It inverts the color components from both inputs, multiplies them, and inverts the result.

OVERLAY

Depending on the bottom input color, it multiplies or screens the input color components.

HARD_LIGHT

Depending on the top input color, it multiplies or screens the input color components.

SOFT_LIGHT

Depending on the top input color, it darkens or lightens the input color components.

SRC_ATOP

It keeps the bottom input for the nonoverlapping area and the top input for the overlapping area.

SRC_OVER

The top input is drawn over the bottom input. Therefore, the overlapping area shows the top input.

The program in Listing 17-10 creates two ColorInput effects of the same size. Their x and y properties are set in such a way that they overlap. These two effects are used as top and bottom inputs to the Blend effect. A combo box and a slider are provided to select the blending mode and the opacity of the top input. Figure 17-22 shows the window that results from running this code. Run the program and try selecting different blending modes to see the Blend effect in action.
// BlendTest.java
// ...find in the book's download area.
Listing 17-10

Using the Blend Effect

Figure 17-22

The Blend effect

The Lighting Effect

The Lighting effect, as the name suggests, simulates a light source shining on a specified node in a scene to give the node a 3D look. A Lighting effect uses a light source, which is an instance of the Light class , to produce the effect. Different types of configurable lights are available. If you do not specify a light source, the effect uses a default light source.

An instance of the Lighting class represents a Lighting effect. The class contains two constructors:
  • Lighting()

  • Lighting(Light light)

The no-args constructor uses a default light source. The other constructor lets you specify a light source.

Applying a Lighting effect to a node may be a simple or complex task depending on the type of effect you want to achieve. Let’s look at a simple example. The following snippet of code applies a Lighting effect to a Text node to give it a 3D look, as shown in Figure 17-23:
// Create a Text Node
Text text = new Text("Chatar");
text.setFill(Color.RED);
text.setFont(Font.font(null, FontWeight.BOLD, 72));
HBox.setMargin(text, new Insets(10));
// Set a Lighting effect to the Text node
text.setEffect(new Lighting());
Figure 17-23

A Text node with a Lighting effect using the default for the light source

In the preceding example, adding the Lighting effect is as simple as creating an object of the Lighting class and setting it as the effect for the Text node. I will discuss some complex Lighting effects later. The Lighting class contains several properties to configure the effect:
  • contentInput

  • surfaceScale

  • bumpInput

  • diffuseConstant

  • specularConstant

  • specularExponent

  • light

If you use a chain of effects, the contentInput property specifies the input effect to the Lighting effect. This property is named as input in all other effects discussed earlier. I will not discuss this property further in this section. Please refer to the section “Chaining Effects” for more details on how to use this property.

Customizing the Surface Texture

The surfaceScale and bumpInput properties are used to provide texture to a 2D surface to make it look like a 3D surface. Pixels, based on their opacity, look high or low to give the surface a texture. Transparent pixels appear low, and opaque pixels appear raised.

The surfaceScale property lets you control the surface roughness. Its value ranges from 0.0 to 10.0. The default is 1.5. For a higher surfaceScale, the surface appears rougher, giving it a more 3D look.

You can pass an Effect as an input to the Lighting effect using its bumpInput property. The opacity of the pixels in the bumpInput is used to obtain the height of the pixels of the lighted surface, and then the surfaceScale is applied to increase the roughness. If bumpInput is null, the opacity of the pixels from the node on which the effect is applied is used to generate the roughness of the surface. By default, a Shadow effect with a radius of 10 is used as the bumpInput. You can use an ImageInput, a blur effect, or any other effect as the bumpInput for a Lighting effect.

The program in Listing 17-11 displays a Text node with a Lighting effect. The bumpInput is set to null. It provides a check box to set a GaussianBlur effect as the bumpInput and a slider to adjust the surfaceScale value. Figure 17-24 shows two screenshots: one without a bump input and another with a bump input. Notice the difference in the surface texture.
// SurfaceTexture.java
// ...find in the book's download area.
Listing 17-11

Using the surfaceScale and bumpInput Properties

Figure 17-24

The effects of surfaceScale and bumpInput on a Lighting effect on a Text node

Understanding Reflection Types

When light falls on an opaque surface, part of light is absorbed, part is transmitted, and some is reflected. A 3D look is achieved by showing part of the surface brighter and part shadowy. You see the reflected light from the surface. The 3D look varies depending on the light source and the way the node surface reflects the light. The structure of the surface at the microscopic level defines the details of the reflection, such as the intensity and directions. Among several reflection types, two types are worth mentioning at this point: diffuse reflection and specular reflection.

In a diffuse reflection, the surface reflects an incident ray of light at many angles. That is, a diffuse reflection scatters a ray of light by reflecting it in all directions. A perfect diffuse reflection reflects light equally in all directions. The surface using a diffuse reflection appears to be equally bright from all directions. This does not mean that the entire diffuse surface is visible. The visibility of an area on a diffuse surface depends on the direction of the light and the orientation of the surface. The brightness of the surface depends on the surface type itself and the intensity of the light. Typically, a rough surface, for example, clothing, paper, or plastered walls, reflects light using a diffuse reflection. Surfaces may appear smooth to the eyes, for example, paper or clothing, but they are rough at the microscopic level, and they reflect light diffusively.

In a specular reflection, the surface reflects a ray of light in exactly one direction. That is, there is a single reflected ray for one incident ray. A smooth surface at the microscopic level, for example, mirrors or polished marbles, produces a specular reflection. Some smooth surfaces may not be 100% smooth at the microscopic level, and they may reflect part of the light diffusively as well. Specular reflection produces a brighter surface compared to diffuse reflection. Figure 17-25 depicts the ways light is reflected in diffuse and specular reflections .
Figure 17-25

Diffuse and specular reflection types

Three properties of the Lighting class are used to control the size and intensity of the reflection:
  • diffuseConstant

  • specularConstant

  • specularExponent

These properties are of the double type. The diffuseConstant is used for diffuse reflection. The specularConstant and specularExponent are used for specular reflection. The diffuseConstant property specifies a multiplier for the diffuse reflection intensity. Its value ranges from 0.0 to 2.0 with a default of 1.0. A higher value makes the surface brighter. The specularConstant property specifies the fraction of the light to which the specular reflection applies. Its value ranges from 0.0 to 2.0 with a default value of 0.30. A higher value means a bigger-sized specular highlight. The specularExponent specifies the shininess of the surface. A higher value means a more intense reflection and the surface looks shinier. The specularExponent ranges from 0.0 to 40.0 with a default value of 20.0.

Listing 17-12 contains the code for a utility class that binds the properties of the Lighting class to some controls that will be used to control the properties in the examples discussed later.
// LightingUtil.java
// ...find in the book's download area.
Listing 17-12

A Utility Class That Creates a Set of Controls Bound to the Properties of a Lighting Instance

The program in Listing 17-13 uses the utility class to bind the properties of a Lighting effect to UI controls. It displays a window as shown in Figure 17-26. Change the reflection properties using the sliders to see their effects.
// ReflectionTypeTest.java
// ...find in the book's download area.
Listing 17-13

Controlling Reflection’s Details

Figure 17-26

Effects of reflection properties on lighting nodes

Understanding the Light Source

JavaFX provides three built-in light sources: distant light, point light, and spot light. A distant light is also known as a directional or linear light. A distant light source emanates parallel rays of light in a specific direction on the entire surface uniformly. The sun is a perfect example of a distant light source for the lighted surface of an object on the earth. The light source is so distant from the lighted object that the rays are almost parallel. A distant light source lights a surface uniformly, irrespective of its distance from the surface. This does not mean that the entire object is lighted. For example, when you stand in sunlight, not all parts of your body are lighted. However, the lighted part of your body has uniform light. The lighted part of an object depends on the direction distant light source of the light. Figure 17-27 shows a distant light hitting some part of the surface of an object. Notice that the rays of light are seen, not the light source itself, because, for a distant light, only the direction of the light is important, not the distance of the light source from the lighted object.
Figure 17-27

A distant light hitting the surface of an object

A point light source emanates rays of light in all directions from an infinitesimally small point in a 3D space. Theoretically, the light source has no dimension. It emanates light uniformly in all directions. Therefore, unlike the distant light, the direction of the point light source relative to the lighted object is immaterial. Bare light bulbs, stars (excluding the sun, which serves like a distant light), and candlelight are examples of point light sources. The intensity of a point light hitting a surface decreases with the square of the distance between the surface and the point light source. If a point light is very close to the surface, it creates a hotspot, which is a very bright point on the surface. To avoid hotspots, you need to move the light source a little away from the surface. A point light source is defined at a specific point in a 3D space, for example, using x, y, and z coordinates of the point. Figure 17-28 shows a point light radiating rays in all directions. The point on the object surface closest to the light will be illuminated the most.
Figure 17-28

A point light hitting the surface of an object

A spot light is a special type of a point light. Like a point light, it emanates rays of light radially from an infinitesimally small point in a 3D space. Unlike a point light, the radiation of light rays is confined to an area defined by a cone—the light source being at the vertex of the cone emanating light toward its base, as shown in Figure 17-29. Examples of spot lights are car headlights, flashlights, spotlights, and desk lights with lampshades. A spot light is aimed at a point on the surface, which is the point on the surface where the cone axis is located. The cone axis is the line joining the vertex of the cone to the center of the base of the cone. In Figure 17-29, the cone axis is shown with a dashed arrow. The effect of a spot light is defined by the position of the vertex of the cone, the cone angle, and the rotation of the cone. The rotation of the cone determines the point on the surface that is intersected by the cone axis. The angle of the cone controls the area of the lighted area. The intensity of a spot light is highest along the cone axis. You can simulate a distant light using a spot light if you pull the spot light “far” back, so the rays of light reaching the surface are parallel.
Figure 17-29

A spot light hitting the surface of an object

A light source is an instance of the abstract Light class. A light has a color, which is specified by using the color property of the Light class. For example, using a red color Light will make a Text node with a white fill look red.

There are three subclasses of the Light class to represent specific types of light source. The subclasses are static inner classes of the Light class:
  • Light.Distant

  • Light.Point

  • Light.Spot

A class diagram for classes representing light sources is shown in Figure 17-30. The Light.Spot class inherits from the Light.Point class. Classes define properties to configure the specific type of light sources.
Figure 17-30

A class diagram for classes representing a light source

Tip

When you do not provide a light source for a lighting effect, a distant light is used, which is an instance of the Light.Distant class.

Using a Distant Light Source

An instance of the Light.Distant class represents a distant light source. The class contains two properties to specify the direction of the light source:
  • azimuth

  • elevation

Both properties are of the double type. Their values are specified in degrees. Both properties are used together to position the light source in a 3D space in a specific direction. By default, their values are 45 degrees. They do not have maximum and minimum values. Their values are computed using modulo 360. For example, an azimuth value of 400 is effectively 40 (400 modulo 360 = 40).

The azimuth property specifies the direction angle in the XY plane. A positive value is measured clockwise, and a negative value is measured counterclockwise. A 0 value for the azimuth is located at the 3 o’clock position, 90 at 6 o’clock, 180 at 9 o’clock, 270 at 12 o’clock, and 360 at 3 o’clock. An azimuth of –90 will be located at 12 o’clock. Figure 17-31 shows the location of the distant light in the XY plane for different azimuth values.
Figure 17-31

Determining the direction of the distant light in the XY plane using the azimuth value

The elevation property specifies the direction angle of the light source in the YZ plane. The elevation property values of 0 and 180 make the light source stay on the XY plane. An elevation of 90 puts the light source in front of the scene, and the entire scene is lighted. An elevation greater than 180 and less than 360 puts the light source behind the scene making it appear dark (without light).

The Light.Distant class contains two constructors:
  • Light.Distant()

  • Light.Distant(double azimuth, double elevation, Color color)

The no-args constructor uses 45.0 degrees for azimuth and elevation and Color.WHITE as the light color. The other constructor lets you specify these properties.

The program in Listing 17-14 shows how to use a Light.Distant light. It displays a window that lets you set the direction for a distant light shining on a rectangle and a Text node. Figure 17-32 shows an example of a text and rectangle with a distant light.
// DistantLightTest.java
// ...find in the book's download area.
Listing 17-14

Using a Distant Light Source

Figure 17-32

A distant light lighting a Text node and a rectangle

Using a Point Light Source

An instance of the Light.Point class represents a point light source. The class contains three properties to specify the position of the light source in space: x, y, and z. The x, y, and z properties are the x, y, and z coordinates of the point where the point light is located in the space. If you set the z property to 0.0, the light source will be in the plane of the scene showing as a very tiny bright point lighting a very small area. As the z value increases, the light source moves away from the scene plane, lighting more area on the scene. A negative value of z will move the light source behind the scene, leaving it with no light, and the scene will look completely dark.

The Light.Point class contains two constructors:
  • Light.Point()

  • Light.Point(double x, double y, double z, Color color)

The no-args constructor places the point light at (0, 0, 0) and uses a Color.WHITE color for the light. The other constructor lets you specify the location and the color of the light source.

The program in Listing 17-15 shows how to use a Light.Point light. It displays a window with sliders at the bottom to change the location of the point light source. As the point light source moves away from the scene, some area on the scene will be brighter than the other area. Figure 17-33 shows an example of a Text node overlaid on a rectangle being lighted by a point light.
// PointLightTest.java
// ...find in the book's download area.
Listing 17-15

Using a Point Light Source

Figure 17-33

A point light lighting a Text node and a rectangle

Using a Spot Light Source

An instance of the Light.Spot class represents a spot light source. The class inherits from the Light.Point class. The inherited properties (x, y, and z) from the Light.Point class specify the location of the light source, which coincides with the vertex of the cone. The Light.Spot class contains four properties to specify the position of the light source in space:
  • pointsAtX

  • pointsAtY

  • pointsAtZ

  • specularExponent

The pointsAtX, pointsAtY, and pointsAtZ properties specify a point in the space to set the direction of the light. A line starting from (x, y, z) and going toward (pointsAtX, pointsAtY, pointsAtZ) is the cone axis, which is also the direction of the light. By default, they are set to 0.0. The specularExponent property defines the focus of the light (the width of the cone), which ranges from 0.0 to 4.0. The default is 1.0. The higher the value for the specularExponent, the narrower the cone is and the more focused light will be on the scene.

The Light.Spot class contains two constructors:
  • Light.Spot()

  • Light.Spot(double x, double y, double z, double specularExponent, Color color)

The no-args constructor places the light at (0, 0, 0) and uses a Color.WHITE color for the light. Because the default values for pointsAtX, pointsAtY, and pointsAtZ are 0.0, the light does not have a direction. The other constructor lets you specify the location and the color of the light source. The cone axis will pass from the specified (x, y, x) to (0, 0, 0).

The program in Listing 17-16 shows how to use a Light.Spot light. It displays a window that lets you configure the location, direction, and focus of the light using sliders at the bottom. Figure 17-34 shows an example of a Light.Spot light focused almost in the middle of the rectangle.
// SpotLightTest.java
// ...find in the book's download area.
Listing 17-16

Using a Spot Light Source

Figure 17-34

A spot light lighting a Text node and a rectangle

The PerspectiveTransform Effect

A PerspectiveTransform effect gives a 2D node a 3D look by mapping the corners to different locations. The straight lines in the original nodes remain straight. However, parallel lines in the original nodes may not necessarily remain parallel.

An instance of the PerspectiveTransform class represents a PerspectiveTransform effect. The class contains eight properties to specify the x and y coordinates of four corners:
  • ulx

  • uly

  • urx

  • ury

  • lrx

  • lry

  • llx

  • lly

The first letter in the property names (u or l) indicates upper and lower. The second letter in the property names (l or r) indicates left and right. The last letter in the property names (x or y) indicates the x or y coordinate of a corner. For example, urx indicates the x coordinate of the upper-right corner.

Tip

The PerspectiveTransform class also contains an input property to specify the input effect to it in a chain of effects.

The PerspectiveTransform class contains two constructors:
  • PerspectiveTransform()

  • PerspectiveTransform(double ulx, double uly, double urx, double ury, double lrx, double lry, double llx, double lly)

The no-args constructor creates a PerspectiveTransform object with all new corners at (0, 0). If you set the object as an effect to a node, the node will be reduced to a point, and you will not be able to see the node. The other constructor lets you specify the new coordinates for the four corners of the node.

The program in Listing 17-17 creates two sets of a Text node and a rectangle. It adds two sets to two different groups. It applies a PerspectiveTransform effect on the second group. Both groups are shown in Figure 17-35. The group on the left shows the original nodes; the group on the right has the effect applied to it.
// PerspectiveTransformTest.java
// ...find in the book's download area.
Listing 17-17

Using the PerspectiveTransform Effect

Figure 17-35

Text and Rectangle nodes with a PerspectiveTransform effect

Summary

An effect is a filter that accepts one or more graphical inputs, applies an algorithm on the inputs, and produces an output. Typically, effects are applied to nodes to create visually appealing user interfaces. Examples of effects are shadow, blur, warp, glow, reflection, blending, and different types of lighting. The JavaFX library provides several effect-related classes. Effect is a conditional feature. Effects applied to nodes will be ignored if it is not available on a platform. The Node class contains an effect property that specifies the effect applied to the node. By default, it is null. An instance of the Effect class represents an effect. The Effect class is the abstract base for all effect classes. All effect classes are included in the javafx.scene.effect package.

Some effects can be chained with other effects. The effects are applied in sequence. The output of the first effect becomes the input for the second effect and so on. Effect classes that allow chaining contain an input property to specify the effect that precedes it. If the input property is null, the effect is applied to the node on which this effect is set. By default, the input property is null.

A shadowing effect draws a shadow and applies it to an input. JavaFX supports three types of shadowing effects: DropShadow, InnerShadow, and Shadow.

A blurring effect produces a blurred version of an input. JavaFX lets you apply different types of blurring effects, which differ in the algorithms they use to create the effect. Three types of blurring effects are BoxBlur, GaussianBlur, and MotionBlur.

The Bloom effect adds glow to the pixels of its input that have a luminosity greater than or equal to a specified limit. Note that not all pixels in a Bloom effect are made to glow. An instance of the Bloom class represents a Bloom effect.

The Glow effect makes the bright pixels of the input brighter. An instance of the Glow class represents a Glow effect.

The Reflection effect adds a reflection of the input below the input. An instance of the Reflection class represents a reflection effect.

Sepia is a reddish-brown color. Sepia toning is performed on black-and-white photographic prints to give them a warmer tone. An instance of the SepiaTone class represents a SepiaTone effect.

The DisplacementMap effect shifts each pixel in the input to produce an output. The name has two parts: Displacement and Map. The first part implies that the effect displaces the pixels in the input. The second part implies that the displacement is based on a map that provides a displacement factor for each pixel in the output. An instance of the DisplacementMap class represents a DisplacementMap.

The ColorInput effect is a simple effect that fills (floods) a rectangular region with a specified paint. Typically, it is used as an input to another effect. An instance of the ColorInput class represents the ColorInput effect.

The ImageInput effect works like the ColorInput effect. It passes the given image as an input to another effect. The given image is not modified by this effect. Typically, it is used as an input to another effect, not as an effect directly applied to a node. An instance of the ImageInput class represents the ImageInput effect.

Blending combines two pixels at the same location from two inputs to produce one composite pixel in the output. The Blend effect takes two input effects and blends the overlapping pixels of the inputs to produce an output. The blending of two inputs is controlled by a blending mode. JavaFX provides 17 predefined blending modes. An instance of the Blend class represents the Blend effect.

The Lighting effect, as the name suggests, simulates a light source shining on a specified node in a scene to give the node a 3D look. A Lighting effect uses a light source, which is an instance of the Light class, to produce the effect.

A PerspectiveTransform effect gives a 2D node a 3D look by mapping the corners to different locations. The straight lines in the original nodes remain straight. However, parallel lines in the original nodes may not necessarily remain parallel. An instance of the PerspectiveTransform class represents a PerspectiveTransform effect.

The next chapter will discuss how to apply different types of transformations to nodes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset