001 /* 002 * 003 004 Licensed under the Apache License, Version 2.0 (the "License"); 005 you may not use this file except in compliance with the License. 006 You may obtain a copy of the License at 007 008 http://www.apache.org/licenses/LICENSE-2.0 009 010 Unless required by applicable law or agreed to in writing, software 011 distributed under the License is distributed on an "AS IS" BASIS, 012 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 See the License for the specific language governing permissions and 014 limitations under the License. 015 */ 016 017 package railo.runtime.img.filter;import java.awt.Rectangle; 018 import java.awt.image.BufferedImage; 019 020 import railo.runtime.engine.ThreadLocalPageContext; 021 import railo.runtime.exp.ExpressionException; 022 import railo.runtime.exp.FunctionException; 023 import railo.runtime.exp.PageException; 024 import railo.runtime.img.math.Noise; 025 import railo.runtime.type.KeyImpl; 026 import railo.runtime.type.List; 027 import railo.runtime.type.Struct; 028 029 /** 030 * A filter which distorts an image by rippling it in the X or Y directions. 031 * The amplitude and wavelength of rippling can be specified as well as whether 032 * pixels going off the edges are wrapped or not. 033 */ 034 public class RippleFilter extends TransformFilter implements DynFiltering { 035 036 /** 037 * Sine wave ripples. 038 */ 039 public final static int SINE = 0; 040 041 /** 042 * Sawtooth wave ripples. 043 */ 044 public final static int SAWTOOTH = 1; 045 046 /** 047 * Triangle wave ripples. 048 */ 049 public final static int TRIANGLE = 2; 050 051 /** 052 * Noise ripples. 053 */ 054 public final static int NOISE = 3; 055 056 private float xAmplitude, yAmplitude; 057 private float xWavelength, yWavelength; 058 private int waveType; 059 060 /** 061 * Construct a RippleFilter. 062 */ 063 public RippleFilter() { 064 xAmplitude = 5.0f; 065 yAmplitude = 0.0f; 066 xWavelength = yWavelength = 16.0f; 067 } 068 069 /** 070 * Set the amplitude of ripple in the X direction. 071 * @param xAmplitude the amplitude (in pixels). 072 * @see #getXAmplitude 073 */ 074 public void setXAmplitude(float xAmplitude) { 075 this.xAmplitude = xAmplitude; 076 } 077 078 /** 079 * Get the amplitude of ripple in the X direction. 080 * @return the amplitude (in pixels). 081 * @see #setXAmplitude 082 */ 083 public float getXAmplitude() { 084 return xAmplitude; 085 } 086 087 /** 088 * Set the wavelength of ripple in the X direction. 089 * @param xWavelength the wavelength (in pixels). 090 * @see #getXWavelength 091 */ 092 public void setXWavelength(float xWavelength) { 093 this.xWavelength = xWavelength; 094 } 095 096 /** 097 * Get the wavelength of ripple in the X direction. 098 * @return the wavelength (in pixels). 099 * @see #setXWavelength 100 */ 101 public float getXWavelength() { 102 return xWavelength; 103 } 104 105 /** 106 * Set the amplitude of ripple in the Y direction. 107 * @param yAmplitude the amplitude (in pixels). 108 * @see #getYAmplitude 109 */ 110 public void setYAmplitude(float yAmplitude) { 111 this.yAmplitude = yAmplitude; 112 } 113 114 /** 115 * Get the amplitude of ripple in the Y direction. 116 * @return the amplitude (in pixels). 117 * @see #setYAmplitude 118 */ 119 public float getYAmplitude() { 120 return yAmplitude; 121 } 122 123 /** 124 * Set the wavelength of ripple in the Y direction. 125 * @param yWavelength the wavelength (in pixels). 126 * @see #getYWavelength 127 */ 128 public void setYWavelength(float yWavelength) { 129 this.yWavelength = yWavelength; 130 } 131 132 /** 133 * Get the wavelength of ripple in the Y direction. 134 * @return the wavelength (in pixels). 135 * @see #setYWavelength 136 */ 137 public float getYWavelength() { 138 return yWavelength; 139 } 140 141 142 /** 143 * Set the wave type. 144 * valid values are: 145 * - sine (default): Sine wave ripples. 146 * - sawtooth: Sawtooth wave ripples. 147 * - triangle: Triangle wave ripples. 148 * - noise: Noise ripples. 149 * @param waveType the type. 150 * @throws ExpressionException 151 * @see #getWaveType 152 */ 153 public void setWaveType(String waveType) throws ExpressionException { 154 155 String str=waveType.trim().toUpperCase(); 156 if("SINE".equals(str)) this.waveType = SINE; 157 else if("SAWTOOTH".equals(str)) this.waveType = SAWTOOTH; 158 else if("TRIANGLE".equals(str)) this.waveType = TRIANGLE; 159 else if("NOISE".equals(str)) this.waveType = NOISE; 160 else 161 throw new ExpressionException("invalid value ["+waveType+"] for waveType, valid values are [sine,sawtooth,triangle,noise]"); 162 163 } 164 165 /** 166 * Get the wave type. 167 * @return the type. 168 * @see #setWaveType 169 */ 170 public int getWaveType() { 171 return waveType; 172 } 173 174 protected void transformSpace(Rectangle r) { 175 if (edgeAction == ConvolveFilter.ZERO_EDGES) { 176 r.x -= (int)xAmplitude; 177 r.width += (int)(2*xAmplitude); 178 r.y -= (int)yAmplitude; 179 r.height += (int)(2*yAmplitude); 180 } 181 } 182 183 protected void transformInverse(int x, int y, float[] out) { 184 float nx = y / xWavelength; 185 float ny = x / yWavelength; 186 float fx, fy; 187 switch (waveType) { 188 case SINE: 189 default: 190 fx = (float)Math.sin(nx); 191 fy = (float)Math.sin(ny); 192 break; 193 case SAWTOOTH: 194 fx = ImageMath.mod(nx, 1); 195 fy = ImageMath.mod(ny, 1); 196 break; 197 case TRIANGLE: 198 fx = ImageMath.triangle(nx); 199 fy = ImageMath.triangle(ny); 200 break; 201 case NOISE: 202 fx = Noise.noise1(nx); 203 fy = Noise.noise1(ny); 204 break; 205 } 206 out[0] = x + xAmplitude * fx; 207 out[1] = y + yAmplitude * fy; 208 } 209 210 public String toString() { 211 return "Distort/Ripple..."; 212 } 213 214 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException { 215 //BufferedImage dst=ImageUtil.createBufferedImage(src,src.getWidth()+400,src.getHeight()+400); 216 Object o; 217 if((o=parameters.removeEL(KeyImpl.init("XAmplitude")))!=null)setXAmplitude(ImageFilterUtil.toFloatValue(o,"XAmplitude")); 218 if((o=parameters.removeEL(KeyImpl.init("XWavelength")))!=null)setXWavelength(ImageFilterUtil.toFloatValue(o,"XWavelength")); 219 if((o=parameters.removeEL(KeyImpl.init("YAmplitude")))!=null)setYAmplitude(ImageFilterUtil.toFloatValue(o,"YAmplitude")); 220 if((o=parameters.removeEL(KeyImpl.init("YWavelength")))!=null)setYWavelength(ImageFilterUtil.toFloatValue(o,"YWavelength")); 221 if((o=parameters.removeEL(KeyImpl.init("WaveType")))!=null)setWaveType(ImageFilterUtil.toString(o,"WaveType")); 222 if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction")); 223 if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation")); 224 225 // check for arguments not supported 226 if(parameters.size()>0) { 227 throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+List.arrayToList(parameters.keysAsString(),", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [XAmplitude, XWavelength, YAmplitude, YWavelength, WaveType, EdgeAction, Interpolation]"); 228 } 229 230 return filter(src, (BufferedImage)null); 231 } 232 }