1 /* 2 * Copyright (c) 2008, 2013, 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 com.sun.scenario.effect; 27 28 import com.sun.javafx.geom.Point2D; 29 import com.sun.javafx.geom.Rectangle; 30 import com.sun.javafx.geom.transform.BaseTransform; 31 import com.sun.scenario.effect.impl.state.RenderState; 32 33 /** 34 * An effect that blends the two inputs together using one of the 35 * pre-defined {@code Mode}s. 36 */ 37 public class Blend extends CoreEffect { 38 39 /** 40 * A blending mode that defines the manner in which the inputs 41 * are composited together. 42 * Each {@code Mode} describes a mathematical equation that 43 * combines premultiplied inputs to produce some premultiplied result. 44 */ 45 public enum Mode { 46 /** 47 * The top input is blended over the bottom input. 48 * (Equivalent to the Porter-Duff "source over destination" rule.) 49 * <p> 50 * Thus: 51 * <pre> 52 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 53 * <em>C<sub>r</sub></em> = <em>C<sub>top</sub></em> + <em>C<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 54 * </pre> 55 */ 56 SRC_OVER, 57 58 /** 59 * The part of the top input lying inside of the bottom input 60 * is kept in the resulting image. 61 * (Equivalent to the Porter-Duff "source in destination" rule.) 62 * <p> 63 * Thus: 64 * <pre> 65 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em>*<em>A<sub>bot</sub></em> 66 * <em>C<sub>r</sub></em> = <em>C<sub>top</sub></em>*<em>A<sub>bot</sub></em> 67 * </pre> 68 */ 69 SRC_IN, 70 71 /** 72 * The part of the top input lying outside of the bottom input 73 * is kept in the resulting image. 74 * (Equivalent to the Porter-Duff "source held out by destination" 75 * rule.) 76 * <p> 77 * Thus: 78 * <pre> 79 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em>*(1-<em>A<sub>bot</sub></em>) 80 * <em>C<sub>r</sub></em> = <em>C<sub>top</sub></em>*(1-<em>A<sub>bot</sub></em>) 81 * </pre> 82 */ 83 SRC_OUT, 84 85 /** 86 * The part of the top input lying inside of the bottom input 87 * is blended with the bottom input. 88 * (Equivalent to the Porter-Duff "source atop destination" rule.) 89 * <p> 90 * Thus: 91 * <pre> 92 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em>*<em>A<sub>bot</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) = <em>A<sub>bot</sub></em> 93 * <em>C<sub>r</sub></em> = <em>C<sub>top</sub></em>*<em>A<sub>bot</sub></em> + <em>C<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 94 * </pre> 95 */ 96 SRC_ATOP, 97 98 /** 99 * The color and alpha components from the top input are 100 * added to those from the bottom input. 101 * The result is clamped to 1.0 if it exceeds the logical 102 * maximum of 1.0. 103 * <p> 104 * Thus: 105 * <pre> 106 * <em>A<sub>r</sub></em> = min(1, <em>A<sub>top</sub></em>+<em>A<sub>bot</sub></em>) 107 * <em>C<sub>r</sub></em> = min(1, <em>C<sub>top</sub></em>+<em>C<sub>bot</sub></em>) 108 * </pre> 109 * <p> 110 * Notes: 111 * <ul> 112 * <li>This mode is commutative (ordering of inputs 113 * does not matter). 114 * <li>This mode is sometimes referred to as "linear dodge" in 115 * imaging software packages. 116 * </ul> 117 */ 118 ADD, 119 120 /** 121 * The color components from the first input are multiplied with those 122 * from the second input. 123 * The alpha components are blended according to 124 * the {@link #SRC_OVER} equation. 125 * <p> 126 * Thus: 127 * <pre> 128 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 129 * <em>C<sub>r</sub></em> = <em>C<sub>top</sub></em> * <em>C<sub>bot</sub></em> 130 * </pre> 131 * <p> 132 * Notes: 133 * <ul> 134 * <li>This mode is commutative (ordering of inputs 135 * does not matter). 136 * <li>This mode is the mathematical opposite of 137 * the {@link #SCREEN} mode. 138 * <li>The resulting color is always at least as dark as either 139 * of the input colors. 140 * <li>Rendering with a completely black top input produces black; 141 * rendering with a completely white top input produces a result 142 * equivalent to the bottom input. 143 * </ul> 144 */ 145 MULTIPLY, 146 147 /** 148 * The color components from both of the inputs are 149 * inverted, multiplied with each other, and that result 150 * is again inverted to produce the resulting color. 151 * The alpha components are blended according 152 * to the {@link #SRC_OVER} equation. 153 * <p> 154 * Thus: 155 * <pre> 156 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 157 * <em>C<sub>r</sub></em> = 1 - ((1-<em>C<sub>top</sub></em>) * (1-<em>C<sub>bot</sub></em>)) 158 * </pre> 159 * <p> 160 * Notes: 161 * <ul> 162 * <li>This mode is commutative (ordering of inputs 163 * does not matter). 164 * <li>This mode is the mathematical opposite of 165 * the {@link #MULTIPLY} mode. 166 * <li>The resulting color is always at least as light as either 167 * of the input colors. 168 * <li>Rendering with a completely white top input produces white; 169 * rendering with a completely black top input produces a result 170 * equivalent to the bottom input. 171 * </ul> 172 */ 173 SCREEN, 174 175 /** 176 * The input color components are either multiplied or screened, 177 * depending on the bottom input color. 178 * The alpha components are blended according 179 * to the {@link #SRC_OVER} equation. 180 * <p> 181 * Thus: 182 * <pre> 183 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 184 * REMIND: not sure how to express this succinctly yet... 185 * </pre> 186 * <p> 187 * Notes: 188 * <ul> 189 * <li>This mode is a combination of {@link #SCREEN} and 190 * {@link #MULTIPLY}, depending on the bottom input color. 191 * <li>This mode is the mathematical opposite of 192 * the {@link #HARD_LIGHT} mode. 193 * <li>In this mode, the top input colors "overlay" the bottom input 194 * while preserving highlights and shadows of the latter. 195 * </ul> 196 */ 197 OVERLAY, 198 199 /** 200 * REMIND: cross check this formula with OpenVG spec... 201 * 202 * The darker of the color components from the two inputs are 203 * selected to produce the resulting color. 204 * The alpha components are blended according 205 * to the {@link #SRC_OVER} equation. 206 * <p> 207 * Thus: 208 * <pre> 209 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 210 * <em>C<sub>r</sub></em> = min(<em>C<sub>top</sub></em>, <em>C<sub>bot</sub></em>) 211 * </pre> 212 * <p> 213 * Notes: 214 * <ul> 215 * <li>This mode is commutative (ordering of inputs 216 * does not matter). 217 * <li>This mode is the mathematical opposite of 218 * the {@link #LIGHTEN} mode. 219 * </ul> 220 */ 221 DARKEN, 222 223 /** 224 * REMIND: cross check this formula with OpenVG spec... 225 * 226 * The lighter of the color components from the two inputs are 227 * selected to produce the resulting color. 228 * The alpha components are blended according 229 * to the {@link #SRC_OVER} equation. 230 * <p> 231 * Thus: 232 * <pre> 233 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 234 * <em>C<sub>r</sub></em> = max(<em>C<sub>top</sub></em>, <em>C<sub>bot</sub></em>) 235 * </pre> 236 * <p> 237 * Notes: 238 * <ul> 239 * <li>This mode is commutative (ordering of inputs 240 * does not matter). 241 * <li>This mode is the mathematical opposite of 242 * the {@link #DARKEN} mode. 243 * </ul> 244 */ 245 LIGHTEN, 246 247 /** 248 * The bottom input color components are divided by the inverse 249 * of the top input color components to produce the resulting color. 250 * The alpha components are blended according 251 * to the {@link #SRC_OVER} equation. 252 * <p> 253 * Thus: 254 * <pre> 255 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 256 * <em>C<sub>r</sub></em> = <em>C<sub>bot</sub></em> / (1-<em>C<sub>top</sub></em>) 257 * </pre> 258 */ 259 COLOR_DODGE, 260 261 /** 262 * The inverse of the bottom input color components are divided by 263 * the top input color components, all of which is then inverted 264 * to produce the resulting color. 265 * The alpha components are blended according 266 * to the {@link #SRC_OVER} equation. 267 * <p> 268 * Thus: 269 * <pre> 270 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 271 * <em>C<sub>r</sub></em> = 1-((1-<em>C<sub>bot</sub></em>) / <em>C<sub>top</sub></em>) 272 * </pre> 273 */ 274 COLOR_BURN, 275 276 /** 277 * The input color components are either multiplied or screened, 278 * depending on the top input color. 279 * The alpha components are blended according 280 * to the {@link #SRC_OVER} equation. 281 * <p> 282 * Thus: 283 * <pre> 284 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 285 * REMIND: not sure how to express this succinctly yet... 286 * </pre> 287 * <p> 288 * Notes: 289 * <ul> 290 * <li>This mode is a combination of {@link #SCREEN} and 291 * {@link #MULTIPLY}, depending on the top input color. 292 * <li>This mode is the mathematical opposite of 293 * the {@link #OVERLAY} mode. 294 * </ul> 295 */ 296 HARD_LIGHT, 297 298 /** 299 * REMIND: this is a complicated formula, TBD... 300 */ 301 SOFT_LIGHT, 302 303 /** 304 * The darker of the color components from the two inputs are 305 * subtracted from the lighter ones to produce the resulting color. 306 * The alpha components are blended according 307 * to the {@link #SRC_OVER} equation. 308 * <p> 309 * Thus: 310 * <pre> 311 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 312 * <em>C<sub>r</sub></em> = abs(<em>C<sub>top</sub></em>-<em>C<sub>bot</sub></em>) 313 * </pre> 314 * <p> 315 * Notes: 316 * <ul> 317 * <li>This mode is commutative (ordering of inputs 318 * does not matter). 319 * <li>This mode can be used to invert parts of the bottom input 320 * image, or to quickly compare two images (equal pixels will result 321 * in black). 322 * <li>Rendering with a completely white top input inverts the 323 * bottom input; rendering with a completely black top input produces 324 * a result equivalent to the bottom input. 325 * </ul> 326 */ 327 DIFFERENCE, 328 329 /** 330 * The color components from the two inputs are multiplied and 331 * doubled, and then subtracted from the sum of the bottom input 332 * color components, to produce the resulting color. 333 * The alpha components are blended according 334 * to the {@link #SRC_OVER} equation. 335 * <p> 336 * Thus: 337 * <pre> 338 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 339 * <em>C<sub>r</sub></em> = <em>C<sub>top</sub></em> + <em>C<sub>bot</sub></em> - (2*<em>C<sub>top</sub></em>*<em>C<sub>bot</sub></em>) 340 * </pre> 341 * <p> 342 * Notes: 343 * <ul> 344 * <li>This mode is commutative (ordering of inputs 345 * does not matter). 346 * <li>This mode can be used to invert parts of the bottom input. 347 * <li>This mode produces results that are similar to those of 348 * {@link #DIFFERENCE}, except with lower contrast. 349 * <li>Rendering with a completely white top input inverts the 350 * bottom input; rendering with a completely black top input produces 351 * a result equivalent to the bottom input. 352 * </ul> 353 */ 354 EXCLUSION, 355 356 /** 357 * The red component of the bottom input is replaced with the 358 * red component of the top input; the other color components 359 * are unaffected. 360 * The alpha components are blended according 361 * to the {@link #SRC_OVER} equation. 362 * <p> 363 * Thus: 364 * <pre> 365 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 366 * <em>R<sub>r</sub></em> = <em>R<sub>top</sub></em> 367 * <em>G<sub>r</sub></em> = <em>G<sub>bot</sub></em> 368 * <em>B<sub>r</sub></em> = <em>B<sub>bot</sub></em> 369 * </pre> 370 */ 371 RED, 372 373 /** 374 * The green component of the bottom input is replaced with the 375 * green component of the top input; the other color components 376 * are unaffected. 377 * The alpha components are blended according 378 * to the {@link #SRC_OVER} equation. 379 * <p> 380 * Thus: 381 * <pre> 382 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 383 * <em>R<sub>r</sub></em> = <em>R<sub>bot</sub></em> 384 * <em>G<sub>r</sub></em> = <em>G<sub>top</sub></em> 385 * <em>B<sub>r</sub></em> = <em>B<sub>bot</sub></em> 386 * </pre> 387 */ 388 GREEN, 389 390 /** 391 * The blue component of the bottom input is replaced with the 392 * blue component of the top input; the other color components 393 * are unaffected. 394 * The alpha components are blended according 395 * to the {@link #SRC_OVER} equation. 396 * <p> 397 * Thus: 398 * <pre> 399 * <em>A<sub>r</sub></em> = <em>A<sub>top</sub></em> + <em>A<sub>bot</sub></em>*(1-<em>A<sub>top</sub></em>) 400 * <em>R<sub>r</sub></em> = <em>R<sub>bot</sub></em> 401 * <em>G<sub>r</sub></em> = <em>G<sub>bot</sub></em> 402 * <em>B<sub>r</sub></em> = <em>B<sub>top</sub></em> 403 * </pre> 404 */ 405 BLUE, 406 } 407 408 private Mode mode; 409 private float opacity; 410 411 /** 412 * Constructs a new {@code Blend} effect with the given mode and the 413 * default opacity (1.0). 414 * Either or both inputs may be {@code null} to indicate that the default 415 * input should be used. 416 * 417 * @param mode the blending mode 418 * @param bottomInput the bottom input 419 * @param topInput the top input 420 * @throws IllegalArgumentException if {@code mode} is null 421 */ 422 public Blend(Mode mode, Effect bottomInput, Effect topInput) { 423 super(bottomInput, topInput); 424 setMode(mode); 425 setOpacity(1f); 426 } 427 428 /** 429 * Returns the bottom input for this {@code Effect}. 430 * 431 * @return the bottom input for this {@code Effect} 432 */ 433 public final Effect getBottomInput() { 434 return getInputs().get(0); 435 } 436 437 /** 438 * Sets the bottom input for this {@code Effect} to a specific 439 * {@code Effect} or to the default input if {@code input} is 440 * {@code null}. 441 * 442 * @param bottomInput the bottom input for this {@code Effect} 443 */ 444 public void setBottomInput(Effect bottomInput) { 445 setInput(0, bottomInput); 446 } 447 448 /** 449 * Returns the top input for this {@code Effect}. 450 * 451 * @return the top input for this {@code Effect} 452 */ 453 public final Effect getTopInput() { 454 return getInputs().get(1); 455 } 456 457 /** 458 * Sets the top input for this {@code Effect} to a specific 459 * {@code Effect} or to the default input if {@code input} is 460 * {@code null}. 461 * 462 * @param topInput the top input for this {@code Effect} 463 */ 464 public void setTopInput(Effect topInput) { 465 setInput(1, topInput); 466 } 467 468 /** 469 * Returns the {@code Mode} used to blend the two inputs together. 470 * 471 * @return the {@code Mode} used to blend the two inputs together. 472 */ 473 public Mode getMode() { 474 return mode; 475 } 476 477 /** 478 * Sets the {@code Mode} used to blend the two inputs together. 479 * <pre> 480 * Min: n/a 481 * Max: n/a 482 * Default: Mode.SRC_OVER 483 * Identity: n/a 484 * </pre> 485 * 486 * @param mode the blending mode 487 * @throws IllegalArgumentException if {@code mode} is null 488 */ 489 public void setMode(Mode mode) { 490 if (mode == null) { 491 throw new IllegalArgumentException("Mode must be non-null"); 492 } 493 Blend.Mode old = this.mode; 494 this.mode = mode; 495 updatePeerKey("Blend_" + mode.name()); 496 } 497 498 /** 499 * Returns the opacity value, which is modulated with the top input 500 * prior to blending. 501 * 502 * @return the opacity value 503 */ 504 public float getOpacity() { 505 return opacity; 506 } 507 508 /** 509 * Sets the opacity value, which is modulated with the top input prior 510 * to blending. 511 * <pre> 512 * Min: 0.0 513 * Max: 1.0 514 * Default: 1.0 515 * Identity: 1.0 516 * </pre> 517 * 518 * @param opacity the opacity value 519 * @throws IllegalArgumentException if {@code opacity} is outside the 520 * allowable range 521 */ 522 public void setOpacity(float opacity) { 523 if (opacity < 0f || opacity > 1f) { 524 throw new IllegalArgumentException("Opacity must be in the range [0,1]"); 525 } 526 float old = this.opacity; 527 this.opacity = opacity; 528 } 529 530 /** 531 * Transform the specified point {@code p} from the coordinate space 532 * of the primary content input to the coordinate space of the effect 533 * output. 534 * In essence, this method asks the question "Which output coordinate 535 * is most affected by the data at the specified coordinate in the 536 * primary source input?" 537 * <p> 538 * The {@code Blend} effect delegates this operation to its {@code top} 539 * input, or the {@code defaultInput} if the {@code top} input is 540 * {@code null}. 541 * 542 * @param p the point in the coordinate space of the primary content 543 * input to be transformed 544 * @param defaultInput the default input {@code Effect} to be used in 545 * all cases where a filter has a null input 546 * @return the transformed point in the coordinate space of the result 547 */ 548 @Override 549 public Point2D transform(Point2D p, Effect defaultInput) { 550 return getDefaultedInput(1, defaultInput).transform(p, defaultInput); 551 } 552 553 /** 554 * Transform the specified point {@code p} from the coordinate space 555 * of the output of the effect into the coordinate space of the 556 * primary content input. 557 * In essence, this method asks the question "Which source coordinate 558 * contributes most to the definition of the output at the specified 559 * coordinate?" 560 * <p> 561 * The {@code Blend} effect delegates this operation to its {@code top} 562 * input, or the {@code defaultInput} if the {@code top} input is 563 * {@code null}. 564 * 565 * @param p the point in the coordinate space of the result output 566 * to be transformed 567 * @param defaultInput the default input {@code Effect} to be used in 568 * all cases where a filter has a null input 569 * @return the untransformed point in the coordinate space of the 570 * primary content input 571 */ 572 @Override 573 public Point2D untransform(Point2D p, Effect defaultInput) { 574 return getDefaultedInput(1, defaultInput).untransform(p, defaultInput); 575 } 576 577 @Override 578 public RenderState getRenderState(FilterContext fctx, 579 BaseTransform transform, 580 Rectangle outputClip, 581 Object renderHelper, 582 Effect defaultInput) 583 { 584 // A blend operation operates on its inputs pixel-by-pixel 585 // with no expansion or contraction. 586 // RT-27563 587 // TODO: The RenderSpaceRenderState object uses the output clip unchanged 588 // for its inputs, but we could further restrict the amount we ask for 589 // each input to the intersection of the two input bounds, but for now we 590 // will simply let it pass along the output clip as the input clip. 591 return RenderState.RenderSpaceRenderState; 592 } 593 594 @Override 595 public boolean reducesOpaquePixels() { 596 final Effect bottomInput = getBottomInput(); 597 final Effect topInput = getTopInput(); 598 switch (getMode()) { 599 case SRC_IN: 600 case SRC_OUT: 601 return true; 602 case SRC_ATOP: 603 return bottomInput != null && bottomInput.reducesOpaquePixels(); 604 case SRC_OVER: 605 case ADD: 606 case MULTIPLY: 607 case SCREEN: 608 case OVERLAY: 609 case DARKEN: 610 case LIGHTEN: 611 case COLOR_DODGE: 612 case COLOR_BURN: 613 case HARD_LIGHT: 614 case SOFT_LIGHT: 615 case DIFFERENCE: 616 case EXCLUSION: 617 case RED: 618 case GREEN: 619 case BLUE: 620 return topInput != null && topInput.reducesOpaquePixels() && bottomInput != null && bottomInput.reducesOpaquePixels(); 621 } 622 return true; 623 } 624 }