1 /* 2 * Copyright (c) 2009, 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.impl.state; 27 28 import com.sun.scenario.effect.Color4f; 29 30 /** 31 * The helper class for defining a 1 dimensional linear convolution shadow 32 * kernel for the gaussian Shadow shader. This class leverages the 33 * {@link GaussianBlurState} class for defining the kernel and simply stores 34 * additional {@code shadowColor} and {@code spread} properties for the 35 * associated support methods for the shadow version of the shader. 36 */ 37 public class GaussianShadowState extends GaussianBlurState { 38 private Color4f shadowColor; 39 private float spread; 40 41 @Override 42 void checkRadius(float radius) { 43 if (radius < 0f || radius > 127f) { 44 throw new IllegalArgumentException("Radius must be in the range [1,127]"); 45 } 46 } 47 48 private int getPow2Scale(int pass) { 49 // Technically a radius of 127 would be scaled once down to a radius 50 // of 63.5 which generates a kernel of length 129, but we will be 51 // fudging it so that it only generates the central 127 values and 52 // the last value should be so close to 3*sigma to not be noticeable. 53 // This avoids a special "extra" scaling just for values > 126. 54 // So, we only ever scale at most once and then just short the kernel 55 // for those last couple of values. 56 return (getRadius(pass) > 63) ? -1 : 0; 57 } 58 59 @Override 60 public int getPow2ScaleX() { 61 return getPow2Scale(0); 62 } 63 64 @Override 65 public int getPow2ScaleY() { 66 return getPow2Scale(1); 67 } 68 69 @Override 70 public float getScaledRadius(int pass) { 71 float r = getRadius(pass); 72 int s = getPow2Scale(pass); 73 while (s < 0) { 74 r /= 2; 75 s++; 76 } 77 return r; 78 } 79 80 @Override 81 public int getScaledPad(int pass) { 82 float r = getScaledRadius(pass); 83 // A radius of 127 will be scaled back to 63.5 which technically 84 // requires a pad of 64 and a kernel size of 129, but we need to 85 // clip the kernel at 128 and thus the pad at 63. 86 return (r > 63.0f) ? 63 : (int) Math.ceil(r); 87 } 88 89 @Override 90 public int getScaledKernelSize(int pass) { 91 return getScaledPad(pass) * 2 + 1; 92 } 93 94 public Color4f getShadowColor() { 95 return shadowColor; 96 } 97 98 public void setShadowColor(Color4f shadowColor) { 99 if (shadowColor == null) { 100 throw new IllegalArgumentException("Color must be non-null"); 101 } 102 this.shadowColor = shadowColor; 103 } 104 105 @Override 106 public float getSpread() { 107 return spread; 108 } 109 110 public void setSpread(float spread) { 111 if (spread < 0f || spread > 1f) { 112 throw new IllegalArgumentException("Spread must be in the range [0,1]"); 113 } 114 this.spread = spread; 115 } 116 117 @Override 118 public boolean isNop() { 119 // The shadow operation is never a NOP since it replaces the colors 120 // if nothing else. 121 return false; 122 } 123 124 @Override 125 public boolean isNop(int pass) { 126 // Only the first pass of a shadow can be a NOP since the second 127 // pass always replaces the colors if nothing else. 128 return (pass == 0) && super.isNop(pass); 129 } 130 131 @Override 132 public boolean isShadow() { 133 return true; 134 } 135 136 @Override 137 public float[] getShadowColorComponents(int pass) { 138 return (pass == 0) 139 ? BLACK_COMPONENTS 140 : shadowColor.getPremultipliedRGBComponents(); 141 } 142 }