/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package javafx.scene.paint; import java.util.List; import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection; import com.sun.javafx.scene.paint.GradientUtils; import com.sun.javafx.tk.Toolkit; import javafx.beans.NamedArg; /** *
The {@code LinearGradient} class fills a shape * with a linear color gradient pattern. The user may specify two or * more gradient colors, and this Paint will provide an interpolation * between each color.
* ** The application provides an array of {@code Stop}s specifying how to distribute * the colors along the gradient. The {@code Stop#offset} variable must be * the range 0.0 to 1.0 and act like keyframes along the gradient. * The offsets mark where the gradient should be exactly a particular color.
* *If the proportional variable is set to true * then the start and end points of the gradient * should be specified relative to the unit square (0.0->1.0) and will * be stretched across the shape. If the proportional variable is set * to false, then the start and end points should be specified * in the local coordinate system of the shape and the gradient will * not be stretched at all.
* ** The two filled rectangles in the example below will render the same. * The one on the left uses proportional coordinates to specify * the end points of the gradient. The one on the right uses absolute * coordinates. Both of them fill the specified rectangle with a * horizontal gradient that varies from black to red
*// object bounding box relative (proportional = true) Stop[] stops = new Stop[] { new Stop(0, Color.BLACK), new Stop(1, Color.RED)}; LinearGradient lg1 = new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE, stops); Rectangle r1 = new Rectangle(0, 0, 100, 100); r1.setFill(lg1); // user space relative (proportional: = false) LinearGradient lg2 = new LinearGradient(125, 0, 225, 0, false, CycleMethod.NO_CYCLE, stops); Rectangle r2 = new Rectangle(125, 0, 100, 100); r2.setFill(lg2);* @since JavaFX 2.0 */ public final class LinearGradient extends Paint { private double startX; /** * Defines the X coordinate of the gradient axis start point. * If proportional is true (the default), this value specifies a * point on a unit square that will be scaled to match the size of the * the shape that the gradient fills. ( * @defaultValue 0.0 */ public final double getStartX() { return startX; } private double startY; /** * Defines the Y coordinate of the gradient axis start point. * If proportional is true (the default), this value specifies a * point on a unit square that will be scaled to match the size of the * the shape that the gradient fills. * * @defaultValue 0.0 */ public final double getStartY() { return startY; } private double endX; /** * Defines the X coordinate of the gradient axis end point. * If proportional is true (the default), this value specifies a * point on a unit square that will be scaled to match the size of the * the shape that the gradient fills. * * @defaultValue 1.0 */ public final double getEndX() { return endX; } private double endY; /** * Defines the Y coordinate of the gradient axis end point. * If proportional is true (the default), this value specifies a * point on a unit square that will be scaled to match the size of the * the shape that the gradient fills. * * @defaultValue 1.0 */ public final double getEndY() { return endY; } private boolean proportional; /** * Indicates whether start and end locations are proportional or absolute. * If this flag is true, the two end points are defined in a coordinate * space where coordinates in the range {@code [0..1]} are scaled to map * onto the bounds of the shape that the gradient fills. * If this flag is false, then the coordinates are specified in the local * coordinate system of the node. * * @defaultValue true */ public final boolean isProportional() { return proportional; } private CycleMethod cycleMethod; /** * Defines which of the following cycle method is applied * to the {@code LinearGradient}: {@code CycleMethod.NO_CYCLE}, * {@code CycleMethod.REFLECT}, or {@code CycleMethod.REPEAT}. * * @defaultValue NO_CYCLE */ public final CycleMethod getCycleMethod() { return cycleMethod; } private List
Each stop in the sequence must have an offset that is greater than the * previous stop in the sequence.
* *The list is unmodifiable and will throw * {@code UnsupportedOperationException} on each modification attempt.
* * @defaultValue empty */ @ReturnsUnmodifiableCollection public final ListThe format of the string representation is based on * JavaFX CSS specification for linear gradient which is *
* linear-gradient( [ [from <point> to <point>| [ to <side-or-corner>], ]? [ [ repeat | reflect ], ]? <color-stop>[, <color-stop>]+) ** where *
* <side-or-corner> = [left | right] || [top | bottom] * <point> = [ [ <length> <length> ] | [ <percentage> | <percentage> ] ] * <color-stop> = [ <color> [ <percentage> | <length>]? ] ** *
Currently length can be only specified in px, the specification of unit can be omited. * Format of color representation is the one used in {@link Color#web(String color)}. * The linear-gradient keyword can be omited. * For additional information about the format of string representation, see the * CSS Reference Guide. *
* * Examples: *
* LinearGradient g
* = LinearGradient.valueOf("linear-gradient(from 0% 0% to 100% 100%, red 0% , blue 30%, black 100%)");
* LinearGradient g
* = LinearGradient.valueOf("from 0% 0% to 100% 100%, red 0% , blue 30%, black 100%");
* LinearGradient g
* = LinearGradient.valueOf("linear-gradient(from 0px 0px to 200px 0px, #00ff00 0%, 0xff0000 50%, 0x1122ff40 100%)");
* LinearGradient g
* = LinearGradient.valueOf("from 0px 0px to 200px 0px, #00ff00 0%, 0xff0000 50%, 0x1122ff40 100%");
*
*
* @param value the string to convert
* @throws NullPointerException if the {@code value} is {@code null}
* @throws IllegalArgumentException if the {@code value} cannot be parsed
* @return a {@code LinearGradient} object holding the value represented
* by the string argument.
* @since JavaFX 2.1
*/
public static LinearGradient valueOf(String value) {
if (value == null) {
throw new NullPointerException("gradient must be specified");
}
String start = "linear-gradient(";
String end = ")";
if (value.startsWith(start)) {
if (!value.endsWith(end)) {
throw new IllegalArgumentException("Invalid gradient specification, "
+ "must end with \"" + end + '"');
}
value = value.substring(start.length(), value.length() - end.length());
}
GradientUtils.Parser parser = new GradientUtils.Parser(value);
if (parser.getSize() < 2) {
throw new IllegalArgumentException("Invalid gradient specification");
}
GradientUtils.Point startX = GradientUtils.Point.MIN;
GradientUtils.Point startY = GradientUtils.Point.MIN;
GradientUtils.Point endX = GradientUtils.Point.MIN;
GradientUtils.Point endY = GradientUtils.Point.MIN;
String[] tokens = parser.splitCurrentToken();
if ("from".equals(tokens[0])) {
GradientUtils.Parser.checkNumberOfArguments(tokens, 5);
startX = parser.parsePoint(tokens[1]);
startY = parser.parsePoint(tokens[2]);
if (!"to".equals(tokens[3])) {
throw new IllegalArgumentException("Invalid gradient specification, \"to\" expected");
}
endX = parser.parsePoint(tokens[4]);
endY = parser.parsePoint(tokens[5]);
parser.shift();
} else if ("to".equals(tokens[0])) {
int horizontalSet = 0;
int verticalSet = 0;
for (int i = 1; i < 3 && i < tokens.length; i++) {
if ("left".equals(tokens[i])) {
startX = GradientUtils.Point.MAX;
endX = GradientUtils.Point.MIN;
horizontalSet++;
} else if ("right".equals(tokens[i])) {
startX = GradientUtils.Point.MIN;
endX = GradientUtils.Point.MAX;
horizontalSet++;
} else if ("top".equals(tokens[i])) {
startY = GradientUtils.Point.MAX;
endY = GradientUtils.Point.MIN;
verticalSet++;
} else if ("bottom".equals(tokens[i])) {
startY = GradientUtils.Point.MIN;
endY = GradientUtils.Point.MAX;
verticalSet++;
} else {
throw new IllegalArgumentException("Invalid gradient specification,"
+ " unknown value after 'to'");
}
}
if (verticalSet > 1) {
throw new IllegalArgumentException("Invalid gradient specification,"
+ " vertical direction set twice after 'to'");
}
if (horizontalSet > 1) {
throw new IllegalArgumentException("Invalid gradient specification,"
+ " horizontal direction set twice after 'to'");
}
parser.shift();
} else {
// default is "to bottom"
startY = GradientUtils.Point.MIN;
endY = GradientUtils.Point.MAX;
}
// repeat/reflect
CycleMethod method = CycleMethod.NO_CYCLE;
String currentToken = parser.getCurrentToken();
if ("repeat".equals(currentToken)) {
method = CycleMethod.REPEAT;
parser.shift();
} else if ("reflect".equals(currentToken)) {
method = CycleMethod.REFLECT;
parser.shift();
}
double dist = 0;
if (!startX.proportional) {
double dx = endX.value - startX.value;
double dy = endY.value - startY.value;
dist = Math.sqrt(dx*dx + dy*dy);
}
Stop[] stops = parser.parseStops(startX.proportional, dist);
return new LinearGradient(startX.value, startY.value, endX.value, endY.value,
startX.proportional, method, stops);
}
}