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