© 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_7

7. Playing with Colors

Kishori Sharan1   and Peter Späth2
(1)
Montgomery, AL, USA
(2)
Leipzig, Sachsen, Germany
 
In this chapter, you will learn:
  • How colors are represented in JavaFX

  • What different color patterns are

  • How to use an image pattern

  • How to use a linear color gradient

  • How to use a radial color gradient

The examples of this chapter lie in the com.jdojo.color package . In order for them to work, you must add a corresponding line to the module-info.java file:
...
opens com.jdojo.color to javafx.graphics, javafx.base;
...
This is the first time we use files from the resources folder. In order to simplify access to the resource files, we introduce a utility class in package com.jdojo.util:
 package com.jdojo.util;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class ResourceUtil {
      // Where the resources directory is, seen from current working
      // directory. This differs from build tool to build tool, and
      // from IDE to IDE, so you might have to adapt this.
      private final static String RSRC_PATH_FROM_CURRENT_DIR = "bin";
    public static URL getResourceURL(String inResourcesPath) {
        var fStr = (RSRC_PATH_FROM_CURRENT_DIR +
             "/resources/" +
             inResourcesPath).replace("/", File.separator);
        try {
             return new File(fStr).getCanonicalFile().toURI().toURL();
        } catch (IOException e) {
             System.err.println("Cannot fetch URL for '" +
                 inResourcesPath + "'");
             System.err.println("""
                 If the path is correct, try to adapt the
                 RSRC_PATH_FROM_CURRENT_DIR constant in class
                 ResourceUtil""".stripIndent());
             e.printStackTrace(System.err);
             return null;
        }
    }
    public static String getResourceURLStr(String inResourcesPath) {
      return getResourceURL(inResourcesPath).toString();
    }
    public static String getResourcePath(String inResourcesPath) {
        var fStr = (RSRC_PATH_FROM_CURRENT_DIR +
             "/resources/" +
             inResourcesPath).replace("/", File.separator);
      return new File(fStr).getAbsolutePath();
    }
}

Understanding Colors

In JavaFX, you can specify color for text and background color for regions. You can specify a color as a uniform color, an image pattern, or a color gradient. A uniform color uses the same color to fill the entire region. An image pattern lets you fill a region with an image pattern. A color gradient defines a color pattern in which the color varies along a straight line from one color to another. The variation in a color gradient can be linear or radial. I will present examples using all color types in this chapter. Figure 7-1 shows the class diagram for color-related classes in JavaFX. All classes are included in the javafx.scene.paint package.
Figure 7-1

The class diagram of color-related classes in JavaFX

The Paint class is an abstract class, and it is the base class for other color classes. It contains only one static method that takes a String argument and returns a Paint instance. The returned Paint instance would be of the Color, LinearGradient, or RadialGradient class, as shown in the following code:
public static Paint valueOf(String value)
You will not use the valueOf() method of the Paint class directly. It is used to convert the color value read in a String from the CSS files. The following snippet of code creates instances of the Paint class from Strings:
// redColor is an instance of the Color class
Paint redColor = Paint.valueOf("red");
// aLinearGradientColor is an instance of the LinearGradient class
Paint aLinearGradientColor = Paint.valueOf("linear-gradient(to bottom right, red, black)" );
// aRadialGradientColor is an instance of the RadialGradient class
Paint aRadialGradientColor =     Paint.valueOf("radial-gradient(radius 100%, red, blue, black)");

A uniform color, an image pattern, a linear color gradient, and a radial color gradient are instances of the Color, ImagePattern, LinearGradient, and RadialGradient classes, respectively. The Stop class and the CycleMethod enum are used while working with color gradients.

Tip

Typically, methods for setting the color attribute of a node take the Paint type as an argument, allowing you to use any of the four color patterns.

Using the Color Class

The Color class represents a solid uniform color from the RGB color space. Every color has an alpha value defined between 0.0 and 1.0 or 0 and 255. An alpha value of 0.0 or 0 means the color is completely transparent, and an alpha value of 1.0 or 255 denotes a completely opaque color. By default, the alpha value is set to 1.0. You can have an instance of the Color class in three ways:
  • Using the constructor

  • Using one of the factory methods

  • Using one of the color constants declared in the Color class

The Color class has only one constructor that lets you specify the RGB and opacity in the range [0.0;1.0]:
public Color(double red, double green, double blue, double opacity)
The following snippet of code creates a completely opaque blue color:
Color blue = new  Color(0.0, 0.0, 1.0, 1.0);
You can use the following static methods in the Color class to create Color objects . The double values need to be between 0.0 and 1.0 and int values between 0 and 255:
  • Color color(double red, double green, double blue)

  • Color color(double red, double green, double blue, double opacity)

  • Color hsb(double hue, double saturation, double brightness)

  • Color hsb(double hue, double saturation, double brightness, double opacity)

  • Color rgb(int red, int green, int blue)

  • Color rgb(int red, int green, int blue, double opacity)

The valueOf() and web() factory methods let you create Color objects from strings in web color value formats. The following snippet of code creates blue Color objects using different string formats:
Color blue = Color.valueOf("blue");
Color blue = Color.web("blue");
Color blue = Color.web("#0000FF");
Color blue = Color.web("0X0000FF");
Color blue = Color.web("rgb(0, 0, 255)");
Color blue = Color.web("rgba(0, 0, 255, 0.5)"); // 50% transparent blue

The Color class defines about 140 color constants, for example, RED, WHITE, TAN, and BLUE, among others. Colors defined by these constants are completely opaque.

Using the ImagePattern Class

An image pattern lets you fill a shape with an image. The image may fill the entire shape or use a tiling pattern. Here are the steps you would use to get an image pattern:
  1. 1.

    Create an Image object using an image from a file.

     
  2. 2.

    Define a rectangle, known as the anchor rectangle, relative to the upper-left corner of the shape to be filled.

     

The image is shown in the anchor rectangle and is then resized to fit the anchor rectangle. If the bounding box for the shape to be filled is bigger than that of the anchor rectangle, the anchor rectangle with the image is repeated within the shape in a tiling pattern.

You can create an object of the ImagePattern using one of its constructors:
  • ImagePattern(Image image)

  • ImagePattern(Image image, double x, double y, double width, double height, boolean proportional)

The first constructor fills the entire bounding box with the image without any pattern. The second constructor lets you specify the x and y coordinates, width, and height of the anchor rectangle. If the proportional argument is true, the anchor rectangle is specified relative to the bounding box of the shape to be filled in terms of a unit square. If the proportional argument is false, the anchor rectangle is specified in the local coordinate system of the shape. The following two calls to the two constructors would produce the same result:
ImagePatterm ip1 = new ImagePattern(anImage);
ImagePatterm ip2 = new ImagePattern(anImage, 0.0, 0.0, 1.0, 1.0, true);
For the example here, you will use the image shown in Figure 7-2. It is a 37px by 25px blue rounded rectangle. It can be found in the resources/picture/blue_rounded_rectangle.png file under the source code folder.
Figure 7-2

A blue rounded rectangle

Using that file, let’s create an image pattern, using the following code:
Image img = create the image object...
ImagePattern p1 = new ImagePattern(img, 0, 0, 0.25, 0.25, true);
The last argument in the ImagePattern constructor set to true makes the bounds of the anchor rectangle, 0, 0, 0.25, and 0.25, to be interpreted proportional to the size of the shape to be filled. The image pattern will create an anchor rectangle at (0, 0) of the shape to be filled. Its width and height will be 25% of the shape to be filled. This will make the anchor rectangle repeat four times horizontally and four times vertically. If you use the following code with the preceding image pattern, it will produce a rectangle as shown in Figure 7-3:
Rectangle r1 = new Rectangle(100, 50);
r1.setFill(p1);
Figure 7-3

Filling a rectangle with an image pattern

If you use the same image pattern to fill a triangle with the following snippet of code, the resulting triangle will look like the one shown in Figure 7-4:
Polygon triangle = new Polygon(50, 0, 0, 50, 100, 50);
triangle.setFill(p1);
Figure 7-4

Filling a triangle with an image pattern

How would you fill a shape completely with an image without having a tiling pattern? You would need to use an ImagePattern with the proportional argument set to true. The center of the anchor rectangle should be at (0, 0), and its width and height should be set to 1 as follows:
// An image pattern to completely fill a shape with the image
ImagePatterm ip = new ImagePattern(yourImage, 0.0, 0.0, 1.0, 1.0, true);
The program in Listing 7-1 shows how to use an image pattern. The resulting screen is shown in Figure 7-5. Its init() method loads an image in an Image object and stores it in an instance variable. If the image file is not found in the CLASSPATH, it prints an error message and quits.
// ImagePatternApp.java
package com.jdojo.color;
import com.jdojo.util.ResourceUtil;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
import javafx.scene.paint.ImagePattern;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ImagePatternApp extends Application {
      private Image img;
      public static void main(String[] args) {
            Application.launch(args);
      }
      @Override
      public void init() {
            // Create an Image object
            final String imgPath = ResourceUtil.getResourceURLStr(
                 "picture/blue_rounded_rectangle.png");
            img = new Image(imgPath);
      }
      @Override
      public void start(Stage stage) {
            // An anchor rectangle at (0, 0) that is 25% wide and 25% tall
            // relative to the rectangle to be filled
            ImagePattern p1 = new ImagePattern(img, 0, 0, 0.25, 0.25, true);
            Rectangle r1 = new Rectangle(100, 50);
            r1.setFill(p1);
            // An anchor rectangle at (0, 0) that is 50% wide and 50% tall
            // relative to the rectangle to be filled
            ImagePattern p2 = new ImagePattern(img, 0, 0, 0.5, 0.5, true);
            Rectangle r2 = new Rectangle(100, 50);
            r2.setFill(p2);
            // Using absolute bounds for the anchor rectangle
            ImagePattern p3 = new ImagePattern(img, 40, 15, 20, 20, false);
            Rectangle r3 = new Rectangle(100, 50);
            r3.setFill(p3);
            // Fill a circle
            ImagePattern p4 = new ImagePattern(img, 0, 0, 0.1, 0.1, true);
            Circle c = new Circle(50, 50, 25);
            c.setFill(p4);
            HBox root = new HBox();
            root.getChildren().addAll(r1, r2, r3, c);
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.setTitle("Using Image Patterns");
            stage.show();
      }
}
Listing 7-1

Using an Image Pattern to Fill Different Shapes

Figure 7-5

Filling different shapes with image patterns

Understanding Linear Color Gradient

A linear color gradient is defined using an axis known as a gradient line . Each point on the gradient line is of a different color. All points on a line that is perpendicular to the gradient line have the same color, which is the color of the point of intersection between the two lines. The gradient line is defined by a starting point and an ending point. Colors along the gradient line are defined at some points on the gradient line, which are known as stop-color points (or stop points). Colors between two stop points are computed using interpolation.

The gradient line has a direction, which is from the starting point to the ending point. All points on a line perpendicular to the gradient line that pass through a stop point will have the color of the stop point. For example, suppose you have defined a stop point P1 with a color C1. If you draw a line perpendicular to the gradient line passing through the point P1, all points on that line will have the color C1.

Figure 7-6 shows the details of the elements constituting a linear color gradient. It shows a rectangular region filled with a linear color gradient. The gradient line is defined from the left side to the right side. The starting point has a white color, and the ending point has a black color. On the left side of the rectangle, all points have the white color, and on the right side, all points have the black color. In between the left and the right sides, the color varies between white and black.
Figure 7-6

The details of a linear color gradient

Using the LinearGradient Class

In JavaFX, an instance of the LinearGradient class represents a linear color gradient. The class has the following two constructors. The types of their last arguments are different:
  • LinearGradient(double startX, double startY, double endX, double endY, boolean proportional, CycleMethod cycleMethod, List<Stop> stops)

  • LinearGradient(double startX, double startY, double endX, double endY, boolean proportional, CycleMethod cycleMethod, Stop... stops)

The startX and startY arguments define the x and y coordinates of the starting point of the gradient line. The endX and endY arguments define the x and y coordinates of the ending point of the gradient line.

The proportional argument affects the way the coordinates of the starting and ending points are treated. If it is true, the starting and ending points are treated relative to a unit square. Otherwise, they are treated as absolute values in the local coordinate system. The use of this argument needs a little more explanation.

Typically, a color gradient is used to fill a region, for example, a rectangle. Sometimes, you know the size of the region, and sometimes you will not. The value of this argument lets you specify the gradient line in relative or absolute form. In relative form, the region is treated as a unit square. That is, the coordinates of the upper-left and the lower-right corners are (0.0, 0.0) and (1.0, 1.0), respectively. Other points in the regions will have x and y coordinates between 0.0 and 1.0. Suppose you specify the starting point as (0.0, 0.0) and the ending point as (1.0, 0.0). It defines a horizontal gradient line from left to right. The starting and ending points of (0.0, 0.0) and (0.0, 1.0) define a vertical gradient line from top to bottom. The starting and ending points of (0.0, 0.0) and (0.5, 0.0) define a horizontal gradient line from the left to the middle of the region.

When the proportional argument is false, the coordinate values for the starting and ending points are treated as absolute values with respect to the local coordinate system. Suppose you have a rectangle of width 200 and height 100. The starting and ending points of (0.0, 0.0) and (200.0, 0.0) define a horizontal gradient line from left to right. The starting and ending points of (0.0, 0.0) and (200.0, 100.0) define a diagonal gradient line from the top-left corner to the bottom-right corner.

The cycleMethod argument defines how the regions outside the color gradient bounds, defined by the starting and ending points, should be filled. Suppose you define the starting and ending points with the proportional argument set to true as (0.0, 0.0) and (0.5, 0.0), respectively. This covers only the left half of the region. How should the right half of the region be filled? You specify this behavior using the cycleMethod argument. Its value is one of the enum constants defined in the CycleMethod enum :
  • CycleMethod.NO_CYCLE

  • CycleMethod.REFLECT

  • CycleMethod.REPEAT

The cycle method of NO_CYCLE fills the remaining region with the terminal color. If you have defined color a stop point only from the left to the middle of a region, the right half will be filled with the color that is defined for the middle of the region. Suppose you define a color gradient for only the middle half of a region, leaving the 25% at the left side and 25% at the right side undefined. The NO_CYCLE method will fill the left 25% region with the color that is defined at the 25% distance from left and the right 25% region with the color defined at the 25% distance from right. The color for the middle 50% will be determined by the color-stop points.

The cycle method of REFLECT fills the remaining regions by reflecting the color gradient, as start-to-end and end-to-start, from the nearest filled region. The cycle method of REPEAT repeats the color gradient to fill the remaining region.

The stops argument defines the color-stop points along the gradient line. A color-stop point is represented by an instance of the Stop class, which has only one constructor:
Stop(double offset, Color color)

The offset value is between 0.0 and 1.0. It defines the relative distance of the stop point along the gradient line from the starting point. For example, an offset of 0.0 is the starting point, an offset of 1.0 is the ending point, an offset of 0.5 is in the middle of the starting and ending points, and so forth. You define at least two stop points with two different colors to have a color gradient. There are no limits on the number of stop points you can define for a color gradient.

That covers the explanation for the arguments of the LinearGradient constructors. So let’s look at some examples on how to use them.

The following snippet of code fills a rectangle with a linear color gradient, as shown in Figure 7-7:
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
LinearGradient lg = new LinearGradient(0, 0, 1, 0, true, NO_CYCLE, stops);
Rectangle r = new Rectangle(200, 100);
r.setFill(lg);
Figure 7-7

A horizontal linear color gradient with two stop points: white at starting and black at ending point

You have two color-stop points. The stop point in the beginning is colored white and that of the end is colored black. The starting point (0, 0) and ending point (1, 0) define a horizontal gradient from left to right. The proportional argument is set to true, which means the coordinate values are interpreted as relative to a unit square. The cycle method argument, which is set to NO_CYCLE, has no effect in this case as your gradient bounds cover the entire region. In the preceding code, if you want to set the proportional argument value to false, to have the same effect, you would create the LinearGradient object as follows. Note the use of 200 as the x coordinate for the ending point to denote the end of the rectangle width:
LinearGradient lg = new LinearGradient(0, 0, 200, 0, false, NO_CYCLE, stops);
Let’s look at another example. The resulting rectangle after running the following snippet of code is shown in Figure 7-8:
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
LinearGradient lg = new LinearGradient(0, 0, 0.5, 0, true, NO_CYCLE, stops);
Rectangle r = new Rectangle(200, 100);
r.setFill(lg);
Figure 7-8

A horizontal linear color gradient with two stop points: white at starting and black at midpoint

In this code, you have made a slight change. You defined a horizontal gradient line, which starts at the left side of the rectangle and ends in the middle. Note the use of (0.5, 0) as the coordinates for the ending point. This leaves the right half of the rectangle with no color gradient. The cycle method is effective in this case as its job is to fill the unfilled regions. The color at the middle of the rectangle is black, which is defined by the second stop point. The NO_CYCLE value uses the terminal black color to fill the right half of the rectangle.

Let’s look at a slight variant of the previous example. You change the cycle method from NO_CYCLE to REFLECT, as shown in the following snippet of code, which results in a rectangle as shown in Figure 7-9. Note that the right half region (the region with undefined gradient) is the reflection of the left half:
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
LinearGradient lg = new LinearGradient(0, 0, 0.5, 0, true, REFLECT, stops);
Rectangle r = new Rectangle(200, 100);
r.setFill(lg);
Figure 7-9

A horizontal linear color gradient with two stop points: white at starting and black at midpoint and REFLECT as the cycle method

Let’s make a slight change in the previous example so the ending point coordinate covers only one-tenth of the width of the rectangle. The code is as follows, and the resulting rectangle is shown in Figure 7-10. The right 90% of the rectangle is filled using the REFLECT cycle method by alternating end-to-start and start-to-end color patterns:
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
LinearGradient lg = new LinearGradient(0, 0, 0.1, 0, true, REFLECT, stops);
Rectangle r = new Rectangle(200, 100);
r.setFill(lg);
Figure 7-10

A horizontal linear color gradient with two stop points: white at starting and black at one-tenth point and REFLECT as the cycle method

Now let’s look at the effect of using the REPEAT cycle method. The following snippet of code uses an ending point at the middle of the width of the rectangle and a cycle method of REPEAT. This results in a rectangle as shown in Figure 7-11. If you set the ending point to one-tenth of the width in this example, it will result in a rectangle as shown in Figure 7-12.
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
LinearGradient lg = new LinearGradient(0, 0, 0.5, 0, true, REPEAT, stops);
Rectangle r = new Rectangle(200, 100);
r.setFill(lg);
Figure 7-11

A horizontal linear color gradient with two stop points: white at starting and black at midpoint and REPEAT as the cycle method

Figure 7-12

A horizontal linear color gradient with two stop points: white at starting and black at one-tenth point and REPEAT as the cycle method

You could also define more than two stop points, as shown in the following snippet of code. It divides the distance between the starting and the ending points on the gradient line into four segments, each by 25% of the width. The first segment (from left) will have colors between red and green, the second between green and blue, the third between blue and orange, and the fourth between orange and yellow. The resulting rectangle is shown in Figure 7-13. If you are reading a printed copy of the book, you may not see the colors.
Stop[] stops = new Stop[]{new Stop(0, Color.RED),
                          new Stop(0.25, Color.GREEN),
                          new Stop(0.50, Color.BLUE),
                          new Stop(0.75, Color.ORANGE),
                          new Stop(1, Color.YELLOW)};
LinearGradient lg = new LinearGradient(0, 0, 1, 0, true, NO_CYCLE, stops);
Rectangle r = new Rectangle(200, 100);
r.setFill(lg);
Figure 7-13

A horizontal linear color gradient with five stop points

You are not limited to defining only horizontal color gradients. You can define a color gradient with a gradient line with any angle. The following snippet of code creates a gradient from the top-left corner to the bottom-right corner. Note that when the proportional argument is true, (0, 0) and (1, 1) define the (x, y) coordinates of the top-left and bottom-right corners of the region:
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
LinearGradient lg = new LinearGradient(0, 0, 1, 1, true, NO_CYCLE, stops);
Rectangle r = new Rectangle(200, 100);
r.setFill(lg);
The following snippet of code defines a gradient line between (0, 0) and (0.1, 0.1) points. It uses the REPEAT cycle method to fill the rest of the region. The resulting rectangle is shown in Figure 7-14.
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
LinearGradient lg = new LinearGradient(0, 0, 0.1, 0.1, true, REPEAT, stops);
Rectangle r = new Rectangle(200, 100);
r.setFill(lg);
Figure 7-14

An angled linear color gradient with two stop points: white at the starting point (0, 0) and black at the ending point (0.1, 0.1) with REPEAT as the cycle method

Defining Linear Color Gradients Using a String Format

You can also specify a linear color gradient in string format using the static method valueOf(String colorString) of the LinearGradient class. Typically, the string format is used to specify a linear color gradient in a CSS file. It has the following syntax:
linear-gradient([gradient-line], [cycle-method], color-stops-list)
The arguments within square brackets ([ and ]) are optional. If you do not specify an optional argument, the comma that follows also needs to be excluded. The default value for the gradient-line argument is “to bottom.” The default value for the cycle-method argument is NO_CYCLE. You can specify the gradient line in two ways:
  • Using two points—the starting point and the ending point

  • Using a side or a corner

The syntax for using two points for the gradient line is
from point-1 to point-2
The coordinates of the points may be specified in percentage of the area or in actual measurement in pixels. For a 200px wide by 100px tall rectangle, a horizontal gradient line may be specified in the following two ways:
from 0% 0% to 100% 0%
or
from 0px 0px to 200px 0px
The syntax for using a side or a corner is
to side-or-corner

The side-or-corner value may be top, left, bottom, right, top left, bottom left, bottom right, or top right. When you define the gradient line using a side or a corner, you specify only the ending point. The starting point is inferred. For example, the value “to top” infers the starting point as “from bottom,” the value “to bottom right” infers the starting point as “from top left,” and so forth. If the gradient-line value is missing, it defaults to “to bottom.”

The valid values for the cycle-method are repeat and reflect. If it is missing, it defaults to NO_CYCLE. It is a runtime error to specify the value of the cycle-method argument as NO_CYCLE. If you want it to be NO_CYCLE, simply omit the cycle-method argument from the syntax.

The color-stops-list argument is a list of color stops. A color stop consists of a web color name and, optionally, a position in pixels or percentage from the starting point. Examples of lists of color stops are
  • white, black

  • white 0%, black 100%

  • white 0%, yellow 50%, blue 100%

  • white 0px, yellow 100px, red 200px

When you do not specify positions for the first and the last color stops, the position for the first one defaults to 0% and the second one to 100%. So, the color stop lists "white, black" and "white 0%, black 100%" are fundamentally the same.

If you do not specify positions for any of the color stops in the list, they are assigned positions in such a way that they are evenly placed between the starting point and the ending point. The following two lists of color stops are the same:
  • white, yellow, black, red, green

  • white 0%, yellow 25%, black 50%, red 75%, green 100%

You can specify positions for some color stops in a list and not for others. In this case, the color stops without positions are evenly spaced between the preceding and following color stops with positions. The following two lists of color stops are the same:
  • white, yellow, black 60%, red, green

  • white 0%, yellow 30%, black 50%, red 80%, green 100%

If a color stop in a list has its position set less than the position specified for any previous color stops, its position is set equal to the maximum position set for the previous color stops. The following list of color stops sets 10% for the third color stop, which is less than the position of the second color stop (50%):
white, yellow 50%, black 10%, green
This will be changed at runtime to use 50% for the third color stop as follows:
white 0%, yellow 50%, black 50%, green 100%
Now let’s look at some examples. The following string will create a linear gradient from top to bottom with NO_CYCLE as the cycle method. Colors are white and black at the top and bottom, respectively:
linear-gradient(white, black)
This value is the same as
linear-gradient(to bottom, white, black)
The following snippet of code will create a rectangle as shown in Figure 7-15. It defines a horizontal color gradient with the ending point midway through the width of the rectangle. It uses repeat as the cycle method:
String value = "from 0px 0px to 100px 0px, repeat, white 0%, black 100%";
LinearGradient lg2 = LinearGradient.valueOf(value);
Rectangle r2 = new Rectangle(200, 100);
r2.setFill(lg2);
Figure 7-15

Creating a linear color gradient using the string format

The following string value for a linear color gradient will create a diagonal gradient from the top-left corner to the bottom-right corner filling the area with white and black colors:
"to bottom right, white 0%, black 100%"

Understanding Radial Color Gradient

In a radial color gradient, colors start at a single point, transitioning smoothly outward in a circular or elliptical shape. The shape, let’s say a circle, is defined by a center point and a radius. The starting point of colors is known as the focus point of the gradient. The colors change along a line, starting at the focus point of the gradient, in all directions until the periphery of the shape is reached. A radial color gradient is defined using three components:
  • A gradient shape (the center and radius of the gradient circle)

  • A focus point that has the first color of the gradient

  • Color stops

The focus point of the gradient and the center point of the gradient shape may be different. Figure 7-16 shows the components of a radial color gradient. The figure shows two radial gradients: in the left side, the focus point and the center point are located at the same place; in the right side, the focus point is located horizontally right to the center point of the shape.
Figure 7-16

Elements defining a radial color gradient

The focus point is defined in terms of a focus angle and a focus distance, as shown in Figure 7-17. The focus angle is the angle between a horizontal line passing through the center point of the shape and a line joining the center point and the focus point. The focus distance is the distance between the center point of the shape and the focus point of the gradient.
Figure 7-17

Defining a focus point in a radial color gradient

The list of color stops determines the value of the color at a point inside the gradient shape. The focus point defines the 0% position of the color stops. The points on the periphery of the circle define the 100% position for the color stops. How would you determine the color at a point inside the gradient circle? You would draw a line passing through the point and the focus point. The color at the point will be interpolated using the nearest color stops on each side of the point on the line.

Using the RadialGradient Class

An instance of the RadialGradient class represents a radial color gradient. The class contains the following two constructors that differ in the types of their last argument:
  • RadialGradient(double focusAngle, double focusDistance, double centerX, double centerY, double radius, boolean proportional, CycleMethod cycleMethod, List<Stop> stops)

  • RadialGradient(double focusAngle, double focusDistance, double centerX, double centerY, double radius, boolean proportional, CycleMethod cycleMethod, Stop... stops)

The focusAngle argument defines the focus angle for the focus point. A positive focus angle is measured clockwise from the horizontal line passing through the center point and the line connecting the center point and the focus point. A negative value is measured counterclockwise.

The focusDistance argument is specified in terms of the percentage of the radius of the circle. The value is clamped between –1 and 1. That is, the focus point is always inside the gradient circle. If the focus distance sets the focus point outside the periphery of the gradient circle, the focus point that is used is the point of intersection of the periphery of the circle and the line connecting the center point and the set focus point.

The focus angle and the focus distance can have positive and negative values. Figure 7-18 illustrates this: it shows four focus points located at 80% distance, positive and negative, from the center point and at a 60-degree angle, positive and negative.
Figure 7-18

Locating a focus point with its focus angle and focus distance

The centerX and centerY arguments define the x and y coordinates of the center point, respectively, and the radius argument is the radius of the gradient circle. These arguments can be specified relative to a unit square (between 0.0 and 1.0) or in pixels.

The proportional argument affects the way the values for the coordinates of the center point and radius are treated. If it is true, they are treated relative to a unit square. Otherwise, they are treated as absolute values in the local coordinate system. For more details on the use of the proportional argument, please refer to the section “Using the LinearGradient Class” earlier in this chapter.

Tip

JavaFX lets you create a radial gradient of a circular shape. However, when the region to be filled by a radial color gradient has a nonsquare bounding box (e.g., a rectangle) and you specify the radius of the gradient circle relative to the size of the shape to be filled, JavaFX will use an elliptical radial color gradient. This is not documented in the API documentation of the RadialGradient class. I will present an example of this kind shortly.

The cycleMethod and stops arguments have the same meaning as described earlier in the section on using the LinearGradient class. In a radial color gradient, stops are defined along lines connecting the focus point and points on the periphery of the gradient circle. The focus point defines the 0% stop point, and the points on the circle periphery define 100% stop points.

Let’s look at some examples of using the RadialGradient class. The following snippet of code produces a radial color gradient for a circle as shown in Figure 7-19:
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
RadialGradient rg = new RadialGradient(0, 0, 0.5, 0.5, 0.5, true, NO_CYCLE, stops);
Circle c = new Circle(50, 50, 50);
c.setFill(rg);
Figure 7-19

A radial color gradient with the same center point and focus point

The zero value for the focus angle and focus distance locates the focus point at the center of the gradient circle. A true proportional argument interprets the center point coordinates (0.5, 0.5) as (25px, 25px) for the 50 by 50 rectangular bounds of the circle. The radius value of 0.5 is interpreted as 25px, and that places the center of the gradient circle at the same location as the center of the circle to fill. The cycle method of NO_CYCLE has no effect in this case as the gradient circle fills the entire circular area. The color stop at the focus point is white, and at the periphery of the gradient circle, it is black.

The following snippet of code specifies the radius of the gradient circle as 0.2 of the circle to be filled. This means that it will use a gradient circle of 10px (0.2 multiplied by 50px, which is the radius of the circle to be filled). The resulting circle is shown in Figure 7-20. The region of the circle beyond the 0.2 of its radius has been filled with the color black, as the cycle method was specified as NO_CYCLE:
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
RadialGradient rg = new RadialGradient(0, 0, 0.5, 0.5, 0.2, true, NO_CYCLE, stops);
Circle c = new Circle(50, 50, 50);
c.setFill(rg);
Figure 7-20

A radial color gradient with the same center point and focus point having a gradient circle with a radius of 0.20

Now let’s use the cycle method of REPEAT in the preceding snippet of code. The resulting circle is shown in Figure 7-21.
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
RadialGradient rg = new RadialGradient(0, 0, 0.5, 0.5, 0.2, true, REPEAT, stops);
Circle c = new Circle(50, 50, 50);
c.setFill(rg);
Figure 7-21

A radial color gradient with the same center point and focus point, a gradient circle with a radius of 0.20, and the cycle method as REPEAT

So now let’s use a different center point and focus point. Use a 60-degree focus angle and 0.2 times the radius as the focus distance as in the following code. The resulting circle is shown in Figure 7-22. Notice the 3D effect you get by moving the focus point away from the center point.
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
RadialGradient rg =
    new RadialGradient(60, 0.2, 0.5, 0.5, 0.2, true, REPEAT, stops);
Circle c = new Circle(50, 50, 50);
c.setFill(rg);
Figure 7-22

A radial color gradient using different center and focus points

Now let’s fill a rectangular region (nonsquare) with a radial color gradient. The code for this effect follows, and the resulting rectangle is shown in Figure 7-23. Notice the elliptical gradient shape used by JavaFX. You have specified the radius of the gradient as 0.5 and the proportional argument as true. Since your rectangle is 200px wide and 100px tall, it results in two radii: one along the x-axis and one along the y-axis, giving rise to an ellipse. The radii along the x- and y-axes are 100px and 50px, respectively.
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
RadialGradient rg =
    new RadialGradient(0, 0, 0.5, 0.5, 0.5, true, REPEAT, stops);
Rectangle r = new Rectangle(200, 100);
r.setFill(rg);
Figure 7-23

A rectangle filled with a radial color gradient with a proportional argument value of true

If you want a rectangle to be filled with a color gradient of a circular shape rather than elliptical shape, you should specify the proportional argument as false, and the radius value will be treated in pixels. The following snippet of code produces a rectangle, as shown in Figure 7-24:
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
RadialGradient rg =
    new RadialGradient(0, 0, 100, 50, 50, false, REPEAT, stops);
Rectangle r = new Rectangle(200, 100);
r.setFill(rg);
Figure 7-24

A rectangle filled with a radial color gradient with a proportional argument value of false

How can you fill a triangle or any other shape with a radial color gradient? The shape of a radial gradient, circular or elliptical, depends on several conditions. Table 7-1 shows the combinations of the criteria that will determine the shape of a radial color gradient.
Table 7-1

Criteria Used to Determine the Shape of a Radial Color Gradient

Proportional Argument

Bounding Box for the Filled Region

Gradient Shape

true

Square

Circle

true

Nonsquare

Ellipse

false

Square

Circle

false

Nonsquare

Circle

I should emphasize here that, in the preceding discussion, I am talking about the bounds of the regions to be filled, not the region. For example, suppose you want to fill a triangle with a radial color gradient. The bounds of the triangle will be determined by its width and height. If the triangle has the same width and height, its bounds take a square region. Otherwise, its bounds take a rectangular region.

The following snippet of code fills a triangle with vertices (0.0, 0.0), (0.0, 100.0), and (100.0, 100.0). Notice that the bounding box for this triangle is a 100px by 100px square. The resulting triangle is the left one shown in Figure 7-25.
Stop[] stops = new Stop[]{new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
RadialGradient rg =
    new RadialGradient(0, 0, 0.5, 0.5, 0.2, true, REPEAT, stops);
Polygon triangle = new Polygon(0.0, 0.0, 0.0, 100.0, 100.0, 100.0);
triangle.setFill(rg);
Figure 7-25

Filling triangles with radial color gradients of circular and elliptical shapes

The triangle in the right side of Figure 7-25 uses a rectangular bounding box of 200px by 100px, which is produced by the following snippet of code. Notice that the gradient uses an elliptical shape:
Polygon triangle = new Polygon(0.0, 0.0, 0.0, 100.0, 200.0, 100.0);
Finally, let’s look at an example of using multiple color stops with the focus point on the periphery of the circle, as shown in Figure 7-26. The code to produce the effect is as follows:
Stop[] stops = new Stop[]{
   new Stop(0, Color.WHITE),
   new Stop(0.40, Color.GRAY),
   new Stop(0.60, Color.TAN),
   new Stop(1, Color.BLACK)};
RadialGradient rg =
   new RadialGradient(-30, 1.0, 0.5, 0.5, 0.5, true, REPEAT, stops);
Circle c = new Circle(50, 50, 50);
c.setFill(rg);
Figure 7-26

Using multiple color stops in a radial color gradient

Defining Radial Color Gradients in String Format

You can also specify a radial color gradient in string format by using the static method valueOf(String colorString) of the RadialGradient class. Typically, the string format is used to specify a radial color gradient in a CSS file. It has the following syntax:
radial-gradient([focus-angle], [focus-distance], [center], radius, [cycle-method], color-stops-list)

The arguments within square brackets are optional. If you do not specify an optional argument, the comma that follows needs to be excluded as well.

The default value for focus-angle and focus-distance is 0. You can specify the focus angle in degrees, radians, gradians, and turns. The focus distance is specified as a percentage of the radius. Examples are as follows:
  • focus-angle 45.0deg

  • focus-angle 0.5rad

  • focus-angle 30.0grad

  • focus-angle 0.125turn

  • focus-distance 50%

The center and radius arguments are specified in a percentage relative to the region being filled or in absolute pixels. You cannot specify one argument in a percentage and the other in pixels. Both must be specified in the same unit. The default value for center is (0, 0) in the unit. Examples are as follows:
  • center 50px 50px, radius 50px

  • center 50% 50%, radius 50%

The valid values for the cycle-method argument are repeat and reflect. If this is not specified, it defaults to NO_CYCLE.

A list of color stops is specified using colors and their positions. Positions are specified as a percentage of distance on a line from the focus point to the periphery of the shape of the gradient. Please refer to the earlier discussion on specifying the color stops in a linear color gradient for more details. Examples are as follows:
  • white, black

  • white 0%, black 100%

  • red, green, blue

  • red 0%, green 80%, blue 100%

The following snippet of code will produce a circle, as shown in Figure 7-27:
String colorValue =
   "radial-gradient(focus-angle 45deg, focus-distance 50%, " +
   "center 50% 50%, radius 50%, white 0%, black 100%)";
RadialGradient rg = RadialGradient.valueOf(colorValue);
Circle c = new Circle(50, 50, 50);
c.setFill(rg);
Figure 7-27

Using string format for specifying a radial color gradient

Summary

In JavaFX, you can specify text color and background color for regions. You can specify a color as a uniform color, an image pattern, or a color gradient. A uniform color uses the same color to fill the entire region. An image pattern lets you fill a region with an image pattern. A color gradient defines a color pattern in which the color varies along a straight line from one color to another. The variation in a color gradient can be linear or radial. All classes are included in the javafx.scene.paint package.

The Paint class is an abstract class, and it is the base class for other color classes. A uniform color, an image pattern, a linear color gradient, and a radial color gradient are instances of the Color, ImagePattern, LinearGradient, and RadialGradient classes, respectively. The Stop class and the CycleMethod enum are used when working with color gradients. You can specify colors using instances of one of these classes or in string forms. When you use a CSS to style nodes, you specify colors using string forms.

An image pattern lets you fill a shape with an image. The image may fill the entire shape or use a tiling pattern.

A linear color gradient is defined using an axis known as a gradient line. Each point on the gradient line is of a different color. All points on a line that is perpendicular to the gradient line have the same color, which is the color of the point of intersection between the two lines. The gradient line is defined by a starting point and an ending point. Colors along the gradient line are defined at some points on the gradient line, which are known as stop-color points (or stop points). Colors between two stop points are computed using interpolation. The gradient line has a direction, which is from the starting point to the ending point. All points on a line perpendicular to the gradient line that passes through a stop point will have the color of the stop point. For example, suppose you have defined a stop point P1 with a color C1. If you draw a line perpendicular to the gradient line passing through the point P1, all points on that line will have the color C1.

In a radial color gradient, colors start at a single point, transitioning smoothly outward in a circular or elliptical shape. The shape is defined by a center point and a radius. The starting point of colors is known as the focus point of the gradient. The colors change along a line, starting at the focus point of the gradient, in all directions until the periphery of the shape is reached.

The next chapter will show you how to style nodes in a scene graph using CSS.

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

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