blob: 278906bb7716a574c9b183256aad2a70c3a1cb18 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.awt;
27
28import java.awt.geom.AffineTransform;
29import java.awt.geom.Point2D;
30import java.awt.geom.Rectangle2D;
31import java.awt.image.ColorModel;
32
33/**
34 * The {@code RadialGradientPaint} class provides a way to fill a shape with
35 * a circular radial color gradient pattern. The user may specify 2 or more
36 * gradient colors, and this paint will provide an interpolation between
37 * each color.
38 * <p>
39 * The user must specify the circle controlling the gradient pattern,
40 * which is described by a center point and a radius. The user can also
41 * specify a separate focus point within that circle, which controls the
42 * location of the first color of the gradient. By default the focus is
43 * set to be the center of the circle.
44 * <p>
45 * This paint will map the first color of the gradient to the focus point,
46 * and the last color to the perimeter of the circle, interpolating
47 * smoothly for any in-between colors specified by the user. Any line drawn
48 * from the focus point to the circumference will thus span all the gradient
49 * colors.
50 * <p>
51 * Specifying a focus point outside of the circle's radius will result in the
52 * focus being set to the intersection point of the focus-center line and the
53 * perimeter of the circle.
54 * <p>
55 * The user must provide an array of floats specifying how to distribute the
56 * colors along the gradient. These values should range from 0.0 to 1.0 and
57 * act like keyframes along the gradient (they mark where the gradient should
58 * be exactly a particular color).
59 * <p>
60 * In the event that the user does not set the first keyframe value equal
61 * to 0 and/or the last keyframe value equal to 1, keyframes will be created
62 * at these positions and the first and last colors will be replicated there.
63 * So, if a user specifies the following arrays to construct a gradient:<br>
64 * <pre>
65 * {Color.BLUE, Color.RED}, {.3f, .7f}
66 * </pre>
67 * this will be converted to a gradient with the following keyframes:<br>
68 * <pre>
69 * {Color.BLUE, Color.BLUE, Color.RED, Color.RED}, {0f, .3f, .7f, 1f}
70 * </pre>
71 *
72 * <p>
73 * The user may also select what action the {@code RadialGradientPaint}
74 * should take when filling color outside the bounds of the circle's radius.
75 * If no cycle method is specified, {@code NO_CYCLE} will be chosen by
76 * default, which means the the last keyframe color will be used to fill the
77 * remaining area.
78 * <p>
79 * The colorSpace parameter allows the user to specify in which colorspace
80 * the interpolation should be performed, default sRGB or linearized RGB.
81 *
82 * <p>
83 * The following code demonstrates typical usage of
84 * {@code RadialGradientPaint}, where the center and focus points are
85 * the same:
86 * <p>
87 * <pre>
88 * Point2D center = new Point2D.Float(50, 50);
89 * float radius = 25;
90 * float[] dist = {0.0f, 0.2f, 1.0f};
91 * Color[] colors = {Color.RED, Color.WHITE, Color.BLUE};
92 * RadialGradientPaint p =
93 * new RadialGradientPaint(center, radius, dist, colors);
94 * </pre>
95 *
96 * <p>
97 * This image demonstrates the example code above, with default
98 * (centered) focus for each of the three cycle methods:
99 * <p>
100 * <center>
101 * <img src = "doc-files/RadialGradientPaint-1.png">
102 * </center>
103 *
104 * <p>
105 * It is also possible to specify a non-centered focus point, as
106 * in the following code:
107 * <p>
108 * <pre>
109 * Point2D center = new Point2D.Float(50, 50);
110 * float radius = 25;
111 * Point2D focus = new Point2D.Float(40, 40);
112 * float[] dist = {0.0f, 0.2f, 1.0f};
113 * Color[] colors = {Color.RED, Color.WHITE, Color.BLUE};
114 * RadialGradientPaint p =
115 * new RadialGradientPaint(center, radius, focus,
116 * dist, colors,
117 * CycleMethod.NO_CYCLE);
118 * </pre>
119 *
120 * <p>
121 * This image demonstrates the previous example code, with non-centered
122 * focus for each of the three cycle methods:
123 * <p>
124 * <center>
125 * <img src = "doc-files/RadialGradientPaint-2.png">
126 * </center>
127 *
128 * @see java.awt.Paint
129 * @see java.awt.Graphics2D#setPaint
130 * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
131 * @since 1.6
132 */
133public final class RadialGradientPaint extends MultipleGradientPaint {
134
135 /** Focus point which defines the 0% gradient stop X coordinate. */
136 private final Point2D focus;
137
138 /** Center of the circle defining the 100% gradient stop X coordinate. */
139 private final Point2D center;
140
141 /** Radius of the outermost circle defining the 100% gradient stop. */
142 private final float radius;
143
144 /**
145 * Constructs a {@code RadialGradientPaint} with a default
146 * {@code NO_CYCLE} repeating method and {@code SRGB} color space,
147 * using the center as the focus point.
148 *
149 * @param cx the X coordinate in user space of the center point of the
150 * circle defining the gradient. The last color of the
151 * gradient is mapped to the perimeter of this circle.
152 * @param cy the Y coordinate in user space of the center point of the
153 * circle defining the gradient. The last color of the
154 * gradient is mapped to the perimeter of this circle.
155 * @param radius the radius of the circle defining the extents of the
156 * color gradient
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 to use in the gradient. The first color
160 * is used at the focus point, the last color around the
161 * perimeter of the circle.
162 *
163 * @throws NullPointerException
164 * if {@code fractions} array is null,
165 * or {@code colors} array is null
166 * @throws IllegalArgumentException
167 * if {@code radius} is non-positive,
168 * or {@code fractions.length != colors.length},
169 * or {@code colors} is less than 2 in size,
170 * or a {@code fractions} value is less than 0.0 or greater than 1.0,
171 * or the {@code fractions} are not provided in strictly increasing order
172 */
173 public RadialGradientPaint(float cx, float cy, float radius,
174 float[] fractions, Color[] colors)
175 {
176 this(cx, cy,
177 radius,
178 cx, cy,
179 fractions,
180 colors,
181 CycleMethod.NO_CYCLE);
182 }
183
184 /**
185 * Constructs a {@code RadialGradientPaint} with a default
186 * {@code NO_CYCLE} repeating method and {@code SRGB} color space,
187 * using the center as the focus point.
188 *
189 * @param center the center point, in user space, of the circle defining
190 * the gradient
191 * @param radius the radius of the circle defining the extents of the
192 * color gradient
193 * @param fractions numbers ranging from 0.0 to 1.0 specifying the
194 * distribution of colors along the gradient
195 * @param colors array of colors to use in the gradient. The first color
196 * is used at the focus point, the last color around the
197 * perimeter of the circle.
198 *
199 * @throws NullPointerException
200 * if {@code center} point is null,
201 * or {@code fractions} array is null,
202 * or {@code colors} array is null
203 * @throws IllegalArgumentException
204 * if {@code radius} is non-positive,
205 * or {@code fractions.length != colors.length},
206 * or {@code colors} is less than 2 in size,
207 * or a {@code fractions} value is less than 0.0 or greater than 1.0,
208 * or the {@code fractions} are not provided in strictly increasing order
209 */
210 public RadialGradientPaint(Point2D center, float radius,
211 float[] fractions, Color[] colors)
212 {
213 this(center,
214 radius,
215 center,
216 fractions,
217 colors,
218 CycleMethod.NO_CYCLE);
219 }
220
221 /**
222 * Constructs a {@code RadialGradientPaint} with a default
223 * {@code SRGB} color space, using the center as the focus point.
224 *
225 * @param cx the X coordinate in user space of the center point of the
226 * circle defining the gradient. The last color of the
227 * gradient is mapped to the perimeter of this circle.
228 * @param cy the Y coordinate in user space of the center point of the
229 * circle defining the gradient. The last color of the
230 * gradient is mapped to the perimeter of this circle.
231 * @param radius the radius of the circle defining the extents of the
232 * color gradient
233 * @param fractions numbers ranging from 0.0 to 1.0 specifying the
234 * distribution of colors along the gradient
235 * @param colors array of colors to use in the gradient. The first color
236 * is used at the focus point, the last color around the
237 * perimeter of the circle.
238 * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
239 * or {@code REPEAT}
240 *
241 * @throws NullPointerException
242 * if {@code fractions} array is null,
243 * or {@code colors} array is null,
244 * or {@code cycleMethod} is null
245 * @throws IllegalArgumentException
246 * if {@code radius} is non-positive,
247 * or {@code fractions.length != colors.length},
248 * or {@code colors} is less than 2 in size,
249 * or a {@code fractions} value is less than 0.0 or greater than 1.0,
250 * or the {@code fractions} are not provided in strictly increasing order
251 */
252 public RadialGradientPaint(float cx, float cy, float radius,
253 float[] fractions, Color[] colors,
254 CycleMethod cycleMethod)
255 {
256 this(cx, cy,
257 radius,
258 cx, cy,
259 fractions,
260 colors,
261 cycleMethod);
262 }
263
264 /**
265 * Constructs a {@code RadialGradientPaint} with a default
266 * {@code SRGB} color space, using the center as the focus point.
267 *
268 * @param center the center point, in user space, of the circle defining
269 * the gradient
270 * @param radius the radius of the circle defining the extents of the
271 * color gradient
272 * @param fractions numbers ranging from 0.0 to 1.0 specifying the
273 * distribution of colors along the gradient
274 * @param colors array of colors to use in the gradient. The first color
275 * is used at the focus point, the last color around the
276 * perimeter of the circle.
277 * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
278 * or {@code REPEAT}
279 *
280 * @throws NullPointerException
281 * if {@code center} point is null,
282 * or {@code fractions} array is null,
283 * or {@code colors} array is null,
284 * or {@code cycleMethod} is null
285 * @throws IllegalArgumentException
286 * if {@code radius} is non-positive,
287 * or {@code fractions.length != colors.length},
288 * or {@code colors} is less than 2 in size,
289 * or a {@code fractions} value is less than 0.0 or greater than 1.0,
290 * or the {@code fractions} are not provided in strictly increasing order
291 */
292 public RadialGradientPaint(Point2D center, float radius,
293 float[] fractions, Color[] colors,
294 CycleMethod cycleMethod)
295 {
296 this(center,
297 radius,
298 center,
299 fractions,
300 colors,
301 cycleMethod);
302 }
303
304 /**
305 * Constructs a {@code RadialGradientPaint} with a default
306 * {@code SRGB} color space.
307 *
308 * @param cx the X coordinate in user space of the center point of the
309 * circle defining the gradient. The last color of the
310 * gradient is mapped to the perimeter of this circle.
311 * @param cy the Y coordinate in user space of the center point of the
312 * circle defining the gradient. The last color of the
313 * gradient is mapped to the perimeter of this circle.
314 * @param radius the radius of the circle defining the extents of the
315 * color gradient
316 * @param fx the X coordinate of the point in user space to which the
317 * first color is mapped
318 * @param fy the Y coordinate of the point in user space to which the
319 * first color is mapped
320 * @param fractions numbers ranging from 0.0 to 1.0 specifying the
321 * distribution of colors along the gradient
322 * @param colors array of colors to use in the gradient. The first color
323 * is used at the focus point, the last color around the
324 * perimeter of the circle.
325 * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
326 * or {@code REPEAT}
327 *
328 * @throws NullPointerException
329 * if {@code fractions} array is null,
330 * or {@code colors} array is null,
331 * or {@code cycleMethod} is null
332 * @throws IllegalArgumentException
333 * if {@code radius} is non-positive,
334 * or {@code fractions.length != colors.length},
335 * or {@code colors} is less than 2 in size,
336 * or a {@code fractions} value is less than 0.0 or greater than 1.0,
337 * or the {@code fractions} are not provided in strictly increasing order
338 */
339 public RadialGradientPaint(float cx, float cy, float radius,
340 float fx, float fy,
341 float[] fractions, Color[] colors,
342 CycleMethod cycleMethod)
343 {
344 this(new Point2D.Float(cx, cy),
345 radius,
346 new Point2D.Float(fx, fy),
347 fractions,
348 colors,
349 cycleMethod);
350 }
351
352 /**
353 * Constructs a {@code RadialGradientPaint} with a default
354 * {@code SRGB} color space.
355 *
356 * @param center the center point, in user space, of the circle defining
357 * the gradient. The last color of the gradient is mapped
358 * to the perimeter of this circle.
359 * @param radius the radius of the circle defining the extents of the color
360 * gradient
361 * @param focus the point in user space to which the first color is mapped
362 * @param fractions numbers ranging from 0.0 to 1.0 specifying the
363 * distribution of colors along the gradient
364 * @param colors array of colors to use in the gradient. The first color
365 * is used at the focus point, the last color around the
366 * perimeter of the circle.
367 * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
368 * or {@code REPEAT}
369 *
370 * @throws NullPointerException
371 * if one of the points is null,
372 * or {@code fractions} array is null,
373 * or {@code colors} array is null,
374 * or {@code cycleMethod} is null
375 * @throws IllegalArgumentException
376 * if {@code radius} is non-positive,
377 * or {@code fractions.length != colors.length},
378 * or {@code colors} is less than 2 in size,
379 * or a {@code fractions} value is less than 0.0 or greater than 1.0,
380 * or the {@code fractions} are not provided in strictly increasing order
381 */
382 public RadialGradientPaint(Point2D center, float radius,
383 Point2D focus,
384 float[] fractions, Color[] colors,
385 CycleMethod cycleMethod)
386 {
387 this(center,
388 radius,
389 focus,
390 fractions,
391 colors,
392 cycleMethod,
393 ColorSpaceType.SRGB,
394 new AffineTransform());
395 }
396
397 /**
398 * Constructs a {@code RadialGradientPaint}.
399 *
400 * @param center the center point in user space of the circle defining the
401 * gradient. The last color of the gradient is mapped to
402 * the perimeter of this circle.
403 * @param radius the radius of the circle defining the extents of the
404 * color gradient
405 * @param focus the point in user space to which the first color is mapped
406 * @param fractions numbers ranging from 0.0 to 1.0 specifying the
407 * distribution of colors along the gradient
408 * @param colors array of colors to use in the gradient. The first color
409 * is used at the focus point, the last color around the
410 * perimeter of the circle.
411 * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
412 * or {@code REPEAT}
413 * @param colorSpace which color space to use for interpolation,
414 * either {@code SRGB} or {@code LINEAR_RGB}
415 * @param gradientTransform transform to apply to the gradient
416 *
417 * @throws NullPointerException
418 * if one of the points is null,
419 * or {@code fractions} array is null,
420 * or {@code colors} array is null,
421 * or {@code cycleMethod} is null,
422 * or {@code colorSpace} is null,
423 * or {@code gradientTransform} is null
424 * @throws IllegalArgumentException
425 * if {@code radius} is non-positive,
426 * or {@code fractions.length != colors.length},
427 * or {@code colors} is less than 2 in size,
428 * or a {@code fractions} value is less than 0.0 or greater than 1.0,
429 * or the {@code fractions} are not provided in strictly increasing order
430 */
431 public RadialGradientPaint(Point2D center,
432 float radius,
433 Point2D focus,
434 float[] fractions, Color[] colors,
435 CycleMethod cycleMethod,
436 ColorSpaceType colorSpace,
437 AffineTransform gradientTransform)
438 {
439 super(fractions, colors, cycleMethod, colorSpace, gradientTransform);
440
441 // check input arguments
442 if (center == null) {
443 throw new NullPointerException("Center point must be non-null");
444 }
445
446 if (focus == null) {
447 throw new NullPointerException("Focus point must be non-null");
448 }
449
450 if (radius <= 0) {
451 throw new IllegalArgumentException("Radius must be greater " +
452 "than zero");
453 }
454
455 // copy parameters
456 this.center = new Point2D.Double(center.getX(), center.getY());
457 this.focus = new Point2D.Double(focus.getX(), focus.getY());
458 this.radius = radius;
459 }
460
461 /**
462 * Constructs a {@code RadialGradientPaint} with a default
463 * {@code SRGB} color space.
464 * The gradient circle of the {@code RadialGradientPaint} is defined
465 * by the given bounding box.
466 * <p>
467 * This constructor is a more convenient way to express the
468 * following (equivalent) code:<br>
469 *
470 * <pre>
471 * double gw = gradientBounds.getWidth();
472 * double gh = gradientBounds.getHeight();
473 * double cx = gradientBounds.getCenterX();
474 * double cy = gradientBounds.getCenterY();
475 * Point2D center = new Point2D.Double(cx, cy);
476 *
477 * AffineTransform gradientTransform = new AffineTransform();
478 * gradientTransform.translate(cx, cy);
479 * gradientTransform.scale(gw / 2, gh / 2);
480 * gradientTransform.translate(-cx, -cy);
481 *
482 * RadialGradientPaint gp =
483 * new RadialGradientPaint(center, 1.0f, center,
484 * fractions, colors,
485 * cycleMethod,
486 * ColorSpaceType.SRGB,
487 * gradientTransform);
488 * </pre>
489 *
490 * @param gradientBounds the bounding box, in user space, of the circle
491 * defining the outermost extent of the gradient
492 * @param fractions numbers ranging from 0.0 to 1.0 specifying the
493 * distribution of colors along the gradient
494 * @param colors array of colors to use in the gradient. The first color
495 * is used at the focus point, the last color around the
496 * perimeter of the circle.
497 * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
498 * or {@code REPEAT}
499 *
500 * @throws NullPointerException
501 * if {@code gradientBounds} is null,
502 * or {@code fractions} array is null,
503 * or {@code colors} array is null,
504 * or {@code cycleMethod} is null
505 * @throws IllegalArgumentException
506 * if {@code gradientBounds} is empty,
507 * or {@code fractions.length != colors.length},
508 * or {@code colors} is less than 2 in size,
509 * or a {@code fractions} value is less than 0.0 or greater than 1.0,
510 * or the {@code fractions} are not provided in strictly increasing order
511 */
512 public RadialGradientPaint(Rectangle2D gradientBounds,
513 float[] fractions, Color[] colors,
514 CycleMethod cycleMethod)
515 {
516 // gradient center/focal point is the center of the bounding box,
517 // radius is set to 1.0, and then we set a scale transform
518 // to achieve an elliptical gradient defined by the bounding box
519 this(new Point2D.Double(gradientBounds.getCenterX(),
520 gradientBounds.getCenterY()),
521 1.0f,
522 new Point2D.Double(gradientBounds.getCenterX(),
523 gradientBounds.getCenterY()),
524 fractions,
525 colors,
526 cycleMethod,
527 ColorSpaceType.SRGB,
528 createGradientTransform(gradientBounds));
529
530 if (gradientBounds.isEmpty()) {
531 throw new IllegalArgumentException("Gradient bounds must be " +
532 "non-empty");
533 }
534 }
535
536 private static AffineTransform createGradientTransform(Rectangle2D r) {
537 double cx = r.getCenterX();
538 double cy = r.getCenterY();
539 AffineTransform xform = AffineTransform.getTranslateInstance(cx, cy);
540 xform.scale(r.getWidth()/2, r.getHeight()/2);
541 xform.translate(-cx, -cy);
542 return xform;
543 }
544
545 /**
546 * {@inheritDoc}
547 */
548 public PaintContext createContext(ColorModel cm,
549 Rectangle deviceBounds,
550 Rectangle2D userBounds,
551 AffineTransform transform,
552 RenderingHints hints)
553 {
554 // avoid modifying the user's transform...
555 transform = new AffineTransform(transform);
556 // incorporate the gradient transform
557 transform.concatenate(gradientTransform);
558
559 return new RadialGradientPaintContext(this, cm,
560 deviceBounds, userBounds,
561 transform, hints,
562 (float)center.getX(),
563 (float)center.getY(),
564 radius,
565 (float)focus.getX(),
566 (float)focus.getY(),
567 fractions, colors,
568 cycleMethod, colorSpace);
569 }
570
571 /**
572 * Returns a copy of the center point of the radial gradient.
573 *
574 * @return a {@code Point2D} object that is a copy of the center point
575 */
576 public Point2D getCenterPoint() {
577 return new Point2D.Double(center.getX(), center.getY());
578 }
579
580 /**
581 * Returns a copy of the end point of the gradient axis.
582 *
583 * @return a {@code Point2D} object that is a copy of the focus point
584 */
585 public Point2D getFocusPoint() {
586 return new Point2D.Double(focus.getX(), focus.getY());
587 }
588
589 /**
590 * Returns the radius of the circle defining the radial gradient.
591 *
592 * @return the radius of the circle defining the radial gradient
593 */
594 public float getRadius() {
595 return radius;
596 }
597}