1 /* 2 * Copyright (c) 2010, 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 javafx.scene.effect; 27 28 import javafx.beans.property.DoubleProperty; 29 import javafx.beans.property.DoublePropertyBase; 30 import javafx.beans.property.IntegerProperty; 31 import javafx.beans.property.IntegerPropertyBase; 32 import javafx.beans.property.ObjectProperty; 33 import javafx.scene.Node; 34 35 import com.sun.javafx.util.Utils; 36 import com.sun.javafx.effect.EffectDirtyBits; 37 import com.sun.javafx.geom.BaseBounds; 38 import com.sun.javafx.geom.transform.BaseTransform; 39 import com.sun.javafx.scene.BoundsAccessor; 40 41 42 /** 43 * A blur effect using a simple box filter kernel, with separately 44 * configurable sizes in both dimensions, and an iteration parameter 45 * that controls the quality of the resulting blur. 46 * 47 * <p> 48 * Example: 49 * <pre><code> 50 * BoxBlur boxBlur = new BoxBlur(); 51 * boxBlur.setWidth(10); 52 * boxBlur.setHeight(3); 53 * boxBlur.setIterations(3); 54 * 55 * Text text = new Text(); 56 * text.setText("Blurry Text!"); 57 * text.setFill(Color.web("0x3b596d")); 58 * text.setFont(Font.font(null, FontWeight.BOLD, 50)); 59 * text.setX(10); 60 * text.setY(50); 61 * text.setEffect(boxBlur); 62 * </pre></code> 63 * <p> 64 * The code above produces the following: 65 * </p> 66 * <p> 67 * <img src="doc-files/boxblur.png"/> 68 * </p> 69 * @since JavaFX 2.0 70 */ 71 public class BoxBlur extends Effect { 72 73 /** 74 * Creates a new instance of BoxBlur with default parameters. 75 */ 76 public BoxBlur() {} 77 78 /** 79 * Creates a new instance of BoxBlur with specified width, height and 80 * iterations. 81 * @param width the horizontal dimension of the blur effect 82 * @param height the vertical dimension of the blur effect 83 * @param iterations the number of times to iterate the blur effect to 84 * improve its "quality" or "smoothness" 85 */ 86 public BoxBlur(double width, double height, int iterations) { 87 setWidth(width); 88 setHeight(height); 89 setIterations(iterations); 90 } 91 92 @Override 93 com.sun.scenario.effect.BoxBlur impl_createImpl() { 94 return new com.sun.scenario.effect.BoxBlur(); 95 }; 96 /** 97 * The input for this {@code Effect}. 98 * If set to {@code null}, or left unspecified, a graphical image of 99 * the {@code Node} to which the {@code Effect} is attached will be 100 * used as the input. 101 * @defaultValue null 102 */ 103 private ObjectProperty<Effect> input; 104 105 106 public final void setInput(Effect value) { 107 inputProperty().set(value); 108 } 109 110 public final Effect getInput() { 111 return input == null ? null : input.get(); 112 } 113 114 public final ObjectProperty<Effect> inputProperty() { 115 if (input == null) { 116 input = new EffectInputProperty("input"); 117 } 118 return input; 119 } 120 121 @Override 122 boolean impl_checkChainContains(Effect e) { 123 Effect localInput = getInput(); 124 if (localInput == null) 125 return false; 126 if (localInput == e) 127 return true; 128 return localInput.impl_checkChainContains(e); 129 } 130 131 /** 132 * The horizontal dimension of the blur effect. 133 * The color information for a given pixel will be spread across 134 * a Box of the indicated width centered over the pixel. 135 * Values less than or equal to 1 will not spread the color data 136 * beyond the pixel where it originated from and so will have 137 * no effect. 138 * <pre> 139 * Min: 0.0 140 * Max: 255.0 141 * Default: 5.0 142 * Identity: <1.0 143 * </pre> 144 * @defaultValue 5.0 145 */ 146 private DoubleProperty width; 147 148 149 public final void setWidth(double value) { 150 widthProperty().set(value); 151 } 152 153 public final double getWidth() { 154 return width == null ? 5 : width.get(); 155 } 156 157 public final DoubleProperty widthProperty() { 158 if (width == null) { 159 width = new DoublePropertyBase(5) { 160 161 @Override 162 public void invalidated() { 163 markDirty(EffectDirtyBits.EFFECT_DIRTY); 164 effectBoundsChanged(); 165 } 166 167 @Override 168 public Object getBean() { 169 return BoxBlur.this; 170 } 171 172 @Override 173 public String getName() { 174 return "width"; 175 } 176 }; 177 } 178 return width; 179 } 180 181 /** 182 * The vertical dimension of the blur effect. 183 * The color information for a given pixel will be spread across 184 * a Box of the indicated height centered over the pixel. 185 * Values less than or equal to 1 will not spread the color data 186 * beyond the pixel where it originated from and so will have 187 * no effect. 188 * <pre> 189 * Min: 0.0 190 * Max: 255.0 191 * Default: 5.0 192 * Identity: <1.0 193 * </pre> 194 * @defaultValue 5.0 195 */ 196 private DoubleProperty height; 197 198 199 public final void setHeight(double value) { 200 heightProperty().set(value); 201 } 202 203 public final double getHeight() { 204 return height == null ? 5 : height.get(); 205 } 206 207 public final DoubleProperty heightProperty() { 208 if (height == null) { 209 height = new DoublePropertyBase(5) { 210 211 @Override 212 public void invalidated() { 213 markDirty(EffectDirtyBits.EFFECT_DIRTY); 214 effectBoundsChanged(); 215 } 216 217 @Override 218 public Object getBean() { 219 return BoxBlur.this; 220 } 221 222 @Override 223 public String getName() { 224 return "height"; 225 } 226 }; 227 } 228 return height; 229 } 230 231 /** 232 * The number of times to iterate the blur effect to improve its 233 * "quality" or "smoothness". 234 * Iterating the effect 3 times approximates the quality of a 235 * Gaussian Blur to within 3%. 236 * <pre> 237 * Min: 0 238 * Max: 3 239 * Default: 1 240 * Identity: 0 241 * </pre> 242 * @defaultValue 1 243 */ 244 private IntegerProperty iterations; 245 246 247 public final void setIterations(int value) { 248 iterationsProperty().set(value); 249 } 250 251 public final int getIterations() { 252 return iterations == null ? 1 : iterations.get(); 253 } 254 255 public final IntegerProperty iterationsProperty() { 256 if (iterations == null) { 257 iterations = new IntegerPropertyBase(1) { 258 259 @Override 260 public void invalidated() { 261 markDirty(EffectDirtyBits.EFFECT_DIRTY); 262 effectBoundsChanged(); 263 } 264 265 @Override 266 public Object getBean() { 267 return BoxBlur.this; 268 } 269 270 @Override 271 public String getName() { 272 return "iterations"; 273 } 274 }; 275 } 276 return iterations; 277 } 278 279 private int getClampedWidth() { 280 return Utils.clamp(0, (int) getWidth(), 255); 281 } 282 283 private int getClampedHeight() { 284 return Utils.clamp(0, (int) getHeight(), 255); 285 } 286 287 private int getClampedIterations() { 288 return Utils.clamp(0, getIterations(), 3); 289 } 290 291 @Override 292 void impl_update() { 293 Effect localInput = getInput(); 294 if (localInput != null) { 295 localInput.impl_sync(); 296 } 297 298 com.sun.scenario.effect.BoxBlur peer = 299 (com.sun.scenario.effect.BoxBlur) impl_getImpl(); 300 peer.setInput(localInput == null ? null : localInput.impl_getImpl()); 301 peer.setHorizontalSize(getClampedWidth()); 302 peer.setVerticalSize(getClampedHeight()); 303 peer.setPasses(getClampedIterations()); 304 } 305 306 /** 307 * @treatAsPrivate implementation detail 308 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 309 */ 310 @Deprecated 311 @Override 312 public BaseBounds impl_getBounds(BaseBounds bounds, 313 BaseTransform tx, 314 Node node, 315 BoundsAccessor boundsAccessor) { 316 bounds = getInputBounds(bounds, 317 BaseTransform.IDENTITY_TRANSFORM, 318 node, boundsAccessor, 319 getInput()); 320 321 int localIterations = getClampedIterations(); 322 323 int hgrow = getKernelSize(getClampedWidth(), localIterations); 324 int vgrow = getKernelSize(getClampedHeight(), localIterations); 325 326 bounds = bounds.deriveWithPadding(hgrow, vgrow, 0); 327 328 return transformBounds(tx, bounds); 329 } 330 331 /** 332 * @treatAsPrivate implementation detail 333 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 334 */ 335 @Deprecated 336 @Override 337 public Effect impl_copy() { 338 BoxBlur bb = new BoxBlur(this.getWidth(), this.getHeight(), this.getIterations()); 339 bb.setInput(this.getInput()); 340 return bb; 341 } 342 }