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.Point2D; 32 import com.sun.javafx.geom.Rectangle; 33 import com.sun.javafx.geom.transform.BaseTransform; 34 import com.sun.scenario.effect.light.Light; 35 36 /** 37 * An effect that applies diffuse and specular lighting to an arbitrary 38 * input using a positionable light source. 39 */ 40 public class PhongLighting extends CoreEffect { 41 42 private float surfaceScale; 43 private float diffuseConstant; 44 private float specularConstant; 45 private float specularExponent; 46 private Light light; 47 48 /** 49 * Constructs a new {@code PhongLighting} effect for the given 50 * {@code Light}, with default values for all other properties, 51 * using the default input for source data. 52 * This is a convenience constructor that automatically generates a 53 * bump map using the default input. 54 * 55 * @param light the light source 56 * @throws IllegalArgumentException if {@code light} is null 57 */ 58 public PhongLighting(Light light) { 59 this(light, new GaussianShadow(10f), DefaultInput); 60 } 61 62 /** 63 * Constructs a new {@code PhongLighting} effect for the given 64 * {@code Light} and the given bump and content input {@code Effect}s 65 * with default values for all other properties. 66 * 67 * @param light the light source 68 * @param bumpInput the input containing the bump map 69 * @param contentInput the input containing the content data 70 * @throws IllegalArgumentException if {@code light} is null 71 */ 72 public PhongLighting(Light light, Effect bumpInput, Effect contentInput) { 73 super(bumpInput, contentInput); 74 75 this.surfaceScale = 1f; 76 this.diffuseConstant = 1f; 77 this.specularConstant = 1f; 78 this.specularExponent = 1f; 79 80 setLight(light); 81 } 82 83 /** 84 * Returns the bump input for this {@code Effect}. 85 * 86 * @return the bump input for this {@code Effect} 87 */ 88 public final Effect getBumpInput() { 89 return getInputs().get(0); 90 } 91 92 /** 93 * Sets the bump input for this {@code Effect} to a specific 94 * {@code Effect} or to the default input if {@code input} is 95 * {@code null}. 96 * 97 * @param bumpInput the bump input for this {@code Effect} 98 */ 99 public void setBumpInput(Effect bumpInput) { 100 setInput(0, bumpInput); 101 } 102 103 /** 104 * Returns the content input for this {@code Effect}. 105 * 106 * @return the content input for this {@code Effect} 107 */ 108 public final Effect getContentInput() { 109 return getInputs().get(1); 110 } 111 112 private Effect getContentInput(Effect defaultInput) { 113 return getDefaultedInput(1, defaultInput); 114 } 115 116 /** 117 * Sets the content input for this {@code Effect} to a specific 118 * {@code Effect} or to the default input if {@code input} is 119 * {@code null}. 120 * 121 * @param contentInput the content input for this {@code Effect} 122 */ 123 public void setContentInput(Effect contentInput) { 124 setInput(1, contentInput); 125 } 126 127 /** 128 * Returns the light source. 129 * 130 * @return the light source 131 */ 132 public Light getLight() { 133 return light; 134 } 135 136 /** 137 * Sets the light source. 138 * 139 * @param light the light source 140 * @throws IllegalArgumentException if {@code light} is null 141 */ 142 public void setLight(Light light) { 143 if (light == null) { 144 throw new IllegalArgumentException("Light must be non-null"); 145 } 146 this.light = light; 147 updatePeerKey("PhongLighting_" + light.getType().name()); 148 } 149 150 /** 151 * Returns the diffuse constant. 152 * 153 * @return the diffuse constant value 154 */ 155 public float getDiffuseConstant() { 156 return diffuseConstant; 157 } 158 159 /** 160 * Sets the diffuse constant. 161 * <pre> 162 * Min: 0.0 163 * Max: 2.0 164 * Default: 1.0 165 * Identity: n/a 166 * </pre> 167 * 168 * @param diffuseConstant the diffuse constant value 169 * @throws IllegalArgumentException if {@code diffuseConstant} is outside 170 * the allowable range 171 */ 172 public void setDiffuseConstant(float diffuseConstant) { 173 if (diffuseConstant < 0f || diffuseConstant > 2f) { 174 throw new IllegalArgumentException("Diffuse constant must be in the range [0,2]"); 175 } 176 float old = this.diffuseConstant; 177 this.diffuseConstant = diffuseConstant; 178 } 179 180 /** 181 * Returns the specular constant. 182 * 183 * @return the specular constant value 184 */ 185 public float getSpecularConstant() { 186 return specularConstant; 187 } 188 189 /** 190 * Sets the specular constant. 191 * <pre> 192 * Min: 0.0 193 * Max: 2.0 194 * Default: 1.0 195 * Identity: n/a 196 * </pre> 197 * 198 * @param specularConstant the specular constant value 199 * @throws IllegalArgumentException if {@code specularConstant} is outside 200 * the allowable range 201 */ 202 public void setSpecularConstant(float specularConstant) { 203 if (specularConstant < 0f || specularConstant > 2f) { 204 throw new IllegalArgumentException("Specular constant must be in the range [0,2]"); 205 } 206 float old = this.specularConstant; 207 this.specularConstant = specularConstant; 208 } 209 210 /** 211 * Returns the specular exponent. 212 * 213 * @return the specular exponent value 214 */ 215 public float getSpecularExponent() { 216 return specularExponent; 217 } 218 219 /** 220 * Sets the specular exponent. 221 * <pre> 222 * Min: 0.0 223 * Max: 40.0 224 * Default: 1.0 225 * Identity: n/a 226 * </pre> 227 * 228 * @param specularExponent the specular exponent value 229 * @throws IllegalArgumentException if {@code specularExponent} is outside 230 * the allowable range 231 */ 232 public void setSpecularExponent(float specularExponent) { 233 if (specularExponent < 0f || specularExponent > 40f) { 234 throw new IllegalArgumentException("Specular exponent must be in the range [0,40]"); 235 } 236 float old = this.specularExponent; 237 this.specularExponent = specularExponent; 238 } 239 240 /** 241 * Returns the surface scale. 242 * 243 * @return the surface scale value 244 */ 245 public float getSurfaceScale() { 246 return surfaceScale; 247 } 248 249 /** 250 * Sets the surface scale. 251 * <pre> 252 * Min: 0.0 253 * Max: 10.0 254 * Default: 1.0 255 * Identity: n/a 256 * </pre> 257 * 258 * @param surfaceScale the surface scale value 259 * @throws IllegalArgumentException if {@code surfaceScale} is outside 260 * the allowable range 261 */ 262 public void setSurfaceScale(float surfaceScale) { 263 if (surfaceScale < 0f || surfaceScale > 10f) { 264 throw new IllegalArgumentException("Surface scale must be in the range [0,10]"); 265 } 266 float old = this.surfaceScale; 267 this.surfaceScale = surfaceScale; 268 } 269 270 @Override 271 public BaseBounds getBounds(BaseTransform transform, 272 Effect defaultInput) 273 { 274 // effect inherits its bounds from the content input 275 return getContentInput(defaultInput).getBounds(transform, defaultInput); 276 } 277 278 @Override 279 public Rectangle getResultBounds(BaseTransform transform, 280 Rectangle outputClip, 281 ImageData... inputDatas) 282 { 283 // result inherits its dimensions from the content input 284 return super.getResultBounds(transform, outputClip, inputDatas[1]); 285 } 286 287 @Override 288 public Point2D transform(Point2D p, Effect defaultInput) { 289 return getContentInput(defaultInput).transform(p, defaultInput); 290 } 291 292 @Override 293 public Point2D untransform(Point2D p, Effect defaultInput) { 294 return getContentInput(defaultInput).untransform(p, defaultInput); 295 } 296 297 @Override 298 protected Rectangle getInputClip(int inputIndex, 299 BaseTransform transform, 300 Rectangle outputClip) 301 { 302 // RT-27564 303 // TODO: Since only the content input is used for the output 304 // bounds we could attempt to factor the bounds of the content 305 // input in our answer here for the other inputs. 306 return outputClip; 307 } 308 309 @Override 310 public boolean reducesOpaquePixels() { 311 final Effect contentInput = getContentInput(); 312 return contentInput != null && contentInput.reducesOpaquePixels(); 313 } 314 315 @Override 316 public DirtyRegionContainer getDirtyRegions(Effect defaultInput, DirtyRegionPool regionPool) { 317 Effect bump = getDefaultedInput(0, defaultInput); 318 DirtyRegionContainer drc1 = bump.getDirtyRegions(defaultInput, regionPool); 319 drc1.grow(1, 1); 320 321 Effect content = getDefaultedInput(1, defaultInput); 322 DirtyRegionContainer drc2 = content.getDirtyRegions(defaultInput, regionPool); 323 324 drc1.merge(drc2); 325 regionPool.checkIn(drc2); 326 327 return drc1; 328 } 329 }