/*****************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package com.kitfox.svg.batik;
import java.awt.Color;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;
/**
* <p>
* This class provides a way to fill a shape with a circular radial color
* gradient pattern. The user may specify 2 or more gradient colors, and this
* paint will provide an interpolation between each color.
* <p>
*
* The user must provide an array of floats specifying how to distribute the
* colors along the gradient. These values should range from 0.0 to 1.0 and act
* like keyframes along the gradient (they mark where the gradient should be
* exactly a particular color).
*
* <p>
* This paint will map the first color of the gradient to a focus point within
* the circle, and the last color to the perimeter of the circle, interpolating
* smoothly for any inbetween colors specified by the user. Any line drawn from
* the focus point to the circumference will span the all the gradient colors.
* By default the focus is set to be the center of the circle.
*
* <p>
* Specifying a focus point outside of the circle's radius will result in the
* focus being set to the intersection point of the focus-center line and the
* perimenter of the circle.
* <p>
*
* Specifying a cycle method allows the user to control the painting behavior
* outside of the bounds of the circle's radius. See LinearGradientPaint for
* more details.
*
* <p>
* The following code demonstrates typical usage of RadialGradientPaint:
* <p>
* <code>
* Point2D center = new Point2D.Float(0, 0);<br>
* float radius = 20;
* float[] dist = {0.0, 0.2, 1.0};<br>
* Color[] colors = {Color.red, Color.white, Color.blue};<br>
* RadialGradientPaint p = new RadialGradientPaint(center, radius,
* dist, colors);
* </code>
*
* <p>
* In the event that the user does not set the first keyframe value equal to 0
* and the last keyframe value equal to 1, keyframes will be created at these
* positions and the first and last colors will be replicated there. So, if a
* user specifies the following arrays to construct a gradient:<br>
* {Color.blue, Color.red}, {.3, .7}<br>
* this will be converted to a gradient with the following keyframes:
* {Color.blue, Color.blue, Color.red, Color.red}, {0, .3, .7, 1}
*
*
* <p>
* <img src = "radial.jpg">
* <p>
* This image demonstrates a radial gradient with NO_CYCLE and default focus.
* <p>
*
* <img src = "radial2.jpg">
* <p>
* This image demonstrates a radial gradient with NO_CYCLE and non-centered
* focus.
* <p>
*
* <img src = "radial3.jpg">
* <p>
* This image demonstrates a radial gradient with REFLECT and non-centered
* focus.
*
* @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
* @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
* @version $Id: RadialGradientPaint.java,v 1.1 2004/09/06 19:35:39 kitfox Exp $
*
*/
public final class RadialGradientPaint extends MultipleGradientPaint {
/** Focus point which defines the 0% gradient stop x coordinate. */
private Point2D focus;
/** Center of the circle defining the 100% gradient stop x coordinate. */
private Point2D center;
/** Radius of the outermost circle defining the 100% gradient stop. */
private float radius;
/**
* <p>
*
* Constructs a <code>RadialGradientPaint</code>, using the center as the
* focus point.
*
* @param cx
* the x coordinate in user space of the center point of the
* circle defining the gradient. The last color of the gradient
* is mapped to the perimeter of this circle
*
* @param cy
* the y coordinate in user space of the center point of the
* circle defining the gradient. The last color of the gradient
* is mapped to the perimeter of this circle
*
* @param radius
* the radius of the circle defining the extents of the color
* gradient
*
* @param fractions
* numbers ranging from 0.0 to 1.0 specifying the distribution of
* colors along the gradient
*
* @param colors
* array of colors to use in the gradient. The first color is
* used at the focus point, the last color around the perimeter
* of the circle.
*
*
* @throws IllegalArgumentException
* if fractions.length != colors.length, or if colors is less
* than 2 in size, or if radius < 0
*
*
*/
public RadialGradientPaint(float cx, float cy, float radius,
float[] fractions, Color[] colors) {
this(cx, cy, radius, cx, cy, fractions, colors);
}
/**
* <p>
*
* Constructs a <code>RadialGradientPaint</code>, using the center as the
* focus point.
*
* @param center
* the center point, in user space, of the circle defining the
* gradient
*
* @param radius
* the radius of the circle defining the extents of the color
* gradient
*
* @param fractions
* numbers ranging from 0.0 to 1.0 specifying the distribution of
* colors along the gradient
*
* @param colors
* array of colors to use in the gradient. The first color is
* used at the focus point, the last color around the perimeter
* of the circle.
*
* @throws NullPointerException
* if center point is null
*
* @throws IllegalArgumentException
* if fractions.length != colors.length, or if colors is less
* than 2 in size, or if radius < 0
*
*
*/
public RadialGradientPaint(Point2D center, float radius, float[] fractions,
Color[] colors) {
this(center, radius, center, fractions, colors);
}
/**
* <p>
*
* Constructs a <code>RadialGradientPaint</code>.
*
* @param cx
* the x coordinate in user space of the center point of the
* circle defining the gradient. The last color of the gradient
* is mapped to the perimeter of this circle
*
* @param cy
* the y coordinate in user space of the center point of the
* circle defining the gradient. The last color of the gradient
* is mapped to the perimeter of this circle
*
* @param radius
* the radius of the circle defining the extents of the color
* gradient
*
* @param fx
* the x coordinate of the point in user space to which the first
* color is mapped
*
* @param fy
* the y coordinate of the point in user space to which the first
* color is mapped
*
* @param fractions
* numbers ranging from 0.0 to 1.0 specifying the distribution of
* colors along the gradient
*
* @param colors
* array of colors to use in the gradient. The first color is
* used at the focus point, the last color around the perimeter
* of the circle.
*
* @throws IllegalArgumentException
* if fractions.length != colors.length, or if colors is less
* than 2 in size, or if radius < 0
*
*
*/
public RadialGradientPaint(float cx, float cy, float radius, float fx,
float fy, float[] fractions, Color[] colors) {
this(new Point2D.Float(cx, cy), radius, new Point2D.Float(fx, fy),
fractions, colors, NO_CYCLE, SRGB);
}
/**
* <p>
*
* Constructs a <code>RadialGradientPaint</code>.
*
* @param center
* the center point, in user space, of the circle defining the
* gradient. The last color of the gradient is mapped to the
* perimeter of this circle
*
* @param radius
* the radius of the circle defining the extents of the color
* gradient
*
* @param focus
* the point, in user space, to which the first color is mapped
*
* @param fractions
* numbers ranging from 0.0 to 1.0 specifying the distribution of
* colors along the gradient
*
* @param colors
* array of colors to use in the gradient. The first color is
* used at the focus point, the last color around the perimeter
* of the circle.
*
* @throws NullPointerException
* if one of the points is null
*
* @throws IllegalArgumentException
* if fractions.length != colors.length, or if colors is less
* than 2 in size, or if radius < 0
*
*/
public RadialGradientPaint(Point2D center, float radius, Point2D focus,
float[] fractions, Color[] colors) {
this(center, radius, focus, fractions, colors, NO_CYCLE, SRGB);
}
/**
* <p>
*
* Constructs a <code>RadialGradientPaint</code>.
*
* @param center
* the center point in user space of the circle defining the
* gradient. The last color of the gradient is mapped to the
* perimeter of this circle
*
* @param radius
* the radius of the circle defining the extents of the color
* gradient
*
* @param focus
* the point in user space to which the first color is mapped
*
* @param fractions
* numbers ranging from 0.0 to 1.0 specifying the distribution of
* colors along the gradient
*
* @param colors
* array of colors to use in the gradient. The first color is
* used at the focus point, the last color around the perimeter
* of the circle.
*
* @param cycleMethod
* either NO_CYCLE, REFLECT, or REPEAT
*
* @param colorSpace
* which colorspace to use for interpolation, either SRGB or
* LINEAR_RGB
*
* @throws NullPointerException
* if one of the points is null
*
* @throws IllegalArgumentException
* if fractions.length != colors.length, or if colors is less
* than 2 in size, or if radius < 0
*
*/
public RadialGradientPaint(Point2D center, float radius, Point2D focus,
float[] fractions, Color[] colors, CycleMethodEnum cycleMethod,
ColorSpaceEnum colorSpace) {
this(center, radius, focus, fractions, colors, cycleMethod, colorSpace,
new AffineTransform());
}
/**
* <p>
*
* Constructs a <code>RadialGradientPaint</code>.
*
* @param center
* the center point in user space of the circle defining the
* gradient. The last color of the gradient is mapped to the
* perimeter of this circle
*
* @param radius
* the radius of the circle defining the extents of the color
* gradient.
*
* @param focus
* the point in user space to which the first color is mapped
*
* @param fractions
* numbers ranging from 0.0 to 1.0 specifying the distribution of
* colors along the gradient
*
* @param colors
* array of colors to use in the gradient. The first color is
* used at the focus point, the last color around the perimeter
* of the circle.
*
* @param cycleMethod
* either NO_CYCLE, REFLECT, or REPEAT
*
* @param colorSpace
* which colorspace to use for interpolation, either SRGB or
* LINEAR_RGB
*
* @param gradientTransform
* transform to apply to the gradient
*
* @throws NullPointerException
* if one of the points is null, or gradientTransform is null
*
* @throws IllegalArgumentException
* if fractions.length != colors.length, or if colors is less
* than 2 in size, or if radius < 0
*
*/
public RadialGradientPaint(Point2D center, float radius, Point2D focus,
float[] fractions, Color[] colors, CycleMethodEnum cycleMethod,
ColorSpaceEnum colorSpace, AffineTransform gradientTransform) {
super(fractions, colors, cycleMethod, colorSpace, gradientTransform);
// Check input arguments
if (center == null) {
throw new NullPointerException("Center point should not be null.");
}
if (focus == null) {
throw new NullPointerException("Focus point should not be null.");
}
if (radius <= 0) {
throw new IllegalArgumentException(
"radius should be greater than zero");
}
// copy parameters
this.center = (Point2D) center.clone();
this.focus = (Point2D) focus.clone();
this.radius = radius;
}
/**
* <p>
*
* Constructs a <code>RadialGradientPaint</code>, the gradient circle is
* defined by a bounding box.
*
* @param gradientBounds
* the bounding box, in user space, of the circle defining
* outermost extent of the gradient.
*
* @param fractions
* numbers ranging from 0.0 to 1.0 specifying the distribution of
* colors along the gradient
*
* @param colors
* array of colors to use in the gradient. The first color is
* used at the focus point, the last color around the perimeter
* of the circle.
*
* @throws NullPointerException
* if the gradientBounds is null
*
* @throws IllegalArgumentException
* if fractions.length != colors.length, or if colors is less
* than 2 in size, or if radius < 0
*
*/
public RadialGradientPaint(Rectangle2D gradientBounds, float[] fractions,
Color[] colors) {
// calculate center point and radius based on bounding box coordinates.
this((float) gradientBounds.getX()
+ ((float) gradientBounds.getWidth() / 2),
(float) gradientBounds.getY()
+ ((float) gradientBounds.getWidth() / 2),
(float) gradientBounds.getWidth() / 2, fractions, colors);
}
/**
* <p>
* Creates and returns a PaintContext used to generate the color pattern,
* for use by the internal rendering engine.
*
* @param cm
* {@link ColorModel} that receives the <code>Paint</code> data.
* This is used only as a hint.
*
* @param deviceBounds
* the device space bounding box of the graphics primitive being
* rendered
*
* @param userBounds
* the user space bounding box of the graphics primitive being
* rendered
*
* @param transform
* the {@link AffineTransform} from user space into device space
*
* @param hints
* the hints that the context object uses to choose between
* rendering alternatives
*
* @return the {@link PaintContext} that generates color patterns.
*
* @throws IllegalArgumentException
* if the transform is not invertible
*
* @see PaintContext
*/
@Override
public PaintContext createContext(ColorModel cm, Rectangle deviceBounds,
Rectangle2D userBounds, AffineTransform transform,
RenderingHints hints) {
// Can't modify the transform passed in...
transform = new AffineTransform(transform);
// incorporate the gradient transform
transform.concatenate(gradientTransform);
try {
return new RadialGradientPaintContext(cm, deviceBounds, userBounds,
transform, hints, (float) center.getX(),
(float) center.getY(), radius, (float) focus.getX(),
(float) focus.getY(), fractions, colors, cycleMethod,
colorSpace);
}
catch (NoninvertibleTransformException e) {
throw new IllegalArgumentException(
"transform should be " + "invertible");
}
}
/**
* Returns a copy of the center point of the radial gradient.
*
* @return a {@link Point2D} object that is a copy of the center point
*/
public Point2D getCenterPoint() {
return new Point2D.Double(center.getX(), center.getY());
}
/**
* Returns a copy of the end point of the gradient axis.
*
* @return a {@link Point2D} object that is a copy of the focus point
*/
public Point2D getFocusPoint() {
return new Point2D.Double(focus.getX(), focus.getY());
}
/**
* Returns the radius of the circle defining the radial gradient.
*
* @return the radius of the circle defining the radial gradient
*/
public float getRadius() {
return radius;
}
}