001/* 002Copyright 2006 Jerry Huxtable 003 004Licensed under the Apache License, Version 2.0 (the "License"); 005you may not use this file except in compliance with the License. 006You may obtain a copy of the License at 007 008 http://www.apache.org/licenses/LICENSE-2.0 009 010Unless required by applicable law or agreed to in writing, software 011distributed under the License is distributed on an "AS IS" BASIS, 012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013See the License for the specific language governing permissions and 014limitations under the License. 015*/ 016 017package com.jhlabs.image; 018 019import java.awt.*; 020import java.awt.image.*; 021import java.util.*; 022import com.jhlabs.math.*; 023 024/** 025 * A filter which simulates underwater caustics. This can be animated to get a bottom-of-the-swimming-pool effect. 026 */ 027public class CausticsFilter extends WholeImageFilter { 028 029 private float scale = 32; 030 private float angle = 0.0f; 031 private int brightness = 10; 032 private float amount = 1.0f; 033 private float turbulence = 1.0f; 034 private float dispersion = 0.0f; 035 private float time = 0.0f; 036 private int samples = 2; 037 private int bgColor = 0xff799fff; 038 039 private float s, c; 040 041 public CausticsFilter() { 042 } 043 044 /** 045 * Specifies the scale of the texture. 046 * @param scale the scale of the texture. 047 * @min-value 1 048 * @max-value 300+ 049 * @see #getScale 050 */ 051 public void setScale(float scale) { 052 this.scale = scale; 053 } 054 055 /** 056 * Returns the scale of the texture. 057 * @return the scale of the texture. 058 * @see #setScale 059 */ 060 public float getScale() { 061 return scale; 062 } 063 064 /** 065 * Set the brightness. 066 * @param brightness the brightness. 067 * @min-value 0 068 * @max-value 1 069 * @see #getBrightness 070 */ 071 public void setBrightness(int brightness) { 072 this.brightness = brightness; 073 } 074 075 /** 076 * Get the brightness. 077 * @return the brightness. 078 * @see #setBrightness 079 */ 080 public int getBrightness() { 081 return brightness; 082 } 083 084 /** 085 * Specifies the turbulence of the texture. 086 * @param turbulence the turbulence of the texture. 087 * @min-value 0 088 * @max-value 1 089 * @see #getTurbulence 090 */ 091 public void setTurbulence(float turbulence) { 092 this.turbulence = turbulence; 093 } 094 095 /** 096 * Returns the turbulence of the effect. 097 * @return the turbulence of the effect. 098 * @see #setTurbulence 099 */ 100 public float getTurbulence() { 101 return turbulence; 102 } 103 104 /** 105 * Set the amount of effect. 106 * @param amount the amount 107 * @min-value 0 108 * @max-value 1 109 * @see #getAmount 110 */ 111 public void setAmount(float amount) { 112 this.amount = amount; 113 } 114 115 /** 116 * Get the amount of effect. 117 * @return the amount 118 * @see #setAmount 119 */ 120 public float getAmount() { 121 return amount; 122 } 123 124 /** 125 * Set the dispersion. 126 * @param dispersion the dispersion 127 * @min-value 0 128 * @max-value 1 129 * @see #getDispersion 130 */ 131 public void setDispersion(float dispersion) { 132 this.dispersion = dispersion; 133 } 134 135 /** 136 * Get the dispersion. 137 * @return the dispersion 138 * @see #setDispersion 139 */ 140 public float getDispersion() { 141 return dispersion; 142 } 143 144 /** 145 * Set the time. Use this to animate the effect. 146 * @param time the time 147 * @see #getTime 148 */ 149 public void setTime(float time) { 150 this.time = time; 151 } 152 153 /** 154 * Set the time. 155 * @return the time 156 * @see #setTime 157 */ 158 public float getTime() { 159 return time; 160 } 161 162 /** 163 * Set the number of samples per pixel. More samples means better quality, but slower rendering. 164 * @param samples the number of samples 165 * @see #getSamples 166 */ 167 public void setSamples(int samples) { 168 this.samples = samples; 169 } 170 171 /** 172 * Get the number of samples per pixel. 173 * @return the number of samples 174 * @see #setSamples 175 */ 176 public int getSamples() { 177 return samples; 178 } 179 180 /** 181 * Set the background color. 182 * @param c the color 183 * @see #getBgColor 184 */ 185 public void setBgColor(int c) { 186 bgColor = c; 187 } 188 189 /** 190 * Get the background color. 191 * @return the color 192 * @see #setBgColor 193 */ 194 public int getBgColor() { 195 return bgColor; 196 } 197 198 protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) { 199 Random random = new Random(0); 200 201 s = (float)Math.sin(0.1); 202 c = (float)Math.cos(0.1); 203 204 int srcWidth = originalSpace.width; 205 int srcHeight = originalSpace.height; 206 int outWidth = transformedSpace.width; 207 int outHeight = transformedSpace.height; 208 int index = 0; 209 int[] pixels = new int[outWidth * outHeight]; 210 211 for (int y = 0; y < outHeight; y++) { 212 for (int x = 0; x < outWidth; x++) { 213 pixels[index++] = bgColor; 214 } 215 } 216 217 int v = brightness/samples; 218 if (v == 0) 219 v = 1; 220 221 float rs = 1.0f/scale; 222 float d = 0.95f; 223 index = 0; 224 for (int y = 0; y < outHeight; y++) { 225 for (int x = 0; x < outWidth; x++) { 226 for (int s = 0; s < samples; s++) { 227 float sx = x+random.nextFloat(); 228 float sy = y+random.nextFloat(); 229 float nx = sx*rs; 230 float ny = sy*rs; 231 float xDisplacement, yDisplacement; 232 float focus = 0.1f+amount; 233 xDisplacement = evaluate(nx-d, ny) - evaluate(nx+d, ny); 234 yDisplacement = evaluate(nx, ny+d) - evaluate(nx, ny-d); 235 236 if (dispersion > 0) { 237 for (int c = 0; c < 3; c++) { 238 float ca = (1+c*dispersion); 239 float srcX = sx + scale*focus * xDisplacement*ca; 240 float srcY = sy + scale*focus * yDisplacement*ca; 241 242 if (srcX < 0 || srcX >= outWidth-1 || srcY < 0 || srcY >= outHeight-1) { 243 } else { 244 int i = ((int)srcY)*outWidth+(int)srcX; 245 int rgb = pixels[i]; 246 int r = (rgb >> 16) & 0xff; 247 int g = (rgb >> 8) & 0xff; 248 int b = rgb & 0xff; 249 if (c == 2) 250 r += v; 251 else if (c == 1) 252 g += v; 253 else 254 b += v; 255 if (r > 255) 256 r = 255; 257 if (g > 255) 258 g = 255; 259 if (b > 255) 260 b = 255; 261 pixels[i] = 0xff000000 | (r << 16) | (g << 8) | b; 262 } 263 } 264 } else { 265 float srcX = sx + scale*focus * xDisplacement; 266 float srcY = sy + scale*focus * yDisplacement; 267 268 if (srcX < 0 || srcX >= outWidth-1 || srcY < 0 || srcY >= outHeight-1) { 269 } else { 270 int i = ((int)srcY)*outWidth+(int)srcX; 271 int rgb = pixels[i]; 272 int r = (rgb >> 16) & 0xff; 273 int g = (rgb >> 8) & 0xff; 274 int b = rgb & 0xff; 275 r += v; 276 g += v; 277 b += v; 278 if (r > 255) 279 r = 255; 280 if (g > 255) 281 g = 255; 282 if (b > 255) 283 b = 255; 284 pixels[i] = 0xff000000 | (r << 16) | (g << 8) | b; 285 } 286 } 287 } 288 } 289 } 290 return pixels; 291 } 292 293 private static int add(int rgb, float brightness) { 294 int r = (rgb >> 16) & 0xff; 295 int g = (rgb >> 8) & 0xff; 296 int b = rgb & 0xff; 297 r += brightness; 298 g += brightness; 299 b += brightness; 300 if (r > 255) 301 r = 255; 302 if (g > 255) 303 g = 255; 304 if (b > 255) 305 b = 255; 306 return 0xff000000 | (r << 16) | (g << 8) | b; 307 } 308 309 private static int add(int rgb, float brightness, int c) { 310 int r = (rgb >> 16) & 0xff; 311 int g = (rgb >> 8) & 0xff; 312 int b = rgb & 0xff; 313 if (c == 2) 314 r += brightness; 315 else if (c == 1) 316 g += brightness; 317 else 318 b += brightness; 319 if (r > 255) 320 r = 255; 321 if (g > 255) 322 g = 255; 323 if (b > 255) 324 b = 255; 325 return 0xff000000 | (r << 16) | (g << 8) | b; 326 } 327 328 private static float turbulence2(float x, float y, float time, float octaves) { 329 float value = 0.0f; 330 float remainder; 331 float lacunarity = 2.0f; 332 float f = 1.0f; 333 int i; 334 335 // to prevent "cascading" effects 336 x += 371; 337 y += 529; 338 339 for (i = 0; i < (int)octaves; i++) { 340 value += Noise.noise3(x, y, time) / f; 341 x *= lacunarity; 342 y *= lacunarity; 343 f *= 2; 344 } 345 346 remainder = octaves - (int)octaves; 347 if (remainder != 0) 348 value += remainder * Noise.noise3(x, y, time) / f; 349 350 return value; 351 } 352 353 private float evaluate(float x, float y) { 354 float xt = s*x + c*time; 355 float tt = c*x - c*time; 356 float f = turbulence == 0.0 ? Noise.noise3(xt, y, tt) : turbulence2(xt, y, tt, turbulence); 357 return f; 358 } 359 360 public String toString() { 361 return "Texture/Caustics..."; 362 } 363 364}