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