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.List; 026 import railo.runtime.type.Struct; 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":"")+" ["+List.arrayToList(parameters.keysAsString(),", ")+"] "+(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