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.Struct;
025    import railo.runtime.type.util.CollectionUtil;
026    
027    /**
028     * A filter which simulates a lens placed over an image.
029     */
030    public class SphereFilter extends TransformFilter  implements DynFiltering {
031    
032            private float a = 0;
033            private float b = 0;
034            private float a2 = 0;
035            private float b2 = 0;
036            private float centreX = 0.5f;
037            private float centreY = 0.5f;
038            private float refractionIndex = 1.5f;
039    
040            private float icentreX;
041            private float icentreY;
042    
043            public SphereFilter() {
044                    super(ConvolveFilter.CLAMP_EDGES );
045                    setRadius( 100.0f );
046            }
047    
048            /**
049             * Set the index of refaction.
050             * @param refractionIndex the index of refaction
051         * @see #getRefractionIndex
052             */
053            public void setRefractionIndex(float refractionIndex) {
054                    this.refractionIndex = refractionIndex;
055            }
056    
057            /**
058             * Get the index of refaction.
059             * @return the index of refaction
060         * @see #setRefractionIndex
061             */
062            public float getRefractionIndex() {
063                    return refractionIndex;
064            }
065    
066            /**
067             * Set the radius of the effect.
068             * @param r the radius
069         * @min-value 0
070         * @see #getRadius
071             */
072            public void setRadius(float r) {
073                    this.a = r;
074                    this.b = r;
075            }
076    
077            /**
078             * Get the radius of the effect.
079             * @return the radius
080         * @see #setRadius
081             */
082            public float getRadius() {
083                    return a;
084            }
085    
086            /**
087             * Set the centre of the effect in the X direction as a proportion of the image size.
088             * @param centreX the center
089         * @see #getCentreX
090             */
091            public void setCentreX( float centreX ) {
092                    this.centreX = centreX;
093            }
094    
095            public float getCentreX() {
096                    return centreX;
097            }
098            
099            /**
100             * Set the centre of the effect in the Y direction as a proportion of the image size.
101             * @param centreY the center
102         * @see #getCentreY
103             */
104            public void setCentreY( float centreY ) {
105                    this.centreY = centreY;
106            }
107    
108            /**
109             * Get the centre of the effect in the Y direction as a proportion of the image size.
110             * @return the center
111         * @see #setCentreY
112             */
113            public float getCentreY() {
114                    return centreY;
115            }
116            
117            /**
118             * Set the centre of the effect as a proportion of the image size.
119             * @param centre the center
120         * @see #getCentre
121             */
122            public void setCentre( Point2D centre ) {
123                    this.centreX = (float)centre.getX();
124                    this.centreY = (float)centre.getY();
125            }
126    
127            /**
128             * Get the centre of the effect as a proportion of the image size.
129             * @return the center
130         * @see #setCentre
131             */
132            public Point2D getCentre() {
133                    return new Point2D.Float( centreX, centreY );
134            }
135            
136        public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
137                    int width = src.getWidth();
138                    int height = src.getHeight();
139                    icentreX = width * centreX;
140                    icentreY = height * centreY;
141                    if (a == 0)
142                            a = width/2;
143                    if (b == 0)
144                            b = height/2;
145                    a2 = a*a;
146                    b2 = b*b;
147                    return super.filter( src, dst );
148            }
149            
150            protected void transformInverse(int x, int y, float[] out) {
151                    float dx = x-icentreX;
152                    float dy = y-icentreY;
153                    float x2 = dx*dx;
154                    float y2 = dy*dy;
155                    if (y2 >= (b2 - (b2*x2)/a2)) {
156                            out[0] = x;
157                            out[1] = y;
158                    } else {
159                            float rRefraction = 1.0f / refractionIndex;
160    
161                            float z = (float)Math.sqrt((1.0f - x2/a2 - y2/b2) * (a*b));
162                            float z2 = z*z;
163    
164                            float xAngle = (float)Math.acos(dx / Math.sqrt(x2+z2));
165                            float angle1 = ImageMath.HALF_PI - xAngle;
166                            float angle2 = (float)Math.asin(Math.sin(angle1)*rRefraction);
167                            angle2 = ImageMath.HALF_PI - xAngle - angle2;
168                            out[0] = x - (float)Math.tan(angle2)*z;
169    
170                            float yAngle = (float)Math.acos(dy / Math.sqrt(y2+z2));
171                            angle1 = ImageMath.HALF_PI - yAngle;
172                            angle2 = (float)Math.asin(Math.sin(angle1)*rRefraction);
173                            angle2 = ImageMath.HALF_PI - yAngle - angle2;
174                            out[1] = y - (float)Math.tan(angle2)*z;
175                    }
176            }
177    
178            public String toString() {
179                    return "Distort/Sphere...";
180            }
181    
182            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src);
183                    Object o;
184                    if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toFloatValue(o,"Radius"));
185                    if((o=parameters.removeEL(KeyImpl.init("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX"));
186                    if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY"));
187                    //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre"));
188                    if((o=parameters.removeEL(KeyImpl.init("RefractionIndex")))!=null)setRefractionIndex(ImageFilterUtil.toFloatValue(o,"RefractionIndex"));
189                    if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction"));
190                    if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation"));
191    
192                    // check for arguments not supported
193                    if(parameters.size()>0) {
194                            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 [Radius, CentreX, CentreY, Centre, RefractionIndex, EdgeAction, Interpolation]");
195                    }
196    
197                    return filter(src, dst);
198            }
199    }