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.Rectangle;
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.img.ImageUtil;
024    import railo.runtime.type.KeyImpl;
025    import railo.runtime.type.Struct;
026    import railo.runtime.type.util.CollectionUtil;
027    
028    /**
029     * A filter which performs a perspective distortion on an image.
030     */
031    public class PerspectiveFilter extends TransformFilter  implements DynFiltering {
032    
033            private float xlt, ylt, xrt, yrt, xrb, yrb, xlb, ylb;
034            private float dx1, dy1, dx2, dy2, dx3, dy3;
035            private float A, B, C, D, E, F, G, H, I;
036            
037            /**
038         * Construct a PerspectiveFilter.
039         */
040        public PerspectiveFilter() {
041                    this(0, 0, 0, 0, 0, 0, 0, 0);
042                    //this(0, 0, 100, 0, 100, 100, 0, 100);
043            }
044            
045            /**
046         * Construct a PerspectiveFilter.
047         * @param x0 the new position of the top left corner
048         * @param y0 the new position of the top left corner
049         * @param x1 the new position of the top right corner
050         * @param y1 the new position of the top right corner
051         * @param x2 the new position of the bottom right corner
052         * @param y2 the new position of the bottom right corner
053         * @param x3 the new position of the bottom left corner
054         * @param y3 the new position of the bottom left corner
055         */
056            public PerspectiveFilter(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) {
057                    setCorners(x0, y0, x1, y1, x2, y2, x3, y3);
058            }
059            
060            /**
061         * Set the new positions of the image corners.
062         * @param x0 the new position of the top left corner
063         * @param y0 the new position of the top left corner
064         * @param x1 the new position of the top right corner
065         * @param y1 the new position of the top right corner
066         * @param x2 the new position of the bottom right corner
067         * @param y2 the new position of the bottom right corner
068         * @param x3 the new position of the bottom left corner
069         * @param y3 the new position of the bottom left corner
070         */
071            public void setCorners(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) {
072                    this.xlt = x0;
073                    this.ylt = y0;
074                    this.xrt = x1;
075                    this.yrt = y1;
076                    this.xrb = x2;
077                    this.yrb = y2;
078                    this.xlb = x3;
079                    this.ylb = y3;
080                    
081                    dx1 = x1-x2;
082                    dy1 = y1-y2;
083                    dx2 = x3-x2;
084                    dy2 = y3-y2;
085                    dx3 = x0-x1+x2-x3;
086                    dy3 = y0-y1+y2-y3;
087                    
088                    float a11, a12, a13, a21, a22, a23, a31, a32;
089    
090                    if (dx3 == 0 && dy3 == 0) {
091                            a11 = x1-x0;
092                            a21 = x2-x1;
093                            a31 = x0;
094                            a12 = y1-y0;
095                            a22 = y2-y1;
096                            a32 = y0;
097                            a13 = a23 = 0;
098                    } else {
099                            a13 = (dx3*dy2-dx2*dy3)/(dx1*dy2-dy1*dx2);
100                            a23 = (dx1*dy3-dy1*dx3)/(dx1*dy2-dy1*dx2);
101                            a11 = x1-x0+a13*x1;
102                            a21 = x3-x0+a23*x3;
103                            a31 = x0;
104                            a12 = y1-y0+a13*y1;
105                            a22 = y3-y0+a23*y3;
106                            a32 = y0;
107                    }
108    
109                A = a22 - a32*a23;
110                B = a31*a23 - a21;
111                C = a21*a32 - a31*a22;
112                D = a32*a13 - a12;
113                E = a11 - a31*a13;
114                F = a31*a12 - a11*a32;
115                G = a12*a23 - a22*a13;
116                H = a21*a13 - a11*a23;
117                I = a11*a22 - a21*a12;
118            }
119            
120            /**
121             * the new horizontal position of the top left corner, negative values are translated to image-width - x.
122             * @param x0 the x0 to set
123             */
124            public void setXLT(float xlt) {
125                    this.xlt = xlt;
126            }
127    
128            /**
129             * the new vertical position of the top left corner, negative values are translated to image-height - y.
130             * @param y0 the y0 to set
131             */
132            public void setYLT(float ylt) {
133                    this.ylt = ylt;
134            }
135    
136            /**
137             * the new horizontal position of the top right corner, negative values are translated to image-width - x.
138             * @param x1 the x1 to set
139             */
140            public void setXRT(float xrt) {
141                    this.xrt = xrt;
142            }
143    
144            /**
145             * the new vertical position of the top right corner, negative values are translated to image-height - y.
146             * @param y1 the y1 to set
147             */
148            public void setYRT(float yrt) {
149                    this.yrt = yrt;
150            }
151    
152            /**
153             * the new horizontal position of the bottom right corner, negative values are translated to image-width - x.
154             * @param x2 the x2 to set
155             */
156            public void setXRB(float xrb) {
157                    this.xrb = xrb;
158            }
159            
160            /**
161             * the new vertical position of the bottom right corner, negative values are translated to image-height - y.
162             * @param y2 the y2 to set
163             */
164            public void setYRB(float yrb) {
165                    this.yrb = yrb;
166            }
167    
168            /**
169             * the new horizontal position of the bottom left corner, negative values are translated to image-width - x.
170             * @param xlb the x3 to set
171             */
172            public void setXLB(float xlb) {
173                    this.xlb = xlb;
174            }
175    
176            /**
177             * the new vertical position of the bottom left corner, negative values are translated to image-height - y.
178             * @param y3 the y3 to set
179             */
180            public void setYLB(float ylb) {
181                    this.ylb = ylb;
182            }
183    
184    
185            protected void transformSpace(Rectangle rect) {
186                    rect.x = (int)Math.min( Math.min( xlt, xrt ), Math.min( xrb, xlb ) );
187                    rect.y = (int)Math.min( Math.min( ylt, yrt ), Math.min( yrb, ylb ) );
188                    rect.width = (int)Math.max( Math.max( xlt, xrt ), Math.max( xrb, xlb ) ) - rect.x;
189                    rect.height = (int)Math.max( Math.max( ylt, yrt ), Math.max( yrb, ylb ) ) - rect.y;
190            }
191    
192        /**
193         * Get the origin of the output image. Use this for working out where to draw your new image.
194         * @return the X origin.
195         */
196            public float getOriginX() {
197                    return xlt - (int)Math.min( Math.min( xlt, xrt ), Math.min( xrb, xlb ) );
198            }
199    
200        /**
201         * Get the origin of the output image. Use this for working out where to draw your new image.
202         * @return the Y origin.
203         */
204            public float getOriginY() {
205                    return ylt - (int)Math.min( Math.min( ylt, yrt ), Math.min( yrb, ylb ) );
206            }
207    
208    /*
209        public Point2D getPoint2D( Point2D srcPt, Point2D dstPt ) {
210            if ( dstPt == null )
211                dstPt = new Point2D.Double();
212    
213                    dx1 = x1-x2;
214                    dy1 = y1-y2;
215                    dx2 = x3-x2;
216                    dy2 = y3-y2;
217                    dx3 = x0-x1+x2-x3;
218                    dy3 = y0-y1+y2-y3;
219                    
220                    float a11, a12, a13, a21, a22, a23, a31, a32;
221    
222                    if (dx3 == 0 && dy3 == 0) {
223                            a11 = x1-x0;
224                            a21 = x2-x1;
225                            a31 = x0;
226                            a12 = y1-y0;
227                            a22 = y2-y1;
228                            a32 = y0;
229                            a13 = a23 = 0;
230                    } else {
231                            a13 = (dx3*dy2-dx2*dy3)/(dx1*dy2-dy1*dx2);
232                            a23 = (dx1*dy3-dy1*dx3)/(dx1*dy2-dy1*dx2);
233                            a11 = x1-x0+a13*x1;
234                            a21 = x3-x0+a23*x3;
235                            a31 = x0;
236                            a12 = y1-y0+a13*y1;
237                            a22 = y3-y0+a23*y3;
238                            a32 = y0;
239                    }
240    
241                    float x = (float)srcPt.getX();
242                    float y = (float)srcPt.getY();
243                    float D = 1.0f/(a13*x + a23*y + 1);
244    
245            dstPt.setLocation( (a11*x + a21*y + a31)*D, (a12*x + a22*y + a32)*D );
246            return dstPt;
247        }
248    */
249    
250            protected void transformInverse(int x, int y, float[] out) {
251                    out[0] = originalSpace.width * (A*x+B*y+C)/(G*x+H*y+I);
252                    out[1] = originalSpace.height * (D*x+E*y+F)/(G*x+H*y+I);
253            }
254    
255            public String toString() {
256                    return "Distort/Perspective...";
257            }
258            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {
259                    Object o;
260                    if((o=parameters.removeEL(KeyImpl.init("xlt")))!=null)setXLT(ImageFilterUtil.toFloatValue(o,"xlt"));
261                    if((o=parameters.removeEL(KeyImpl.init("ylt")))!=null)setYLT(ImageFilterUtil.toFloatValue(o,"ylt"));
262                    
263                    if((o=parameters.removeEL(KeyImpl.init("xrt")))!=null)setXRT(ImageFilterUtil.toFloatValue(o,"xrt"));
264                    if((o=parameters.removeEL(KeyImpl.init("yrt")))!=null)setYRT(ImageFilterUtil.toFloatValue(o,"yrt"));
265                    
266                    if((o=parameters.removeEL(KeyImpl.init("xrb")))!=null)setXRB(ImageFilterUtil.toFloatValue(o,"xrb"));
267                    if((o=parameters.removeEL(KeyImpl.init("yrb")))!=null)setYRB(ImageFilterUtil.toFloatValue(o,"yrb"));
268                    
269                    if((o=parameters.removeEL(KeyImpl.init("xlb")))!=null)setXLB(ImageFilterUtil.toFloatValue(o,"xlb"));
270                    if((o=parameters.removeEL(KeyImpl.init("ylb")))!=null)setYLB(ImageFilterUtil.toFloatValue(o,"ylb"));
271                    
272                    if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction"));
273                    if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation"));
274    
275                    // check for arguments not supported
276                    if(parameters.size()>0) {
277                            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 [Corners, EdgeAction, Interpolation]");
278                    }
279    
280                    return filter(src, (BufferedImage)null);
281            }
282            
283            public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
284            
285                    int width = src.getWidth();
286                int height = src.getHeight();
287    
288                if(xrt==0)xrt=width;
289                if(xrb==0)xrb=width;
290                if(yrb==0)yrb=height;
291                if(ylb==0)ylb=height;
292                
293                if(xlt<0) xlt=width+xlt;
294                if(xrt<0) xrt=width+xrt;
295                if(xrb<0) xrb=width+xrb;
296                if(xlb<0) xlb=width+xlb;
297                
298                if(ylt<0) ylt=width+ylt;
299                if(yrt<0) yrt=width+yrt;
300                if(yrb<0) yrb=width+yrb;
301                if(ylb<0) ylb=width+ylb;
302                
303                
304                
305                setCorners(xlt, ylt, xrt, yrt, xrb, yrb, xlb, ylb);
306                
307    
308                float t=ylt<yrt?ylt:yrt;
309                float l=xlt<xlb?xlt:xlb;
310                
311                float b=ylb>yrb?ylb:yrb;
312                float r=xrt>xrb?xrt:xrb;
313                
314                float w=r-l;
315                float h=b-t;
316                
317                dst=ImageUtil.createBufferedImage(src,Math.round(w),Math.round(h));
318                
319                
320                
321                return super.filter(src, dst);
322                            
323            }
324    }
325