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