001/*
002Copyright 2006 Jerry Huxtable
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008   http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015*/
016
017package com.jhlabs.image;
018
019import java.awt.*;
020import java.awt.geom.*;
021import java.awt.image.*;
022import com.jhlabs.math.*;
023
024/**
025 * A filter which produces a water ripple distortion.
026 */
027public class WaterFilter extends TransformFilter {
028        
029        private float wavelength = 16;
030        private float amplitude = 10;
031        private float phase = 0;
032        private float centreX = 0.5f;
033        private float centreY = 0.5f;
034        private float radius = 50;
035
036        private float radius2 = 0;
037        private float icentreX;
038        private float icentreY;
039
040        public WaterFilter() {
041                setEdgeAction( CLAMP );
042        }
043
044        /**
045         * Set the wavelength of the ripples.
046         * @param wavelength the wavelength
047     * @see #getWavelength
048         */
049        public void setWavelength(float wavelength) {
050                this.wavelength = wavelength;
051        }
052
053        /**
054         * Get the wavelength of the ripples.
055         * @return the wavelength
056     * @see #setWavelength
057         */
058        public float getWavelength() {
059                return wavelength;
060        }
061
062        /**
063         * Set the amplitude of the ripples.
064         * @param amplitude the amplitude
065     * @see #getAmplitude
066         */
067        public void setAmplitude(float amplitude) {
068                this.amplitude = amplitude;
069        }
070
071        /**
072         * Get the amplitude of the ripples.
073         * @return the amplitude
074     * @see #setAmplitude
075         */
076        public float getAmplitude() {
077                return amplitude;
078        }
079
080        /**
081         * Set the phase of the ripples.
082         * @param phase the phase
083     * @see #getPhase
084         */
085        public void setPhase(float phase) {
086                this.phase = phase;
087        }
088
089        /**
090         * Get the phase of the ripples.
091         * @return the phase
092     * @see #setPhase
093         */
094        public float getPhase() {
095                return phase;
096        }
097
098        /**
099         * Set the centre of the effect in the X direction as a proportion of the image size.
100         * @param centreX the center
101     * @see #getCentreX
102         */
103        public void setCentreX( float centreX ) {
104                this.centreX = centreX;
105        }
106
107        /**
108         * Get the centre of the effect in the X direction as a proportion of the image size.
109         * @return the center
110     * @see #setCentreX
111         */
112        public float getCentreX() {
113                return centreX;
114        }
115        
116        /**
117         * Set the centre of the effect in the Y direction as a proportion of the image size.
118         * @param centreY the center
119     * @see #getCentreY
120         */
121        public void setCentreY( float centreY ) {
122                this.centreY = centreY;
123        }
124
125        /**
126         * Get the centre of the effect in the Y direction as a proportion of the image size.
127         * @return the center
128     * @see #setCentreY
129         */
130        public float getCentreY() {
131                return centreY;
132        }
133        
134        /**
135         * Set the centre of the effect as a proportion of the image size.
136         * @param centre the center
137     * @see #getCentre
138         */
139        public void setCentre( Point2D centre ) {
140                this.centreX = (float)centre.getX();
141                this.centreY = (float)centre.getY();
142        }
143
144        /**
145         * Get the centre of the effect as a proportion of the image size.
146         * @return the center
147     * @see #setCentre
148         */
149        public Point2D getCentre() {
150                return new Point2D.Float( centreX, centreY );
151        }
152        
153        /**
154         * Set the radius of the effect.
155         * @param radius the radius
156     * @min-value 0
157     * @see #getRadius
158         */
159        public void setRadius(float radius) {
160                this.radius = radius;
161        }
162
163        /**
164         * Get the radius of the effect.
165         * @return the radius
166     * @see #setRadius
167         */
168        public float getRadius() {
169                return radius;
170        }
171
172        private boolean inside(int v, int a, int b) {
173                return a <= v && v <= b;
174        }
175        
176    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
177                icentreX = src.getWidth() * centreX;
178                icentreY = src.getHeight() * centreY;
179                if ( radius == 0 )
180                        radius = Math.min(icentreX, icentreY);
181                radius2 = radius*radius;
182                return super.filter( src, dst );
183        }
184        
185        protected void transformInverse(int x, int y, float[] out) {
186                float dx = x-icentreX;
187                float dy = y-icentreY;
188                float distance2 = dx*dx + dy*dy;
189                if (distance2 > radius2) {
190                        out[0] = x;
191                        out[1] = y;
192                } else {
193                        float distance = (float)Math.sqrt(distance2);
194                        float amount = amplitude * (float)Math.sin(distance / wavelength * ImageMath.TWO_PI - phase);
195                        amount *= (radius-distance)/radius;
196                        if ( distance != 0 )
197                                amount *= wavelength/distance;
198                        out[0] = x + dx*amount;
199                        out[1] = y + dy*amount;
200                }
201        }
202
203        public String toString() {
204                return "Distort/Water Ripples...";
205        }
206        
207}