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 performs the popular whirl-and-pinch distortion effect.
029     */
030    public class PinchFilter extends TransformFilter  implements DynFiltering {
031    
032            private float angle = 0;
033            private float centreX = 0.5f;
034            private float centreY = 0.5f;
035            private float radius = 100;
036            private float amount = 0.5f;
037    
038            private float radius2 = 0;
039            private float icentreX;
040            private float icentreY;
041            private float width;
042            private float height;
043            
044            public PinchFilter() {
045            }
046    
047            /**
048             * Set the angle of twirl in radians. 0 means no distortion.
049             * @param angle the angle of twirl. This is the angle by which pixels at the nearest edge of the image will move.
050         * @see #getAngle
051             */
052            public void setAngle(float angle) {
053                    this.angle = angle;
054            }
055            
056            /**
057             * Get the angle of twist.
058             * @return the angle in radians.
059         * @see #setAngle
060             */
061            public float getAngle() {
062                    return angle;
063            }
064            
065            /**
066             * Set the centre of the effect in the X direction as a proportion of the image size.
067             * @param centreX the center
068         * @see #getCentreX
069             */
070            public void setCentreX( float centreX ) {
071                    this.centreX = centreX;
072            }
073    
074            /**
075             * Get the centre of the effect in the X direction as a proportion of the image size.
076             * @return the center
077         * @see #setCentreX
078             */
079            public float getCentreX() {
080                    return centreX;
081            }
082            
083            /**
084             * Set the centre of the effect in the Y direction as a proportion of the image size.
085             * @param centreY the center
086         * @see #getCentreY
087             */
088            public void setCentreY( float centreY ) {
089                    this.centreY = centreY;
090            }
091    
092            /**
093             * Get the centre of the effect in the Y direction as a proportion of the image size.
094             * @return the center
095         * @see #setCentreY
096             */
097            public float getCentreY() {
098                    return centreY;
099            }
100            
101            /**
102             * Set the centre of the effect as a proportion of the image size.
103             * @param centre the center
104         * @see #getCentre
105             */
106            public void setCentre( Point2D centre ) {
107                    this.centreX = (float)centre.getX();
108                    this.centreY = (float)centre.getY();
109            }
110    
111            /**
112             * Get the centre of the effect as a proportion of the image size.
113             * @return the center
114         * @see #setCentre
115             */
116            public Point2D getCentre() {
117                    return new Point2D.Float( centreX, centreY );
118            }
119            
120            /**
121             * Set the radius of the effect.
122             * @param radius the radius
123         * @min-value 0
124         * @see #getRadius
125             */
126            public void setRadius(float radius) {
127                    this.radius = radius;
128            }
129    
130            /**
131             * Get the radius of the effect.
132             * @return the radius
133         * @see #setRadius
134             */
135            public float getRadius() {
136                    return radius;
137            }
138    
139            /**
140             * Set the amount of pinch.
141             * @param amount the amount
142         * @min-value -1
143         * @max-value 1
144         * @see #getAmount
145             */
146            public void setAmount(float amount) {
147                    this.amount = amount;
148            }
149    
150            /**
151             * Get the amount of pinch.
152             * @return the amount
153         * @see #setAmount
154             */
155            public float getAmount() {
156                    return amount;
157            }
158    
159        public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
160                    width = src.getWidth();
161                    height = src.getHeight();
162                    icentreX = width * centreX;
163                    icentreY = height * centreY;
164                    if ( radius == 0 )
165                            radius = Math.min(icentreX, icentreY);
166                    radius2 = radius*radius;
167                    return super.filter( src, dst );
168            }
169            
170            protected void transformInverse(int x, int y, float[] out) {
171                    float dx = x-icentreX;
172                    float dy = y-icentreY;
173                    float distance = dx*dx + dy*dy;
174    
175                    if ( distance > radius2 || distance == 0 ) {
176                            out[0] = x;
177                            out[1] = y;
178                    } else {
179                            float d = (float)Math.sqrt( distance / radius2 );
180                            float t = (float)Math.pow( Math.sin( Math.PI*0.5 * d ), -amount);
181    
182                            dx *= t;
183                            dy *= t;
184    
185                            float e = 1 - d;
186                            float a = angle * e * e;
187    
188                            float s = (float)Math.sin( a );
189                            float c = (float)Math.cos( a );
190    
191                            out[0] = icentreX + c*dx - s*dy;
192                            out[1] = icentreY + s*dx + c*dy;
193                    }
194            }
195    
196            public String toString() {
197                    return "Distort/Pinch...";
198            }
199    
200            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src);
201                    Object o;
202                    if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toFloatValue(o,"Radius"));
203                    if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount"));
204                    if((o=parameters.removeEL(KeyImpl.init("Angle")))!=null)setAngle(ImageFilterUtil.toFloatValue(o,"Angle"));
205                    if((o=parameters.removeEL(KeyImpl.init("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX"));
206                    if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY"));
207                    //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre"));
208                    if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction"));
209                    if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation"));
210    
211                    // check for arguments not supported
212                    if(parameters.size()>0) {
213                            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, Amount, Angle, CentreX, CentreY, Centre, EdgeAction, Interpolation]");
214                    }
215    
216                    return filter(src, dst);
217            }
218    }