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.BaseBounds;
  29 import com.sun.javafx.geom.DirtyRegionContainer;
  30 import com.sun.javafx.geom.DirtyRegionPool;
  31 import com.sun.javafx.geom.RectBounds;
  32 import com.sun.javafx.geom.Rectangle;
  33 import com.sun.javafx.geom.transform.BaseTransform;
  34 import com.sun.scenario.effect.impl.Renderer;
  35 import com.sun.scenario.effect.impl.state.MotionBlurState;
  36 
  37 /**
  38  * A motion blur effect using a Gaussian convolution kernel, with a
  39  * configurable radius and angle.
  40  */
  41 public class MotionBlur extends CoreEffect {
  42 
  43     private MotionBlurState state = new MotionBlurState();
  44 
  45     /**
  46      * Constructs a new {@code MotionBlur} effect with the default radius
  47      * (10.0) and default angle (0.0), using the default input for source
  48      * data.
  49      * This is a shorthand equivalent to:
  50      * <pre>
  51      *     new MotionBlur(10f, 0f, DefaultInput)
  52      * </pre>
  53      */
  54     public MotionBlur() {
  55         this(10f, 0f, DefaultInput);
  56     }
  57 
  58     /**
  59      * Constructs a new {@code MotionBlur} effect with the given radius
  60      * and angle, using the default input for source data.
  61      * This is a shorthand equivalent to:
  62      * <pre>
  63      *     new MotionBlur(radius, angle, DefaultInput)
  64      * </pre>
  65      *
  66      * @param radius the radius of the Gaussian kernel
  67      * @param angle the angle of the motion effect, in radians
  68      * @throws IllegalArgumentException if {@code radius} is outside the
  69      * allowable range
  70      */
  71     public MotionBlur(float radius, float angle) {
  72         this(radius, angle, DefaultInput);
  73     }
  74 
  75     /**
  76      * Constructs a new {@code MotionBlur} effect with the given radius
  77      * and angle.
  78      *
  79      * @param radius the radius of the Gaussian kernel
  80      * @param angle the angle of the motion effect, in radians
  81      * @param input the single input {@code Effect}
  82      * @throws IllegalArgumentException if {@code radius} is outside the
  83      * allowable range
  84      */
  85     public MotionBlur(float radius, float angle, Effect input) {
  86         super(input);
  87         setRadius(radius);
  88         setAngle(angle);
  89     }
  90 
  91     @Override
  92     Object getState() {
  93         return state;
  94     }
  95 
  96     @Override
  97     public AccelType getAccelType(FilterContext fctx) {
  98         return Renderer.getRenderer(fctx).getAccelType();
  99     }
 100 
 101     /**
 102      * Returns the input for this {@code Effect}.
 103      *
 104      * @return the input for this {@code Effect}
 105      */
 106     public final Effect getInput() {
 107         return getInputs().get(0);
 108     }
 109 
 110     /**
 111      * Sets the input for this {@code Effect} to a specific {@code Effect}
 112      * or to the default input if {@code input} is {@code null}.
 113      *
 114      * @param input the input for this {@code Effect}
 115      */
 116     public void setInput(Effect input) {
 117         setInput(0, input);
 118     }
 119 
 120     /**
 121      * Returns the radius of the Gaussian kernel.
 122      *
 123      * @return the radius of the Gaussian kernel
 124      */
 125     public float getRadius() {
 126         return state.getRadius();
 127     }
 128 
 129     /**
 130      * Sets the radius of the Gaussian kernel.
 131      * <pre>
 132      *       Min:  0.0
 133      *       Max: 63.0
 134      *   Default: 10.0
 135      *  Identity:  0.0
 136      * </pre>
 137      *
 138      * @param radius the radius of the Gaussian kernel
 139      * @throws IllegalArgumentException if {@code radius} is outside the
 140      * allowable range
 141      */
 142     public void setRadius(float radius) {
 143         float old = state.getRadius();
 144         state.setRadius(radius);
 145     }
 146 
 147     /**
 148      * Returns the angle of the motion effect, in radians.
 149      *
 150      * @return the angle of the motion effect, in radians
 151      */
 152     public float getAngle() {
 153         return state.getAngle();
 154     }
 155 
 156     /**
 157      * Sets the angle of the motion effect, in radians.
 158      * <pre>
 159      *       Min: n/a
 160      *       Max: n/a
 161      *   Default: 0.0
 162      *  Identity: n/a
 163      * </pre>
 164      *
 165      * @param angle the angle of the motion effect, in radians
 166      */
 167     public void setAngle(float angle) {
 168         float old = state.getAngle();
 169         state.setAngle(angle);
 170     }
 171 
 172     @Override
 173     public BaseBounds getBounds(BaseTransform transform, Effect defaultInput) {
 174         BaseBounds r = super.getBounds(null, defaultInput);
 175         int hpad = state.getHPad();
 176         int vpad = state.getVPad();
 177         BaseBounds ret = new RectBounds(r.getMinX(), r.getMinY(), r.getMaxX(), r.getMaxY());
 178         ((RectBounds) ret).grow(hpad, vpad);
 179         return transformBounds(transform, ret);
 180     }
 181 
 182     @Override
 183     public Rectangle getResultBounds(BaseTransform transform,
 184                                      Rectangle outputClip,
 185                                      ImageData... inputDatas)
 186     {
 187         Rectangle r = super.getResultBounds(transform, outputClip, inputDatas);
 188         int hpad = state.getHPad();
 189         int vpad = state.getVPad();
 190         Rectangle ret = new Rectangle(r);
 191         ret.grow(hpad, vpad);
 192         return ret;
 193     }
 194 
 195     @Override
 196     public ImageData filterImageDatas(FilterContext fctx,
 197                                       BaseTransform transform,
 198                                       Rectangle outputClip,
 199                                       ImageData... inputs)
 200     {
 201         return state.filterImageDatas(this, fctx, transform, outputClip, inputs);
 202     }
 203 
 204     @Override
 205     public boolean operatesInUserSpace() {
 206         return true;
 207     }
 208 
 209     @Override
 210     protected Rectangle getInputClip(int inputIndex,
 211                                      BaseTransform transform,
 212                                      Rectangle outputClip)
 213     {
 214         // A blur needs as much "fringe" data from its input as it creates
 215         // around its output so we use the same expansion as is used in the
 216         // result bounds.
 217         if (outputClip != null) {
 218             int hpad = state.getHPad();
 219             int vpad = state.getVPad();
 220             if ((hpad | vpad) != 0) {
 221                 outputClip = new Rectangle(outputClip);
 222                 outputClip.grow(hpad, vpad);
 223             }
 224         }
 225         return outputClip;
 226     }
 227 
 228     @Override
 229     public boolean reducesOpaquePixels() {
 230         if (!state.isNop()) {
 231             return true;
 232         }
 233         final Effect input = getInput();
 234         return input != null && input.reducesOpaquePixels();
 235     }
 236 
 237     @Override
 238     public DirtyRegionContainer getDirtyRegions(Effect defaultInput, DirtyRegionPool regionPool) {
 239         Effect di = getDefaultedInput(0, defaultInput);
 240         DirtyRegionContainer drc = di.getDirtyRegions(defaultInput, regionPool);
 241         
 242         drc.grow(state.getHPad(), state.getVPad());
 243 
 244         return drc;
 245     }
 246 }