001/*****************************************************************************
002 * Copyright (C) The Apache Software Foundation. All rights reserved.        *
003 * ------------------------------------------------------------------------- *
004 * This software is published under the terms of the Apache Software License *
005 * version 1.1, a copy of which has been included with this distribution in  *
006 * the LICENSE file.                                                         *
007 *****************************************************************************/
008
009package com.kitfox.svg.batik;
010
011import java.awt.Color;
012import java.awt.PaintContext;
013import java.awt.Rectangle;
014import java.awt.RenderingHints;
015import java.awt.geom.AffineTransform;
016import java.awt.geom.NoninvertibleTransformException;
017import java.awt.geom.Point2D;
018import java.awt.geom.Rectangle2D;
019import java.awt.image.ColorModel;
020
021/**
022 * <p>
023 * This class provides a way to fill a shape with a circular radial color 
024 * gradient pattern. The user may specify 2 or more gradient colors, and this 
025 * paint will provide an interpolation between each color.
026 * <p>
027 *
028 * The user must provide an array of floats specifying how to distribute the 
029 * colors along the gradient.  These values should range from 0.0 to 1.0 and 
030 * act like keyframes along the gradient (they mark where the gradient should 
031 * be exactly a particular color).
032 *
033 * <p>
034 * This paint will map the first color of the gradient to a focus point within
035 * the circle, and the last color to the perimeter of the circle, interpolating
036 * smoothly for any inbetween colors specified by the user.  Any line drawn 
037 * from the focus point to the circumference will span the all the gradient 
038 * colors.  By default the focus is set to be the center of the circle.
039 *
040 * <p>
041 * Specifying a focus point outside of the circle's radius will result in the 
042 * focus being set to the intersection point of the focus-center line and the 
043 * perimenter of the circle.
044 * <p>
045 *
046 * Specifying a cycle method allows the user to control the painting behavior 
047 * outside of the bounds of the circle's radius.  See LinearGradientPaint for 
048 * more details.
049 *
050 * <p>
051 * The following code demonstrates typical usage of RadialGradientPaint:
052 * <p>
053 * <code>
054 * Point2D center = new Point2D.Float(0, 0);<br>
055 * float radius = 20;
056 * float[] dist = {0.0, 0.2, 1.0};<br>
057 * Color[] colors = {Color.red, Color.white, Color.blue};<br>
058 * RadialGradientPaint p = new RadialGradientPaint(center, radius, 
059 * dist, colors);
060 * </code>
061 *
062 * <p> In the event that the user does not set the first keyframe value equal
063 * to 0 and the last keyframe value equal to 1, keyframes will be created at
064 * these positions and the first and last colors will be replicated there.
065 * So, if a user specifies the following arrays to construct a gradient:<br>
066 * {Color.blue, Color.red}, {.3, .7}<br>
067 * this will be converted to a gradient with the following keyframes:
068 * {Color.blue, Color.blue, Color.red, Color.red}, {0, .3, .7, 1}
069 *
070 *
071 * <p>
072 * <img src = "radial.jpg">
073 * <p>
074 * This image demonstrates a radial gradient with NO_CYCLE and default focus.
075 * <p>
076 *
077 * <img src = "radial2.jpg">
078 * <p>
079 * This image demonstrates a radial gradient with NO_CYCLE and non-centered 
080 * focus.
081 * <p>
082 * 
083 * <img src = "radial3.jpg">
084 * <p>
085 * This image demonstrates a radial gradient with REFLECT and non-centered 
086 * focus.
087 *
088 * @author  Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
089 * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
090 * @version $Id: RadialGradientPaint.java,v 1.1 2004/09/06 19:35:39 kitfox Exp $
091 *
092 */
093
094public final class RadialGradientPaint extends MultipleGradientPaint {
095
096    /** Focus point which defines the 0% gradient stop x coordinate. */
097    private Point2D focus;
098
099    /** Center of the circle defining the 100% gradient stop x coordinate. */
100    private Point2D center;
101
102    /** Radius of the outermost circle defining the 100% gradient stop. */
103    private float radius;
104
105    /**
106     * <p>
107     *
108     * Constructs a <code>RadialGradientPaint</code>, using the center as the 
109     * focus point.
110     *
111     * @param cx the x coordinate in user space of the center point of the 
112     * circle defining the gradient.  The last color of the gradient is mapped
113     * to the perimeter of this circle
114     *
115     * @param cy the y coordinate in user space of the center point of the 
116     * circle defining the gradient.  The last color of the gradient is mapped
117     * to the perimeter of this circle
118     *
119     * @param radius the radius of the circle defining the extents of the 
120     * color gradient   
121     *
122     * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
123     * distribution of colors along the gradient
124     *
125     * @param colors array of colors to use in the gradient. The first color 
126     * is used at the focus point, the last color around the perimeter of the 
127     * circle.
128     *        
129     *
130     * @throws IllegalArgumentException  
131     *         if fractions.length != colors.length, or if colors is less 
132     *         than 2 in size, or if radius < 0
133     *
134     *
135     */
136    public RadialGradientPaint(float cx, float cy, float radius,
137                               float[] fractions, Color[] colors) {
138        this(cx, cy,
139             radius,
140             cx, cy,
141             fractions,
142             colors);
143    }    
144    
145    /**
146     * <p>
147     *
148     * Constructs a <code>RadialGradientPaint</code>, using the center as the 
149     * focus point.
150     *
151     * @param center the center point, in user space, of the circle defining 
152     * the gradient
153     *
154     * @param radius the radius of the circle defining the extents of the 
155     * color gradient
156     *
157     * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
158     * distribution of colors along the gradient
159     *
160     * @param colors array of colors to use in the gradient. The first color 
161     * is used at the focus point, the last color around the perimeter of the 
162     * circle.
163     *   
164     * @throws NullPointerException if center point is null
165     *
166     * @throws IllegalArgumentException  
167     *         if fractions.length != colors.length, or if colors is less 
168     *         than 2 in size, or if radius < 0
169     *
170     *
171     */
172    public RadialGradientPaint(Point2D center, float radius,
173                               float[] fractions, Color[] colors) {
174        this(center,
175             radius,
176             center,        
177             fractions,
178             colors);
179    }
180
181    /**
182     * <p>
183     *
184     * Constructs a <code>RadialGradientPaint</code>.
185     *
186     * @param cx the x coordinate in user space of the center point of the 
187     * circle defining the gradient.  The last color of the gradient is mapped
188     * to the perimeter of this circle
189     *
190     * @param cy the y coordinate in user space of the center point of the 
191     * circle defining the gradient.  The last color of the gradient is mapped
192     * to the perimeter of this circle
193     *
194     * @param radius the radius of the circle defining the extents of the 
195     * color gradient
196     *
197     * @param fx the x coordinate of the point in user space to which the 
198     * first color is mapped
199     *
200     * @param fy the y coordinate of the point in user space to which the 
201     * first color is mapped
202     *
203     * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
204     * distribution of colors along the gradient
205     *
206     * @param colors array of colors to use in the gradient. The first color 
207     * is used at the focus point, the last color around the perimeter of the 
208     * circle.
209     *  
210     * @throws IllegalArgumentException  
211     *         if fractions.length != colors.length, or if colors is less 
212     *         than 2 in size, or if radius < 0
213     *
214     *
215     */
216    public RadialGradientPaint(float cx, float cy, float radius,
217                               float fx, float fy,
218                               float[] fractions, Color[] colors) {
219        this(new Point2D.Float(cx, cy),
220             radius,
221             new Point2D.Float(fx, fy),
222             fractions,
223             colors,
224             NO_CYCLE,
225             SRGB);
226    }
227    
228    /**
229     * <p>
230     *
231     * Constructs a <code>RadialGradientPaint</code>.
232     *
233     * @param center the center point, in user space, of the circle defining 
234     * the gradient. The last color of the gradient is mapped to the perimeter
235     * of this circle
236     *
237     * @param radius the radius of the circle defining the extents of the color
238     * gradient
239     *
240     * @param focus the point, in user space, to which the first color is 
241     * mapped    
242     *
243     * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
244     * distribution of colors along the gradient
245     *
246     * @param colors array of colors to use in the gradient. The first color 
247     * is used at the focus point, the last color around the perimeter of the
248     * circle.
249     *   
250     * @throws NullPointerException if one of the points is null
251     *
252     * @throws IllegalArgumentException  
253     *         if fractions.length != colors.length, or if colors is less 
254     *         than 2 in size, or if radius < 0
255     *
256     */
257    public RadialGradientPaint(Point2D center, float radius,
258                               Point2D focus,
259                               float[] fractions, Color[] colors) {
260        this(center,
261             radius,
262             focus,
263             fractions,
264             colors,
265             NO_CYCLE,
266             SRGB);     
267    }
268    
269    /**
270     * <p>
271     *
272     * Constructs a <code>RadialGradientPaint</code>.
273     *
274     * @param center the center point in user space of the circle defining the
275     * gradient. The last color of the gradient is mapped to the perimeter of 
276     * this circle
277     *
278     * @param radius the radius of the circle defining the extents of the color
279     * gradient
280     *
281     * @param focus the point in user space to which the first color is mapped
282     *   
283     * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
284     * distribution of colors along the gradient
285     *
286     * @param colors array of colors to use in the gradient. The first color is
287     * used at the focus point, the last color around the perimeter of the 
288     * circle.
289     *
290     * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
291     *
292     * @param colorSpace which colorspace to use for interpolation, 
293     * either SRGB or LINEAR_RGB
294     *   
295     * @throws NullPointerException if one of the points is null
296     *
297     * @throws IllegalArgumentException 
298     *         if fractions.length != colors.length, or if colors is less 
299     *         than 2 in size, or if radius < 0
300     *
301     */
302    public RadialGradientPaint(Point2D center, float radius,
303                               Point2D focus,
304                               float[] fractions, Color[] colors,
305                               CycleMethodEnum cycleMethod, 
306                               ColorSpaceEnum colorSpace) {
307        this(center,
308             radius,
309             focus,
310             fractions,
311             colors,
312             cycleMethod,
313             colorSpace,
314             new AffineTransform());
315    }
316
317    /**
318     * <p>
319     *
320     * Constructs a <code>RadialGradientPaint</code>.
321     *
322     * @param center the center point in user space of the circle defining the
323     * gradient.  The last color of the gradient is mapped to the perimeter of
324     * this circle
325     *
326     * @param radius the radius of the circle defining the extents of the color
327     * gradient. 
328     *
329     * @param focus the point in user space to which the first color is mapped
330     *
331     * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
332     * distribution of colors along the gradient
333     *
334     * @param colors array of colors to use in the gradient. The first color is
335     * used at the focus point, the last color around the perimeter of the 
336     * circle.
337     *
338     * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
339     *
340     * @param colorSpace which colorspace to use for interpolation, 
341     * either SRGB or LINEAR_RGB          
342     *
343     * @param gradientTransform transform to apply to the gradient
344     *
345     * @throws NullPointerException if one of the points is null, 
346     * or gradientTransform is null
347     *
348     * @throws IllegalArgumentException 
349     *         if fractions.length != colors.length, or if colors is less 
350     *         than 2 in size, or if radius < 0
351     *
352     */
353    public RadialGradientPaint(Point2D center,
354                               float radius,
355                               Point2D focus,
356                               float[] fractions,  Color[] colors,
357                               CycleMethodEnum cycleMethod, 
358                               ColorSpaceEnum colorSpace,
359                               AffineTransform gradientTransform){
360        super(fractions, colors, cycleMethod, colorSpace, gradientTransform);
361
362        // Check input arguments
363        if (center == null) {
364            throw new NullPointerException("Center point should not be null.");
365        }
366        
367        if (focus == null) {
368            throw new NullPointerException("Focus point should not be null.");
369        }
370
371        if (radius <= 0) {
372            throw new IllegalArgumentException("radius should be greater than zero");
373        }
374
375        //copy parameters
376        this.center = (Point2D)center.clone();
377        this.focus = (Point2D)focus.clone();
378        this.radius = radius;
379    }
380    
381    /**
382     * <p>
383     *
384     * Constructs a <code>RadialGradientPaint</code>, the gradient circle is 
385     * defined by a bounding box.
386     *    
387     * @param gradientBounds the bounding box, in user space, of the circle 
388     * defining outermost extent of the gradient.
389     *
390     * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
391     * distribution of colors along the gradient
392     *
393     * @param colors array of colors to use in the gradient. The first color 
394     * is used at the focus point, the last color around the perimeter of the 
395     * circle.
396     *
397     * @throws NullPointerException if the gradientBounds is null
398     *
399     * @throws IllegalArgumentException 
400     *         if fractions.length != colors.length, or if colors is less 
401     *         than 2 in size, or if radius < 0
402     *
403     */    
404    public RadialGradientPaint(Rectangle2D gradientBounds,
405                               float[] fractions,  Color[] colors) {
406
407        //calculate center point and radius based on bounding box coordinates.
408        this((float)gradientBounds.getX() +
409             ( (float)gradientBounds.getWidth() / 2),
410             
411             (float)gradientBounds.getY() +
412             ( (float)gradientBounds.getWidth() / 2),
413             
414             (float)gradientBounds.getWidth() / 2, 
415             fractions, colors);
416    }
417
418
419    /** <p>
420     * Creates and returns a PaintContext used to generate the color pattern,
421     * for use by the internal rendering engine.
422     *
423     * @param cm {@link ColorModel} that receives
424     * the <code>Paint</code> data. This is used only as a hint.
425     *
426     * @param deviceBounds the device space bounding box of the 
427     * graphics primitive being rendered
428     *
429     * @param userBounds the user space bounding box of the 
430     * graphics primitive being rendered
431     *
432     * @param transform the {@link AffineTransform} from user
433     * space into device space
434     *
435     * @param hints the hints that the context object uses to choose
436     * between rendering alternatives
437     *
438     * @return the {@link PaintContext} that generates color patterns.
439     *
440     * @throws IllegalArgumentException if the transform is not invertible
441     *
442     * @see PaintContext
443     */
444    public PaintContext createContext(ColorModel cm,
445                                      Rectangle deviceBounds,
446                                      Rectangle2D userBounds,
447                                      AffineTransform transform,
448                                      RenderingHints hints) {
449        // Can't modify the transform passed in...
450        transform = new AffineTransform(transform);
451        // incorporate the gradient transform
452        transform.concatenate(gradientTransform);
453
454        try{
455            return new RadialGradientPaintContext
456                (cm, deviceBounds, userBounds, transform, hints,
457                 (float)center.getX(), (float)center.getY(), radius,
458                 (float)focus.getX(), (float)focus.getY(),
459                 fractions, colors, cycleMethod, colorSpace);               
460        }
461        
462        catch(NoninvertibleTransformException e){
463            throw new IllegalArgumentException("transform should be " +
464                                               "invertible");
465        }
466    }
467
468    /**
469     * Returns a copy of the center point of the radial gradient.
470     * @return a {@link Point2D} object that is a copy of the center point     
471     */
472    public Point2D getCenterPoint() {
473        return new Point2D.Double(center.getX(), center.getY());
474    }
475    
476    /** Returns a copy of the end point of the gradient axis.
477     * @return a {@link Point2D} object that is a copy of the focus point     
478     */
479    public Point2D getFocusPoint() {
480        return new Point2D.Double(focus.getX(), focus.getY());
481    }
482
483    /** Returns the radius of the circle defining the radial gradient.
484     * @return the radius of the circle defining the radial gradient
485     */
486    public float getRadius() {
487        return radius;
488    }
489    
490}
491