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 * An effect which creates a monochrome duplicate of an input with 046 * blurry edges. 047 * This effect is primarily used along with its default black color for 048 * purposes of combining it with the original to create a shadow. 049 * It can also be used with a light color and combined with an original 050 * to create a glow effect. 051 * The {@link DropShadow} effect is a utility effect which automatically 052 * combines this {@code Shadow} effect with an original graphic for ease 053 * of adding a shadow to an existing scene graph {@code Node} with a 054 * single effect. 055 */ 056public class Shadow extends Effect { 057 private boolean changeIsLocal; 058 059 /** 060 * Creates a new instance of Shadow with default parameters. 061 */ 062 public Shadow() {} 063 064 /** 065 * Creates a new instance of Shadow with specified radius and color. 066 * @param radius the radius of the shadow blur kernel 067 * @param color the shadow {@code Color} 068 */ 069 public Shadow(double radius, Color color) { 070 setRadius(radius); 071 setColor(color); 072 } 073 074 /** 075 * Creates a new instance of Shadow with the specified blurType, color, 076 * radius. 077 * @param blurType the algorithm used to blur the shadow 078 * @param color the shadow {@code Color} 079 * @param radius the radius of the shadow blur kernel 080 */ 081 public Shadow(BlurType blurType, Color color, double radius) { 082 setBlurType(blurType); 083 setColor(color); 084 setRadius(radius); 085 } 086 087 @Override 088 com.sun.scenario.effect.GeneralShadow impl_createImpl() { 089 return new com.sun.scenario.effect.GeneralShadow(); 090 }; 091 /** 092 * The input for this {@code Effect}. 093 * If set to {@code null}, or left unspecified, a graphical image of 094 * the {@code Node} to which the {@code Effect} is attached will be 095 * used as the input. 096 * @defaultValue null 097 */ 098 private ObjectProperty<Effect> input; 099 100 101 public final void setInput(Effect value) { 102 inputProperty().set(value); 103 } 104 105 public final Effect getInput() { 106 return input == null ? null : input.get(); 107 } 108 109 public final ObjectProperty<Effect> inputProperty() { 110 if (input == null) { 111 input = new EffectInputProperty("input"); 112 } 113 return input; 114 } 115 116 @Override 117 boolean impl_checkChainContains(Effect e) { 118 Effect localInput = getInput(); 119 if (localInput == null) 120 return false; 121 if (localInput == e) 122 return true; 123 return localInput.impl_checkChainContains(e); 124 } 125 126 /** 127 * The radius of the shadow blur kernel. 128 * This attribute controls the distance that the shadow is spread 129 * to each side of the source pixels. 130 * Setting the radius is equivalent to setting both the {@code width} 131 * and {@code height} attributes to a value of {@code (2 * radius + 1)}. 132 * <pre> 133 * Min: 0.0 134 * Max: 127.0 135 * Default: 10.0 136 * Identity: 0.0 137 * </pre> 138 * @defaultValue 10.0 139 */ 140 private DoubleProperty radius; 141 142 143 public final void setRadius(double value) { 144 radiusProperty().set(value); 145 } 146 147 public final double getRadius() { 148 return radius == null ? 10 : radius.get(); 149 } 150 151 public final DoubleProperty radiusProperty() { 152 if (radius == null) { 153 radius = new DoublePropertyBase(10) { 154 155 @Override 156 public void invalidated() { 157 // gettter here is necessary to make the property valid 158 double localRadius = getRadius(); 159 if (!changeIsLocal) { 160 changeIsLocal = true; 161 updateRadius(localRadius); 162 changeIsLocal = false; 163 markDirty(EffectDirtyBits.EFFECT_DIRTY); 164 effectBoundsChanged(); 165 } 166 } 167 168 @Override 169 public Object getBean() { 170 return Shadow.this; 171 } 172 173 @Override 174 public String getName() { 175 return "radius"; 176 } 177 }; 178 } 179 return radius; 180 } 181 182 private void updateRadius(double value) { 183 double newdim = (value * 2 + 1); 184 if (width != null && width.isBound()) { 185 if (height == null || !height.isBound()) { 186 setHeight(newdim * 2 - getWidth()); 187 } 188 } else if (height != null && height.isBound()) { 189 setWidth(newdim * 2 - getHeight()); 190 } else { 191 setWidth(newdim); 192 setHeight(newdim); 193 } 194 } 195 196 /** 197 * The horizontal size of the shadow blur kernel. 198 * This attribute controls the horizontal size of the total area over 199 * which the shadow of a single pixel is distributed by the blur algorithm. 200 * Values less than {@code 1.0} are not distributed beyond the original 201 * pixel and so have no blurring effect on the shadow. 202 * <pre> 203 * Min: 0.0 204 * Max: 255.0 205 * Default: 21.0 206 * Identity: <1.0 207 * </pre> 208 * @defaultValue 21.0 209 */ 210 private DoubleProperty width; 211 212 213 public final void setWidth(double value) { 214 widthProperty().set(value); 215 } 216 217 public final double getWidth() { 218 return width == null ? 21 : width.get(); 219 } 220 221 public final DoubleProperty widthProperty() { 222 if (width == null) { 223 width = new DoublePropertyBase(21) { 224 225 @Override 226 public void invalidated() { 227 // gettter here is necessary to make the property valid 228 double localWidth = getWidth(); 229 if (!changeIsLocal) { 230 changeIsLocal = true; 231 updateWidth(localWidth); 232 changeIsLocal = false; 233 markDirty(EffectDirtyBits.EFFECT_DIRTY); 234 effectBoundsChanged(); 235 } 236 } 237 238 @Override 239 public Object getBean() { 240 return Shadow.this; 241 } 242 243 @Override 244 public String getName() { 245 return "width"; 246 } 247 }; 248 } 249 return width; 250 } 251 252 private void updateWidth(double value) { 253 if (radius == null || !radius.isBound()) { 254 double newrad = ((value + getHeight()) / 2); 255 newrad = ((newrad - 1) / 2); 256 if (newrad < 0) { 257 newrad = 0; 258 } 259 setRadius(newrad); 260 } else { 261 // special case when radius is bound 262 if (height == null || !height.isBound()) { 263 double newdim = (getRadius() * 2 + 1); 264 setHeight(newdim * 2 - value); 265 } 266 } 267 } 268 269 /** 270 * The vertical size of the shadow blur kernel. 271 * This attribute controls the vertical size of the total area over 272 * which the shadow of a single pixel is distributed by the blur algorithm. 273 * Values less than {@code 1.0} are not distributed beyond the original 274 * pixel and so have no blurring effect on the shadow. 275 * <pre> 276 * Min: 0.0 277 * Max: 255.0 278 * Default: 21.0 279 * Identity: <1.0 280 * </pre> 281 * @defaultValue 21.0 282 */ 283 private DoubleProperty height; 284 285 286 public final void setHeight(double value) { 287 heightProperty().set(value); 288 } 289 290 public final double getHeight() { 291 return height == null ? 21 : height.get(); 292 } 293 294 public final DoubleProperty heightProperty() { 295 if (height == null) { 296 height = new DoublePropertyBase(21) { 297 298 @Override 299 public void invalidated() { 300 // gettter here is necessary to make the property valid 301 double localHeight = getHeight(); 302 if (!changeIsLocal) { 303 changeIsLocal = true; 304 updateHeight(localHeight); 305 changeIsLocal = false; 306 markDirty(EffectDirtyBits.EFFECT_DIRTY); 307 effectBoundsChanged(); 308 } 309 } 310 311 @Override 312 public Object getBean() { 313 return Shadow.this; 314 } 315 316 @Override 317 public String getName() { 318 return "height"; 319 } 320 }; 321 } 322 return height; 323 } 324 325 private void updateHeight(double value) { 326 if (radius == null || !radius.isBound()) { 327 double newrad = ((getWidth() + value) / 2); 328 newrad = ((newrad - 1) / 2); 329 if (newrad < 0) { 330 newrad = 0; 331 } 332 setRadius(newrad); 333 } else { 334 if (width == null || !width.isBound()) { 335 double newdim = (getRadius() * 2 + 1); 336 setWidth(newdim * 2 - value); 337 } 338 } 339 } 340 341 /** 342 * The algorithm used to blur the shadow. 343 * <pre> 344 * Min: n/a 345 * Max: n/a 346 * Default: BlurType.THREE_PASS_BOX 347 * Identity: n/a 348 * </pre> 349 * @defaultValue THREE_PASS_BOX 350 */ 351 private ObjectProperty<BlurType> blurType; 352 353 354 public final void setBlurType(BlurType value) { 355 blurTypeProperty().set(value); 356 } 357 358 public final BlurType getBlurType() { 359 return blurType == null ? BlurType.THREE_PASS_BOX : blurType.get(); 360 } 361 362 public final ObjectProperty<BlurType> blurTypeProperty() { 363 if (blurType == null) { 364 blurType = new ObjectPropertyBase<BlurType>(BlurType.THREE_PASS_BOX) { 365 366 @Override 367 public void invalidated() { 368 markDirty(EffectDirtyBits.EFFECT_DIRTY); 369 effectBoundsChanged(); 370 } 371 372 @Override 373 public Object getBean() { 374 return Shadow.this; 375 } 376 377 @Override 378 public String getName() { 379 return "blurType"; 380 } 381 }; 382 } 383 return blurType; 384 } 385 386 /** 387 * The shadow {@code Color}. 388 * <pre> 389 * Min: n/a 390 * Max: n/a 391 * Default: Color.BLACK 392 * Identity: n/a 393 * </pre> 394 * @defaultValue BLACK 395 */ 396 private ObjectProperty<Color> color; 397 398 399 public final void setColor(Color value) { 400 colorProperty().set(value); 401 } 402 403 public final Color getColor() { 404 return color == null ? Color.BLACK : color.get(); 405 } 406 407 public final ObjectProperty<Color> colorProperty() { 408 if (color == null) { 409 color = new ObjectPropertyBase<Color>(Color.BLACK) { 410 411 @Override 412 public void invalidated() { 413 markDirty(EffectDirtyBits.EFFECT_DIRTY); 414 } 415 416 @Override 417 public Object getBean() { 418 return Shadow.this; 419 } 420 421 @Override 422 public String getName() { 423 return "color"; 424 } 425 }; 426 } 427 return color; 428 } 429 430 private float getClampedWidth() { 431 return (float) Utils.clamp(0, getWidth(), 255); 432 } 433 434 private float getClampedHeight() { 435 return (float) Utils.clamp(0, getHeight(), 255); 436 } 437 438 private Color getColorInternal() { 439 Color c = getColor(); 440 return c == null ? Color.BLACK : c; 441 } 442 443 private BlurType getBlurTypeInternal() { 444 BlurType bt = getBlurType(); 445 return bt == null ? BlurType.THREE_PASS_BOX : bt; 446 } 447 448 @Override 449 void impl_update() { 450 Effect localInput = getInput(); 451 if (localInput != null) { 452 localInput.impl_sync(); 453 } 454 455 com.sun.scenario.effect.GeneralShadow peer = 456 (com.sun.scenario.effect.GeneralShadow) impl_getImpl(); 457 peer.setInput(localInput == null ? null : localInput.impl_getImpl()); 458 peer.setGaussianWidth(getClampedWidth()); 459 peer.setGaussianHeight(getClampedHeight()); 460 peer.setShadowMode(Toolkit.getToolkit().toShadowMode(getBlurTypeInternal())); 461 peer.setColor(Toolkit.getToolkit().toColor4f(getColorInternal())); 462 } 463 464 /** 465 * @treatAsPrivate implementation detail 466 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 467 */ 468 @Deprecated 469 @Override 470 public BaseBounds impl_getBounds(BaseBounds bounds, 471 BaseTransform tx, 472 Node node, 473 BoundsAccessor boundsAccessor) { 474 bounds = EffectUtils.getInputBounds(bounds, 475 BaseTransform.IDENTITY_TRANSFORM, 476 node, boundsAccessor, 477 getInput()); 478 return EffectUtils.getShadowBounds(bounds, tx, 479 getClampedWidth(), 480 getClampedHeight(), 481 getBlurTypeInternal()); 482 } 483 484 /** 485 * 486 * @treatAsPrivate implementation detail 487 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 488 */ 489 @Deprecated 490 @Override 491 public Effect impl_copy() { 492 Shadow shadow = new Shadow(this.getBlurType(), this.getColor(), this.getRadius()); 493 shadow.setInput(this.getInput()); 494 shadow.setHeight(this.getHeight()); 495 shadow.setWidth(this.getWidth()); 496 return shadow; 497 } 498}