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 }