1 /*
2  * Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.awt;
27 
28 import java.awt.geom.AffineTransform;
29 import java.awt.geom.Point2D;
30 import java.awt.geom.Rectangle2D;
31 import java.awt.image.ColorModel;
32 import java.beans.ConstructorProperties;
33 
34 /**
35  * The {@code LinearGradientPaint} class provides a way to fill
36  * a {@link java.awt.Shape} with a linear color gradient pattern.  The user
37  * may specify two or more gradient colors, and this paint will provide an
38  * interpolation between each color.  The user also specifies start and end
39  * points which define where in user space the color gradient should begin
40  * and end.
41  * <p>
42  * The user must provide an array of floats specifying how to distribute the
43  * colors along the gradient.  These values should range from 0.0 to 1.0 and
44  * act like keyframes along the gradient (they mark where the gradient should
45  * be exactly a particular color).
46  * <p>
47  * In the event that the user does not set the first keyframe value equal
48  * to 0 and/or the last keyframe value equal to 1, keyframes will be created
49  * at these positions and the first and last colors will be replicated there.
50  * So, if a user specifies the following arrays to construct a gradient:<br>
51  * <pre>
52  *     {Color.BLUE, Color.RED}, {.3f, .7f}
53  * </pre>
54  * this will be converted to a gradient with the following keyframes:<br>
55  * <pre>
56  *     {Color.BLUE, Color.BLUE, Color.RED, Color.RED}, {0f, .3f, .7f, 1f}
57  * </pre>
58  *
59  * <p>
60  * The user may also select what action the {@code LinearGradientPaint} object
61  * takes when it is filling the space outside the start and end points by
62  * setting {@code CycleMethod} to either {@code REFLECTION} or {@code REPEAT}.
63  * The distances between any two colors in any of the reflected or repeated
64  * copies of the gradient are the same as the distance between those same two
65  * colors between the start and end points.
66  * Note that some minor variations in distances may occur due to sampling at
67  * the granularity of a pixel.
68  * If no cycle method is specified, {@code NO_CYCLE} will be chosen by
69  * default, which means the endpoint colors will be used to fill the
70  * remaining area.
71  * <p>
72  * The colorSpace parameter allows the user to specify in which colorspace
73  * the interpolation should be performed, default sRGB or linearized RGB.
74  *
75  * <p>
76  * The following code demonstrates typical usage of
77  * {@code LinearGradientPaint}:
78  * <pre>
79  *     Point2D start = new Point2D.Float(0, 0);
80  *     Point2D end = new Point2D.Float(50, 50);
81  *     float[] dist = {0.0f, 0.2f, 1.0f};
82  *     Color[] colors = {Color.RED, Color.WHITE, Color.BLUE};
83  *     LinearGradientPaint p =
84  *         new LinearGradientPaint(start, end, dist, colors);
85  * </pre>
86  * <p>
87  * This code will create a {@code LinearGradientPaint} which interpolates
88  * between red and white for the first 20% of the gradient and between white
89  * and blue for the remaining 80%.
90  *
91  * <p>
92  * This image demonstrates the example code above for each
93  * of the three cycle methods:
94  * <p style="text-align:center">
95  * <img src = "doc-files/LinearGradientPaint.png"
96  * alt="image showing the output of the example code">
97  *
98  * @see java.awt.Paint
99  * @see java.awt.Graphics2D#setPaint
100  * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
101  * @since 1.6
102  */
103 public final class LinearGradientPaint extends MultipleGradientPaint {
104 
105     /** Gradient start and end points. */
106     private final Point2D start, end;
107 
108     /**
109      * Constructs a {@code LinearGradientPaint} with a default
110      * {@code NO_CYCLE} repeating method and {@code SRGB} color space.
111      *
112      * @param startX the X coordinate of the gradient axis start point
113      *               in user space
114      * @param startY the Y coordinate of the gradient axis start point
115      *               in user space
116      * @param endX   the X coordinate of the gradient axis end point
117      *               in user space
118      * @param endY   the Y coordinate of the gradient axis end point
119      *               in user space
120      * @param fractions numbers ranging from 0.0 to 1.0 specifying the
121      *                  distribution of colors along the gradient
122      * @param colors array of colors corresponding to each fractional value
123      *
124      * @throws NullPointerException
125      * if {@code fractions} array is null,
126      * or {@code colors} array is null,
127      * @throws IllegalArgumentException
128      * if start and end points are the same points,
129      * or {@code fractions.length != colors.length},
130      * or {@code colors} is less than 2 in size,
131      * or a {@code fractions} value is less than 0.0 or greater than 1.0,
132      * or the {@code fractions} are not provided in strictly increasing order
133      */
LinearGradientPaint(float startX, float startY, float endX, float endY, float[] fractions, Color[] colors)134     public LinearGradientPaint(float startX, float startY,
135                                float endX, float endY,
136                                float[] fractions, Color[] colors)
137     {
138         this(new Point2D.Float(startX, startY),
139              new Point2D.Float(endX, endY),
140              fractions,
141              colors,
142              CycleMethod.NO_CYCLE);
143     }
144 
145     /**
146      * Constructs a {@code LinearGradientPaint} with a default {@code SRGB}
147      * color space.
148      *
149      * @param startX the X coordinate of the gradient axis start point
150      *               in user space
151      * @param startY the Y coordinate of the gradient axis start point
152      *               in user space
153      * @param endX   the X coordinate of the gradient axis end point
154      *               in user space
155      * @param endY   the Y coordinate of the gradient axis end point
156      *               in user space
157      * @param fractions numbers ranging from 0.0 to 1.0 specifying the
158      *                  distribution of colors along the gradient
159      * @param colors array of colors corresponding to each fractional value
160      * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
161      *                    or {@code REPEAT}
162      *
163      * @throws NullPointerException
164      * if {@code fractions} array is null,
165      * or {@code colors} array is null,
166      * or {@code cycleMethod} is null
167      * @throws IllegalArgumentException
168      * if start and end points are the same points,
169      * or {@code fractions.length != colors.length},
170      * or {@code colors} is less than 2 in size,
171      * or a {@code fractions} value is less than 0.0 or greater than 1.0,
172      * or the {@code fractions} are not provided in strictly increasing order
173      */
LinearGradientPaint(float startX, float startY, float endX, float endY, float[] fractions, Color[] colors, CycleMethod cycleMethod)174     public LinearGradientPaint(float startX, float startY,
175                                float endX, float endY,
176                                float[] fractions, Color[] colors,
177                                CycleMethod cycleMethod)
178     {
179         this(new Point2D.Float(startX, startY),
180              new Point2D.Float(endX, endY),
181              fractions,
182              colors,
183              cycleMethod);
184     }
185 
186     /**
187      * Constructs a {@code LinearGradientPaint} with a default
188      * {@code NO_CYCLE} repeating method and {@code SRGB} color space.
189      *
190      * @param start the gradient axis start {@code Point2D} in user space
191      * @param end the gradient axis end {@code Point2D} in user space
192      * @param fractions numbers ranging from 0.0 to 1.0 specifying the
193      *                  distribution of colors along the gradient
194      * @param colors array of colors corresponding to each fractional value
195      *
196      * @throws NullPointerException
197      * if one of the points is null,
198      * or {@code fractions} array is null,
199      * or {@code colors} array is null
200      * @throws IllegalArgumentException
201      * if start and end points are the same points,
202      * or {@code fractions.length != colors.length},
203      * or {@code colors} is less than 2 in size,
204      * or a {@code fractions} value is less than 0.0 or greater than 1.0,
205      * or the {@code fractions} are not provided in strictly increasing order
206      */
LinearGradientPaint(Point2D start, Point2D end, float[] fractions, Color[] colors)207     public LinearGradientPaint(Point2D start, Point2D end,
208                                float[] fractions, Color[] colors)
209     {
210         this(start, end,
211              fractions, colors,
212              CycleMethod.NO_CYCLE);
213     }
214 
215     /**
216      * Constructs a {@code LinearGradientPaint} with a default {@code SRGB}
217      * color space.
218      *
219      * @param start the gradient axis start {@code Point2D} in user space
220      * @param end the gradient axis end {@code Point2D} in user space
221      * @param fractions numbers ranging from 0.0 to 1.0 specifying the
222      *                  distribution of colors along the gradient
223      * @param colors array of colors corresponding to each fractional value
224      * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
225      *                    or {@code REPEAT}
226      *
227      * @throws NullPointerException
228      * if one of the points is null,
229      * or {@code fractions} array is null,
230      * or {@code colors} array is null,
231      * or {@code cycleMethod} is null
232      * @throws IllegalArgumentException
233      * if start and end points are the same points,
234      * or {@code fractions.length != colors.length},
235      * or {@code colors} is less than 2 in size,
236      * or a {@code fractions} value is less than 0.0 or greater than 1.0,
237      * or the {@code fractions} are not provided in strictly increasing order
238      */
LinearGradientPaint(Point2D start, Point2D end, float[] fractions, Color[] colors, CycleMethod cycleMethod)239     public LinearGradientPaint(Point2D start, Point2D end,
240                                float[] fractions, Color[] colors,
241                                CycleMethod cycleMethod)
242     {
243         this(start, end,
244              fractions, colors,
245              cycleMethod,
246              ColorSpaceType.SRGB,
247              new AffineTransform());
248     }
249 
250     /**
251      * Constructs a {@code LinearGradientPaint}.
252      *
253      * @param start the gradient axis start {@code Point2D} in user space
254      * @param end the gradient axis end {@code Point2D} in user space
255      * @param fractions numbers ranging from 0.0 to 1.0 specifying the
256      *                  distribution of colors along the gradient
257      * @param colors array of colors corresponding to each fractional value
258      * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
259      *                    or {@code REPEAT}
260      * @param colorSpace which color space to use for interpolation,
261      *                   either {@code SRGB} or {@code LINEAR_RGB}
262      * @param gradientTransform transform to apply to the gradient
263      *
264      * @throws NullPointerException
265      * if one of the points is null,
266      * or {@code fractions} array is null,
267      * or {@code colors} array is null,
268      * or {@code cycleMethod} is null,
269      * or {@code colorSpace} is null,
270      * or {@code gradientTransform} is null
271      * @throws IllegalArgumentException
272      * if start and end points are the same points,
273      * or {@code fractions.length != colors.length},
274      * or {@code colors} is less than 2 in size,
275      * or a {@code fractions} value is less than 0.0 or greater than 1.0,
276      * or the {@code fractions} are not provided in strictly increasing order
277      */
278     @ConstructorProperties({ "startPoint", "endPoint", "fractions", "colors", "cycleMethod", "colorSpace", "transform" })
LinearGradientPaint(Point2D start, Point2D end, float[] fractions, Color[] colors, CycleMethod cycleMethod, ColorSpaceType colorSpace, AffineTransform gradientTransform)279     public LinearGradientPaint(Point2D start, Point2D end,
280                                float[] fractions, Color[] colors,
281                                CycleMethod cycleMethod,
282                                ColorSpaceType colorSpace,
283                                AffineTransform gradientTransform)
284     {
285         super(fractions, colors, cycleMethod, colorSpace, gradientTransform);
286 
287         // check input parameters
288         if (start == null || end == null) {
289             throw new NullPointerException("Start and end points must be" +
290                                            "non-null");
291         }
292 
293         if (start.equals(end)) {
294             throw new IllegalArgumentException("Start point cannot equal" +
295                                                "endpoint");
296         }
297 
298         // copy the points...
299         this.start = new Point2D.Double(start.getX(), start.getY());
300         this.end = new Point2D.Double(end.getX(), end.getY());
301     }
302 
303     /**
304      * Creates and returns a {@link PaintContext} used to
305      * generate a linear color gradient pattern.
306      * See the {@link Paint#createContext specification} of the
307      * method in the {@link Paint} interface for information
308      * on null parameter handling.
309      *
310      * @param cm the preferred {@link ColorModel} which represents the most convenient
311      *           format for the caller to receive the pixel data, or {@code null}
312      *           if there is no preference.
313      * @param deviceBounds the device space bounding box
314      *                     of the graphics primitive being rendered.
315      * @param userBounds the user space bounding box
316      *                   of the graphics primitive being rendered.
317      * @param transform the {@link AffineTransform} from user
318      *              space into device space.
319      * @param hints the set of hints that the context object can use to
320      *              choose between rendering alternatives.
321      * @return the {@code PaintContext} for
322      *         generating color patterns.
323      * @see Paint
324      * @see PaintContext
325      * @see ColorModel
326      * @see Rectangle
327      * @see Rectangle2D
328      * @see AffineTransform
329      * @see RenderingHints
330      */
createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform transform, RenderingHints hints)331     public PaintContext createContext(ColorModel cm,
332                                       Rectangle deviceBounds,
333                                       Rectangle2D userBounds,
334                                       AffineTransform transform,
335                                       RenderingHints hints)
336     {
337         // avoid modifying the user's transform...
338         transform = new AffineTransform(transform);
339         // incorporate the gradient transform
340         transform.concatenate(gradientTransform);
341 
342         if ((fractions.length == 2) &&
343             (cycleMethod != CycleMethod.REPEAT) &&
344             (colorSpace == ColorSpaceType.SRGB))
345         {
346             // faster to use the basic GradientPaintContext for this
347             // common case
348             boolean cyclic = (cycleMethod != CycleMethod.NO_CYCLE);
349             return new GradientPaintContext(cm, start, end,
350                                             transform,
351                                             colors[0], colors[1],
352                                             cyclic);
353         } else {
354             return new LinearGradientPaintContext(this, cm,
355                                                   deviceBounds, userBounds,
356                                                   transform, hints,
357                                                   start, end,
358                                                   fractions, colors,
359                                                   cycleMethod, colorSpace);
360         }
361     }
362 
363     /**
364      * Returns a copy of the start point of the gradient axis.
365      *
366      * @return a {@code Point2D} object that is a copy of the point
367      * that anchors the first color of this {@code LinearGradientPaint}
368      */
getStartPoint()369     public Point2D getStartPoint() {
370         return new Point2D.Double(start.getX(), start.getY());
371     }
372 
373     /**
374      * Returns a copy of the end point of the gradient axis.
375      *
376      * @return a {@code Point2D} object that is a copy of the point
377      * that anchors the last color of this {@code LinearGradientPaint}
378      */
getEndPoint()379     public Point2D getEndPoint() {
380         return new Point2D.Double(end.getX(), end.getY());
381     }
382 }
383