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