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.Struct;
027    import railo.runtime.type.util.CollectionUtil;
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":"")+" ["+CollectionUtil.getKeyList(parameters,", ")+"] "+(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    }