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 produces a water ripple distortion. 047 */ 048public class WaterFilter extends TransformFilter implements DynFiltering { 049 050 private float wavelength = 16; 051 private float amplitude = 10; 052 private float phase = 0; 053 private float centreX = 0.5f; 054 private float centreY = 0.5f; 055 private float radius = 50; 056 057 private float radius2 = 0; 058 private float icentreX; 059 private float icentreY; 060 061 public WaterFilter() { 062 super(ConvolveFilter.CLAMP_EDGES ); 063 } 064 065 /** 066 * Set the wavelength of the ripples. 067 * @param wavelength the wavelength 068 * @see #getWavelength 069 */ 070 public void setWavelength(float wavelength) { 071 this.wavelength = wavelength; 072 } 073 074 /** 075 * Get the wavelength of the ripples. 076 * @return the wavelength 077 * @see #setWavelength 078 */ 079 public float getWavelength() { 080 return wavelength; 081 } 082 083 /** 084 * Set the amplitude of the ripples. 085 * @param amplitude the amplitude 086 * @see #getAmplitude 087 */ 088 public void setAmplitude(float amplitude) { 089 this.amplitude = amplitude; 090 } 091 092 /** 093 * Get the amplitude of the ripples. 094 * @return the amplitude 095 * @see #setAmplitude 096 */ 097 public float getAmplitude() { 098 return amplitude; 099 } 100 101 /** 102 * Set the phase of the ripples. 103 * @param phase the phase 104 * @see #getPhase 105 */ 106 public void setPhase(float phase) { 107 this.phase = phase; 108 } 109 110 /** 111 * Get the phase of the ripples. 112 * @return the phase 113 * @see #setPhase 114 */ 115 public float getPhase() { 116 return phase; 117 } 118 119 /** 120 * Set the centre of the effect in the X direction as a proportion of the image size. 121 * @param centreX the center 122 * @see #getCentreX 123 */ 124 public void setCentreX( float centreX ) { 125 this.centreX = centreX; 126 } 127 128 /** 129 * Get the centre of the effect in the X direction as a proportion of the image size. 130 * @return the center 131 * @see #setCentreX 132 */ 133 public float getCentreX() { 134 return centreX; 135 } 136 137 /** 138 * Set the centre of the effect in the Y direction as a proportion of the image size. 139 * @param centreY the center 140 * @see #getCentreY 141 */ 142 public void setCentreY( float centreY ) { 143 this.centreY = centreY; 144 } 145 146 /** 147 * Get the centre of the effect in the Y direction as a proportion of the image size. 148 * @return the center 149 * @see #setCentreY 150 */ 151 public float getCentreY() { 152 return centreY; 153 } 154 155 /** 156 * Set the centre of the effect as a proportion of the image size. 157 * @param centre the center 158 * @see #getCentre 159 */ 160 public void setCentre( Point2D centre ) { 161 this.centreX = (float)centre.getX(); 162 this.centreY = (float)centre.getY(); 163 } 164 165 /** 166 * Get the centre of the effect as a proportion of the image size. 167 * @return the center 168 * @see #setCentre 169 */ 170 public Point2D getCentre() { 171 return new Point2D.Float( centreX, centreY ); 172 } 173 174 /** 175 * Set the radius of the effect. 176 * @param radius the radius 177 * @min-value 0 178 * @see #getRadius 179 */ 180 public void setRadius(float radius) { 181 this.radius = radius; 182 } 183 184 /** 185 * Get the radius of the effect. 186 * @return the radius 187 * @see #setRadius 188 */ 189 public float getRadius() { 190 return radius; 191 } 192 193 private boolean inside(int v, int a, int b) { 194 return a <= v && v <= b; 195 } 196 197 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 198 icentreX = src.getWidth() * centreX; 199 icentreY = src.getHeight() * centreY; 200 if ( radius == 0 ) 201 radius = Math.min(icentreX, icentreY); 202 radius2 = radius*radius; 203 return super.filter( src, dst ); 204 } 205 206 protected void transformInverse(int x, int y, float[] out) { 207 float dx = x-icentreX; 208 float dy = y-icentreY; 209 float distance2 = dx*dx + dy*dy; 210 if (distance2 > radius2) { 211 out[0] = x; 212 out[1] = y; 213 } else { 214 float distance = (float)Math.sqrt(distance2); 215 float amount = amplitude * (float)Math.sin(distance / wavelength * ImageMath.TWO_PI - phase); 216 amount *= (radius-distance)/radius; 217 if ( distance != 0 ) 218 amount *= wavelength/distance; 219 out[0] = x + dx*amount; 220 out[1] = y + dy*amount; 221 } 222 } 223 224 public String toString() { 225 return "Distort/Water Ripples..."; 226 } 227 228 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src); 229 Object o; 230 if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toFloatValue(o,"Radius")); 231 if((o=parameters.removeEL(KeyImpl.init("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX")); 232 if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY")); 233 //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre")); 234 if((o=parameters.removeEL(KeyImpl.init("Wavelength")))!=null)setWavelength(ImageFilterUtil.toFloatValue(o,"Wavelength")); 235 if((o=parameters.removeEL(KeyImpl.init("Amplitude")))!=null)setAmplitude(ImageFilterUtil.toFloatValue(o,"Amplitude")); 236 if((o=parameters.removeEL(KeyImpl.init("Phase")))!=null)setPhase(ImageFilterUtil.toFloatValue(o,"Phase")); 237 if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction")); 238 if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation")); 239 240 // check for arguments not supported 241 if(parameters.size()>0) { 242 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, CentreX, CentreY, Centre, Wavelength, Amplitude, Phase, EdgeAction, Interpolation]"); 243 } 244 245 return filter(src, dst); 246 } 247}