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