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.ExpressionException;
040import lucee.runtime.exp.FunctionException;
041import lucee.runtime.exp.PageException;
042import lucee.runtime.type.KeyImpl;
043import lucee.runtime.type.Struct;
044import lucee.runtime.type.util.CollectionUtil;
045/**
046 * A filter which wraps an image around a circular arc.
047 */
048public class CircleFilter extends TransformFilter  implements DynFiltering {
049
050        private float radius = 10;
051        private float height = 20;
052        private float angle = 0;
053        private float spreadAngle = (float)Math.PI;
054        private float centreX = 0.5f;
055        private float centreY = 0.5f;
056
057        private float icentreX;
058        private float icentreY;
059        private float iWidth;
060        private float iHeight;
061
062        /**
063     * Construct a CircleFilter.
064     */
065    public CircleFilter() {
066                try {
067                        setEdgeAction( "ZERO" );
068                } catch (ExpressionException e) {}
069        }
070
071        /**
072     * Set the height of the arc.
073     * @param height the height
074     * @see #getHeight
075     */
076        public void setHeight(float height) {
077                this.height = height;
078        }
079
080        /**
081     * Get the height of the arc.
082     * @return the height
083     * @see #setHeight
084     */
085        public float getHeight() {
086                return height;
087        }
088
089        /**
090     * Set the angle of the arc.
091     * @param angle the angle of the arc.
092     * @angle
093     * @see #getAngle
094     */
095        public void setAngle(float angle) {
096                this.angle = angle;
097        }
098
099        /**
100     * Returns the angle of the arc.
101     * @return the angle of the arc.
102     * @see #setAngle
103     */
104        public float getAngle() {
105                return angle;
106        }
107
108        /**
109     * Set the spread angle of the arc.
110     * @param spreadAngle the angle
111     * @angle
112     * @see #getSpreadAngle
113     */
114        public void setSpreadAngle(float spreadAngle) {
115                this.spreadAngle = spreadAngle;
116        }
117
118        /**
119     * Get the spread angle of the arc.
120     * @return the angle
121     * @angle
122     * @see #setSpreadAngle
123     */
124        public float getSpreadAngle() {
125                return spreadAngle;
126        }
127
128        /**
129         * Set the radius of the effect.
130         * @param radius the radius
131     * @min-value 0
132     * @see #getRadius
133         */
134        public void setRadius(float radius) {
135                this.radius = radius;
136        }
137
138        /**
139         * Get the radius of the effect.
140         * @return the radius
141     * @see #setRadius
142         */
143        public float getRadius() {
144                return radius;
145        }
146
147        /**
148         * Set the centre of the effect in the Y direction as a proportion of the image size.
149         * @param centreX the center
150     * @see #getCentreX
151         */
152        public void setCentreX( float centreX ) {
153                this.centreX = centreX;
154        }
155
156        /**
157         * Get the centre of the effect in the X direction as a proportion of the image size.
158         * @return the center
159     * @see #setCentreX
160         */
161        public float getCentreX() {
162                return centreX;
163        }
164        
165        /**
166         * Set the centre of the effect in the Y direction as a proportion of the image size.
167         * @param centreY the center
168     * @see #getCentreY
169         */
170        public void setCentreY( float centreY ) {
171                this.centreY = centreY;
172        }
173
174        /**
175         * Get the centre of the effect in the Y direction as a proportion of the image size.
176         * @return the center
177     * @see #setCentreY
178         */
179        public float getCentreY() {
180                return centreY;
181        }
182        
183        /**
184         * Set the centre of the effect as a proportion of the image size.
185         * @param centre the center
186     * @see #getCentre
187         */
188        public void setCentre( Point2D centre ) {
189                this.centreX = (float)centre.getX();
190                this.centreY = (float)centre.getY();
191        }
192
193        /**
194         * Get the centre of the effect as a proportion of the image size.
195         * @return the center
196     * @see #setCentre
197         */
198        public Point2D getCentre() {
199                return new Point2D.Float( centreX, centreY );
200        }
201        
202    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
203                iWidth = src.getWidth();
204                iHeight = src.getHeight();
205                icentreX = iWidth * centreX;
206                icentreY = iHeight * centreY;
207                iWidth--;
208                return super.filter( src, dst );
209        }
210        
211        protected void transformInverse(int x, int y, float[] out) {
212                float dx = x-icentreX;
213                float dy = y-icentreY;
214                float theta = (float)Math.atan2( -dy, -dx ) + angle;
215                float r = (float)Math.sqrt( dx*dx + dy*dy );
216
217                theta = ImageMath.mod( theta, 2*(float)Math.PI );
218
219                out[0] = iWidth * theta/(spreadAngle+0.00001f);
220                out[1] = iHeight * (1-(r-radius)/(height+0.00001f));
221        }
222
223        public String toString() {
224                return "Distort/Circle...";
225        }
226
227        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src);
228                Object o;
229                if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toFloatValue(o,"Radius"));
230                if((o=parameters.removeEL(KeyImpl.init("Angle")))!=null)setAngle(ImageFilterUtil.toFloatValue(o,"Angle"));
231                if((o=parameters.removeEL(KeyImpl.init("SpreadAngle")))!=null)setSpreadAngle(ImageFilterUtil.toFloatValue(o,"SpreadAngle"));
232                if((o=parameters.removeEL(KeyImpl.init("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX"));
233                if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY"));
234                //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre"));
235                if((o=parameters.removeEL(KeyImpl.init("Height")))!=null)setHeight(ImageFilterUtil.toFloatValue(o,"Height"));
236                if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction"));
237                if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation"));
238
239                // check for arguments not supported
240                if(parameters.size()>0) {
241                        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, Angle, SpreadAngle, CentreX, CentreY, Centre, Height, EdgeAction, Interpolation]");
242                }
243
244                return filter(src, dst);
245        }
246}