Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.effect; 027 028import javafx.beans.property.DoubleProperty; 029import javafx.beans.property.DoublePropertyBase; 030import javafx.beans.property.ObjectProperty; 031import javafx.beans.property.ObjectPropertyBase; 032import javafx.scene.Node; 033import javafx.scene.paint.Color; 034 035import com.sun.javafx.Utils; 036import com.sun.javafx.effect.EffectDirtyBits; 037import com.sun.javafx.effect.EffectUtils; 038import com.sun.javafx.geom.BaseBounds; 039import com.sun.javafx.geom.transform.BaseTransform; 040import com.sun.javafx.scene.BoundsAccessor; 041import com.sun.javafx.tk.Toolkit; 042 043 044/** 045 * A high-level effect that renders a shadow of the given content behind 046 * the content with the specified color, radius, and offset. 047 * 048 * <p> 049 * Example: 050 * <pre><code> 051 052 * DropShadow dropShadow = new DropShadow(); 053 * dropShadow.setRadius(5.0); 054 * dropShadow.setOffsetX(3.0); 055 * dropShadow.setOffsetY(3.0); 056 * dropShadow.setColor(Color.color(0.4, 0.5, 0.5)); * 057 * 058 * Text text = new Text(); 059 * text.setEffect(dropShadow); 060 * text.setCache(true); 061 * text.setX(10.0); 062 * text.setY(70.0); 063 * text.setFill(Color.web("0x3b596d")); 064 * text.setText("JavaFX drop shadow..."); 065 * text.setFont(Font.font(null, FontWeight.BOLD, 40)); 066 * 067 * DropShadow dropShadow2 = new DropShadow(); 068 * dropShadow2.setOffsetX(6.0); 069 * dropShadow2.setOffsetY(4.0); 070 * 071 * Circle circle = new Circle(); 072 * circle.setEffect(dropShadow2); 073 * circle.setCenterX(50.0); 074 * circle.setCenterY(125.0); 075 * circle.setRadius(30.0); 076 * circle.setFill(Color.STEELBLUE); 077 * circle.setCache(true); 078 * </pre></code> 079 * 080 * <p> 081 * The code above produces the following: 082 * </p> 083 * <p> 084 * <img src="doc-files/dropshadow.png"/> 085 * </p> 086 */ 087public class DropShadow extends Effect { 088 private boolean changeIsLocal; 089 090 /** 091 * Creates a new instance of DropShadow with default parameters. 092 */ 093 public DropShadow() {} 094 095 /** 096 * Creates a new instance of DropShadow with specified radius and color. 097 * @param radius the radius of the shadow blur kernel 098 * @param color the shadow {@code Color} 099 */ 100 public DropShadow(double radius, Color color) { 101 setRadius(radius); 102 setColor(color); 103 } 104 105 /** 106 * Creates a new instance of DropShadow with the specified radius, offsetX, 107 * offsetY and color. 108 * @param radius the radius of the shadow blur kernel 109 * @param offsetX the shadow offset in the x direction 110 * @param offsetY the shadow offset in the y direction 111 * @param color the shadow {@code Color} 112 */ 113 public DropShadow(double radius, double offsetX, double offsetY, Color color) { 114 setRadius(radius); 115 setOffsetX(offsetX); 116 setOffsetY(offsetY); 117 setColor(color); 118 } 119 120 /** 121 * Creates a new instance of DropShadow with the specified blurType, color, 122 * radius, spread, offsetX and offsetY. 123 * @param blurType the algorithm used to blur the shadow 124 * @param color the shadow {@code Color} 125 * @param radius the radius of the shadow blur kernel 126 * @param spread the portion of the radius where the contribution of 127 * the source material will be 100% 128 * @param offsetX the shadow offset in the x direction 129 * @param offsetY the shadow offset in the y direction 130 */ 131 public DropShadow(BlurType blurType, Color color, double radius, double spread, 132 double offsetX, double offsetY) { 133 setBlurType(blurType); 134 setColor(color); 135 setRadius(radius); 136 setSpread(spread); 137 setOffsetX(offsetX); 138 setOffsetY(offsetY); 139 } 140 141 @Override 142 com.sun.scenario.effect.DropShadow impl_createImpl() { 143 return new com.sun.scenario.effect.DropShadow(); 144 }; 145 /** 146 * The input for this {@code Effect}. 147 * If set to {@code null}, or left unspecified, a graphical image of 148 * the {@code Node} to which the {@code Effect} is attached will be 149 * used as the input. 150 * @defaultValue null 151 * @since JavaFX 1.3 152 */ 153 private ObjectProperty<Effect> input; 154 155 156 public final void setInput(Effect value) { 157 inputProperty().set(value); 158 } 159 160 public final Effect getInput() { 161 return input == null ? null : input.get(); 162 } 163 164 public final ObjectProperty<Effect> inputProperty() { 165 if (input == null) { 166 input = new EffectInputProperty("input"); 167 } 168 return input; 169 } 170 171 @Override 172 boolean impl_checkChainContains(Effect e) { 173 Effect localInput = getInput(); 174 if (localInput == null) 175 return false; 176 if (localInput == e) 177 return true; 178 return localInput.impl_checkChainContains(e); 179 } 180 181 /** 182 * The radius of the shadow blur kernel. 183 * This attribute controls the distance that the shadow is spread 184 * to each side of the source pixels. 185 * Setting the radius is equivalent to setting both the {@code width} 186 * and {@code height} attributes to a value of {@code (2 * radius + 1)}. 187 * <pre> 188 * Min: 0.0 189 * Max: 127.0 190 * Default: 10.0 191 * Identity: 0.0 192 * </pre> 193 * @defaultValue 10.0 194 */ 195 private DoubleProperty radius; 196 197 198 public final void setRadius(double value) { 199 radiusProperty().set(value); 200 } 201 202 public final double getRadius() { 203 return radius == null ? 10 : radius.get(); 204 } 205 206 public final DoubleProperty radiusProperty() { 207 if (radius == null) { 208 radius = new DoublePropertyBase(10) { 209 210 @Override 211 public void invalidated() { 212 // gettter here is necessary to make the property valid 213 double localRadius = getRadius(); 214 if (!changeIsLocal) { 215 changeIsLocal = true; 216 updateRadius(localRadius); 217 changeIsLocal = false; 218 markDirty(EffectDirtyBits.EFFECT_DIRTY); 219 effectBoundsChanged(); 220 } 221 } 222 223 @Override 224 public Object getBean() { 225 return DropShadow.this; 226 } 227 228 @Override 229 public String getName() { 230 return "radius"; 231 } 232 }; 233 } 234 return radius; 235 } 236 237 private void updateRadius(double value) { 238 double newdim = (value * 2 + 1); 239 if (width != null && width.isBound()) { 240 // if neither is readonly we would set both width and 241 // height to radius*2+1 (i.e. newdim), but if one of 242 // them is bound then we need to set the other to a 243 // value that would map back to the radius we have been 244 // given. 245 // To do that we equate the average of the two values 246 // to newdim, the value we want them both to have, and 247 // then solve for the missing value: 248 // avg(w,h) == radius * 2 + 1 249 // (w+h)/2 == newdim 250 // w+h == newdim * 2 251 // h = newdim * 2 - w 252 // w = newdim * 2 - h 253 if (height == null || !height.isBound()) { 254 setHeight(newdim * 2 - getWidth()); 255 } 256 } else if (height != null && height.isBound()) { 257 setWidth(newdim * 2 - getHeight()); 258 } else { 259 setWidth(newdim); 260 setHeight(newdim); 261 } 262 } 263 264 /** 265 * The horizontal size of the shadow blur kernel. 266 * This attribute controls the horizontal size of the total area over 267 * which the shadow of a single pixel is distributed by the blur algorithm. 268 * Values less than {@code 1.0} are not distributed beyond the original 269 * pixel and so have no blurring effect on the shadow. 270 * <pre> 271 * Min: 0.0 272 * Max: 255.0 273 * Default: 21.0 274 * Identity: <1.0 275 * </pre> 276 * @defaultValue 21.0 277 */ 278 private DoubleProperty width; 279 280 281 public final void setWidth(double value) { 282 widthProperty().set(value); 283 } 284 285 public final double getWidth() { 286 return width == null ? 21 : width.get(); 287 } 288 289 public final DoubleProperty widthProperty() { 290 if (width == null) { 291 width = new DoublePropertyBase(21) { 292 293 @Override 294 public void invalidated() { 295 // gettter here is necessary to make the property valid 296 double localWidth = getWidth(); 297 if (!changeIsLocal) { 298 changeIsLocal = true; 299 updateWidth(localWidth); 300 changeIsLocal = false; 301 markDirty(EffectDirtyBits.EFFECT_DIRTY); 302 effectBoundsChanged(); 303 } 304 } 305 306 @Override 307 public Object getBean() { 308 return DropShadow.this; 309 } 310 311 @Override 312 public String getName() { 313 return "width"; 314 } 315 }; 316 } 317 return width; 318 } 319 320 private void updateWidth(double value) { 321 if (radius == null || !radius.isBound()) { 322 double newrad = ((value + getHeight()) / 2); 323 newrad = ((newrad - 1) / 2); 324 if (newrad < 0) { 325 newrad = 0; 326 } 327 setRadius(newrad); 328 } else { 329 if (height == null || !height.isBound()) { 330 double newdim = (getRadius() * 2 + 1); 331 setHeight(newdim * 2 - value); 332 } 333 } 334 } 335 336 /** 337 * The vertical size of the shadow blur kernel. 338 * This attribute controls the vertical size of the total area over 339 * which the shadow of a single pixel is distributed by the blur algorithm. 340 * Values less than {@code 1.0} are not distributed beyond the original 341 * pixel and so have no blurring effect on the shadow. 342 * <pre> 343 * Min: 0.0 344 * Max: 255.0 345 * Default: 21.0 346 * Identity: <1.0 347 * </pre> 348 * @defaultValue 21.0 349 */ 350 private DoubleProperty height; 351 352 353 public final void setHeight(double value) { 354 heightProperty().set(value); 355 } 356 357 public final double getHeight() { 358 return height == null ? 21 : height.get(); 359 } 360 361 public final DoubleProperty heightProperty() { 362 if (height == null) { 363 height = new DoublePropertyBase(21) { 364 365 @Override 366 public void invalidated() { 367 // gettter here is necessary to make the property valid 368 double localHeight = getHeight(); 369 if (!changeIsLocal) { 370 changeIsLocal = true; 371 updateHeight(localHeight); 372 changeIsLocal = false; 373 markDirty(EffectDirtyBits.EFFECT_DIRTY); 374 effectBoundsChanged(); 375 } 376 } 377 378 @Override 379 public Object getBean() { 380 return DropShadow.this; 381 } 382 383 @Override 384 public String getName() { 385 return "height"; 386 } 387 }; 388 } 389 return height; 390 } 391 392 private void updateHeight(double value) { 393 if (radius == null || !radius.isBound()) { 394 double newrad = ((getWidth() + value) / 2); 395 newrad = ((newrad - 1) / 2); 396 if (newrad < 0) { 397 newrad = 0; 398 } 399 setRadius(newrad); 400 } else { 401 if (width == null || !width.isBound()) { 402 double newdim = (getRadius() * 2 + 1); 403 setWidth(newdim * 2 - value); 404 } 405 } 406 } 407 408 /** 409 * The algorithm used to blur the shadow. 410 * <pre> 411 * Min: n/a 412 * Max: n/a 413 * Default: BlurType.THREE_PASS_BOX 414 * Identity: n/a 415 * </pre> 416 * @defaultValue THREE_PASS_BOX 417 */ 418 private ObjectProperty<BlurType> blurType; 419 420 421 public final void setBlurType(BlurType value) { 422 blurTypeProperty().set(value); 423 } 424 425 public final BlurType getBlurType() { 426 return blurType == null ? BlurType.THREE_PASS_BOX : blurType.get(); 427 } 428 429 public final ObjectProperty<BlurType> blurTypeProperty() { 430 if (blurType == null) { 431 blurType = new ObjectPropertyBase<BlurType>(BlurType.THREE_PASS_BOX) { 432 433 @Override 434 public void invalidated() { 435 markDirty(EffectDirtyBits.EFFECT_DIRTY); 436 effectBoundsChanged(); 437 } 438 439 @Override 440 public Object getBean() { 441 return DropShadow.this; 442 } 443 444 @Override 445 public String getName() { 446 return "blurType"; 447 } 448 }; 449 } 450 return blurType; 451 } 452 453 /** 454 * The spread of the shadow. 455 * The spread is the portion of the radius where the contribution of 456 * the source material will be 100%. 457 * The remaining portion of the radius will have a contribution 458 * controlled by the blur kernel. 459 * A spread of {@code 0.0} will result in a distribution of the 460 * shadow determined entirely by the blur algorithm. 461 * A spread of {@code 1.0} will result in a solid growth outward of the 462 * source material opacity to the limit of the radius with a very sharp 463 * cutoff to transparency at the radius. 464 * <pre> 465 * Min: 0.0 466 * Max: 1.0 467 * Default: 0.0 468 * Identity: 0.0 469 * </pre> 470 * @defaultValue 0.0 471 */ 472 private DoubleProperty spread; 473 474 475 public final void setSpread(double value) { 476 spreadProperty().set(value); 477 } 478 479 public final double getSpread() { 480 return spread == null ? 0.0 : spread.get(); 481 } 482 483 public final DoubleProperty spreadProperty() { 484 if (spread == null) { 485 spread = new DoublePropertyBase() { 486 487 @Override 488 public void invalidated() { 489 markDirty(EffectDirtyBits.EFFECT_DIRTY); 490 } 491 492 @Override 493 public Object getBean() { 494 return DropShadow.this; 495 } 496 497 @Override 498 public String getName() { 499 return "spread"; 500 } 501 }; 502 } 503 return spread; 504 } 505 506 /** 507 * The shadow {@code Color}. 508 * <pre> 509 * Min: n/a 510 * Max: n/a 511 * Default: Color.BLACK 512 * Identity: n/a 513 * </pre> 514 * @defaultValue BLACK 515 */ 516 private ObjectProperty<Color> color; 517 518 519 public final void setColor(Color value) { 520 colorProperty().set(value); 521 } 522 523 public final Color getColor() { 524 return color == null ? Color.BLACK : color.get(); 525 } 526 527 public final ObjectProperty<Color> colorProperty() { 528 if (color == null) { 529 color = new ObjectPropertyBase<Color>(Color.BLACK) { 530 531 @Override 532 public void invalidated() { 533 markDirty(EffectDirtyBits.EFFECT_DIRTY); 534 } 535 536 @Override 537 public Object getBean() { 538 return DropShadow.this; 539 } 540 541 @Override 542 public String getName() { 543 return "color"; 544 } 545 }; 546 } 547 return color; 548 } 549 550 /** 551 * The shadow offset in the x direction, in pixels. 552 * <pre> 553 * Min: n/a 554 * Max: n/a 555 * Default: 0.0 556 * Identity: 0.0 557 * </pre> 558 * @defaultValue 0.0 559 */ 560 private DoubleProperty offsetX; 561 562 public final void setOffsetX(double value) { 563 offsetXProperty().set(value); 564 565 } 566 567 public final double getOffsetX() { 568 return offsetX == null ? 0 : offsetX.get(); 569 } 570 571 public final DoubleProperty offsetXProperty() { 572 if (offsetX == null) { 573 offsetX = new DoublePropertyBase() { 574 575 @Override 576 public void invalidated() { 577 markDirty(EffectDirtyBits.EFFECT_DIRTY); 578 effectBoundsChanged(); 579 } 580 581 @Override 582 public Object getBean() { 583 return DropShadow.this; 584 } 585 586 @Override 587 public String getName() { 588 return "offsetX"; 589 } 590 }; 591 } 592 return offsetX; 593 } 594 595 /** 596 * The shadow offset in the y direction, in pixels. 597 * <pre> 598 * Min: n/a 599 * Max: n/a 600 * Default: 0.0 601 * Identity: 0.0 602 * </pre> 603 * @defaultValue 0.0 604 */ 605 private DoubleProperty offsetY; 606 607 608 public final void setOffsetY(double value) { 609 offsetYProperty().set(value); 610 } 611 612 public final double getOffsetY() { 613 return offsetY == null ? 0 : offsetY.get(); 614 } 615 616 public final DoubleProperty offsetYProperty() { 617 if (offsetY == null) { 618 offsetY = new DoublePropertyBase() { 619 620 @Override 621 public void invalidated() { 622 markDirty(EffectDirtyBits.EFFECT_DIRTY); 623 effectBoundsChanged(); 624 } 625 626 @Override 627 public Object getBean() { 628 return DropShadow.this; 629 } 630 631 @Override 632 public String getName() { 633 return "offsetY"; 634 } 635 }; 636 } 637 return offsetY; 638 } 639 640 private float getClampedWidth() { 641 return (float) Utils.clamp(0, getWidth(), 255); 642 } 643 644 private float getClampedHeight() { 645 return (float) Utils.clamp(0, getHeight(), 255); 646 } 647 648 private float getClampedSpread() { 649 return (float) Utils.clamp(0, getSpread(), 1); 650 } 651 652 private Color getColorInternal() { 653 Color c = getColor(); 654 return c == null ? Color.BLACK : c; 655 } 656 657 private BlurType getBlurTypeInternal() { 658 BlurType bt = getBlurType(); 659 return bt == null ? BlurType.THREE_PASS_BOX : bt; 660 } 661 662 @Override 663 void impl_update() { 664 Effect localInput = getInput(); 665 if (localInput != null) { 666 localInput.impl_sync(); 667 } 668 669 com.sun.scenario.effect.DropShadow peer = 670 (com.sun.scenario.effect.DropShadow) impl_getImpl(); 671 peer.setShadowSourceInput(localInput == null ? null : localInput.impl_getImpl()); 672 peer.setContentInput(localInput == null ? null : localInput.impl_getImpl()); 673 peer.setGaussianWidth(getClampedWidth()); 674 peer.setGaussianHeight(getClampedHeight()); 675 peer.setSpread(getClampedSpread()); 676 peer.setShadowMode(Toolkit.getToolkit().toShadowMode(getBlurTypeInternal())); 677 peer.setColor(Toolkit.getToolkit().toColor4f(getColorInternal())); 678 peer.setOffsetX((int) getOffsetX()); 679 peer.setOffsetY((int) getOffsetY()); 680 } 681 682 /** 683 * @treatAsPrivate implementation detail 684 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 685 */ 686 @Deprecated 687 @Override 688 public BaseBounds impl_getBounds(BaseBounds bounds, 689 BaseTransform tx, 690 Node node, 691 BoundsAccessor boundsAccessor) { 692 bounds = EffectUtils.getInputBounds(bounds, 693 BaseTransform.IDENTITY_TRANSFORM, 694 node, boundsAccessor, 695 getInput()); 696 697 int shadowX = (int) getOffsetX(); 698 int shadowY = (int) getOffsetY(); 699 700 BaseBounds shadowBounds = BaseBounds.getInstance(bounds.getMinX() + shadowX, 701 bounds.getMinY() + shadowY, 702 bounds.getMinZ(), 703 bounds.getMaxX() + shadowX, 704 bounds.getMaxY() + shadowY, 705 bounds.getMaxZ()); 706 707 shadowBounds = EffectUtils.getShadowBounds(shadowBounds, tx, 708 getClampedWidth(), 709 getClampedHeight(), 710 getBlurTypeInternal()); 711 BaseBounds contentBounds = EffectUtils.transformBounds(tx, bounds); 712 BaseBounds ret = contentBounds.deriveWithUnion(shadowBounds); 713 714 return ret; 715 } 716 717 /** 718 * 719 * @treatAsPrivate implementation detail 720 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 721 */ 722 @Deprecated 723 @Override 724 public Effect impl_copy() { 725 DropShadow d = new DropShadow(this.getBlurType(), this.getColor(), 726 this.getRadius(), this.getSpread(), this.getOffsetX(), 727 this.getOffsetY()); 728 d.setInput(this.getInput()); 729 d.setWidth(this.getWidth()); 730 d.setHeight(this.getHeight()); 731 return d; 732 } 733}