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 distorts an image by twisting it from the centre out. 029 * The twisting is centred at the centre of the image and extends out to the smallest of 030 * the width and height. Pixels outside this radius are unaffected. 031 */ 032 public class TwirlFilter extends TransformFilter implements DynFiltering { 033 034 private float angle = 0; 035 private float centreX = 0.5f; 036 private float centreY = 0.5f; 037 private float radius = 100; 038 039 private float radius2 = 0; 040 private float icentreX; 041 private float icentreY; 042 043 /** 044 * Construct a TwirlFilter with no distortion. 045 */ 046 public TwirlFilter() { 047 super(ConvolveFilter.CLAMP_EDGES ); 048 } 049 050 /** 051 * Set the angle of twirl in radians. 0 means no distortion. 052 * @param angle the angle of twirl. This is the angle by which pixels at the nearest edge of the image will move. 053 * @see #getAngle 054 */ 055 public void setAngle(float angle) { 056 this.angle = angle; 057 } 058 059 /** 060 * Get the angle of twist. 061 * @return the angle in radians. 062 * @see #setAngle 063 */ 064 public float getAngle() { 065 return angle; 066 } 067 068 /** 069 * Set the centre of the effect in the X direction as a proportion of the image size. 070 * @param centreX the center 071 * @see #getCentreX 072 */ 073 public void setCentreX( float centreX ) { 074 this.centreX = centreX; 075 } 076 077 /** 078 * Get the centre of the effect in the X direction as a proportion of the image size. 079 * @return the center 080 * @see #setCentreX 081 */ 082 public float getCentreX() { 083 return centreX; 084 } 085 086 /** 087 * Set the centre of the effect in the Y direction as a proportion of the image size. 088 * @param centreY the center 089 * @see #getCentreY 090 */ 091 public void setCentreY( float centreY ) { 092 this.centreY = centreY; 093 } 094 095 /** 096 * Get the centre of the effect in the Y direction as a proportion of the image size. 097 * @return the center 098 * @see #setCentreY 099 */ 100 public float getCentreY() { 101 return centreY; 102 } 103 104 /** 105 * Set the centre of the effect as a proportion of the image size. 106 * @param centre the center 107 * @see #getCentre 108 */ 109 public void setCentre( Point2D centre ) { 110 this.centreX = (float)centre.getX(); 111 this.centreY = (float)centre.getY(); 112 } 113 114 /** 115 * Get the centre of the effect as a proportion of the image size. 116 * @return the center 117 * @see #setCentre 118 */ 119 public Point2D getCentre() { 120 return new Point2D.Float( centreX, centreY ); 121 } 122 123 /** 124 * Set the radius of the effect. 125 * @param radius the radius 126 * @min-value 0 127 * @see #getRadius 128 */ 129 public void setRadius(float radius) { 130 this.radius = radius; 131 } 132 133 /** 134 * Get the radius of the effect. 135 * @return the radius 136 * @see #setRadius 137 */ 138 public float getRadius() { 139 return radius; 140 } 141 142 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 143 icentreX = src.getWidth() * centreX; 144 icentreY = src.getHeight() * centreY; 145 if ( radius == 0 ) 146 radius = Math.min(icentreX, icentreY); 147 radius2 = radius*radius; 148 return super.filter( src, dst ); 149 } 150 151 protected void transformInverse(int x, int y, float[] out) { 152 float dx = x-icentreX; 153 float dy = y-icentreY; 154 float distance = dx*dx + dy*dy; 155 if (distance > radius2) { 156 out[0] = x; 157 out[1] = y; 158 } else { 159 distance = (float)Math.sqrt(distance); 160 float a = (float)Math.atan2(dy, dx) + angle * (radius-distance) / radius; 161 out[0] = icentreX + distance*(float)Math.cos(a); 162 out[1] = icentreY + distance*(float)Math.sin(a); 163 } 164 } 165 166 public String toString() { 167 return "Distort/Twirl..."; 168 } 169 170 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src); 171 Object o; 172 if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toFloatValue(o,"Radius")); 173 if((o=parameters.removeEL(KeyImpl.init("Angle")))!=null)setAngle(ImageFilterUtil.toFloatValue(o,"Angle")); 174 if((o=parameters.removeEL(KeyImpl.init("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX")); 175 if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY")); 176 //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre")); 177 if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction")); 178 if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation")); 179 180 // check for arguments not supported 181 if(parameters.size()>0) { 182 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, Angle, CentreX, CentreY, Centre, EdgeAction, Interpolation]"); 183 } 184 185 return filter(src, dst); 186 } 187 }