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