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.BoxBlurState;
  36 import com.sun.scenario.effect.impl.state.LinearConvolveKernel;
  37 
  38 /**
  39  * A blur effect using a box-shaped convolution kernel, with a configurable
  40  * size for each dimension of the kernel and a number of passes to control
  41  * the quality of the blur.
  42  */
  43 public class BoxBlur extends LinearConvolveCoreEffect {
  44 
  45     private final BoxBlurState state = new BoxBlurState();
  46 
  47     /**
  48      * Constructs a new {@code BoxBlur} effect with
  49      * the default blur sizes (1, 1)
  50      * and the default number of passes (1),
  51      * using the default input for source data.
  52      * This is a shorthand equivalent to:
  53      * <pre>
  54      *     new BoxBlur(1, 1, 1, DefaultInput)
  55      * </pre>
  56      */
  57     public BoxBlur() {
  58         this(1.0f, 1.0f);
  59     }
  60 
  61     /**
  62      * Constructs a new {@code BoxBlur} effect with
  63      * the given blur sizes
  64      * and the default number of passes (1),
  65      * using the default input for source data.
  66      * This is a shorthand equivalent to:
  67      * <pre>
  68      *     new BoxBlur(hsize, vsize, 1, DefaultInput)
  69      * </pre>
  70      *
  71      * @param hsize the horizontal size of the BoxBlur kernel
  72      * @param vsize the vertical size of the BoxBlur kernel
  73      * @throws IllegalArgumentException if either {@code hsize}
  74      * or {@code vsize} is outside the allowable range
  75      */
  76     public BoxBlur(float hsize, float vsize) {
  77         this(hsize, vsize, 1, DefaultInput);
  78     }
  79 
  80     /**
  81      * Constructs a new {@code BoxBlur} effect with
  82      * the given blur sizes
  83      * and number of passes,
  84      * using the default input for source data.
  85      * This is a shorthand equivalent to:
  86      * <pre>
  87      *     new BoxBlur(hsize, vsize, passes, DefaultInput)
  88      * </pre>
  89      *
  90      * @param hsize the horizontal size of the BoxBlur kernel
  91      * @param vsize the vertical size of the BoxBlur kernel
  92      * @param passes the number of blur passes to execute
  93      * @throws IllegalArgumentException if either {@code hsize}
  94      * or {@code vsize} or {@code passes}
  95      * is outside the allowable range
  96      */
  97     public BoxBlur(float hsize, float vsize, int passes) {
  98         this(hsize, vsize, passes, DefaultInput);
  99     }
 100 
 101     /**
 102      * Constructs a new {@code BoxBlur} effect with
 103      * the given blur sizes
 104      * and number of passes,
 105      * using the output of the specified effect for source data.
 106      *
 107      * @param hsize the horizontal size of the BoxBlur kernel
 108      * @param vsize the vertical size of the BoxBlur kernel
 109      * @param passes the number of blur passes to execute
 110      * @param input the single input {@code Effect}
 111      * @throws IllegalArgumentException if either {@code hsize}
 112      * or {@code vsize} or {@code passes}
 113      * is outside the allowable range
 114      */
 115     public BoxBlur(float hsize, float vsize, int passes, Effect input) {
 116         super(input);
 117         setHorizontalSize(hsize);
 118         setVerticalSize(vsize);
 119         setPasses(passes);
 120     }
 121 
 122     @Override
 123     LinearConvolveKernel getState() {
 124         return state;
 125     }
 126 
 127     /**
 128      * Returns the input for this {@code Effect}.
 129      *
 130      * @return the input for this {@code Effect}
 131      */
 132     public final Effect getInput() {
 133         return getInputs().get(0);
 134     }
 135 
 136     /**
 137      * Sets the input for this {@code Effect}.
 138      * Sets the input for this {@code Effect} to a specific
 139      * {@code Effect} or to the default input if {@code input} is
 140      * {@code null}.
 141      *
 142      * @param input the input for this {@code Effect}
 143      */
 144     public void setInput(Effect input) {
 145         setInput(0, input);
 146     }
 147 
 148     /**
 149      * Returns the horizontal size of the effect kernel.
 150      *
 151      * @return the horizontal size of the effect kernel
 152      */
 153     public float getHorizontalSize() {
 154         return state.getHsize();
 155     }
 156 
 157     /**
 158      * Sets the horizontal size of the effect kernel.
 159      * <pre>
 160      *       Min:   0
 161      *       Max: 255
 162      *   Default:   1
 163      *  Identity:   0
 164      * </pre>
 165      *
 166      * @param hsize the horizontal size of the effect kernel
 167      * @throws IllegalArgumentException if {@code hsize}
 168      * is outside the allowable range
 169      */
 170     public final void setHorizontalSize(float hsize) {
 171         state.setHsize(hsize);
 172     }
 173 
 174     /**
 175      * Returns the vertical size of the effect kernel.
 176      *
 177      * @return the vertical size of the effect kernel
 178      */
 179     public float getVerticalSize() {
 180         return state.getVsize();
 181     }
 182 
 183     /**
 184      * Sets the vertical size of the effect kernel.
 185      * <pre>
 186      *       Min:   0
 187      *       Max: 255
 188      *   Default:   1
 189      *  Identity:   0
 190      * </pre>
 191      *
 192      * @param vsize the vertical size of the effect kernel
 193      * @throws IllegalArgumentException if {@code vsize}
 194      * is outside the allowable range
 195      */
 196     public final void setVerticalSize(float vsize) {
 197         state.setVsize(vsize);
 198     }
 199 
 200     /**
 201      * Returns the number of passes of the effect kernel to control the
 202      * quality of the blur.
 203      *
 204      * @return the number of passes of the effect kernel
 205      */
 206     public int getPasses() {
 207         return state.getBlurPasses();
 208     }
 209 
 210     /**
 211      * Sets the number of passes of the effect kernel to control the
 212      * quality of the blur.
 213      * <pre>
 214      *       Min:   0
 215      *       Max:   3
 216      *   Default:   1
 217      *  Identity:   0
 218      * </pre>
 219      * A setting of 1 creates a low quality blur.  A setting of 3 creates
 220      * a blur that is very close to a Gaussian blur.
 221      *
 222      * @param passes
 223      * @throws IllegalArgumentException if {@code passes} is outside the
 224      * allowable range
 225      */
 226     public final void setPasses(int passes) {
 227         state.setBlurPasses(passes);
 228     }
 229 
 230     @Override
 231     public AccelType getAccelType(FilterContext fctx) {
 232         return Renderer.getRenderer(fctx).getAccelType();
 233     }
 234 
 235     @Override
 236     public BaseBounds getBounds(BaseTransform transform, Effect defaultInput) {
 237         BaseBounds r = super.getBounds(null, defaultInput);
 238         int hgrow = state.getKernelSize(0) / 2;
 239         int vgrow = state.getKernelSize(1) / 2;
 240         RectBounds ret = new RectBounds(r.getMinX(), r.getMinY(), r.getMaxX(), r.getMaxY());
 241         ret.grow(hgrow, vgrow);
 242         return transformBounds(transform, ret);
 243     }
 244 
 245     @Override
 246     public Rectangle getResultBounds(BaseTransform transform,
 247                                      Rectangle outputClip,
 248                                      ImageData... inputDatas)
 249     {
 250         Rectangle r = inputDatas[0].getTransformedBounds(null);
 251         r = state.getResultBounds(r, 0);
 252         r = state.getResultBounds(r, 1);
 253         r.intersectWith(outputClip);
 254         return r;
 255     }
 256 
 257     @Override
 258     public boolean reducesOpaquePixels() {
 259         if (!state.isNop()) {
 260             return true;
 261         }
 262         final Effect input = getInput();
 263         return input != null && input.reducesOpaquePixels();
 264     }
 265 
 266     @Override
 267     public DirtyRegionContainer getDirtyRegions(Effect defaultInput, DirtyRegionPool regionPool) {
 268         Effect di = getDefaultedInput(0, defaultInput);
 269         DirtyRegionContainer drc = di.getDirtyRegions(defaultInput, regionPool);
 270 
 271         drc.grow(state.getKernelSize(0) / 2, state.getKernelSize(1) / 2);
 272 
 273         return drc;
 274     }
 275 }