1 /* 2 * Copyright (c) 2010, 2015, 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 javafx.scene.effect; 27 28 import javafx.beans.property.DoubleProperty; 29 import javafx.beans.property.DoublePropertyBase; 30 import javafx.beans.property.ObjectProperty; 31 import javafx.scene.Node; 32 33 import com.sun.javafx.util.Utils; 34 import com.sun.javafx.effect.EffectDirtyBits; 35 import com.sun.javafx.geom.BaseBounds; 36 import com.sun.javafx.geom.transform.BaseTransform; 37 import com.sun.javafx.scene.BoundsAccessor; 38 39 40 /** 41 * An effect that renders a reflected version of the input below the 42 * actual input content. 43 * <p> 44 * Note that the reflection of a {@code Node} with a {@code Reflection} 45 * effect installed will not respond to mouse events or the containment 46 * methods on the {@code Node}. 47 * 48 * <p> 49 * Example: 50 * <pre><code> 51 * Reflection reflection = new Reflection(); 52 * reflection.setFraction(0.7); 53 * 54 * Text text = new Text(); 55 * text.setX(10.0); 56 * text.setY(50.0); 57 * text.setCache(true); 58 * text.setText("Reflections on JavaFX..."); 59 * text.setFill(Color.web("0x3b596d")); 60 * text.setFont(Font.font(null, FontWeight.BOLD, 40)); 61 * text.setEffect(reflection); 62 * </pre></code> 63 * <p> The code above produces the following: </p> 64 * <p> 65 * <img src="doc-files/reflection.png"/> 66 * </p> 67 * @since JavaFX 2.0 68 */ 69 public class Reflection extends Effect { 70 /** 71 * Creates a new instance of Reflection with default parameters. 72 */ 73 public Reflection() {} 74 75 /** 76 * Creates a new instance of Reflection with the specified topOffset, fraction, 77 * topOpacity and bottomOpacity. 78 * @param topOffset the distance between the bottom of the input and the top of the reflection 79 * @param fraction the fraction of the input that is visible in the reflection 80 * @param topOpacity the opacity of the reflection at its top extreme 81 * @param bottomOpacity the opacity of the reflection at its bottom extreme 82 * @since JavaFX 2.1 83 */ 84 public Reflection(double topOffset, double fraction, 85 double topOpacity, double bottomOpacity) { 86 setBottomOpacity(bottomOpacity); 87 setTopOffset(topOffset); 88 setTopOpacity(topOpacity); 89 setFraction(fraction); 90 } 91 92 @Override 93 com.sun.scenario.effect.Reflection impl_createImpl() { 94 return new com.sun.scenario.effect.Reflection(); 95 }; 96 /** 97 * The input for this {@code Effect}. 98 * If set to {@code null}, or left unspecified, a graphical image of 99 * the {@code Node} to which the {@code Effect} is attached will be 100 * used as the input. 101 * @defaultValue null 102 */ 103 private ObjectProperty<Effect> input; 104 105 106 public final void setInput(Effect value) { 107 inputProperty().set(value); 108 } 109 110 public final Effect getInput() { 111 return input == null ? null : input.get(); 112 } 113 114 public final ObjectProperty<Effect> inputProperty() { 115 if (input == null) { 116 input = new EffectInputProperty("input"); 117 } 118 return input; 119 } 120 121 @Override 122 boolean impl_checkChainContains(Effect e) { 123 Effect localInput = getInput(); 124 if (localInput == null) 125 return false; 126 if (localInput == e) 127 return true; 128 return localInput.impl_checkChainContains(e); 129 } 130 131 /** 132 * The top offset adjustment, which is the distance between the 133 * bottom of the input and the top of the reflection. 134 * <pre> 135 * Min: n/a 136 * Max: n/a 137 * Default: 0.0 138 * Identity: 0.0 139 * </pre> 140 * @defaultValue 0.0 141 */ 142 private DoubleProperty topOffset; 143 144 145 public final void setTopOffset(double value) { 146 topOffsetProperty().set(value); 147 } 148 149 public final double getTopOffset() { 150 return topOffset == null ? 0 : topOffset.get(); 151 } 152 153 public final DoubleProperty topOffsetProperty() { 154 if (topOffset == null) { 155 topOffset = new DoublePropertyBase() { 156 157 @Override 158 public void invalidated() { 159 markDirty(EffectDirtyBits.EFFECT_DIRTY); 160 effectBoundsChanged(); 161 } 162 163 @Override 164 public Object getBean() { 165 return Reflection.this; 166 } 167 168 @Override 169 public String getName() { 170 return "topOffset"; 171 } 172 }; 173 } 174 return topOffset; 175 } 176 177 /** 178 * The top opacity value, which is the opacity of the reflection 179 * at its top extreme. 180 * <pre> 181 * Min: 0.0 182 * Max: 1.0 183 * Default: 0.5 184 * Identity: 1.0 185 * </pre> 186 * @defaultValue 0.5 187 */ 188 private DoubleProperty topOpacity; 189 190 191 public final void setTopOpacity(double value) { 192 topOpacityProperty().set(value); 193 } 194 195 public final double getTopOpacity() { 196 return topOpacity == null ? 0.5 : topOpacity.get(); 197 } 198 199 public final DoubleProperty topOpacityProperty() { 200 if (topOpacity == null) { 201 topOpacity = new DoublePropertyBase(0.5) { 202 203 @Override 204 public void invalidated() { 205 markDirty(EffectDirtyBits.EFFECT_DIRTY); 206 } 207 208 @Override 209 public Object getBean() { 210 return Reflection.this; 211 } 212 213 @Override 214 public String getName() { 215 return "topOpacity"; 216 } 217 }; 218 } 219 return topOpacity; 220 } 221 222 /** 223 * The bottom opacity value, which is the opacity of the reflection 224 * at its bottom extreme. 225 * <pre> 226 * Min: 0.0 227 * Max: 1.0 228 * Default: 0.0 229 * Identity: 1.0 230 * </pre> 231 * @defaultValue 0.0 232 */ 233 private DoubleProperty bottomOpacity; 234 235 236 public final void setBottomOpacity(double value) { 237 bottomOpacityProperty().set(value); 238 } 239 240 public final double getBottomOpacity() { 241 return bottomOpacity == null ? 0 : bottomOpacity.get(); 242 } 243 244 public final DoubleProperty bottomOpacityProperty() { 245 if (bottomOpacity == null) { 246 bottomOpacity = new DoublePropertyBase() { 247 248 @Override 249 public void invalidated() { 250 markDirty(EffectDirtyBits.EFFECT_DIRTY); 251 } 252 253 @Override 254 public Object getBean() { 255 return Reflection.this; 256 } 257 258 @Override 259 public String getName() { 260 return "bottomOpacity"; 261 } 262 }; 263 } 264 return bottomOpacity; 265 } 266 267 /** 268 * The fraction of the input that is visible in the reflection. 269 * For example, a value of 0.5 means that only the bottom half of the 270 * input will be visible in the reflection. 271 * <pre> 272 * Min: 0.0 273 * Max: 1.0 274 * Default: 0.75 275 * Identity: 1.0 276 * </pre> 277 * @defaultValue 0.75 278 */ 279 private DoubleProperty fraction; 280 281 282 public final void setFraction(double value) { 283 fractionProperty().set(value); 284 } 285 286 public final double getFraction() { 287 return fraction == null ? 0.75 : fraction.get(); 288 } 289 290 public final DoubleProperty fractionProperty() { 291 if (fraction == null) { 292 fraction = new DoublePropertyBase(0.75) { 293 294 @Override 295 public void invalidated() { 296 markDirty(EffectDirtyBits.EFFECT_DIRTY); 297 effectBoundsChanged(); 298 } 299 300 @Override 301 public Object getBean() { 302 return Reflection.this; 303 } 304 305 @Override 306 public String getName() { 307 return "fraction"; 308 } 309 }; 310 } 311 return fraction; 312 } 313 314 private float getClampedFraction() { 315 return (float)Utils.clamp(0, getFraction(), 1); 316 } 317 318 private float getClampedBottomOpacity() { 319 return (float)Utils.clamp(0, getBottomOpacity(), 1); 320 } 321 322 private float getClampedTopOpacity() { 323 return (float)Utils.clamp(0, getTopOpacity(), 1); 324 } 325 326 @Override 327 void impl_update() { 328 Effect localInput = getInput(); 329 if (localInput != null) { 330 localInput.impl_sync(); 331 } 332 333 com.sun.scenario.effect.Reflection peer = 334 (com.sun.scenario.effect.Reflection) impl_getImpl(); 335 peer.setInput(localInput == null ? null : localInput.impl_getImpl()); 336 peer.setFraction(getClampedFraction()); 337 peer.setTopOffset((float)getTopOffset()); 338 peer.setBottomOpacity(getClampedBottomOpacity()); 339 peer.setTopOpacity(getClampedTopOpacity()); 340 } 341 342 /** 343 * @treatAsPrivate implementation detail 344 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 345 */ 346 @Deprecated 347 @Override 348 public BaseBounds impl_getBounds(BaseBounds bounds, 349 BaseTransform tx, 350 Node node, 351 BoundsAccessor boundsAccessor) { 352 bounds = getInputBounds(bounds, 353 BaseTransform.IDENTITY_TRANSFORM, 354 node, boundsAccessor, 355 getInput()); 356 bounds.roundOut(); 357 358 float x1 = bounds.getMinX(); 359 float y1 = bounds.getMaxY() + (float)getTopOffset(); 360 float z1 = bounds.getMinZ(); 361 float x2 = bounds.getMaxX(); 362 float y2 = y1 + (getClampedFraction() * bounds.getHeight()); 363 float z2 = bounds.getMaxZ(); 364 365 BaseBounds ret = BaseBounds.getInstance(x1, y1, z1, x2, y2, z2); 366 ret = ret.deriveWithUnion(bounds); 367 368 return transformBounds(tx, ret); 369 } 370 371 /** 372 * @treatAsPrivate implementation detail 373 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 374 */ 375 @Deprecated 376 @Override 377 public Effect impl_copy() { 378 Reflection ref = new Reflection(this.getTopOffset(), this.getFraction(), 379 this.getTopOpacity(), this.getBottomOpacity()); 380 ref.setInput(ref.getInput()); 381 return ref; 382 } 383 }