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.Rectangle; 018 import java.awt.image.BufferedImage; 019 import java.util.Random; 020 021 import railo.runtime.engine.ThreadLocalPageContext; 022 import railo.runtime.exp.FunctionException; 023 import railo.runtime.exp.PageException; 024 import railo.runtime.img.ImageUtil; 025 import railo.runtime.img.math.Noise; 026 import railo.runtime.type.KeyImpl; 027 import railo.runtime.type.Struct; 028 import railo.runtime.type.util.CollectionUtil; 029 /** 030 * A filter which simulates underwater caustics. This can be animated to get a bottom-of-the-swimming-pool effect. 031 */ 032 public class CausticsFilter extends WholeImageFilter implements DynFiltering { 033 034 private float scale = 32; 035 //private float angle = 0.0f; 036 private int brightness = 10; 037 private float amount = 1.0f; 038 private float turbulence = 1.0f; 039 private float dispersion = 0.0f; 040 private float time = 0.0f; 041 private int samples = 2; 042 private int bgColor = 0xff799fff; 043 044 private float s, c; 045 046 public CausticsFilter() { 047 } 048 049 /** 050 * Specifies the scale of the texture. 051 * @param scale the scale of the texture. 052 * @min-value 1 053 * @max-value 300+ 054 * @see #getScale 055 */ 056 public void setScale(float scale) { 057 this.scale = scale; 058 } 059 060 /** 061 * Returns the scale of the texture. 062 * @return the scale of the texture. 063 * @see #setScale 064 */ 065 public float getScale() { 066 return scale; 067 } 068 069 /** 070 * Set the brightness. 071 * @param brightness the brightness. 072 * @min-value 0 073 * @max-value 1 074 * @see #getBrightness 075 */ 076 public void setBrightness(int brightness) { 077 this.brightness = brightness; 078 } 079 080 /** 081 * Get the brightness. 082 * @return the brightness. 083 * @see #setBrightness 084 */ 085 public int getBrightness() { 086 return brightness; 087 } 088 089 /** 090 * Specifies the turbulence of the texture. 091 * @param turbulence the turbulence of the texture. 092 * @min-value 0 093 * @max-value 1 094 * @see #getTurbulence 095 */ 096 public void setTurbulence(float turbulence) { 097 this.turbulence = turbulence; 098 } 099 100 /** 101 * Returns the turbulence of the effect. 102 * @return the turbulence of the effect. 103 * @see #setTurbulence 104 */ 105 public float getTurbulence() { 106 return turbulence; 107 } 108 109 /** 110 * Set the amount of effect. 111 * @param amount the amount 112 * @min-value 0 113 * @max-value 1 114 * @see #getAmount 115 */ 116 public void setAmount(float amount) { 117 this.amount = amount; 118 } 119 120 /** 121 * Get the amount of effect. 122 * @return the amount 123 * @see #setAmount 124 */ 125 public float getAmount() { 126 return amount; 127 } 128 129 /** 130 * Set the dispersion. 131 * @param dispersion the dispersion 132 * @min-value 0 133 * @max-value 1 134 * @see #getDispersion 135 */ 136 public void setDispersion(float dispersion) { 137 this.dispersion = dispersion; 138 } 139 140 /** 141 * Get the dispersion. 142 * @return the dispersion 143 * @see #setDispersion 144 */ 145 public float getDispersion() { 146 return dispersion; 147 } 148 149 /** 150 * Set the time. Use this to animate the effect. 151 * @param time the time 152 * @see #getTime 153 */ 154 public void setTime(float time) { 155 this.time = time; 156 } 157 158 /** 159 * Set the time. 160 * @return the time 161 * @see #setTime 162 */ 163 public float getTime() { 164 return time; 165 } 166 167 /** 168 * Set the number of samples per pixel. More samples means better quality, but slower rendering. 169 * @param samples the number of samples 170 * @see #getSamples 171 */ 172 public void setSamples(int samples) { 173 this.samples = samples; 174 } 175 176 /** 177 * Get the number of samples per pixel. 178 * @return the number of samples 179 * @see #setSamples 180 */ 181 public int getSamples() { 182 return samples; 183 } 184 185 /** 186 * Set the background color. 187 * @param c the color 188 * @see #getBgColor 189 */ 190 public void setBgColor(int c) { 191 bgColor = c; 192 } 193 194 /** 195 * Get the background color. 196 * @return the color 197 * @see #setBgColor 198 */ 199 public int getBgColor() { 200 return bgColor; 201 } 202 203 protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) { 204 Random random = new Random(0); 205 206 s = (float)Math.sin(0.1); 207 c = (float)Math.cos(0.1); 208 209 //int srcWidth = originalSpace.width; 210 //int srcHeight = originalSpace.height; 211 int outWidth = transformedSpace.width; 212 int outHeight = transformedSpace.height; 213 int index = 0; 214 int[] pixels = new int[outWidth * outHeight]; 215 216 for (int y = 0; y < outHeight; y++) { 217 for (int x = 0; x < outWidth; x++) { 218 pixels[index++] = bgColor; 219 } 220 } 221 222 int v = brightness/samples; 223 if (v == 0) 224 v = 1; 225 226 float rs = 1.0f/scale; 227 float d = 0.95f; 228 index = 0; 229 for (int y = 0; y < outHeight; y++) { 230 for (int x = 0; x < outWidth; x++) { 231 for (int s = 0; s < samples; s++) { 232 float sx = x+random.nextFloat(); 233 float sy = y+random.nextFloat(); 234 float nx = sx*rs; 235 float ny = sy*rs; 236 float xDisplacement, yDisplacement; 237 float focus = 0.1f+amount; 238 xDisplacement = evaluate(nx-d, ny) - evaluate(nx+d, ny); 239 yDisplacement = evaluate(nx, ny+d) - evaluate(nx, ny-d); 240 241 if (dispersion > 0) { 242 for (int c = 0; c < 3; c++) { 243 float ca = (1+c*dispersion); 244 float srcX = sx + scale*focus * xDisplacement*ca; 245 float srcY = sy + scale*focus * yDisplacement*ca; 246 247 if (srcX < 0 || srcX >= outWidth-1 || srcY < 0 || srcY >= outHeight-1) { 248 } else { 249 int i = ((int)srcY)*outWidth+(int)srcX; 250 int rgb = pixels[i]; 251 int r = (rgb >> 16) & 0xff; 252 int g = (rgb >> 8) & 0xff; 253 int b = rgb & 0xff; 254 if (c == 2) 255 r += v; 256 else if (c == 1) 257 g += v; 258 else 259 b += v; 260 if (r > 255) 261 r = 255; 262 if (g > 255) 263 g = 255; 264 if (b > 255) 265 b = 255; 266 pixels[i] = 0xff000000 | (r << 16) | (g << 8) | b; 267 } 268 } 269 } else { 270 float srcX = sx + scale*focus * xDisplacement; 271 float srcY = sy + scale*focus * yDisplacement; 272 273 if (srcX < 0 || srcX >= outWidth-1 || srcY < 0 || srcY >= outHeight-1) { 274 } else { 275 int i = ((int)srcY)*outWidth+(int)srcX; 276 int rgb = pixels[i]; 277 int r = (rgb >> 16) & 0xff; 278 int g = (rgb >> 8) & 0xff; 279 int b = rgb & 0xff; 280 r += v; 281 g += v; 282 b += v; 283 if (r > 255) 284 r = 255; 285 if (g > 255) 286 g = 255; 287 if (b > 255) 288 b = 255; 289 pixels[i] = 0xff000000 | (r << 16) | (g << 8) | b; 290 } 291 } 292 } 293 } 294 } 295 return pixels; 296 } 297 298 /*private static int add(int rgb, float brightness) { 299 int r = (rgb >> 16) & 0xff; 300 int g = (rgb >> 8) & 0xff; 301 int b = rgb & 0xff; 302 r += brightness; 303 g += brightness; 304 b += brightness; 305 if (r > 255) 306 r = 255; 307 if (g > 255) 308 g = 255; 309 if (b > 255) 310 b = 255; 311 return 0xff000000 | (r << 16) | (g << 8) | b; 312 }*/ 313 314 /*private static int add(int rgb, float brightness, int c) { 315 int r = (rgb >> 16) & 0xff; 316 int g = (rgb >> 8) & 0xff; 317 int b = rgb & 0xff; 318 if (c == 2) 319 r += brightness; 320 else if (c == 1) 321 g += brightness; 322 else 323 b += brightness; 324 if (r > 255) 325 r = 255; 326 if (g > 255) 327 g = 255; 328 if (b > 255) 329 b = 255; 330 return 0xff000000 | (r << 16) | (g << 8) | b; 331 }*/ 332 333 private static float turbulence2(float x, float y, float time, float octaves) { 334 float value = 0.0f; 335 float remainder; 336 float lacunarity = 2.0f; 337 float f = 1.0f; 338 int i; 339 340 // to prevent "cascading" effects 341 x += 371; 342 y += 529; 343 344 for (i = 0; i < (int)octaves; i++) { 345 value += Noise.noise3(x, y, time) / f; 346 x *= lacunarity; 347 y *= lacunarity; 348 f *= 2; 349 } 350 351 remainder = octaves - (int)octaves; 352 if (remainder != 0) 353 value += remainder * Noise.noise3(x, y, time) / f; 354 355 return value; 356 } 357 358 private float evaluate(float x, float y) { 359 float xt = s*x + c*time; 360 float tt = c*x - c*time; 361 float f = turbulence == 0.0 ? Noise.noise3(xt, y, tt) : turbulence2(xt, y, tt, turbulence); 362 return f; 363 } 364 365 public String toString() { 366 return "Texture/Caustics..."; 367 } 368 369 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 370 Object o; 371 if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount")); 372 if((o=parameters.removeEL(KeyImpl.init("Brightness")))!=null)setBrightness(ImageFilterUtil.toIntValue(o,"Brightness")); 373 if((o=parameters.removeEL(KeyImpl.init("Turbulence")))!=null)setTurbulence(ImageFilterUtil.toFloatValue(o,"Turbulence")); 374 if((o=parameters.removeEL(KeyImpl.init("Dispersion")))!=null)setDispersion(ImageFilterUtil.toFloatValue(o,"Dispersion")); 375 if((o=parameters.removeEL(KeyImpl.init("BgColor")))!=null)setBgColor(ImageFilterUtil.toColorRGB(o,"BgColor")); 376 if((o=parameters.removeEL(KeyImpl.init("Time")))!=null)setTime(ImageFilterUtil.toFloatValue(o,"Time")); 377 if((o=parameters.removeEL(KeyImpl.init("Scale")))!=null)setScale(ImageFilterUtil.toFloatValue(o,"Scale")); 378 if((o=parameters.removeEL(KeyImpl.init("Samples")))!=null)setSamples(ImageFilterUtil.toIntValue(o,"Samples")); 379 380 // check for arguments not supported 381 if(parameters.size()>0) { 382 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 [Amount, Brightness, Turbulence, Dispersion, BgColor, Time, Scale, Samples]"); 383 } 384 385 return filter(src, dst); 386 } 387 }