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.image.BufferedImage;
018    
019    import railo.runtime.engine.ThreadLocalPageContext;
020    import railo.runtime.exp.ExpressionException;
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 distorts and image by performing coordinate conversions between rectangular and polar coordinates.
029     */
030    public class PolarFilter extends TransformFilter  implements DynFiltering {
031            
032            /**
033         * Convert from rectangular to polar coordinates.
034         */
035        public final static int RECT_TO_POLAR = 0;
036    
037            /**
038         * Convert from polar to rectangular coordinates.
039         */
040            public final static int POLAR_TO_RECT = 1;
041    
042            /**
043         * Invert the image in a circle.
044         */
045            public final static int INVERT_IN_CIRCLE = 2;
046    
047            private int type;
048            private float width, height;
049            private float centreX, centreY;
050            private float radius;
051    
052            /**
053         * Construct a PolarFilter.
054         */
055        public PolarFilter() {
056                    this(RECT_TO_POLAR);
057            }
058    
059            /**
060         * Construct a PolarFilter.
061         * @param type the distortion type
062         */
063            public PolarFilter(int type) {
064                    super(ConvolveFilter.CLAMP_EDGES);
065                    this.type=type;
066            }
067    
068        public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
069                    this.width = src.getWidth();
070                    this.height = src.getHeight();
071                    centreX = width/2;
072                    centreY = height/2;
073                    radius = Math.max(centreY, centreX);
074                    return super.filter( src, dst );
075            }
076            
077            /**
078         * Set the distortion type, valid values are
079         * - RECT_TO_POLAR = Convert from rectangular to polar coordinates
080         * - POLAR_TO_RECT = Convert from polar to rectangular coordinates
081         * - INVERT_IN_CIRCLE = Invert the image in a circle
082         */
083            public void setType(String type) throws ExpressionException {
084                    type=type.trim().toUpperCase();
085                    if("RECT_TO_POLAR".equals(type)) this.type = RECT_TO_POLAR;
086                    else if("POLAR_TO_RECT".equals(type)) this.type = POLAR_TO_RECT;
087                    else if("INVERT_IN_CIRCLE".equals(type)) this.type = INVERT_IN_CIRCLE;
088                    else
089                            throw new ExpressionException("inavlid type defintion ["+type+"], valid types are [RECT_TO_POLAR,POLAR_TO_RECT,INVERT_IN_CIRCLE]");
090            }
091    
092            /**
093         * Get the distortion type.
094         * @return the distortion type
095         * @see #setType
096         */
097            public int getType() {
098                    return type;
099            }
100    
101            private float sqr(float x) {
102                    return x*x;
103            }
104    
105            protected void transformInverse(int x, int y, float[] out) {
106                    float theta, t;
107                    float m, xmax, ymax;
108                    float r = 0;
109                    
110                    switch (type) {
111                    case RECT_TO_POLAR:
112                            theta = 0;
113                            if (x >= centreX) {
114                                    if (y > centreY) {
115                                            theta = ImageMath.PI - (float)Math.atan(((x - centreX))/((y - centreY)));
116                                            r = (float)Math.sqrt(sqr (x - centreX) + sqr (y - centreY));
117                                    } else if (y < centreY) {
118                                            theta = (float)Math.atan (((x - centreX))/((centreY - y)));
119                                            r = (float)Math.sqrt (sqr (x - centreX) + sqr (centreY - y));
120                                    } else {
121                                            theta = ImageMath.HALF_PI;
122                                            r = x - centreX;
123                                    }
124                            } else if (x < centreX) {
125                                    if (y < centreY) {
126                                            theta = ImageMath.TWO_PI - (float)Math.atan (((centreX -x))/((centreY - y)));
127                                            r = (float)Math.sqrt (sqr (centreX - x) + sqr (centreY - y));
128                                    } else if (y > centreY) {
129                                            theta = ImageMath.PI + (float)Math.atan (((centreX - x))/((y - centreY)));
130                                            r = (float)Math.sqrt (sqr (centreX - x) + sqr (y - centreY));
131                                    } else {
132                                            theta = 1.5f * ImageMath.PI;
133                                            r = centreX - x;
134                                    }
135                            }
136                            if (x != centreX)
137                                    m = Math.abs (((y - centreY)) / ((x - centreX)));
138                            else
139                                    m = 0;
140                            
141                            if (m <= (height / width)) {
142                                    if (x == centreX) {
143                                            xmax = 0;
144                                            ymax = centreY;
145                                    } else {
146                                            xmax = centreX;
147                                            ymax = m * xmax;
148                                    }
149                            } else {
150                                    ymax = centreY;
151                                    xmax = ymax / m;
152                            }
153                            
154                            out[0] = (width-1) - (width - 1)/ImageMath.TWO_PI * theta;
155                            out[1] = height * r / radius;
156                            break;
157                    case POLAR_TO_RECT:
158                            theta = x / width * ImageMath.TWO_PI;
159                            float theta2;
160    
161                            if (theta >= 1.5f * ImageMath.PI)
162                                    theta2 = ImageMath.TWO_PI - theta;
163                            else if (theta >= ImageMath.PI)
164                                    theta2 = theta - ImageMath.PI;
165                            else if (theta >= 0.5f * ImageMath.PI)
166                                    theta2 = ImageMath.PI - theta;
167                            else
168                                    theta2 = theta;
169            
170                            t = (float)Math.tan(theta2);
171                            if (t != 0)
172                                    m = 1.0f / t;
173                            else
174                                    m = 0;
175            
176                            if (m <= ((height) / (width))) {
177                                    if (theta2 == 0) {
178                                            xmax = 0;
179                                            ymax = centreY;
180                                    } else {
181                                            xmax = centreX;
182                                            ymax = m * xmax;
183                                    }
184                            } else {
185                                    ymax = centreY;
186                                    xmax = ymax / m;
187                            }
188            
189                            r = radius * (y / (height));
190    
191                            float nx = -r * (float)Math.sin(theta2);
192                            float ny = r * (float)Math.cos(theta2);
193                            
194                            if (theta >= 1.5f * ImageMath.PI) {
195                                    out[0] = centreX - nx;
196                                    out[1] = centreY - ny;
197                            } else if (theta >= Math.PI) {
198                                    out[0] = centreX - nx;
199                                    out[1] = centreY + ny;
200                            } else if (theta >= 0.5 * Math.PI) {
201                                    out[0] = centreX + nx;
202                                    out[1] = centreY + ny;
203                            } else {
204                                    out[0] = centreX + nx;
205                                    out[1] = centreY - ny;
206                            }
207                            break;
208                    case INVERT_IN_CIRCLE:
209                            float dx = x-centreX;
210                            float dy = y-centreY;
211                            float distance2 = dx*dx+dy*dy;
212                            out[0] = centreX + centreX*centreX * dx/distance2;
213                            out[1] = centreY + centreY*centreY * dy/distance2;
214                            break;
215                    }
216            }
217    
218            public String toString() {
219                    return "Distort/Polar Coordinates...";
220            }
221    
222            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src);
223                    Object o;
224                    if((o=parameters.removeEL(KeyImpl.init("Type")))!=null)setType(ImageFilterUtil.toString(o,"Type"));
225                    if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction"));
226                    if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation"));
227    
228                    // check for arguments not supported
229                    if(parameters.size()>0) {
230                            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 [Type, EdgeAction, Interpolation]");
231                    }
232    
233                    return filter(src, dst);
234            }
235    }