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.geom.Point2D;
018    import java.awt.image.BufferedImage;
019    
020    import railo.runtime.engine.ThreadLocalPageContext;
021    import railo.runtime.exp.FunctionException;
022    import railo.runtime.exp.PageException;
023    import railo.runtime.type.KeyImpl;
024    import railo.runtime.type.List;
025    import railo.runtime.type.Struct;
026    
027    /**
028     * A filter which produces a water ripple distortion.
029     */
030    public class WaterFilter extends TransformFilter  implements DynFiltering {
031            
032            private float wavelength = 16;
033            private float amplitude = 10;
034            private float phase = 0;
035            private float centreX = 0.5f;
036            private float centreY = 0.5f;
037            private float radius = 50;
038    
039            private float radius2 = 0;
040            private float icentreX;
041            private float icentreY;
042    
043            public WaterFilter() {
044                    super(ConvolveFilter.CLAMP_EDGES );
045            }
046    
047            /**
048             * Set the wavelength of the ripples.
049             * @param wavelength the wavelength
050         * @see #getWavelength
051             */
052            public void setWavelength(float wavelength) {
053                    this.wavelength = wavelength;
054            }
055    
056            /**
057             * Get the wavelength of the ripples.
058             * @return the wavelength
059         * @see #setWavelength
060             */
061            public float getWavelength() {
062                    return wavelength;
063            }
064    
065            /**
066             * Set the amplitude of the ripples.
067             * @param amplitude the amplitude
068         * @see #getAmplitude
069             */
070            public void setAmplitude(float amplitude) {
071                    this.amplitude = amplitude;
072            }
073    
074            /**
075             * Get the amplitude of the ripples.
076             * @return the amplitude
077         * @see #setAmplitude
078             */
079            public float getAmplitude() {
080                    return amplitude;
081            }
082    
083            /**
084             * Set the phase of the ripples.
085             * @param phase the phase
086         * @see #getPhase
087             */
088            public void setPhase(float phase) {
089                    this.phase = phase;
090            }
091    
092            /**
093             * Get the phase of the ripples.
094             * @return the phase
095         * @see #setPhase
096             */
097            public float getPhase() {
098                    return phase;
099            }
100    
101            /**
102             * Set the centre of the effect in the X direction as a proportion of the image size.
103             * @param centreX the center
104         * @see #getCentreX
105             */
106            public void setCentreX( float centreX ) {
107                    this.centreX = centreX;
108            }
109    
110            /**
111             * Get the centre of the effect in the X direction as a proportion of the image size.
112             * @return the center
113         * @see #setCentreX
114             */
115            public float getCentreX() {
116                    return centreX;
117            }
118            
119            /**
120             * Set the centre of the effect in the Y direction as a proportion of the image size.
121             * @param centreY the center
122         * @see #getCentreY
123             */
124            public void setCentreY( float centreY ) {
125                    this.centreY = centreY;
126            }
127    
128            /**
129             * Get the centre of the effect in the Y direction as a proportion of the image size.
130             * @return the center
131         * @see #setCentreY
132             */
133            public float getCentreY() {
134                    return centreY;
135            }
136            
137            /**
138             * Set the centre of the effect as a proportion of the image size.
139             * @param centre the center
140         * @see #getCentre
141             */
142            public void setCentre( Point2D centre ) {
143                    this.centreX = (float)centre.getX();
144                    this.centreY = (float)centre.getY();
145            }
146    
147            /**
148             * Get the centre of the effect as a proportion of the image size.
149             * @return the center
150         * @see #setCentre
151             */
152            public Point2D getCentre() {
153                    return new Point2D.Float( centreX, centreY );
154            }
155            
156            /**
157             * Set the radius of the effect.
158             * @param radius the radius
159         * @min-value 0
160         * @see #getRadius
161             */
162            public void setRadius(float radius) {
163                    this.radius = radius;
164            }
165    
166            /**
167             * Get the radius of the effect.
168             * @return the radius
169         * @see #setRadius
170             */
171            public float getRadius() {
172                    return radius;
173            }
174    
175            private boolean inside(int v, int a, int b) {
176                    return a <= v && v <= b;
177            }
178            
179        public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
180                    icentreX = src.getWidth() * centreX;
181                    icentreY = src.getHeight() * centreY;
182                    if ( radius == 0 )
183                            radius = Math.min(icentreX, icentreY);
184                    radius2 = radius*radius;
185                    return super.filter( src, dst );
186            }
187            
188            protected void transformInverse(int x, int y, float[] out) {
189                    float dx = x-icentreX;
190                    float dy = y-icentreY;
191                    float distance2 = dx*dx + dy*dy;
192                    if (distance2 > radius2) {
193                            out[0] = x;
194                            out[1] = y;
195                    } else {
196                            float distance = (float)Math.sqrt(distance2);
197                            float amount = amplitude * (float)Math.sin(distance / wavelength * ImageMath.TWO_PI - phase);
198                            amount *= (radius-distance)/radius;
199                            if ( distance != 0 )
200                                    amount *= wavelength/distance;
201                            out[0] = x + dx*amount;
202                            out[1] = y + dy*amount;
203                    }
204            }
205    
206            public String toString() {
207                    return "Distort/Water Ripples...";
208            }
209            
210            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src);
211                    Object o;
212                    if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toFloatValue(o,"Radius"));
213                    if((o=parameters.removeEL(KeyImpl.init("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX"));
214                    if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY"));
215                    //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre"));
216                    if((o=parameters.removeEL(KeyImpl.init("Wavelength")))!=null)setWavelength(ImageFilterUtil.toFloatValue(o,"Wavelength"));
217                    if((o=parameters.removeEL(KeyImpl.init("Amplitude")))!=null)setAmplitude(ImageFilterUtil.toFloatValue(o,"Amplitude"));
218                    if((o=parameters.removeEL(KeyImpl.init("Phase")))!=null)setPhase(ImageFilterUtil.toFloatValue(o,"Phase"));
219                    if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction"));
220                    if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation"));
221    
222                    // check for arguments not supported
223                    if(parameters.size()>0) {
224                            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 [Radius, CentreX, CentreY, Centre, Wavelength, Amplitude, Phase, EdgeAction, Interpolation]");
225                    }
226    
227                    return filter(src, dst);
228            }
229    }