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    
009    package com.kitfox.svg.batik;
010    
011    import java.awt.Color;
012    import java.awt.PaintContext;
013    import java.awt.Rectangle;
014    import java.awt.RenderingHints;
015    import java.awt.geom.AffineTransform;
016    import java.awt.geom.NoninvertibleTransformException;
017    import java.awt.geom.Point2D;
018    import java.awt.geom.Rectangle2D;
019    import 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    
094    public 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