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.GaussianShadowState;
  36 import com.sun.scenario.effect.impl.state.LinearConvolveKernel;
  37 import com.sun.scenario.effect.impl.state.LinearConvolveRenderState;
  38 
  39 /**
  40  * A blurred shadow effect using a Gaussian convolution kernel, with a
  41  * configurable radius and shadow color.  Only the alpha channel of the
  42  * input is used to create the shadow effect.  The alpha value of each
  43  * pixel from the result of the blur operation is modulated with the
  44  * specified shadow color to produce the resulting image.
  45  */
  46 public class GaussianShadow extends AbstractShadow {
  47 
  48     private GaussianShadowState state = new GaussianShadowState();
  49 
  50     /**
  51      * Constructs a new {@code GaussianShadow} effect with the default radius
  52      * (10.0) and the default color ({@code Color4f.BLACK}), using the
  53      * default input for source data.
  54      * This is a shorthand equivalent to:
  55      * <pre>
  56      *     new GaussianShadow(10f, Color4f.BLACK, DefaultInput)
  57      * </pre>
  58      */
  59     public GaussianShadow() {
  60         this(10f);
  61     }
  62 
  63     /**
  64      * Constructs a new {@code GaussianShadow} effect with the given radius
  65      * and the default color ({@code Color4f.BLACK}), using the
  66      * default input for source data.
  67      * This is a shorthand equivalent to:
  68      * <pre>
  69      *     new GaussianShadow(radius, Color4f.BLACK, DefaultInput)
  70      * </pre>
  71      *
  72      * @param radius the radius of the Gaussian kernel
  73      * @throws IllegalArgumentException if {@code radius} is outside the
  74      * allowable range
  75      */
  76     public GaussianShadow(float radius) {
  77         this(radius, Color4f.BLACK);
  78     }
  79 
  80     /**
  81      * Constructs a new {@code GaussianShadow} effect with the given radius
  82      * and color, using the default input for source data.
  83      * This is a shorthand equivalent to:
  84      * <pre>
  85      *     new GaussianShadow(radius, color, DefaultInput)
  86      * </pre>
  87      *
  88      * @param radius the radius of the Gaussian kernel
  89      * @param color the shadow {@code Color4f}
  90      * @throws IllegalArgumentException if {@code radius} is outside the
  91      * allowable range
  92      */
  93     public GaussianShadow(float radius, Color4f color) {
  94         this(radius, color, DefaultInput);
  95     }
  96 
  97     /**
  98      * Constructs a new {@code GaussianShadow} effect with the given
  99      * radius and color.
 100      *
 101      * @param radius the radius of the Gaussian kernel
 102      * @param color the shadow {@code Color4f}
 103      * @param input the single input {@code Effect}
 104      * @throws IllegalArgumentException if {@code radius} is outside the
 105      * allowable range, or if {@code color} is null
 106      */
 107     public GaussianShadow(float radius, Color4f color, Effect input) {
 108         super(input);
 109         state.setRadius(radius);
 110         state.setShadowColor(color);
 111     }
 112 
 113 
 114     @Override
 115     LinearConvolveKernel getState() {
 116         return state;
 117     }
 118 
 119     @Override
 120     public AccelType getAccelType(FilterContext fctx) {
 121         return Renderer.getRenderer(fctx).getAccelType();
 122     }
 123 
 124     /**
 125      * Returns the input for this {@code Effect}.
 126      *
 127      * @return the input for this {@code Effect}
 128      */
 129     public final Effect getInput() {
 130         return getInputs().get(0);
 131     }
 132 
 133     /**
 134      * Sets the input for this {@code Effect} to a specific {@code Effect}
 135      * or to the default input if {@code input} is {@code null}.
 136      *
 137      * @param input the input for this {@code Effect}
 138      */
 139     public void setInput(Effect input) {
 140         setInput(0, input);
 141     }
 142 
 143     /**
 144      * Returns the radius of the Gaussian kernel.
 145      *
 146      * @return the radius of the Gaussian kernel
 147      */
 148     public float getRadius() {
 149         return state.getRadius();
 150     }
 151 
 152     /**
 153      * Sets the radius of the Gaussian kernel.
 154      * <pre>
 155      *       Min:   0.0
 156      *       Max: 127.0
 157      *   Default:  10.0
 158      *  Identity:   0.0
 159      * </pre>
 160      *
 161      * @param radius the radius of the Gaussian kernel
 162      * @throws IllegalArgumentException if {@code radius} is outside the
 163      * allowable range
 164      */
 165     public void setRadius(float radius) {
 166         float old = state.getRadius();
 167         state.setRadius(radius);
 168     }
 169 
 170     /**
 171      * Returns the horizontal radius of the Gaussian kernel.
 172      *
 173      * @return the horizontal radius of the Gaussian kernel
 174      */
 175     public float getHRadius() {
 176         return state.getHRadius();
 177     }
 178 
 179     /**
 180      * Sets the horizontal radius of the Gaussian kernel.
 181      * <pre>
 182      *       Min:   0.0
 183      *       Max: 127.0
 184      *   Default:  10.0
 185      *  Identity:   0.0
 186      * </pre>
 187      *
 188      * @param hradius the horizontal radius of the Gaussian kernel
 189      * @throws IllegalArgumentException if {@code radius} is outside the
 190      * allowable range
 191      */
 192     public void setHRadius(float hradius) {
 193         float old = state.getHRadius();
 194         state.setHRadius(hradius);
 195     }
 196 
 197     /**
 198      * Returns the vertical radius of the Gaussian kernel.
 199      *
 200      * @return the vertical radius of the Gaussian kernel
 201      */
 202     public float getVRadius() {
 203         return state.getVRadius();
 204     }
 205 
 206     /**
 207      * Sets the vertical radius of the Gaussian kernel.
 208      * <pre>
 209      *       Min:   0.0
 210      *       Max: 127.0
 211      *   Default:  10.0
 212      *  Identity:   0.0
 213      * </pre>
 214      *
 215      * @param vradius the vertical radius of the Gaussian kernel
 216      * @throws IllegalArgumentException if {@code radius} is outside the
 217      * allowable range
 218      */
 219     public void setVRadius(float vradius) {
 220         float old = state.getVRadius();
 221         state.setVRadius(vradius);
 222     }
 223 
 224     /**
 225      * Gets the spread of the shadow effect.
 226      *
 227      * @return the spread of the shadow effect
 228      */
 229     public float getSpread() {
 230         return state.getSpread();
 231     }
 232 
 233     /**
 234      * Sets the spread of the shadow effect.
 235      * The spread is the portion of the radius where the contribution of
 236      * the source material will be 100%.
 237      * The remaining portion of the radius will have a contribution
 238      * controlled by the Gaussian kernel.
 239      * A spread of {@code 0.0} will result in a pure Gaussian distribution
 240      * of the shadow.
 241      * A spread of {@code 1.0} will result in a solid growth outward of the
 242      * source material opacity to the limit of the radius with a very sharp
 243      * cutoff to transparency at the radius.
 244      * <pre>
 245      *       Min: 0.0
 246      *       Max: 1.0
 247      *   Default: 0.0
 248      *  Identity: 0.0
 249      * </pre>
 250      *
 251      * @param spread the spread of the shadow effect
 252      * @throws IllegalArgumentException if {@code spread} is outside the
 253      * allowable range
 254      */
 255     public void setSpread(float spread) {
 256         float old = state.getSpread();
 257         state.setSpread(spread);
 258     }
 259 
 260     /**
 261      * Returns the shadow color.
 262      *
 263      * @return the shadow color
 264      */
 265     public Color4f getColor() {
 266         return state.getShadowColor();
 267     }
 268 
 269     /**
 270      * Sets the shadow color.
 271      * <pre>
 272      *       Min: n/a
 273      *       Max: n/a
 274      *   Default: Color4f.BLACK
 275      *  Identity: n/a
 276      * </pre>
 277      *
 278      * @param color the shadow color
 279      * @throws IllegalArgumentException if {@code color} is null
 280      */
 281     public void setColor(Color4f color) {
 282         Color4f old = state.getShadowColor();
 283         state.setShadowColor(color);
 284     }
 285 
 286     public float getGaussianRadius() {
 287         return getRadius();
 288     }
 289 
 290     public float getGaussianWidth() {
 291         return getHRadius() * 2.0f + 1.0f;
 292     }
 293 
 294     public float getGaussianHeight() {
 295         return getVRadius() * 2.0f + 1.0f;
 296     }
 297 
 298     public void setGaussianRadius(float r) {
 299         setRadius(r);
 300     }
 301 
 302     public void setGaussianWidth(float w) {
 303         setHRadius(w < 1.0f ? 0.0f : ((w - 1.0f) / 2.0f));
 304     }
 305 
 306     public void setGaussianHeight(float h) {
 307         setVRadius(h < 1.0f ? 0.0f : ((h - 1.0f) / 2.0f));
 308     }
 309 
 310     public ShadowMode getMode() {
 311         return ShadowMode.GAUSSIAN;
 312     }
 313 
 314     public AbstractShadow implFor(ShadowMode mode) {
 315         int passes = 0;
 316         switch (mode) {
 317             case GAUSSIAN:
 318                 return this;
 319             case ONE_PASS_BOX:
 320                 passes = 1;
 321                 break;
 322             case TWO_PASS_BOX:
 323                 passes = 2;
 324                 break;
 325             case THREE_PASS_BOX:
 326                 passes = 3;
 327                 break;
 328         }
 329         BoxShadow box = new BoxShadow();
 330         box.setInput(getInput());
 331         box.setGaussianWidth(getGaussianWidth());
 332         box.setGaussianHeight(getGaussianHeight());
 333         box.setColor(getColor());
 334         box.setPasses(passes);
 335         box.setSpread(getSpread());
 336         return box;
 337     }
 338 
 339     @Override
 340     public BaseBounds getBounds(BaseTransform transform, Effect defaultInput) {
 341         BaseBounds r = super.getBounds(null, defaultInput);
 342         int hpad = state.getPad(0);
 343         int vpad = state.getPad(1);
 344         RectBounds ret = new RectBounds(r.getMinX(), r.getMinY(), r.getMaxX(), r.getMaxY());
 345         ret.grow(hpad, vpad);
 346         return transformBounds(transform, ret);
 347     }
 348 
 349     @Override
 350     public Rectangle getResultBounds(BaseTransform transform,
 351                                      Rectangle outputClip,
 352                                      ImageData... inputDatas)
 353     {
 354         Rectangle r = super.getResultBounds(transform, outputClip, inputDatas);
 355         int hpad = state.getPad(0);
 356         int vpad = state.getPad(1);
 357         Rectangle ret = new Rectangle(r);
 358         ret.grow(hpad, vpad);
 359         return ret;
 360     }
 361 
 362     @Override
 363     public boolean reducesOpaquePixels() {
 364         return true;
 365     }
 366 
 367     @Override
 368     public DirtyRegionContainer getDirtyRegions(Effect defaultInput, DirtyRegionPool regionPool) {
 369         Effect di = getDefaultedInput(0, defaultInput);
 370         DirtyRegionContainer drc = di.getDirtyRegions(defaultInput, regionPool);
 371         
 372         drc.grow(state.getPad(0), state.getPad(1));
 373 
 374         return drc;
 375     }
 376 }