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.Graphics2D; 018 import java.awt.Toolkit; 019 import java.awt.image.BufferedImage; 020 import java.util.Random; 021 022 import railo.runtime.engine.ThreadLocalPageContext; 023 import railo.runtime.exp.FunctionException; 024 import railo.runtime.exp.PageException; 025 import railo.runtime.img.ImageUtil; 026 import railo.runtime.img.math.Noise; 027 import railo.runtime.type.KeyImpl; 028 import railo.runtime.type.Struct; 029 import railo.runtime.type.util.CollectionUtil; 030 031 public class SkyFilter extends PointFilter implements DynFiltering { 032 033 private float scale = 0.1f; 034 private float stretch = 1.0f; 035 private float angle = 0.0f; 036 private float amount = 1.0f; 037 private float H = 1.0f; 038 private float octaves = 8.0f; 039 private float lacunarity = 2.0f; 040 private float gain = 1.0f; 041 private float bias = 0.6f; 042 private int operation; 043 //private float min; 044 //private float max; 045 //private boolean ridged; 046 //private FBM fBm; 047 protected Random random = new Random(); 048 //private Function2D basis; 049 050 private float cloudCover = 0.5f; 051 private float cloudSharpness = 0.5f; 052 private float time = 0.3f; 053 private float glow = 0.5f; 054 private float glowFalloff = 0.5f; 055 private float haziness = 0.96f; 056 private float t = 0.0f; 057 //private float sunRadius = 10f; 058 private int sunColor = 0xffffffff; 059 private float sunR, sunG, sunB; 060 private float sunAzimuth = 0.5f; 061 private float sunElevation = 0.5f; 062 private float windSpeed = 0.0f; 063 064 private float cameraAzimuth = 0.0f; 065 private float cameraElevation = 0.0f; 066 private float fov = 1.0f; 067 068 private float[] exponents; 069 private float[] tan; 070 private BufferedImage skyColors; 071 //private int[] skyPixels; 072 073 private final static float r255 = 1.0f/255.0f; 074 075 private float width, height; 076 077 public SkyFilter() { 078 if ( skyColors == null ) { 079 skyColors = ImageUtils.createImage( Toolkit.getDefaultToolkit().getImage( getClass().getResource("SkyColors.png") ).getSource() ); 080 } 081 } 082 083 public void setAmount(float amount) { 084 this.amount = amount; 085 } 086 087 public float getAmount() { 088 return amount; 089 } 090 091 public void setOperation(int operation) { 092 this.operation = operation; 093 } 094 095 public int getOperation() { 096 return operation; 097 } 098 099 public void setScale(float scale) { 100 this.scale = scale; 101 } 102 103 public float getScale() { 104 return scale; 105 } 106 107 public void setStretch(float stretch) { 108 this.stretch = stretch; 109 } 110 111 public float getStretch() { 112 return stretch; 113 } 114 115 public void setT(float t) { 116 this.t = t; 117 } 118 119 public float getT() { 120 return t; 121 } 122 123 public void setFOV(float fov) { 124 this.fov = fov; 125 } 126 127 public float getFOV() { 128 return fov; 129 } 130 131 public void setCloudCover(float cloudCover) { 132 this.cloudCover = cloudCover; 133 } 134 135 public float getCloudCover() { 136 return cloudCover; 137 } 138 139 public void setCloudSharpness(float cloudSharpness) { 140 this.cloudSharpness = cloudSharpness; 141 } 142 143 public float getCloudSharpness() { 144 return cloudSharpness; 145 } 146 147 public void setTime(float time) { 148 this.time = time; 149 } 150 151 public float getTime() { 152 return time; 153 } 154 155 public void setGlow(float glow) { 156 this.glow = glow; 157 } 158 159 public float getGlow() { 160 return glow; 161 } 162 163 public void setGlowFalloff(float glowFalloff) { 164 this.glowFalloff = glowFalloff; 165 } 166 167 public float getGlowFalloff() { 168 return glowFalloff; 169 } 170 171 public void setAngle(float angle) { 172 this.angle = angle; 173 } 174 175 public float getAngle() { 176 return angle; 177 } 178 179 public void setOctaves(float octaves) { 180 this.octaves = octaves; 181 } 182 183 public float getOctaves() { 184 return octaves; 185 } 186 187 public void setH(float H) { 188 this.H = H; 189 } 190 191 public float getH() { 192 return H; 193 } 194 195 public void setLacunarity(float lacunarity) { 196 this.lacunarity = lacunarity; 197 } 198 199 public float getLacunarity() { 200 return lacunarity; 201 } 202 203 public void setGain(float gain) { 204 this.gain = gain; 205 } 206 207 public float getGain() { 208 return gain; 209 } 210 211 public void setBias(float bias) { 212 this.bias = bias; 213 } 214 215 public float getBias() { 216 return bias; 217 } 218 219 public void setHaziness(float haziness) { 220 this.haziness = haziness; 221 } 222 223 public float getHaziness() { 224 return haziness; 225 } 226 227 public void setSunElevation(float sunElevation) { 228 this.sunElevation = sunElevation; 229 } 230 231 public float getSunElevation() { 232 return sunElevation; 233 } 234 235 public void setSunAzimuth(float sunAzimuth) { 236 this.sunAzimuth = sunAzimuth; 237 } 238 239 public float getSunAzimuth() { 240 return sunAzimuth; 241 } 242 243 public void setSunColor(int sunColor) { 244 this.sunColor = sunColor; 245 } 246 247 public int getSunColor() { 248 return sunColor; 249 } 250 251 public void setCameraElevation(float cameraElevation) { 252 this.cameraElevation = cameraElevation; 253 } 254 255 public float getCameraElevation() { 256 return cameraElevation; 257 } 258 259 public void setCameraAzimuth(float cameraAzimuth) { 260 this.cameraAzimuth = cameraAzimuth; 261 } 262 263 public float getCameraAzimuth() { 264 return cameraAzimuth; 265 } 266 267 public void setWindSpeed(float windSpeed) { 268 this.windSpeed = windSpeed; 269 } 270 271 public float getWindSpeed() { 272 return windSpeed; 273 } 274 275 float mn, mx; 276 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 277 long start = System.currentTimeMillis(); 278 sunR = ((sunColor >> 16) & 0xff) * r255; 279 sunG = ((sunColor >> 8) & 0xff) * r255; 280 sunB = (sunColor & 0xff) * r255; 281 282 mn = 10000; 283 mx = -10000; 284 exponents = new float[(int)octaves+1]; 285 //float frequency = 1.0f; 286 for (int i = 0; i <= (int)octaves; i++) { 287 exponents[i] = (float)Math.pow(2, -i); 288 //frequency *= lacunarity; 289 } 290 291 //min = -1; 292 //max = 1; 293 294 //min = -1.2f; 295 //max = 1.2f; 296 297 width = src.getWidth(); 298 height = src.getHeight(); 299 300 int h = src.getHeight(); 301 tan = new float[h]; 302 for (int i = 0; i < h; i++) 303 tan[i] = (float)Math.tan( fov * i/h * Math.PI * 0.5 ); 304 305 if ( dst == null ) 306 dst = createCompatibleDestImage( src, null ); 307 int t = (int)(63*time); 308 // skyPixels = getRGB( skyColors, t, 0, 1, 64, skyPixels ); 309 Graphics2D g = dst.createGraphics(); 310 g.drawImage( skyColors, 0, 0, dst.getWidth(), dst.getHeight(), t, 0, t+1, 64, null ); 311 g.dispose(); 312 super.filter( dst, dst ); 313 // g.drawRenderedImage( clouds, null ); 314 // g.dispose(); 315 long finish = System.currentTimeMillis(); 316 System.out.println(mn+" "+mx+" "+(finish-start)*0.001f); 317 exponents = null; 318 tan = null; 319 return dst; 320 } 321 322 public float evaluate(float x, float y) { 323 float value = 0.0f; 324 float remainder; 325 int i; 326 327 // to prevent "cascading" effects 328 x += 371; 329 y += 529; 330 331 for (i = 0; i < (int)octaves; i++) { 332 value += Noise.noise3(x, y, t) * exponents[i]; 333 x *= lacunarity; 334 y *= lacunarity; 335 } 336 337 remainder = octaves - (int)octaves; 338 if (remainder != 0) 339 value += remainder * Noise.noise3(x, y, t) * exponents[i]; 340 341 return value; 342 } 343 344 public int filterRGB(int x, int y, int rgb) { 345 346 // Curvature 347 float fx = x / width; 348 //y += 20*Math.sin( fx*Math.PI*0.5 ); 349 float fy = y / height; 350 float haze = (float)Math.pow( haziness, 100*fy*fy ); 351 // int argb = skyPixels[(int)fy]; 352 float r = ((rgb >> 16) & 0xff) * r255; 353 float g = ((rgb >> 8) & 0xff) * r255; 354 float b = (rgb & 0xff) * r255; 355 356 float cx = width*0.5f; 357 float nx = x-cx; 358 float ny = y; 359 // FOV 360 //ny = (float)Math.tan( fov * fy * Math.PI * 0.5 ); 361 ny = tan[y]; 362 nx = (fx-0.5f) * (1+ny); 363 ny += t*windSpeed;// Wind towards the camera 364 365 // float xscale = scale/(1+y*bias*0.1f); 366 nx /= scale; 367 ny /= scale * stretch; 368 float f = evaluate(nx, ny); 369 //float fg = f;//FIXME-bump map 370 // Normalize to 0..1 371 // f = (f-min)/(max-min); 372 373 f = (f+1.23f)/2.46f; 374 375 // f *= amount; 376 //int a = rgb & 0xff000000; 377 int v; 378 379 // Work out cloud cover 380 float c = f - cloudCover; 381 if (c < 0) 382 c = 0; 383 384 float cloudAlpha = 1 - (float)Math.pow(cloudSharpness, c); 385 //cloudAlpha *= amount; 386 //if ( cloudAlpha > 1 ) 387 // cloudAlpha = 1; 388 mn = Math.min(mn, cloudAlpha); 389 mx = Math.max(mx, cloudAlpha); 390 391 // Sun glow 392 float centreX = width*sunAzimuth; 393 float centreY = height*sunElevation; 394 float dx = x-centreX; 395 float dy = y-centreY; 396 float distance2 = dx*dx+dy*dy; 397 // float sun = 0; 398 //distance2 = (float)Math.sqrt(distance2); 399 distance2 = (float)Math.pow(distance2, glowFalloff); 400 float sun = /*amount**/10*(float)Math.exp(-distance2*glow*0.1f); 401 // sun = glow*10*(float)Math.exp(-distance2); 402 403 // Sun glow onto sky 404 r += sun * sunR; 405 g += sun * sunG; 406 b += sun * sunB; 407 408 409 // float cloudColor = cloudAlpha *sun; 410 // Bump map 411 /* 412 float nnx = x-cx; 413 float nny = y-1; 414 nnx /= xscale; 415 nny /= xscale * stretch; 416 float gf = evaluate(nnx, nny); 417 float gradient = fg-gf; 418 if (y == 100)System.out.println(fg+" "+gf+gradient); 419 cloudColor += amount * gradient; 420 */ 421 // ... 422 /* 423 r += (cloudColor-r) * cloudAlpha; 424 g += (cloudColor-g) * cloudAlpha; 425 b += (cloudColor-b) * cloudAlpha; 426 */ 427 // Clouds get darker as they get thicker 428 float ca = (1-cloudAlpha*cloudAlpha*cloudAlpha*cloudAlpha) /** (1 + sun)*/ * amount; 429 float cloudR = sunR * ca; 430 float cloudG = sunG * ca; 431 float cloudB = sunB * ca; 432 433 // Apply the haziness as we move further away 434 cloudAlpha *= haze; 435 436 // Composite the clouds on the sky 437 float iCloudAlpha = (1-cloudAlpha); 438 r = iCloudAlpha*r + cloudAlpha*cloudR; 439 g = iCloudAlpha*g + cloudAlpha*cloudG; 440 b = iCloudAlpha*b + cloudAlpha*cloudB; 441 442 // Exposure 443 float exposure = gain; 444 r = 1 - (float)Math.exp(-r * exposure); 445 g = 1 - (float)Math.exp(-g * exposure); 446 b = 1 - (float)Math.exp(-b * exposure); 447 448 int ir = (int)(255*r) << 16; 449 int ig = (int)(255*g) << 8; 450 int ib = (int)(255*b); 451 v = 0xff000000|ir|ig|ib; 452 return v; 453 } 454 455 public String toString() { 456 return "Texture/Sky..."; 457 } 458 459 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 460 Object o; 461 if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount")); 462 if((o=parameters.removeEL(KeyImpl.init("Stretch")))!=null)setStretch(ImageFilterUtil.toFloatValue(o,"Stretch")); 463 if((o=parameters.removeEL(KeyImpl.init("Angle")))!=null)setAngle(ImageFilterUtil.toFloatValue(o,"Angle")); 464 if((o=parameters.removeEL(KeyImpl.init("Operation")))!=null)setOperation(ImageFilterUtil.toIntValue(o,"Operation")); 465 if((o=parameters.removeEL(KeyImpl.init("Octaves")))!=null)setOctaves(ImageFilterUtil.toFloatValue(o,"Octaves")); 466 if((o=parameters.removeEL(KeyImpl.init("H")))!=null)setH(ImageFilterUtil.toFloatValue(o,"H")); 467 if((o=parameters.removeEL(KeyImpl.init("Lacunarity")))!=null)setLacunarity(ImageFilterUtil.toFloatValue(o,"Lacunarity")); 468 if((o=parameters.removeEL(KeyImpl.init("Gain")))!=null)setGain(ImageFilterUtil.toFloatValue(o,"Gain")); 469 if((o=parameters.removeEL(KeyImpl.init("Bias")))!=null)setBias(ImageFilterUtil.toFloatValue(o,"Bias")); 470 if((o=parameters.removeEL(KeyImpl.init("T")))!=null)setT(ImageFilterUtil.toFloatValue(o,"T")); 471 if((o=parameters.removeEL(KeyImpl.init("FOV")))!=null)setFOV(ImageFilterUtil.toFloatValue(o,"FOV")); 472 if((o=parameters.removeEL(KeyImpl.init("CloudCover")))!=null)setCloudCover(ImageFilterUtil.toFloatValue(o,"CloudCover")); 473 if((o=parameters.removeEL(KeyImpl.init("CloudSharpness")))!=null)setCloudSharpness(ImageFilterUtil.toFloatValue(o,"CloudSharpness")); 474 if((o=parameters.removeEL(KeyImpl.init("Glow")))!=null)setGlow(ImageFilterUtil.toFloatValue(o,"Glow")); 475 if((o=parameters.removeEL(KeyImpl.init("GlowFalloff")))!=null)setGlowFalloff(ImageFilterUtil.toFloatValue(o,"GlowFalloff")); 476 if((o=parameters.removeEL(KeyImpl.init("Haziness")))!=null)setHaziness(ImageFilterUtil.toFloatValue(o,"Haziness")); 477 if((o=parameters.removeEL(KeyImpl.init("SunElevation")))!=null)setSunElevation(ImageFilterUtil.toFloatValue(o,"SunElevation")); 478 if((o=parameters.removeEL(KeyImpl.init("SunAzimuth")))!=null)setSunAzimuth(ImageFilterUtil.toFloatValue(o,"SunAzimuth")); 479 if((o=parameters.removeEL(KeyImpl.init("SunColor")))!=null)setSunColor(ImageFilterUtil.toColorRGB(o,"SunColor")); 480 if((o=parameters.removeEL(KeyImpl.init("CameraElevation")))!=null)setCameraElevation(ImageFilterUtil.toFloatValue(o,"CameraElevation")); 481 if((o=parameters.removeEL(KeyImpl.init("CameraAzimuth")))!=null)setCameraAzimuth(ImageFilterUtil.toFloatValue(o,"CameraAzimuth")); 482 if((o=parameters.removeEL(KeyImpl.init("WindSpeed")))!=null)setWindSpeed(ImageFilterUtil.toFloatValue(o,"WindSpeed")); 483 if((o=parameters.removeEL(KeyImpl.init("Time")))!=null)setTime(ImageFilterUtil.toFloatValue(o,"Time")); 484 if((o=parameters.removeEL(KeyImpl.init("Scale")))!=null)setScale(ImageFilterUtil.toFloatValue(o,"Scale")); 485 if((o=parameters.removeEL(KeyImpl.init("Dimensions")))!=null){ 486 int[] dim=ImageFilterUtil.toDimensions(o,"Dimensions"); 487 setDimensions(dim[0],dim[1]); 488 } 489 490 // check for arguments not supported 491 if(parameters.size()>0) { 492 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, Stretch, Angle, Operation, Octaves, H, Lacunarity, Gain, Bias, T, FOV, CloudCover, CloudSharpness, Glow, GlowFalloff, Haziness, SunElevation, SunAzimuth, SunColor, CameraElevation, CameraAzimuth, WindSpeed, Time, Scale, Dimensions]"); 493 } 494 495 return filter(src, dst); 496 } 497 }