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