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