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.geom.Point2D; 036import java.awt.image.BufferedImage; 037 038import lucee.runtime.engine.ThreadLocalPageContext; 039import lucee.runtime.exp.FunctionException; 040import lucee.runtime.exp.PageException; 041import lucee.runtime.type.KeyImpl; 042import lucee.runtime.type.Struct; 043import lucee.runtime.type.util.CollectionUtil; 044 045/** 046 * A filter which performs the popular whirl-and-pinch distortion effect. 047 */ 048public class PinchFilter extends TransformFilter implements DynFiltering { 049 050 private float angle = 0; 051 private float centreX = 0.5f; 052 private float centreY = 0.5f; 053 private float radius = 100; 054 private float amount = 0.5f; 055 056 private float radius2 = 0; 057 private float icentreX; 058 private float icentreY; 059 private float width; 060 private float height; 061 062 public PinchFilter() { 063 } 064 065 /** 066 * Set the angle of twirl in radians. 0 means no distortion. 067 * @param angle the angle of twirl. This is the angle by which pixels at the nearest edge of the image will move. 068 * @see #getAngle 069 */ 070 public void setAngle(float angle) { 071 this.angle = angle; 072 } 073 074 /** 075 * Get the angle of twist. 076 * @return the angle in radians. 077 * @see #setAngle 078 */ 079 public float getAngle() { 080 return angle; 081 } 082 083 /** 084 * Set the centre of the effect in the X direction as a proportion of the image size. 085 * @param centreX the center 086 * @see #getCentreX 087 */ 088 public void setCentreX( float centreX ) { 089 this.centreX = centreX; 090 } 091 092 /** 093 * Get the centre of the effect in the X direction as a proportion of the image size. 094 * @return the center 095 * @see #setCentreX 096 */ 097 public float getCentreX() { 098 return centreX; 099 } 100 101 /** 102 * Set the centre of the effect in the Y direction as a proportion of the image size. 103 * @param centreY the center 104 * @see #getCentreY 105 */ 106 public void setCentreY( float centreY ) { 107 this.centreY = centreY; 108 } 109 110 /** 111 * Get the centre of the effect in the Y direction as a proportion of the image size. 112 * @return the center 113 * @see #setCentreY 114 */ 115 public float getCentreY() { 116 return centreY; 117 } 118 119 /** 120 * Set the centre of the effect as a proportion of the image size. 121 * @param centre the center 122 * @see #getCentre 123 */ 124 public void setCentre( Point2D centre ) { 125 this.centreX = (float)centre.getX(); 126 this.centreY = (float)centre.getY(); 127 } 128 129 /** 130 * Get the centre of the effect as a proportion of the image size. 131 * @return the center 132 * @see #setCentre 133 */ 134 public Point2D getCentre() { 135 return new Point2D.Float( centreX, centreY ); 136 } 137 138 /** 139 * Set the radius of the effect. 140 * @param radius the radius 141 * @min-value 0 142 * @see #getRadius 143 */ 144 public void setRadius(float radius) { 145 this.radius = radius; 146 } 147 148 /** 149 * Get the radius of the effect. 150 * @return the radius 151 * @see #setRadius 152 */ 153 public float getRadius() { 154 return radius; 155 } 156 157 /** 158 * Set the amount of pinch. 159 * @param amount the amount 160 * @min-value -1 161 * @max-value 1 162 * @see #getAmount 163 */ 164 public void setAmount(float amount) { 165 this.amount = amount; 166 } 167 168 /** 169 * Get the amount of pinch. 170 * @return the amount 171 * @see #setAmount 172 */ 173 public float getAmount() { 174 return amount; 175 } 176 177 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 178 width = src.getWidth(); 179 height = src.getHeight(); 180 icentreX = width * centreX; 181 icentreY = height * centreY; 182 if ( radius == 0 ) 183 radius = Math.min(icentreX, icentreY); 184 radius2 = radius*radius; 185 return super.filter( src, dst ); 186 } 187 188 protected void transformInverse(int x, int y, float[] out) { 189 float dx = x-icentreX; 190 float dy = y-icentreY; 191 float distance = dx*dx + dy*dy; 192 193 if ( distance > radius2 || distance == 0 ) { 194 out[0] = x; 195 out[1] = y; 196 } else { 197 float d = (float)Math.sqrt( distance / radius2 ); 198 float t = (float)Math.pow( Math.sin( Math.PI*0.5 * d ), -amount); 199 200 dx *= t; 201 dy *= t; 202 203 float e = 1 - d; 204 float a = angle * e * e; 205 206 float s = (float)Math.sin( a ); 207 float c = (float)Math.cos( a ); 208 209 out[0] = icentreX + c*dx - s*dy; 210 out[1] = icentreY + s*dx + c*dy; 211 } 212 } 213 214 public String toString() { 215 return "Distort/Pinch..."; 216 } 217 218 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src); 219 Object o; 220 if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toFloatValue(o,"Radius")); 221 if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount")); 222 if((o=parameters.removeEL(KeyImpl.init("Angle")))!=null)setAngle(ImageFilterUtil.toFloatValue(o,"Angle")); 223 if((o=parameters.removeEL(KeyImpl.init("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX")); 224 if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY")); 225 //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre")); 226 if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction")); 227 if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation")); 228 229 // check for arguments not supported 230 if(parameters.size()>0) { 231 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 [Radius, Amount, Angle, CentreX, CentreY, Centre, EdgeAction, Interpolation]"); 232 } 233 234 return filter(src, dst); 235 } 236}