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.Rectangle; 036import java.awt.image.BufferedImage; 037 038import lucee.runtime.engine.ThreadLocalPageContext; 039import lucee.runtime.exp.ExpressionException; 040import lucee.runtime.exp.FunctionException; 041import lucee.runtime.exp.PageException; 042import lucee.runtime.img.math.Noise; 043import lucee.runtime.type.KeyImpl; 044import lucee.runtime.type.Struct; 045import lucee.runtime.type.util.CollectionUtil; 046 047/** 048 * A filter which distorts an image by rippling it in the X or Y directions. 049 * The amplitude and wavelength of rippling can be specified as well as whether 050 * pixels going off the edges are wrapped or not. 051 */ 052public class RippleFilter extends TransformFilter implements DynFiltering { 053 054 /** 055 * Sine wave ripples. 056 */ 057 public final static int SINE = 0; 058 059 /** 060 * Sawtooth wave ripples. 061 */ 062 public final static int SAWTOOTH = 1; 063 064 /** 065 * Triangle wave ripples. 066 */ 067 public final static int TRIANGLE = 2; 068 069 /** 070 * Noise ripples. 071 */ 072 public final static int NOISE = 3; 073 074 private float xAmplitude, yAmplitude; 075 private float xWavelength, yWavelength; 076 private int waveType; 077 078 /** 079 * Construct a RippleFilter. 080 */ 081 public RippleFilter() { 082 xAmplitude = 5.0f; 083 yAmplitude = 0.0f; 084 xWavelength = yWavelength = 16.0f; 085 } 086 087 /** 088 * Set the amplitude of ripple in the X direction. 089 * @param xAmplitude the amplitude (in pixels). 090 * @see #getXAmplitude 091 */ 092 public void setXAmplitude(float xAmplitude) { 093 this.xAmplitude = xAmplitude; 094 } 095 096 /** 097 * Get the amplitude of ripple in the X direction. 098 * @return the amplitude (in pixels). 099 * @see #setXAmplitude 100 */ 101 public float getXAmplitude() { 102 return xAmplitude; 103 } 104 105 /** 106 * Set the wavelength of ripple in the X direction. 107 * @param xWavelength the wavelength (in pixels). 108 * @see #getXWavelength 109 */ 110 public void setXWavelength(float xWavelength) { 111 this.xWavelength = xWavelength; 112 } 113 114 /** 115 * Get the wavelength of ripple in the X direction. 116 * @return the wavelength (in pixels). 117 * @see #setXWavelength 118 */ 119 public float getXWavelength() { 120 return xWavelength; 121 } 122 123 /** 124 * Set the amplitude of ripple in the Y direction. 125 * @param yAmplitude the amplitude (in pixels). 126 * @see #getYAmplitude 127 */ 128 public void setYAmplitude(float yAmplitude) { 129 this.yAmplitude = yAmplitude; 130 } 131 132 /** 133 * Get the amplitude of ripple in the Y direction. 134 * @return the amplitude (in pixels). 135 * @see #setYAmplitude 136 */ 137 public float getYAmplitude() { 138 return yAmplitude; 139 } 140 141 /** 142 * Set the wavelength of ripple in the Y direction. 143 * @param yWavelength the wavelength (in pixels). 144 * @see #getYWavelength 145 */ 146 public void setYWavelength(float yWavelength) { 147 this.yWavelength = yWavelength; 148 } 149 150 /** 151 * Get the wavelength of ripple in the Y direction. 152 * @return the wavelength (in pixels). 153 * @see #setYWavelength 154 */ 155 public float getYWavelength() { 156 return yWavelength; 157 } 158 159 160 /** 161 * Set the wave type. 162 * valid values are: 163 * - sine (default): Sine wave ripples. 164 * - sawtooth: Sawtooth wave ripples. 165 * - triangle: Triangle wave ripples. 166 * - noise: Noise ripples. 167 * @param waveType the type. 168 * @throws ExpressionException 169 * @see #getWaveType 170 */ 171 public void setWaveType(String waveType) throws ExpressionException { 172 173 String str=waveType.trim().toUpperCase(); 174 if("SINE".equals(str)) this.waveType = SINE; 175 else if("SAWTOOTH".equals(str)) this.waveType = SAWTOOTH; 176 else if("TRIANGLE".equals(str)) this.waveType = TRIANGLE; 177 else if("NOISE".equals(str)) this.waveType = NOISE; 178 else 179 throw new ExpressionException("invalid value ["+waveType+"] for waveType, valid values are [sine,sawtooth,triangle,noise]"); 180 181 } 182 183 /** 184 * Get the wave type. 185 * @return the type. 186 * @see #setWaveType 187 */ 188 public int getWaveType() { 189 return waveType; 190 } 191 192 protected void transformSpace(Rectangle r) { 193 if (edgeAction == ConvolveFilter.ZERO_EDGES) { 194 r.x -= (int)xAmplitude; 195 r.width += (int)(2*xAmplitude); 196 r.y -= (int)yAmplitude; 197 r.height += (int)(2*yAmplitude); 198 } 199 } 200 201 protected void transformInverse(int x, int y, float[] out) { 202 float nx = y / xWavelength; 203 float ny = x / yWavelength; 204 float fx, fy; 205 switch (waveType) { 206 case SINE: 207 default: 208 fx = (float)Math.sin(nx); 209 fy = (float)Math.sin(ny); 210 break; 211 case SAWTOOTH: 212 fx = ImageMath.mod(nx, 1); 213 fy = ImageMath.mod(ny, 1); 214 break; 215 case TRIANGLE: 216 fx = ImageMath.triangle(nx); 217 fy = ImageMath.triangle(ny); 218 break; 219 case NOISE: 220 fx = Noise.noise1(nx); 221 fy = Noise.noise1(ny); 222 break; 223 } 224 out[0] = x + xAmplitude * fx; 225 out[1] = y + yAmplitude * fy; 226 } 227 228 public String toString() { 229 return "Distort/Ripple..."; 230 } 231 232 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException { 233 //BufferedImage dst=ImageUtil.createBufferedImage(src,src.getWidth()+400,src.getHeight()+400); 234 Object o; 235 if((o=parameters.removeEL(KeyImpl.init("XAmplitude")))!=null)setXAmplitude(ImageFilterUtil.toFloatValue(o,"XAmplitude")); 236 if((o=parameters.removeEL(KeyImpl.init("XWavelength")))!=null)setXWavelength(ImageFilterUtil.toFloatValue(o,"XWavelength")); 237 if((o=parameters.removeEL(KeyImpl.init("YAmplitude")))!=null)setYAmplitude(ImageFilterUtil.toFloatValue(o,"YAmplitude")); 238 if((o=parameters.removeEL(KeyImpl.init("YWavelength")))!=null)setYWavelength(ImageFilterUtil.toFloatValue(o,"YWavelength")); 239 if((o=parameters.removeEL(KeyImpl.init("WaveType")))!=null)setWaveType(ImageFilterUtil.toString(o,"WaveType")); 240 if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction")); 241 if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation")); 242 243 // check for arguments not supported 244 if(parameters.size()>0) { 245 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 [XAmplitude, XWavelength, YAmplitude, YWavelength, WaveType, EdgeAction, Interpolation]"); 246 } 247 248 return filter(src, (BufferedImage)null); 249 } 250}