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