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.image.BufferedImage; 018 019 import railo.runtime.engine.ThreadLocalPageContext; 020 import railo.runtime.exp.ExpressionException; 021 import railo.runtime.exp.FunctionException; 022 import railo.runtime.exp.PageException; 023 import railo.runtime.type.KeyImpl; 024 import railo.runtime.type.Struct; 025 import railo.runtime.type.util.CollectionUtil; 026 027 /** 028 * A filter which distorts and image by performing coordinate conversions between rectangular and polar coordinates. 029 */ 030 public class PolarFilter extends TransformFilter implements DynFiltering { 031 032 /** 033 * Convert from rectangular to polar coordinates. 034 */ 035 public final static int RECT_TO_POLAR = 0; 036 037 /** 038 * Convert from polar to rectangular coordinates. 039 */ 040 public final static int POLAR_TO_RECT = 1; 041 042 /** 043 * Invert the image in a circle. 044 */ 045 public final static int INVERT_IN_CIRCLE = 2; 046 047 private int type; 048 private float width, height; 049 private float centreX, centreY; 050 private float radius; 051 052 /** 053 * Construct a PolarFilter. 054 */ 055 public PolarFilter() { 056 this(RECT_TO_POLAR); 057 } 058 059 /** 060 * Construct a PolarFilter. 061 * @param type the distortion type 062 */ 063 public PolarFilter(int type) { 064 super(ConvolveFilter.CLAMP_EDGES); 065 this.type=type; 066 } 067 068 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 069 this.width = src.getWidth(); 070 this.height = src.getHeight(); 071 centreX = width/2; 072 centreY = height/2; 073 radius = Math.max(centreY, centreX); 074 return super.filter( src, dst ); 075 } 076 077 /** 078 * Set the distortion type, valid values are 079 * - RECT_TO_POLAR = Convert from rectangular to polar coordinates 080 * - POLAR_TO_RECT = Convert from polar to rectangular coordinates 081 * - INVERT_IN_CIRCLE = Invert the image in a circle 082 */ 083 public void setType(String type) throws ExpressionException { 084 type=type.trim().toUpperCase(); 085 if("RECT_TO_POLAR".equals(type)) this.type = RECT_TO_POLAR; 086 else if("POLAR_TO_RECT".equals(type)) this.type = POLAR_TO_RECT; 087 else if("INVERT_IN_CIRCLE".equals(type)) this.type = INVERT_IN_CIRCLE; 088 else 089 throw new ExpressionException("inavlid type defintion ["+type+"], valid types are [RECT_TO_POLAR,POLAR_TO_RECT,INVERT_IN_CIRCLE]"); 090 } 091 092 /** 093 * Get the distortion type. 094 * @return the distortion type 095 * @see #setType 096 */ 097 public int getType() { 098 return type; 099 } 100 101 private float sqr(float x) { 102 return x*x; 103 } 104 105 protected void transformInverse(int x, int y, float[] out) { 106 float theta, t; 107 float m, xmax, ymax; 108 float r = 0; 109 110 switch (type) { 111 case RECT_TO_POLAR: 112 theta = 0; 113 if (x >= centreX) { 114 if (y > centreY) { 115 theta = ImageMath.PI - (float)Math.atan(((x - centreX))/((y - centreY))); 116 r = (float)Math.sqrt(sqr (x - centreX) + sqr (y - centreY)); 117 } else if (y < centreY) { 118 theta = (float)Math.atan (((x - centreX))/((centreY - y))); 119 r = (float)Math.sqrt (sqr (x - centreX) + sqr (centreY - y)); 120 } else { 121 theta = ImageMath.HALF_PI; 122 r = x - centreX; 123 } 124 } else if (x < centreX) { 125 if (y < centreY) { 126 theta = ImageMath.TWO_PI - (float)Math.atan (((centreX -x))/((centreY - y))); 127 r = (float)Math.sqrt (sqr (centreX - x) + sqr (centreY - y)); 128 } else if (y > centreY) { 129 theta = ImageMath.PI + (float)Math.atan (((centreX - x))/((y - centreY))); 130 r = (float)Math.sqrt (sqr (centreX - x) + sqr (y - centreY)); 131 } else { 132 theta = 1.5f * ImageMath.PI; 133 r = centreX - x; 134 } 135 } 136 if (x != centreX) 137 m = Math.abs (((y - centreY)) / ((x - centreX))); 138 else 139 m = 0; 140 141 if (m <= (height / width)) { 142 if (x == centreX) { 143 xmax = 0; 144 ymax = centreY; 145 } else { 146 xmax = centreX; 147 ymax = m * xmax; 148 } 149 } else { 150 ymax = centreY; 151 xmax = ymax / m; 152 } 153 154 out[0] = (width-1) - (width - 1)/ImageMath.TWO_PI * theta; 155 out[1] = height * r / radius; 156 break; 157 case POLAR_TO_RECT: 158 theta = x / width * ImageMath.TWO_PI; 159 float theta2; 160 161 if (theta >= 1.5f * ImageMath.PI) 162 theta2 = ImageMath.TWO_PI - theta; 163 else if (theta >= ImageMath.PI) 164 theta2 = theta - ImageMath.PI; 165 else if (theta >= 0.5f * ImageMath.PI) 166 theta2 = ImageMath.PI - theta; 167 else 168 theta2 = theta; 169 170 t = (float)Math.tan(theta2); 171 if (t != 0) 172 m = 1.0f / t; 173 else 174 m = 0; 175 176 if (m <= ((height) / (width))) { 177 if (theta2 == 0) { 178 xmax = 0; 179 ymax = centreY; 180 } else { 181 xmax = centreX; 182 ymax = m * xmax; 183 } 184 } else { 185 ymax = centreY; 186 xmax = ymax / m; 187 } 188 189 r = radius * (y / (height)); 190 191 float nx = -r * (float)Math.sin(theta2); 192 float ny = r * (float)Math.cos(theta2); 193 194 if (theta >= 1.5f * ImageMath.PI) { 195 out[0] = centreX - nx; 196 out[1] = centreY - ny; 197 } else if (theta >= Math.PI) { 198 out[0] = centreX - nx; 199 out[1] = centreY + ny; 200 } else if (theta >= 0.5 * Math.PI) { 201 out[0] = centreX + nx; 202 out[1] = centreY + ny; 203 } else { 204 out[0] = centreX + nx; 205 out[1] = centreY - ny; 206 } 207 break; 208 case INVERT_IN_CIRCLE: 209 float dx = x-centreX; 210 float dy = y-centreY; 211 float distance2 = dx*dx+dy*dy; 212 out[0] = centreX + centreX*centreX * dx/distance2; 213 out[1] = centreY + centreY*centreY * dy/distance2; 214 break; 215 } 216 } 217 218 public String toString() { 219 return "Distort/Polar Coordinates..."; 220 } 221 222 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src); 223 Object o; 224 if((o=parameters.removeEL(KeyImpl.init("Type")))!=null)setType(ImageFilterUtil.toString(o,"Type")); 225 if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction")); 226 if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation")); 227 228 // check for arguments not supported 229 if(parameters.size()>0) { 230 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 [Type, EdgeAction, Interpolation]"); 231 } 232 233 return filter(src, dst); 234 } 235 }