001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019/*
020*
021
022Licensed under the Apache License, Version 2.0 (the "License");
023you may not use this file except in compliance with the License.
024You may obtain a copy of the License at
025
026   http://www.apache.org/licenses/LICENSE-2.0
027
028Unless required by applicable law or agreed to in writing, software
029distributed under the License is distributed on an "AS IS" BASIS,
030WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
031See the License for the specific language governing permissions and
032limitations under the License.
033*/
034
035package lucee.runtime.img.filter;import java.awt.geom.Point2D;
036import java.awt.image.BufferedImage;
037
038import lucee.runtime.engine.ThreadLocalPageContext;
039import lucee.runtime.exp.FunctionException;
040import lucee.runtime.exp.PageException;
041import lucee.runtime.type.KeyImpl;
042import lucee.runtime.type.Struct;
043import lucee.runtime.type.util.CollectionUtil;
044
045/**
046 * A filter which simulates a lens placed over an image.
047 */
048public class SphereFilter extends TransformFilter  implements DynFiltering {
049
050        private float a = 0;
051        private float b = 0;
052        private float a2 = 0;
053        private float b2 = 0;
054        private float centreX = 0.5f;
055        private float centreY = 0.5f;
056        private float refractionIndex = 1.5f;
057
058        private float icentreX;
059        private float icentreY;
060
061        public SphereFilter() {
062                super(ConvolveFilter.CLAMP_EDGES );
063                setRadius( 100.0f );
064        }
065
066        /**
067         * Set the index of refaction.
068         * @param refractionIndex the index of refaction
069     * @see #getRefractionIndex
070         */
071        public void setRefractionIndex(float refractionIndex) {
072                this.refractionIndex = refractionIndex;
073        }
074
075        /**
076         * Get the index of refaction.
077         * @return the index of refaction
078     * @see #setRefractionIndex
079         */
080        public float getRefractionIndex() {
081                return refractionIndex;
082        }
083
084        /**
085         * Set the radius of the effect.
086         * @param r the radius
087     * @min-value 0
088     * @see #getRadius
089         */
090        public void setRadius(float r) {
091                this.a = r;
092                this.b = r;
093        }
094
095        /**
096         * Get the radius of the effect.
097         * @return the radius
098     * @see #setRadius
099         */
100        public float getRadius() {
101                return a;
102        }
103
104        /**
105         * Set the centre of the effect in the X direction as a proportion of the image size.
106         * @param centreX the center
107     * @see #getCentreX
108         */
109        public void setCentreX( float centreX ) {
110                this.centreX = centreX;
111        }
112
113        public float getCentreX() {
114                return centreX;
115        }
116        
117        /**
118         * Set the centre of the effect in the Y direction as a proportion of the image size.
119         * @param centreY the center
120     * @see #getCentreY
121         */
122        public void setCentreY( float centreY ) {
123                this.centreY = centreY;
124        }
125
126        /**
127         * Get the centre of the effect in the Y direction as a proportion of the image size.
128         * @return the center
129     * @see #setCentreY
130         */
131        public float getCentreY() {
132                return centreY;
133        }
134        
135        /**
136         * Set the centre of the effect as a proportion of the image size.
137         * @param centre the center
138     * @see #getCentre
139         */
140        public void setCentre( Point2D centre ) {
141                this.centreX = (float)centre.getX();
142                this.centreY = (float)centre.getY();
143        }
144
145        /**
146         * Get the centre of the effect as a proportion of the image size.
147         * @return the center
148     * @see #setCentre
149         */
150        public Point2D getCentre() {
151                return new Point2D.Float( centreX, centreY );
152        }
153        
154    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
155                int width = src.getWidth();
156                int height = src.getHeight();
157                icentreX = width * centreX;
158                icentreY = height * centreY;
159                if (a == 0)
160                        a = width/2;
161                if (b == 0)
162                        b = height/2;
163                a2 = a*a;
164                b2 = b*b;
165                return super.filter( src, dst );
166        }
167        
168        protected void transformInverse(int x, int y, float[] out) {
169                float dx = x-icentreX;
170                float dy = y-icentreY;
171                float x2 = dx*dx;
172                float y2 = dy*dy;
173                if (y2 >= (b2 - (b2*x2)/a2)) {
174                        out[0] = x;
175                        out[1] = y;
176                } else {
177                        float rRefraction = 1.0f / refractionIndex;
178
179                        float z = (float)Math.sqrt((1.0f - x2/a2 - y2/b2) * (a*b));
180                        float z2 = z*z;
181
182                        float xAngle = (float)Math.acos(dx / Math.sqrt(x2+z2));
183                        float angle1 = ImageMath.HALF_PI - xAngle;
184                        float angle2 = (float)Math.asin(Math.sin(angle1)*rRefraction);
185                        angle2 = ImageMath.HALF_PI - xAngle - angle2;
186                        out[0] = x - (float)Math.tan(angle2)*z;
187
188                        float yAngle = (float)Math.acos(dy / Math.sqrt(y2+z2));
189                        angle1 = ImageMath.HALF_PI - yAngle;
190                        angle2 = (float)Math.asin(Math.sin(angle1)*rRefraction);
191                        angle2 = ImageMath.HALF_PI - yAngle - angle2;
192                        out[1] = y - (float)Math.tan(angle2)*z;
193                }
194        }
195
196        public String toString() {
197                return "Distort/Sphere...";
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("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX"));
204                if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY"));
205                //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre"));
206                if((o=parameters.removeEL(KeyImpl.init("RefractionIndex")))!=null)setRefractionIndex(ImageFilterUtil.toFloatValue(o,"RefractionIndex"));
207                if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction"));
208                if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation"));
209
210                // check for arguments not supported
211                if(parameters.size()>0) {
212                        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]");
213                }
214
215                return filter(src, dst);
216        }
217}