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 32 /** 33 * An effect that merges two inputs together into one result. This produces 34 * the same result as using the {@code Blend} effect with 35 * {@code Blend.Mode.SRC_OVER} and {@code opacity=1.0}, except possibly 36 * more efficient. 37 */ 38 public class Merge extends CoreEffect { 39 40 /** 41 * Constructs a new {@code Merge} effect for the given inputs. 42 * 43 * @param bottomInput the bottom input 44 * @param topInput the top input 45 */ 46 public Merge(Effect bottomInput, Effect topInput) { 47 super(bottomInput, topInput); 48 updatePeerKey("Merge"); 49 } 50 51 /** 52 * Returns the bottom input for this {@code Effect}. 53 * 54 * @return the bottom input for this {@code Effect} 55 */ 56 public final Effect getBottomInput() { 57 return getInputs().get(0); 58 } 59 60 /** 61 * Sets the bottom input for this {@code Effect} to a specific 62 * {@code Effect} or to the default input if {@code input} is 63 * {@code null}. 64 * 65 * @param bottomInput the bottom input for this {@code Effect} 66 */ 67 public void setBottomInput(Effect bottomInput) { 68 setInput(0, bottomInput); 69 } 70 71 /** 72 * Returns the top input for this {@code Effect}. 73 * 74 * @return the top input for this {@code Effect} 75 */ 76 public final Effect getTopInput() { 77 return getInputs().get(1); 78 } 79 80 /** 81 * Sets the top input for this {@code Effect} to a specific 82 * {@code Effect} or to the default input if {@code input} is 83 * {@code null}. 84 * 85 * @param topInput the top input for this {@code Effect} 86 */ 87 public void setTopInput(Effect topInput) { 88 setInput(1, topInput); 89 } 90 91 /** 92 * Transform the specified point {@code p} from the coordinate space 93 * of the primary content input to the coordinate space of the effect 94 * output. 95 * In essence, this method asks the question "Which output coordinate 96 * is most affected by the data at the specified coordinate in the 97 * primary source input?" 98 * <p> 99 * The {@code Merge} effect delegates this operation to its {@code top} 100 * input, or the {@code defaultInput} if the {@code top} input is 101 * {@code null}. 102 * 103 * @param p the point in the coordinate space of the primary content 104 * input to be transformed 105 * @param defaultInput the default input {@code Effect} to be used in 106 * all cases where a filter has a null input 107 * @return the transformed point in the coordinate space of the result 108 */ 109 @Override 110 public Point2D transform(Point2D p, Effect defaultInput) { 111 return getDefaultedInput(1, defaultInput).transform(p, defaultInput); 112 } 113 114 /** 115 * Transform the specified point {@code p} from the coordinate space 116 * of the output of the effect into the coordinate space of the 117 * primary content input. 118 * In essence, this method asks the question "Which source coordinate 119 * contributes most to the definition of the output at the specified 120 * coordinate?" 121 * <p> 122 * The {@code Merge} effect delegates this operation to its {@code top} 123 * input, or the {@code defaultInput} if the {@code top} input is 124 * {@code null}. 125 * 126 * @param p the point in the coordinate space of the result output 127 * to be transformed 128 * @param defaultInput the default input {@code Effect} to be used in 129 * all cases where a filter has a null input 130 * @return the untransformed point in the coordinate space of the 131 * primary content input 132 */ 133 @Override 134 public Point2D untransform(Point2D p, Effect defaultInput) { 135 return getDefaultedInput(1, defaultInput).untransform(p, defaultInput); 136 } 137 138 @Override 139 public ImageData filter(FilterContext fctx, 140 BaseTransform transform, 141 Rectangle outputClip, 142 Object renderHelper, 143 Effect defaultInput) 144 { 145 Effect botinput = getDefaultedInput(0, defaultInput); 146 Effect topinput = getDefaultedInput(1, defaultInput); 147 ImageData botimg = botinput.filter(fctx, transform, outputClip, 148 renderHelper, defaultInput); 149 if (botimg != null) { 150 if (!botimg.validate(fctx)) { 151 return new ImageData(fctx, null, null); 152 } 153 if (renderHelper instanceof ImageDataRenderer) { 154 ImageDataRenderer imgr = (ImageDataRenderer) renderHelper; 155 imgr.renderImage(botimg, BaseTransform.IDENTITY_TRANSFORM, fctx); 156 botimg.unref(); 157 botimg = null; 158 } 159 } 160 if (botimg == null) { 161 return topinput.filter(fctx, transform, outputClip, 162 renderHelper, defaultInput); 163 } 164 ImageData topimg = topinput.filter(fctx, transform, outputClip, 165 null, defaultInput); 166 if (!topimg.validate(fctx)) { 167 return new ImageData(fctx, null, null); 168 } 169 ImageData ret = filterImageDatas(fctx, transform, outputClip, 170 botimg, topimg); 171 botimg.unref(); 172 topimg.unref(); 173 return ret; 174 } 175 176 @Override 177 protected Rectangle getInputClip(int inputIndex, 178 BaseTransform transform, 179 Rectangle outputClip) 180 { 181 // This no longer matters since we completely override the 182 // filter method so the FilterEffect.filter() method no longer 183 // comes into play. 184 throw new InternalError("Merge.getInputClip should not be called"); 185 } 186 187 @Override 188 public boolean reducesOpaquePixels() { 189 final Effect topInput = getTopInput(); 190 final Effect bottomInput = getBottomInput(); 191 return topInput != null && topInput.reducesOpaquePixels() && bottomInput != null && bottomInput.reducesOpaquePixels(); 192 } 193 194 }