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.beans.property.ObjectPropertyBase; 32 import javafx.scene.Node; 33 34 import com.sun.javafx.util.Utils; 35 import com.sun.javafx.effect.EffectDirtyBits; 36 import com.sun.javafx.geom.BaseBounds; 37 import com.sun.javafx.geom.RectBounds; 38 import com.sun.javafx.geom.transform.BaseTransform; 39 import com.sun.javafx.scene.BoundsAccessor; 40 import com.sun.scenario.effect.Blend.Mode; 41 42 43 /** 44 * An effect that blends the two inputs together using one of the 45 * pre-defined {@link BlendMode}s. 46 * 47 * <p> 48 * Example: 49 * <pre><code> 50 * Blend blend = new Blend(); 51 * blend.setMode(BlendMode.COLOR_BURN); 52 * 53 * ColorInput colorInput = new ColorInput(); 54 * colorInput.setPaint(Color.STEELBLUE); 55 * colorInput.setX(10); 56 * colorInput.setY(10); 57 * colorInput.setWidth(100); 58 * colorInput.setHeight(180); 59 * 60 * blend.setTopInput(colorInput); 61 * 62 * Rectangle rect = new Rectangle(); 63 * rect.setWidth(220); 64 * rect.setHeight(100); 65 * Stop[] stops = new Stop[]{new Stop(0, Color.LIGHTSTEELBLUE), new Stop(1, Color.PALEGREEN)}; 66 * LinearGradient lg = new LinearGradient(0, 0, 0.25, 0.25, true, CycleMethod.REFLECT, stops); 67 * rect.setFill(lg); 68 * 69 * Text text = new Text(); 70 * text.setX(15); 71 * text.setY(65); 72 * text.setFill(Color.PALEVIOLETRED); 73 * text.setText("COLOR_BURN"); 74 * text.setFont(Font.font(null, FontWeight.BOLD, 30)); 75 * 76 * Group g = new Group(); 77 * g.setEffect(blend); 78 * g.getChildren().addAll(rect, text); 79 * </pre></code> 80 * 81 * <p> The code above produces the following: </p> 82 * <p> <img src="doc-files/blend.png"/> </p> 83 * @since JavaFX 2.0 84 */ 85 public class Blend extends Effect { 86 87 static private Mode toPGMode(BlendMode mode) { 88 if (mode == null) { 89 return Mode.SRC_OVER; // Default value 90 } else if (mode == BlendMode.SRC_OVER) { 91 return Mode.SRC_OVER; 92 } else if (mode == BlendMode.SRC_ATOP) { 93 return Mode.SRC_ATOP; 94 } else if (mode == BlendMode.ADD) { 95 return Mode.ADD; 96 } else if (mode == BlendMode.MULTIPLY) { 97 return Mode.MULTIPLY; 98 } else if (mode == BlendMode.SCREEN) { 99 return Mode.SCREEN; 100 } else if (mode == BlendMode.OVERLAY) { 101 return Mode.OVERLAY; 102 } else if (mode == BlendMode.DARKEN) { 103 return Mode.DARKEN; 104 } else if (mode == BlendMode.LIGHTEN) { 105 return Mode.LIGHTEN; 106 } else if (mode == BlendMode.COLOR_DODGE) { 107 return Mode.COLOR_DODGE; 108 } else if (mode == BlendMode.COLOR_BURN) { 109 return Mode.COLOR_BURN; 110 } else if (mode == BlendMode.HARD_LIGHT) { 111 return Mode.HARD_LIGHT; 112 } else if (mode == BlendMode.SOFT_LIGHT) { 113 return Mode.SOFT_LIGHT; 114 } else if (mode == BlendMode.DIFFERENCE) { 115 return Mode.DIFFERENCE; 116 } else if (mode == BlendMode.EXCLUSION) { 117 return Mode.EXCLUSION; 118 } else if (mode == BlendMode.RED) { 119 return Mode.RED; 120 } else if (mode == BlendMode.GREEN) { 121 return Mode.GREEN; 122 } else if (mode == BlendMode.BLUE) { 123 return Mode.BLUE; 124 } else { 125 throw new java.lang.AssertionError("Unrecognized blend mode: {mode}"); 126 } 127 } 128 129 /** 130 * Used by Group to convert the FX BlendMode enum value into a Decora value. 131 * @treatAsPrivate implementation detail 132 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 133 */ 134 @Deprecated 135 public static Mode impl_getToolkitMode(BlendMode mode) { 136 return toPGMode(mode); 137 } 138 139 /** 140 * Creates a new instance of Blend with default parameters. 141 */ 142 public Blend() {} 143 144 /** 145 * Creates a new instance of Blend with the specified mode. 146 * @param mode the {@code BlendMode} used to blend the two inputs together 147 * @since JavaFX 2.1 148 */ 149 public Blend(BlendMode mode) { 150 setMode(mode); 151 } 152 153 /** 154 * Creates a new instance of Blend with the specified mode and bottom 155 * and top inputs. 156 * @param mode the {@code BlendMode} used to blend the two inputs together 157 * @param bottomInput the bottom input for this {@code Blend} operation 158 * @param topInput the top input for this {@code Blend} operation 159 * @since JavaFX 2.1 160 */ 161 public Blend(BlendMode mode, Effect bottomInput, Effect topInput) { 162 setMode(mode); 163 setBottomInput(bottomInput); 164 setTopInput(topInput); 165 } 166 167 @Override 168 com.sun.scenario.effect.Blend impl_createImpl() { 169 return new com.sun.scenario.effect.Blend( 170 toPGMode(BlendMode.SRC_OVER), 171 com.sun.scenario.effect.Effect.DefaultInput, 172 com.sun.scenario.effect.Effect.DefaultInput); 173 } 174 175 /** 176 * The {@code BlendMode} used to blend the two inputs together. 177 * <pre> 178 * Min: n/a 179 * Max: n/a 180 * Default: BlendMode.SRC_OVER 181 * Identity: n/a 182 * </pre> 183 * @defaultValue SRC_OVER 184 */ 185 private ObjectProperty<BlendMode> mode; 186 187 188 public final void setMode(BlendMode value) { 189 modeProperty().set(value); 190 } 191 192 public final BlendMode getMode() { 193 return mode == null ? BlendMode.SRC_OVER : mode.get(); 194 } 195 196 public final ObjectProperty<BlendMode> modeProperty() { 197 if (mode == null) { 198 mode = new ObjectPropertyBase<BlendMode>(BlendMode.SRC_OVER) { 199 200 @Override 201 public void invalidated() { 202 markDirty(EffectDirtyBits.EFFECT_DIRTY); 203 } 204 205 @Override 206 public Object getBean() { 207 return Blend.this; 208 } 209 210 @Override 211 public String getName() { 212 return "mode"; 213 } 214 }; 215 } 216 return mode; 217 } 218 219 /** 220 * The opacity value, which is modulated with the top input prior 221 * to blending. 222 * <pre> 223 * Min: 0.0 224 * Max: 1.0 225 * Default: 1.0 226 * Identity: 1.0 227 * </pre> 228 * @defaultValue 1.0 229 */ 230 private DoubleProperty opacity; 231 232 233 public final void setOpacity(double value) { 234 opacityProperty().set(value); 235 } 236 237 public final double getOpacity() { 238 return opacity == null ? 1 : opacity.get(); 239 } 240 241 public final DoubleProperty opacityProperty() { 242 if (opacity == null) { 243 opacity = new DoublePropertyBase(1) { 244 245 @Override 246 public void invalidated() { 247 markDirty(EffectDirtyBits.EFFECT_DIRTY); 248 } 249 250 @Override 251 public Object getBean() { 252 return Blend.this; 253 } 254 255 @Override 256 public String getName() { 257 return "opacity"; 258 } 259 }; 260 } 261 return opacity; 262 } 263 264 /** 265 * The bottom input for this {@code Blend} operation. 266 * If set to {@code null}, or left unspecified, a graphical image of 267 * the {@code Node} to which the {@code Effect} is attached will be 268 * used as the input. 269 * @defaultValue null 270 */ 271 private ObjectProperty<Effect> bottomInput; 272 273 274 public final void setBottomInput(Effect value) { 275 bottomInputProperty().set(value); 276 } 277 278 public final Effect getBottomInput() { 279 return bottomInput == null ? null : bottomInput.get(); 280 } 281 282 public final ObjectProperty<Effect> bottomInputProperty() { 283 if (bottomInput == null) { 284 bottomInput = new EffectInputProperty("bottomInput"); 285 } 286 return bottomInput; 287 } 288 289 /** 290 * The top input for this {@code Blend} operation. 291 * If set to {@code null}, or left unspecified, a graphical image of 292 * the {@code Node} to which the {@code Effect} is attached will be 293 * used as the input. 294 * @defaultValue null 295 */ 296 private ObjectProperty<Effect> topInput; 297 298 299 public final void setTopInput(Effect value) { 300 topInputProperty().set(value); 301 } 302 303 public final Effect getTopInput() { 304 return topInput == null ? null : topInput.get(); 305 } 306 307 public final ObjectProperty<Effect> topInputProperty() { 308 if (topInput == null) { 309 topInput = new EffectInputProperty("topInput"); 310 } 311 return topInput; 312 } 313 314 @Override 315 boolean impl_checkChainContains(Effect e) { 316 Effect localTopInput = getTopInput(); 317 Effect localBottomInput = getBottomInput(); 318 if (localTopInput == e || localBottomInput == e) 319 return true; 320 if (localTopInput != null && localTopInput.impl_checkChainContains(e)) 321 return true; 322 if (localBottomInput != null && localBottomInput.impl_checkChainContains(e)) 323 return true; 324 325 return false; 326 } 327 328 @Override 329 void impl_update() { 330 Effect localBottomInput = getBottomInput(); 331 Effect localTopInput = getTopInput(); 332 333 if (localTopInput != null) { 334 localTopInput.impl_sync(); 335 } 336 if (localBottomInput != null) { 337 localBottomInput.impl_sync(); 338 } 339 340 com.sun.scenario.effect.Blend peer = 341 (com.sun.scenario.effect.Blend) impl_getImpl(); 342 peer.setTopInput(localTopInput == null ? null : localTopInput.impl_getImpl()); 343 peer.setBottomInput(localBottomInput == null ? null : localBottomInput.impl_getImpl()); 344 peer.setOpacity((float)Utils.clamp(0, getOpacity(), 1)); 345 peer.setMode(toPGMode(getMode())); 346 } 347 348 /** 349 * @treatAsPrivate implementation detail 350 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 351 */ 352 @Deprecated 353 @Override 354 public BaseBounds impl_getBounds(BaseBounds bounds, 355 BaseTransform tx, 356 Node node, 357 BoundsAccessor boundsAccessor) { 358 BaseBounds topBounds = new RectBounds(); 359 BaseBounds bottomBounds = new RectBounds(); 360 bottomBounds = getInputBounds(bottomBounds, tx, 361 node, boundsAccessor, 362 getBottomInput()); 363 topBounds = getInputBounds(topBounds, tx, 364 node, boundsAccessor, 365 getTopInput()); 366 BaseBounds ret = topBounds.deriveWithUnion(bottomBounds); 367 return ret; 368 } 369 370 /** 371 * @treatAsPrivate implementation detail 372 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 373 */ 374 @Deprecated 375 @Override 376 public Effect impl_copy() { 377 return new Blend(this.getMode(), this.getBottomInput(), this.getTopInput()); 378 } 379 }