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     * The <code>LinearGradientPaint</code> class provides a way to fill
023     * a {@link java.awt.Shape} with a linear color gradient pattern.  The user may
024     * specify 2 or more gradient colors, and this paint will provide an
025     * interpolation between each color.  The user also specifies start and end
026     * points which define where in user space the color gradient should begin 
027     * and end.
028     * <p>
029     * The user must provide an array of floats specifying how to distribute the
030     * colors along the gradient.  These values should range from 0.0 to 1.0 and 
031     * act like keyframes along the gradient (they mark where the gradient should 
032     * be exactly a particular color).
033     * <p>
034     * For example:
035     * <br>
036     * <code>
037     * <p>
038     * Point2D start = new Point2D.Float(0, 0);<br>
039     * Point2D end = new Point2D.Float(100,100);<br>
040     * float[] dist = {0.0, 0.2, 1.0};<br>
041     * Color[] colors = {Color.red, Color.white, Color.blue};<br>
042     * LinearGradientPaint p = new LinearGradientPaint(start, end, dist, colors);
043     * </code>
044     *<p>
045     * This code will create a LinearGradientPaint which interpolates between 
046     * red and white for the first 20% of the gradient and between white and blue 
047     * for the remaining 80%.
048     *
049     * <p> In the event that the user does not set the first keyframe value equal
050     * to 0 and the last keyframe value equal to 1, keyframes will be created at
051     * these positions and the first and last colors will be replicated there.
052     * So, if a user specifies the following arrays to construct a gradient:<br>
053     * {Color.blue, Color.red}, {.3, .7}<br>
054     * this will be converted to a gradient with the following keyframes:
055     * {Color.blue, Color.blue, Color.red, Color.red}, {0, .3, .7, 1}
056     *
057     * <p>
058     * The user may also select what action the LinearGradientPaint should take
059     * when filling color outside the start and end points. If no cycle method is
060     * specified, NO_CYCLE will be chosen by default, so the endpoint colors 
061     * will be used to fill the remaining area.  
062     *
063     * <p> The following image demonstrates the options NO_CYCLE and REFLECT.
064     *
065     * <p>
066     * <img src = "cyclic.jpg">
067     *
068     * <p> The colorSpace parameter allows the user to specify in which colorspace
069     *  the interpolation should be performed, default sRGB or linearized RGB.
070     *  
071     *
072     * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
073     * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
074     * @version $Id: LinearGradientPaint.java,v 1.2 2004/09/27 09:27:27 kitfox Exp $
075     * @see java.awt.Paint
076     * @see java.awt.Graphics2D#setPaint
077     *
078     */
079    
080    public final class LinearGradientPaint extends MultipleGradientPaint {
081    
082        /** Gradient start and end points. */
083        private Point2D start, end;   
084           
085        /**<p>
086         * Constructs an <code>LinearGradientPaint</code> with the default 
087         * NO_CYCLE repeating method and SRGB colorspace.
088         *
089         * @param startX the x coordinate of the gradient axis start point 
090         * in user space
091         *
092         * @param startY the y coordinate of the gradient axis start point 
093         * in user space
094         *
095         * @param endX the x coordinate of the gradient axis end point 
096         * in user space
097         *
098         * @param endY the y coordinate of the gradient axis end point 
099         * in user space
100         *
101         * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
102         * distribution of colors along the gradient
103         *
104         * @param colors array of colors corresponding to each fractional value
105         *     
106         *
107         * @throws IllegalArgumentException if start and end points are the 
108         * same points, or if fractions.length != colors.length, or if colors 
109         * is less than 2 in size.
110         *
111         */
112        public LinearGradientPaint(float startX, float startY, 
113                                   float endX, float endY, 
114                                   float[] fractions, Color[] colors) {
115    
116            this(new Point2D.Float(startX, startY),
117                 new Point2D.Float(endX, endY), 
118                 fractions, 
119                 colors,
120                 NO_CYCLE,
121                 SRGB);
122        }
123    
124        /**<p>
125         * Constructs an <code>LinearGradientPaint</code> with default SRGB 
126         * colorspace.
127         *
128         * @param startX the x coordinate of the gradient axis start point 
129         * in user space
130         *
131         * @param startY the y coordinate of the gradient axis start point 
132         * in user space
133         *
134         * @param endX the x coordinate of the gradient axis end point 
135         * in user space
136         * 
137         * @param endY the y coordinate of the gradient axis end point 
138         * in user space
139         *
140         * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
141         * distribution of colors along the gradient
142         *
143         * @param colors array of colors corresponding to each fractional value
144         *
145         * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
146         *
147         * @throws IllegalArgumentException if start and end points are the 
148         * same points, or if fractions.length != colors.length, or if colors 
149         * is less than 2 in size.
150         *
151         */
152        public LinearGradientPaint(float startX, float startY, 
153                                   float endX, float endY, 
154                                   float[] fractions, Color[] colors, 
155                                   CycleMethodEnum cycleMethod) {
156            this(new Point2D.Float(startX, startY), 
157                 new Point2D.Float(endX, endY), 
158                 fractions, 
159                 colors,
160                 cycleMethod,
161                 SRGB);
162        }
163    
164        /**<p>
165         * Constructs a <code>LinearGradientPaint</code> with the default 
166         * NO_CYCLE repeating method and SRGB colorspace.
167         *
168         * @param start the gradient axis start <code>Point</code> in user space
169         *
170         * @param end the gradient axis end <code>Point</code> in user space
171         *
172         * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
173         * distribution of colors along the gradient
174         *
175         * @param colors array of colors corresponding to each fractional value
176         *
177         * @throws NullPointerException if one of the points is null
178         *
179         * @throws IllegalArgumentException if start and end points are the 
180         * same points, or if fractions.length != colors.length, or if colors 
181         * is less than 2 in size.
182         *
183         */
184        public LinearGradientPaint(Point2D start, Point2D end, float[] fractions,
185                                   Color[] colors) {
186    
187            this(start, end, fractions, colors, NO_CYCLE, SRGB);
188        }
189        
190        /**<p>
191         * Constructs a <code>LinearGradientPaint</code>.
192         *
193         * @param start the gradient axis start <code>Point</code> in user space
194         *
195         * @param end the gradient axis end <code>Point</code> in user space
196         *
197         * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
198         * distribution of colors along the gradient
199         *
200         * @param colors array of colors corresponding to each fractional value
201         *
202         * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
203         *
204         * @param colorSpace which colorspace to use for interpolation, 
205         * either SRGB or LINEAR_RGB
206         *   
207         * @throws NullPointerException if one of the points is null
208         *
209         * @throws IllegalArgumentException if start and end points are the 
210         * same points, or if fractions.length != colors.length, or if colors 
211         * is less than 2 in size.
212         *
213         */
214        public LinearGradientPaint(Point2D start, Point2D end, float[] fractions,
215                                   Color[] colors, 
216                                   CycleMethodEnum cycleMethod, 
217                                   ColorSpaceEnum colorSpace) {
218            
219            this(start, end, fractions, colors, cycleMethod, colorSpace, 
220                 new AffineTransform());
221            
222        }
223        
224        /**<p>
225         * Constructs a <code>LinearGradientPaint</code>.
226         *
227         * @param start the gradient axis start <code>Point</code> in user space
228         *
229         * @param end the gradient axis end <code>Point</code> in user space
230         *
231         * @param fractions numbers ranging from 0.0 to 1.0 specifying the 
232         * distribution of colors along the gradient
233         *
234         * @param colors array of colors corresponding to each fractional value
235         *
236         * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
237         *
238         * @param colorSpace which colorspace to use for interpolation, 
239         * either SRGB or LINEAR_RGB
240         *
241         * @param gradientTransform transform to apply to the gradient
242         *     
243         * @throws NullPointerException if one of the points is null, 
244         * or gradientTransform is null
245         *
246         * @throws IllegalArgumentException if start and end points are the 
247         * same points, or if fractions.length != colors.length, or if colors 
248         * is less than 2 in size.
249         *
250         */
251        public LinearGradientPaint(Point2D start, Point2D end, float[] fractions,
252                                   Color[] colors,
253                                   CycleMethodEnum cycleMethod, 
254                                   ColorSpaceEnum colorSpace, 
255                                   AffineTransform gradientTransform) {
256            super(fractions, colors, cycleMethod, colorSpace, gradientTransform);
257    
258            //
259            // Check input parameters
260            //      
261            if (start == null || end == null) {
262                throw new NullPointerException("Start and end points must be" +
263                                               "non-null");
264            }
265    
266            if (start.equals(end)) {
267                throw new IllegalArgumentException("Start point cannot equal" +
268                                                   "endpoint");
269            }
270    
271            //copy the points...
272            this.start = (Point2D)start.clone();
273    
274            this.end = (Point2D)end.clone();
275            
276        }
277        
278        /**
279         * Creates and returns a PaintContext used to generate the color pattern,
280         * for use by the internal rendering engine.
281         *
282         * @param cm {@link ColorModel} that receives
283         * the <code>Paint</code> data. This is used only as a hint.
284         *
285         * @param deviceBounds the device space bounding box of the 
286         * graphics primitive being rendered
287         *
288         * @param userBounds the user space bounding box of the 
289         * graphics primitive being rendered
290         *
291         * @param transform the {@link AffineTransform} from user
292         * space into device space
293         *
294         * @param hints the hints that the context object uses to choose
295         * between rendering alternatives
296         *
297         * @return the {@link PaintContext} that generates color patterns.
298         *
299         * @see PaintContext
300         */
301        public PaintContext createContext(ColorModel cm,
302                                          Rectangle deviceBounds,
303                                          Rectangle2D userBounds,
304                                          AffineTransform transform,
305                                          RenderingHints hints) {
306    
307            // Can't modify the transform passed in...
308            transform = new AffineTransform(transform);
309            //incorporate the gradient transform
310            transform.concatenate(gradientTransform); 
311    
312            try {
313                return new LinearGradientPaintContext(cm, 
314                                                      deviceBounds,
315                                                      userBounds, 
316                                                      transform,
317                                                      hints,
318                                                      start, 
319                                                      end,
320                                                      fractions,
321                                                      this.getColors(),
322                                                      cycleMethod,
323                                                      colorSpace);
324            }
325            
326            catch(NoninvertibleTransformException e) {
327                e.printStackTrace();
328                throw new IllegalArgumentException("transform should be" + 
329                                                   "invertible");
330            }
331        }
332        
333        /**
334         * Returns a copy of the start point of the gradient axis
335         * @return a {@link Point2D} object that is a copy of the point
336         * that anchors the first color of this 
337         * <code>LinearGradientPaint</code>.  
338         */
339        public Point2D getStartPoint() {
340            return new Point2D.Double(start.getX(), start.getY());
341        }
342        
343        /** Returns a copy of the end point of the gradient axis
344         * @return a {@link Point2D} object that is a copy of the point
345         * that anchors the last color of this 
346         * <code>LinearGradientPaint</code>.  
347         */
348        public Point2D getEndPoint() {
349            return new Point2D.Double(end.getX(), end.getY());
350        }
351            
352    }
353    
354