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 java.awt.Color; 012import java.awt.PaintContext; 013import java.awt.Rectangle; 014import java.awt.RenderingHints; 015import java.awt.geom.AffineTransform; 016import java.awt.geom.NoninvertibleTransformException; 017import java.awt.geom.Point2D; 018import java.awt.geom.Rectangle2D; 019import java.awt.image.ColorModel; 020 021/** 022 * <p> 023 * This class provides a way to fill a shape with a circular radial color 024 * gradient pattern. The user may specify 2 or more gradient colors, and this 025 * paint will provide an interpolation between each color. 026 * <p> 027 * 028 * The user must provide an array of floats specifying how to distribute the 029 * colors along the gradient. These values should range from 0.0 to 1.0 and 030 * act like keyframes along the gradient (they mark where the gradient should 031 * be exactly a particular color). 032 * 033 * <p> 034 * This paint will map the first color of the gradient to a focus point within 035 * the circle, and the last color to the perimeter of the circle, interpolating 036 * smoothly for any inbetween colors specified by the user. Any line drawn 037 * from the focus point to the circumference will span the all the gradient 038 * colors. By default the focus is set to be the center of the circle. 039 * 040 * <p> 041 * Specifying a focus point outside of the circle's radius will result in the 042 * focus being set to the intersection point of the focus-center line and the 043 * perimenter of the circle. 044 * <p> 045 * 046 * Specifying a cycle method allows the user to control the painting behavior 047 * outside of the bounds of the circle's radius. See LinearGradientPaint for 048 * more details. 049 * 050 * <p> 051 * The following code demonstrates typical usage of RadialGradientPaint: 052 * <p> 053 * <code> 054 * Point2D center = new Point2D.Float(0, 0);<br> 055 * float radius = 20; 056 * float[] dist = {0.0, 0.2, 1.0};<br> 057 * Color[] colors = {Color.red, Color.white, Color.blue};<br> 058 * RadialGradientPaint p = new RadialGradientPaint(center, radius, 059 * dist, colors); 060 * </code> 061 * 062 * <p> In the event that the user does not set the first keyframe value equal 063 * to 0 and the last keyframe value equal to 1, keyframes will be created at 064 * these positions and the first and last colors will be replicated there. 065 * So, if a user specifies the following arrays to construct a gradient:<br> 066 * {Color.blue, Color.red}, {.3, .7}<br> 067 * this will be converted to a gradient with the following keyframes: 068 * {Color.blue, Color.blue, Color.red, Color.red}, {0, .3, .7, 1} 069 * 070 * 071 * <p> 072 * <img src = "radial.jpg"> 073 * <p> 074 * This image demonstrates a radial gradient with NO_CYCLE and default focus. 075 * <p> 076 * 077 * <img src = "radial2.jpg"> 078 * <p> 079 * This image demonstrates a radial gradient with NO_CYCLE and non-centered 080 * focus. 081 * <p> 082 * 083 * <img src = "radial3.jpg"> 084 * <p> 085 * This image demonstrates a radial gradient with REFLECT and non-centered 086 * focus. 087 * 088 * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans 089 * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a> 090 * @version $Id: RadialGradientPaint.java,v 1.1 2004/09/06 19:35:39 kitfox Exp $ 091 * 092 */ 093 094public final class RadialGradientPaint extends MultipleGradientPaint { 095 096 /** Focus point which defines the 0% gradient stop x coordinate. */ 097 private Point2D focus; 098 099 /** Center of the circle defining the 100% gradient stop x coordinate. */ 100 private Point2D center; 101 102 /** Radius of the outermost circle defining the 100% gradient stop. */ 103 private float radius; 104 105 /** 106 * <p> 107 * 108 * Constructs a <code>RadialGradientPaint</code>, using the center as the 109 * focus point. 110 * 111 * @param cx the x coordinate in user space of the center point of the 112 * circle defining the gradient. The last color of the gradient is mapped 113 * to the perimeter of this circle 114 * 115 * @param cy the y coordinate in user space of the center point of the 116 * circle defining the gradient. The last color of the gradient is mapped 117 * to the perimeter of this circle 118 * 119 * @param radius the radius of the circle defining the extents of the 120 * color gradient 121 * 122 * @param fractions numbers ranging from 0.0 to 1.0 specifying the 123 * distribution of colors along the gradient 124 * 125 * @param colors array of colors to use in the gradient. The first color 126 * is used at the focus point, the last color around the perimeter of the 127 * circle. 128 * 129 * 130 * @throws IllegalArgumentException 131 * if fractions.length != colors.length, or if colors is less 132 * than 2 in size, or if radius < 0 133 * 134 * 135 */ 136 public RadialGradientPaint(float cx, float cy, float radius, 137 float[] fractions, Color[] colors) { 138 this(cx, cy, 139 radius, 140 cx, cy, 141 fractions, 142 colors); 143 } 144 145 /** 146 * <p> 147 * 148 * Constructs a <code>RadialGradientPaint</code>, using the center as the 149 * focus point. 150 * 151 * @param center the center point, in user space, of the circle defining 152 * the gradient 153 * 154 * @param radius the radius of the circle defining the extents of the 155 * color gradient 156 * 157 * @param fractions numbers ranging from 0.0 to 1.0 specifying the 158 * distribution of colors along the gradient 159 * 160 * @param colors array of colors to use in the gradient. The first color 161 * is used at the focus point, the last color around the perimeter of the 162 * circle. 163 * 164 * @throws NullPointerException if center point is null 165 * 166 * @throws IllegalArgumentException 167 * if fractions.length != colors.length, or if colors is less 168 * than 2 in size, or if radius < 0 169 * 170 * 171 */ 172 public RadialGradientPaint(Point2D center, float radius, 173 float[] fractions, Color[] colors) { 174 this(center, 175 radius, 176 center, 177 fractions, 178 colors); 179 } 180 181 /** 182 * <p> 183 * 184 * Constructs a <code>RadialGradientPaint</code>. 185 * 186 * @param cx the x coordinate in user space of the center point of the 187 * circle defining the gradient. The last color of the gradient is mapped 188 * to the perimeter of this circle 189 * 190 * @param cy the y coordinate in user space of the center point of the 191 * circle defining the gradient. The last color of the gradient is mapped 192 * to the perimeter of this circle 193 * 194 * @param radius the radius of the circle defining the extents of the 195 * color gradient 196 * 197 * @param fx the x coordinate of the point in user space to which the 198 * first color is mapped 199 * 200 * @param fy the y coordinate of the point in user space to which the 201 * first color is mapped 202 * 203 * @param fractions numbers ranging from 0.0 to 1.0 specifying the 204 * distribution of colors along the gradient 205 * 206 * @param colors array of colors to use in the gradient. The first color 207 * is used at the focus point, the last color around the perimeter of the 208 * circle. 209 * 210 * @throws IllegalArgumentException 211 * if fractions.length != colors.length, or if colors is less 212 * than 2 in size, or if radius < 0 213 * 214 * 215 */ 216 public RadialGradientPaint(float cx, float cy, float radius, 217 float fx, float fy, 218 float[] fractions, Color[] colors) { 219 this(new Point2D.Float(cx, cy), 220 radius, 221 new Point2D.Float(fx, fy), 222 fractions, 223 colors, 224 NO_CYCLE, 225 SRGB); 226 } 227 228 /** 229 * <p> 230 * 231 * Constructs a <code>RadialGradientPaint</code>. 232 * 233 * @param center the center point, in user space, of the circle defining 234 * the gradient. The last color of the gradient is mapped to the perimeter 235 * of this circle 236 * 237 * @param radius the radius of the circle defining the extents of the color 238 * gradient 239 * 240 * @param focus the point, in user space, to which the first color is 241 * mapped 242 * 243 * @param fractions numbers ranging from 0.0 to 1.0 specifying the 244 * distribution of colors along the gradient 245 * 246 * @param colors array of colors to use in the gradient. The first color 247 * is used at the focus point, the last color around the perimeter of the 248 * circle. 249 * 250 * @throws NullPointerException if one of the points is null 251 * 252 * @throws IllegalArgumentException 253 * if fractions.length != colors.length, or if colors is less 254 * than 2 in size, or if radius < 0 255 * 256 */ 257 public RadialGradientPaint(Point2D center, float radius, 258 Point2D focus, 259 float[] fractions, Color[] colors) { 260 this(center, 261 radius, 262 focus, 263 fractions, 264 colors, 265 NO_CYCLE, 266 SRGB); 267 } 268 269 /** 270 * <p> 271 * 272 * Constructs a <code>RadialGradientPaint</code>. 273 * 274 * @param center the center point in user space of the circle defining the 275 * gradient. The last color of the gradient is mapped to the perimeter of 276 * this circle 277 * 278 * @param radius the radius of the circle defining the extents of the color 279 * gradient 280 * 281 * @param focus the point in user space to which the first color is mapped 282 * 283 * @param fractions numbers ranging from 0.0 to 1.0 specifying the 284 * distribution of colors along the gradient 285 * 286 * @param colors array of colors to use in the gradient. The first color is 287 * used at the focus point, the last color around the perimeter of the 288 * circle. 289 * 290 * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT 291 * 292 * @param colorSpace which colorspace to use for interpolation, 293 * either SRGB or LINEAR_RGB 294 * 295 * @throws NullPointerException if one of the points is null 296 * 297 * @throws IllegalArgumentException 298 * if fractions.length != colors.length, or if colors is less 299 * than 2 in size, or if radius < 0 300 * 301 */ 302 public RadialGradientPaint(Point2D center, float radius, 303 Point2D focus, 304 float[] fractions, Color[] colors, 305 CycleMethodEnum cycleMethod, 306 ColorSpaceEnum colorSpace) { 307 this(center, 308 radius, 309 focus, 310 fractions, 311 colors, 312 cycleMethod, 313 colorSpace, 314 new AffineTransform()); 315 } 316 317 /** 318 * <p> 319 * 320 * Constructs a <code>RadialGradientPaint</code>. 321 * 322 * @param center the center point in user space of the circle defining the 323 * gradient. The last color of the gradient is mapped to the perimeter of 324 * this circle 325 * 326 * @param radius the radius of the circle defining the extents of the color 327 * gradient. 328 * 329 * @param focus the point in user space to which the first color is mapped 330 * 331 * @param fractions numbers ranging from 0.0 to 1.0 specifying the 332 * distribution of colors along the gradient 333 * 334 * @param colors array of colors to use in the gradient. The first color is 335 * used at the focus point, the last color around the perimeter of the 336 * circle. 337 * 338 * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT 339 * 340 * @param colorSpace which colorspace to use for interpolation, 341 * either SRGB or LINEAR_RGB 342 * 343 * @param gradientTransform transform to apply to the gradient 344 * 345 * @throws NullPointerException if one of the points is null, 346 * or gradientTransform is null 347 * 348 * @throws IllegalArgumentException 349 * if fractions.length != colors.length, or if colors is less 350 * than 2 in size, or if radius < 0 351 * 352 */ 353 public RadialGradientPaint(Point2D center, 354 float radius, 355 Point2D focus, 356 float[] fractions, Color[] colors, 357 CycleMethodEnum cycleMethod, 358 ColorSpaceEnum colorSpace, 359 AffineTransform gradientTransform){ 360 super(fractions, colors, cycleMethod, colorSpace, gradientTransform); 361 362 // Check input arguments 363 if (center == null) { 364 throw new NullPointerException("Center point should not be null."); 365 } 366 367 if (focus == null) { 368 throw new NullPointerException("Focus point should not be null."); 369 } 370 371 if (radius <= 0) { 372 throw new IllegalArgumentException("radius should be greater than zero"); 373 } 374 375 //copy parameters 376 this.center = (Point2D)center.clone(); 377 this.focus = (Point2D)focus.clone(); 378 this.radius = radius; 379 } 380 381 /** 382 * <p> 383 * 384 * Constructs a <code>RadialGradientPaint</code>, the gradient circle is 385 * defined by a bounding box. 386 * 387 * @param gradientBounds the bounding box, in user space, of the circle 388 * defining outermost extent of the gradient. 389 * 390 * @param fractions numbers ranging from 0.0 to 1.0 specifying the 391 * distribution of colors along the gradient 392 * 393 * @param colors array of colors to use in the gradient. The first color 394 * is used at the focus point, the last color around the perimeter of the 395 * circle. 396 * 397 * @throws NullPointerException if the gradientBounds is null 398 * 399 * @throws IllegalArgumentException 400 * if fractions.length != colors.length, or if colors is less 401 * than 2 in size, or if radius < 0 402 * 403 */ 404 public RadialGradientPaint(Rectangle2D gradientBounds, 405 float[] fractions, Color[] colors) { 406 407 //calculate center point and radius based on bounding box coordinates. 408 this((float)gradientBounds.getX() + 409 ( (float)gradientBounds.getWidth() / 2), 410 411 (float)gradientBounds.getY() + 412 ( (float)gradientBounds.getWidth() / 2), 413 414 (float)gradientBounds.getWidth() / 2, 415 fractions, colors); 416 } 417 418 419 /** <p> 420 * Creates and returns a PaintContext used to generate the color pattern, 421 * for use by the internal rendering engine. 422 * 423 * @param cm {@link ColorModel} that receives 424 * the <code>Paint</code> data. This is used only as a hint. 425 * 426 * @param deviceBounds the device space bounding box of the 427 * graphics primitive being rendered 428 * 429 * @param userBounds the user space bounding box of the 430 * graphics primitive being rendered 431 * 432 * @param transform the {@link AffineTransform} from user 433 * space into device space 434 * 435 * @param hints the hints that the context object uses to choose 436 * between rendering alternatives 437 * 438 * @return the {@link PaintContext} that generates color patterns. 439 * 440 * @throws IllegalArgumentException if the transform is not invertible 441 * 442 * @see PaintContext 443 */ 444 public PaintContext createContext(ColorModel cm, 445 Rectangle deviceBounds, 446 Rectangle2D userBounds, 447 AffineTransform transform, 448 RenderingHints hints) { 449 // Can't modify the transform passed in... 450 transform = new AffineTransform(transform); 451 // incorporate the gradient transform 452 transform.concatenate(gradientTransform); 453 454 try{ 455 return new RadialGradientPaintContext 456 (cm, deviceBounds, userBounds, transform, hints, 457 (float)center.getX(), (float)center.getY(), radius, 458 (float)focus.getX(), (float)focus.getY(), 459 fractions, colors, cycleMethod, colorSpace); 460 } 461 462 catch(NoninvertibleTransformException e){ 463 throw new IllegalArgumentException("transform should be " + 464 "invertible"); 465 } 466 } 467 468 /** 469 * Returns a copy of the center point of the radial gradient. 470 * @return a {@link Point2D} object that is a copy of the center point 471 */ 472 public Point2D getCenterPoint() { 473 return new Point2D.Double(center.getX(), center.getY()); 474 } 475 476 /** Returns a copy of the end point of the gradient axis. 477 * @return a {@link Point2D} object that is a copy of the focus point 478 */ 479 public Point2D getFocusPoint() { 480 return new Point2D.Double(focus.getX(), focus.getY()); 481 } 482 483 /** Returns the radius of the circle defining the radial gradient. 484 * @return the radius of the circle defining the radial gradient 485 */ 486 public float getRadius() { 487 return radius; 488 } 489 490} 491