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 }