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.BaseBounds; 30 import com.sun.javafx.geom.RectBounds; 31 import com.sun.javafx.geom.Rectangle; 32 import com.sun.javafx.geom.transform.BaseTransform; 33 import com.sun.javafx.geom.transform.NoninvertibleTransformException; 34 35 /** 36 * The implementation base class for {@link Effect} subclasses that operate 37 * by filtering the inputs at the pixel level. 38 */ 39 public abstract class FilterEffect extends Effect { 40 protected FilterEffect() { 41 super(); 42 } 43 44 protected FilterEffect(Effect input) { 45 super(input); 46 } 47 48 protected FilterEffect(Effect input1, Effect input2) { 49 super(input1, input2); 50 } 51 52 public boolean operatesInUserSpace() { 53 return false; 54 } 55 56 public BaseBounds getBounds(BaseTransform transform, 57 Effect defaultInput) 58 { 59 int numinputs = getNumInputs(); 60 BaseTransform inputtx = transform; 61 if (operatesInUserSpace()) { 62 inputtx = BaseTransform.IDENTITY_TRANSFORM; 63 } 64 BaseBounds ret; 65 if (numinputs == 1) { 66 Effect input = getDefaultedInput(0, defaultInput); 67 ret = input.getBounds(inputtx, defaultInput); 68 } else { 69 BaseBounds inputBounds[] = new BaseBounds[numinputs]; 70 for (int i = 0; i < numinputs; i++) { 71 Effect input = getDefaultedInput(i, defaultInput); 72 inputBounds[i] = input.getBounds(inputtx, defaultInput); 73 } 74 ret = combineBounds(inputBounds); 75 } 76 if (inputtx != transform) { 77 ret = transformBounds(transform, ret); 78 } 79 return ret; 80 } 81 82 protected abstract Rectangle getInputClip(int inputIndex, 83 BaseTransform transform, 84 Rectangle outputBounds); 85 86 protected static Rectangle untransformClip(BaseTransform transform, 87 Rectangle clip) 88 { 89 if (transform.isIdentity() || clip == null || clip.isEmpty()) { 90 return clip; 91 } 92 // We are asked to produce samples for the pixels in the 93 // Rectangular clip. The samples requested are delivered for 94 // the centers of the pixels for every pixel in that range. 95 // Thus, we need valid data for the clip inset by 0.5 pixels 96 // all around. 97 // But, when we untransform, we need to make sure that the data 98 // we provide can be used to provide a valid sample for each of 99 // those points. If the mapped sample coordinate falls on a 100 // non-integer coordinate then we need the data for the 4 pixels 101 // around that point. Thus, we need a sample for the pixel that it 102 // falls on, and potentially a sample for the next pixel over if 103 // we are within 0.5 pixels of the edge of those border pixels. 104 // The full operation is then: 105 // clip.inset(0.5) // reduce to requested pixel centers 127 } 128 return transformedBounds; 129 } 130 RectBounds b = new RectBounds(clip); 131 try { 132 b.grow(-0.5f, -0.5f); 133 b = (RectBounds) transform.inverseTransform(b, b); 134 b.grow(0.5f, 0.5f); 135 transformedBounds.setBounds(b); 136 } catch (NoninvertibleTransformException e) { 137 // Non-invertible means the transform has collapsed onto 138 // a point or line and so the results of the effect are 139 // not visible so we can use the empty bounds object we 140 // created for transformedBounds. Ideally this would be 141 // checked further up in the chain, but in case we get here 142 // we might as well do as little work as we can. 143 } 144 return transformedBounds; 145 } 146 147 @Override 148 public ImageData filter(FilterContext fctx, 149 BaseTransform transform, 150 Rectangle outputClip, 151 Object renderHelper, 152 Effect defaultInput) 153 { 154 int numinputs = getNumInputs(); 155 ImageData inputDatas[] = new ImageData[numinputs]; 156 Rectangle inputClip; 157 BaseTransform inputtx; 158 if (operatesInUserSpace()) { 159 inputClip = untransformClip(transform, outputClip); 160 inputtx = BaseTransform.IDENTITY_TRANSFORM; 161 } else { 162 inputClip = outputClip; 163 inputtx = transform; 164 } 165 for (int i = 0; i < numinputs; i++) { 166 Effect input = getDefaultedInput(i, defaultInput); 167 inputDatas[i] = 168 input.filter(fctx, inputtx, 169 getInputClip(i, inputtx, inputClip), 170 null, defaultInput); 171 if (!inputDatas[i].validate(fctx)) { 172 for (int j = 0; j <= i; j++) { 173 inputDatas[j].unref(); 174 } 175 return new ImageData(fctx, null, null); 176 } 177 } 178 ImageData ret = filterImageDatas(fctx, inputtx, inputClip, inputDatas); 179 for (int i = 0; i < numinputs; i++) { 180 inputDatas[i].unref(); 181 } 182 if (inputtx != transform) { 183 if (renderHelper instanceof ImageDataRenderer) { 184 ImageDataRenderer renderer = (ImageDataRenderer) renderHelper; 185 renderer.renderImage(ret, transform, fctx); 186 ret.unref(); 187 ret = null; 188 } else { 189 ret = ret.transform(transform); 190 } 191 } 192 return ret; 193 } 194 195 @Override 196 public Point2D transform(Point2D p, Effect defaultInput) { 197 return getDefaultedInput(0, defaultInput).transform(p, defaultInput); 198 } 199 200 @Override 201 public Point2D untransform(Point2D p, Effect defaultInput) { 202 return getDefaultedInput(0, defaultInput).untransform(p, defaultInput); 203 } 204 205 protected abstract ImageData filterImageDatas(FilterContext fctx, 206 BaseTransform transform, 207 Rectangle outputClip, 208 ImageData... inputDatas); 209 } | 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.BaseBounds; 30 import com.sun.javafx.geom.RectBounds; 31 import com.sun.javafx.geom.Rectangle; 32 import com.sun.javafx.geom.transform.BaseTransform; 33 import com.sun.javafx.geom.transform.NoninvertibleTransformException; 34 import com.sun.scenario.effect.impl.state.RenderState; 35 36 /** 37 * The implementation base class for {@link Effect} subclasses that operate 38 * by filtering the inputs at the pixel level. 39 */ 40 public abstract class FilterEffect<T extends RenderState> extends Effect { 41 protected FilterEffect() { 42 super(); 43 } 44 45 protected FilterEffect(Effect input) { 46 super(input); 47 } 48 49 protected FilterEffect(Effect input1, Effect input2) { 50 super(input1, input2); 51 } 52 53 @Override 54 public BaseBounds getBounds(BaseTransform transform, 55 Effect defaultInput) 56 { 57 int numinputs = getNumInputs(); 58 RenderState rstate = getRenderState(null, transform, null, 59 null, defaultInput); 60 BaseTransform inputtx = rstate.getInputTransform(transform); 61 BaseBounds ret; 62 if (numinputs == 1) { 63 Effect input = getDefaultedInput(0, defaultInput); 64 ret = input.getBounds(inputtx, defaultInput); 65 } else { 66 BaseBounds inputBounds[] = new BaseBounds[numinputs]; 67 for (int i = 0; i < numinputs; i++) { 68 Effect input = getDefaultedInput(i, defaultInput); 69 inputBounds[i] = input.getBounds(inputtx, defaultInput); 70 } 71 ret = combineBounds(inputBounds); 72 } 73 return transformBounds(rstate.getResultTransform(transform), ret); 74 } 75 76 protected static Rectangle untransformClip(BaseTransform transform, 77 Rectangle clip) 78 { 79 if (transform.isIdentity() || clip == null || clip.isEmpty()) { 80 return clip; 81 } 82 // We are asked to produce samples for the pixels in the 83 // Rectangular clip. The samples requested are delivered for 84 // the centers of the pixels for every pixel in that range. 85 // Thus, we need valid data for the clip inset by 0.5 pixels 86 // all around. 87 // But, when we untransform, we need to make sure that the data 88 // we provide can be used to provide a valid sample for each of 89 // those points. If the mapped sample coordinate falls on a 90 // non-integer coordinate then we need the data for the 4 pixels 91 // around that point. Thus, we need a sample for the pixel that it 92 // falls on, and potentially a sample for the next pixel over if 93 // we are within 0.5 pixels of the edge of those border pixels. 94 // The full operation is then: 95 // clip.inset(0.5) // reduce to requested pixel centers 117 } 118 return transformedBounds; 119 } 120 RectBounds b = new RectBounds(clip); 121 try { 122 b.grow(-0.5f, -0.5f); 123 b = (RectBounds) transform.inverseTransform(b, b); 124 b.grow(0.5f, 0.5f); 125 transformedBounds.setBounds(b); 126 } catch (NoninvertibleTransformException e) { 127 // Non-invertible means the transform has collapsed onto 128 // a point or line and so the results of the effect are 129 // not visible so we can use the empty bounds object we 130 // created for transformedBounds. Ideally this would be 131 // checked further up in the chain, but in case we get here 132 // we might as well do as little work as we can. 133 } 134 return transformedBounds; 135 } 136 137 /** 138 * Returns the object representing the rendering strategy and state for 139 * the filter operation characterized by the specified arguments. 140 * This call can also be used to get a state object for non-rendering 141 * cases like querying the bounds of an operation in which case the 142 * {@link FilterContext} object may be null. 143 * {@code outputClip} and {@code renderHelper} may always be null just 144 * as they may be null for a given filter operation. 145 * 146 * @param fctx the context object that would be used by the Renderer 147 * if this call is preparing for a render operation, or null 148 * @param transform the transform for the output of this operation 149 * @param outputClip the clip rectangle that may restrict this operation, or null 150 * @param renderHelper the rendering helper object that can be used to shortcut 151 * this operation under certain conditions, or null 152 * @param defaultInput the {@link Effect} to be used in place of any null inputs 153 * @return 154 */ 155 public abstract T getRenderState(FilterContext fctx, 156 BaseTransform transform, 157 Rectangle outputClip, 158 Object renderHelper, 159 Effect defaultInput); 160 161 @Override 162 public ImageData filter(FilterContext fctx, 163 BaseTransform transform, 164 Rectangle outputClip, 165 Object renderHelper, 166 Effect defaultInput) 167 { 168 T rstate = getRenderState(fctx, transform, outputClip, 169 renderHelper, defaultInput); 170 int numinputs = getNumInputs(); 171 ImageData inputDatas[] = new ImageData[numinputs]; 172 Rectangle filterClip; 173 BaseTransform inputtx = rstate.getInputTransform(transform); 174 BaseTransform resulttx = rstate.getResultTransform(transform); 175 if (resulttx.isIdentity()) { 176 filterClip = outputClip; 177 } else { 178 filterClip = untransformClip(resulttx, outputClip); 179 } 180 for (int i = 0; i < numinputs; i++) { 181 Effect input = getDefaultedInput(i, defaultInput); 182 inputDatas[i] = 183 input.filter(fctx, inputtx, 184 rstate.getInputClip(i, filterClip), 185 null, defaultInput); 186 if (!inputDatas[i].validate(fctx)) { 187 for (int j = 0; j <= i; j++) { 188 inputDatas[j].unref(); 189 } 190 return new ImageData(fctx, null, null); 191 } 192 } 193 ImageData ret = filterImageDatas(fctx, inputtx, filterClip, rstate, inputDatas); 194 for (int i = 0; i < numinputs; i++) { 195 inputDatas[i].unref(); 196 } 197 if (!resulttx.isIdentity()) { 198 if (renderHelper instanceof ImageDataRenderer) { 199 ImageDataRenderer renderer = (ImageDataRenderer) renderHelper; 200 renderer.renderImage(ret, resulttx, fctx); 201 ret.unref(); 202 ret = null; 203 } else { 204 ret = ret.transform(resulttx); 205 } 206 } 207 return ret; 208 } 209 210 @Override 211 public Point2D transform(Point2D p, Effect defaultInput) { 212 return getDefaultedInput(0, defaultInput).transform(p, defaultInput); 213 } 214 215 @Override 216 public Point2D untransform(Point2D p, Effect defaultInput) { 217 return getDefaultedInput(0, defaultInput).untransform(p, defaultInput); 218 } 219 220 protected abstract ImageData filterImageDatas(FilterContext fctx, 221 BaseTransform transform, 222 Rectangle outputClip, 223 T rstate, 224 ImageData... inputDatas); 225 } |