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.Color; 036import java.awt.Image; 037import java.awt.Rectangle; 038import java.awt.image.BufferedImage; 039import java.awt.image.Kernel; 040import java.util.Vector; 041 042import lucee.runtime.engine.ThreadLocalPageContext; 043import lucee.runtime.exp.FunctionException; 044import lucee.runtime.exp.PageException; 045import lucee.runtime.img.ImageUtil; 046import lucee.runtime.img.math.Function2D; 047import lucee.runtime.img.math.ImageFunction2D; 048import lucee.runtime.img.vecmath.Color4f; 049import lucee.runtime.img.vecmath.Vector3f; 050import lucee.runtime.type.KeyImpl; 051import lucee.runtime.type.Struct; 052import lucee.runtime.type.util.CollectionUtil; 053 054/** 055 * A filter which produces lighting and embossing effects. 056 */ 057public class LightFilter extends WholeImageFilter implements DynFiltering { 058 059 /** 060 * Take the output colors from the input image. 061 */ 062 public final static int COLORS_FROM_IMAGE = 0; 063 064 /** 065 * Use constant material color. 066 */ 067 public final static int COLORS_CONSTANT = 1; 068 069 /** 070 * Use the input image brightness as the bump map. 071 */ 072 public final static int BUMPS_FROM_IMAGE = 0; 073 074 /** 075 * Use the input image alpha as the bump map. 076 */ 077 public final static int BUMPS_FROM_IMAGE_ALPHA = 1; 078 079 /** 080 * Use a separate image alpha channel as the bump map. 081 */ 082 public final static int BUMPS_FROM_MAP = 2; 083 084 /** 085 * Use a custom function as the bump map. 086 */ 087 public final static int BUMPS_FROM_BEVEL = 3; 088 089 private float bumpHeight; 090 private float bumpSoftness; 091 private int bumpShape; 092 private float viewDistance = 10000.0f; 093 Material material; 094 private Vector lights; 095 private int colorSource = COLORS_FROM_IMAGE; 096 private int bumpSource = BUMPS_FROM_IMAGE; 097 private Function2D bumpFunction; 098 private Image environmentMap; 099 private int[] envPixels; 100 private int envWidth = 1, envHeight = 1; 101 102 // Temporary variables used to avoid per-pixel memory allocation while filtering 103 private Vector3f l; 104 private Vector3f v; 105 private Vector3f n; 106 private Color4f shadedColor; 107 private Color4f diffuse_color; 108 private Color4f specular_color; 109 private Vector3f tmpv, tmpv2; 110 111 public LightFilter() { 112 lights = new Vector(); 113 addLight(new DistantLight()); 114 bumpHeight = 1.0f; 115 bumpSoftness = 5.0f; 116 bumpShape = 0; 117 material = new Material(); 118 l = new Vector3f(); 119 v = new Vector3f(); 120 n = new Vector3f(); 121 shadedColor = new Color4f(); 122 diffuse_color = new Color4f(); 123 specular_color = new Color4f(); 124 tmpv = new Vector3f(); 125 tmpv2 = new Vector3f(); 126 } 127 128 public void setMaterial( Material material ) { 129 this.material = material; 130 } 131 132 public Material getMaterial() { 133 return material; 134 } 135 136 public void setBumpFunction(Function2D bumpFunction) { 137 this.bumpFunction = bumpFunction; 138 } 139 140 public Function2D getBumpFunction() { 141 return bumpFunction; 142 } 143 144 public void setBumpHeight(float bumpHeight) { 145 this.bumpHeight = bumpHeight; 146 } 147 148 public float getBumpHeight() { 149 return bumpHeight; 150 } 151 152 public void setBumpSoftness(float bumpSoftness) { 153 this.bumpSoftness = bumpSoftness; 154 } 155 156 public float getBumpSoftness() { 157 return bumpSoftness; 158 } 159 160 public void setBumpShape(int bumpShape) { 161 this.bumpShape = bumpShape; 162 } 163 164 public int getBumpShape() { 165 return bumpShape; 166 } 167 168 public void setViewDistance(float viewDistance) { 169 this.viewDistance = viewDistance; 170 } 171 172 public float getViewDistance() { 173 return viewDistance; 174 } 175 176 public void setEnvironmentMap(BufferedImage environmentMap) { 177 this.environmentMap = environmentMap; 178 if (environmentMap != null) { 179 envWidth = environmentMap.getWidth(); 180 envHeight = environmentMap.getHeight(); 181 envPixels = getRGB( environmentMap, 0, 0, envWidth, envHeight, null ); 182 } else { 183 envWidth = envHeight = 1; 184 envPixels = null; 185 } 186 } 187 188 public Image getEnvironmentMap() { 189 return environmentMap; 190 } 191 192 public void setColorSource(int colorSource) { 193 this.colorSource = colorSource; 194 } 195 196 public int getColorSource() { 197 return colorSource; 198 } 199 200 public void setBumpSource(int bumpSource) { 201 this.bumpSource = bumpSource; 202 } 203 204 public int getBumpSource() { 205 return bumpSource; 206 } 207 208 public void setDiffuseColor(int diffuseColor) { 209 material.diffuseColor = diffuseColor; 210 } 211 212 public int getDiffuseColor() { 213 return material.diffuseColor; 214 } 215 216 public void addLight(Light light) { 217 lights.addElement(light); 218 } 219 220 public void removeLight(Light light) { 221 lights.removeElement(light); 222 } 223 224 public Vector getLights() { 225 return lights; 226 } 227 228 protected final static float r255 = 1.0f/255.0f; 229 230 protected void setFromRGB( Color4f c, int argb ) { 231 c.set( ((argb >> 16) & 0xff) * r255, ((argb >> 8) & 0xff) * r255, (argb & 0xff) * r255, ((argb >> 24) & 0xff) * r255 ); 232 } 233 234 protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) { 235 int index = 0; 236 int[] outPixels = new int[width * height]; 237 float width45 = Math.abs(6.0f * bumpHeight); 238 boolean invertBumps = bumpHeight < 0; 239 Vector3f position = new Vector3f(0.0f, 0.0f, 0.0f); 240 Vector3f viewpoint = new Vector3f(width / 2.0f, height / 2.0f, viewDistance); 241 Vector3f normal = new Vector3f(); 242 Color4f envColor = new Color4f(); 243 Color4f diffuseColor = new Color4f( new Color(material.diffuseColor) ); 244 Color4f specularColor = new Color4f( new Color(material.specularColor) ); 245 Function2D bump = bumpFunction; 246 247 // Apply the bump softness 248 if (bumpSource == BUMPS_FROM_IMAGE || bumpSource == BUMPS_FROM_IMAGE_ALPHA || bumpSource == BUMPS_FROM_MAP || bump == null) { 249 if ( bumpSoftness != 0 ) { 250 int bumpWidth = width; 251 int bumpHeight = height; 252 int[] bumpPixels = inPixels; 253 if ( bumpSource == BUMPS_FROM_MAP && bumpFunction instanceof ImageFunction2D ) { 254 ImageFunction2D if2d = (ImageFunction2D)bumpFunction; 255 bumpWidth = if2d.getWidth(); 256 bumpHeight = if2d.getHeight(); 257 bumpPixels = if2d.getPixels(); 258 } 259 int [] tmpPixels = new int[bumpWidth * bumpHeight]; 260 int [] softPixels = new int[bumpWidth * bumpHeight]; 261/* 262 for (int i = 0; i < 3; i++ ) { 263 BoxBlurFilter.blur( bumpPixels, tmpPixels, bumpWidth, bumpHeight, (int)bumpSoftness ); 264 BoxBlurFilter.blur( tmpPixels, softPixels, bumpHeight, bumpWidth, (int)bumpSoftness ); 265 } 266*/ 267 Kernel kernel = GaussianFilter.makeKernel( bumpSoftness ); 268 GaussianFilter.convolveAndTranspose( kernel, bumpPixels, tmpPixels, bumpWidth, bumpHeight, true, false, false, GaussianFilter.WRAP_EDGES ); 269 GaussianFilter.convolveAndTranspose( kernel, tmpPixels, softPixels, bumpHeight, bumpWidth, true, false, false, GaussianFilter.WRAP_EDGES ); 270 bump = new ImageFunction2D(softPixels, bumpWidth, bumpHeight, ImageFunction2D.CLAMP, bumpSource == BUMPS_FROM_IMAGE_ALPHA); 271final Function2D bbump = bump; 272if ( bumpShape != 0 ) { 273 bump = new Function2D() { 274 private Function2D original = bbump; 275 276 public float evaluate(float x, float y) { 277 float v = original.evaluate( x, y ); 278 switch ( bumpShape ) { 279 case 1: 280// v = v > 0.5f ? 0.5f : v; 281 v *= ImageMath.smoothStep( 0.45f, 0.55f, v ); 282 break; 283 case 2: 284 v = v < 0.5f ? 0.5f : v; 285 break; 286 case 3: 287 v = ImageMath.triangle( v ); 288 break; 289 case 4: 290 v = ImageMath.circleDown( v ); 291 break; 292 case 5: 293 v = ImageMath.gain( v, 0.75f ); 294 break; 295 } 296 return v; 297 } 298 }; 299} 300 } else if ( bumpSource != BUMPS_FROM_MAP ) 301 bump = new ImageFunction2D(inPixels, width, height, ImageFunction2D.CLAMP, bumpSource == BUMPS_FROM_IMAGE_ALPHA); 302 } 303 304 float reflectivity = material.reflectivity; 305 float areflectivity = (1-reflectivity); 306 Vector3f v1 = new Vector3f(); 307 Vector3f v2 = new Vector3f(); 308 Vector3f n = new Vector3f(); 309 Light[] lightsArray = new Light[lights.size()]; 310 lights.copyInto(lightsArray); 311 for (int i = 0; i < lightsArray.length; i++) 312 lightsArray[i].prepare(width, height); 313 314 float[][] heightWindow = new float[3][width]; 315 for (int x = 0; x < width; x++) 316 heightWindow[1][x] = width45*bump.evaluate(x, 0); 317 318 // Loop through each source pixel 319 for (int y = 0; y < height; y++) { 320 boolean y0 = y > 0; 321 boolean y1 = y < height-1; 322 position.y = y; 323 for (int x = 0; x < width; x++) 324 heightWindow[2][x] = width45*bump.evaluate(x, y+1); 325 for (int x = 0; x < width; x++) { 326 boolean x0 = x > 0; 327 boolean x1 = x < width-1; 328 329 // Calculate the normal at this point 330 if (bumpSource != BUMPS_FROM_BEVEL) { 331 // Complicated and slower method 332 // Calculate four normals using the gradients in +/- X/Y directions 333 int count = 0; 334 normal.x = normal.y = normal.z = 0; 335 float m0 = heightWindow[1][x]; 336 float m1 = x0 ? heightWindow[1][x-1]-m0 : 0; 337 float m2 = y0 ? heightWindow[0][x]-m0 : 0; 338 float m3 = x1 ? heightWindow[1][x+1]-m0 : 0; 339 float m4 = y1 ? heightWindow[2][x]-m0 : 0; 340 341 if (x0 && y1) { 342 v1.x = -1.0f; v1.y = 0.0f; v1.z = m1; 343 v2.x = 0.0f; v2.y = 1.0f; v2.z = m4; 344 n.cross(v1, v2); 345 n.normalize(); 346 if (n.z < 0.0) 347 n.z = -n.z; 348 normal.add(n); 349 count++; 350 } 351 352 if (x0 && y0) { 353 v1.x = -1.0f; v1.y = 0.0f; v1.z = m1; 354 v2.x = 0.0f; v2.y = -1.0f; v2.z = m2; 355 n.cross(v1, v2); 356 n.normalize(); 357 if (n.z < 0.0) 358 n.z = -n.z; 359 normal.add(n); 360 count++; 361 } 362 363 if (y0 && x1) { 364 v1.x = 0.0f; v1.y = -1.0f; v1.z = m2; 365 v2.x = 1.0f; v2.y = 0.0f; v2.z = m3; 366 n.cross(v1, v2); 367 n.normalize(); 368 if (n.z < 0.0) 369 n.z = -n.z; 370 normal.add(n); 371 count++; 372 } 373 374 if (x1 && y1) { 375 v1.x = 1.0f; v1.y = 0.0f; v1.z = m3; 376 v2.x = 0.0f; v2.y = 1.0f; v2.z = m4; 377 n.cross(v1, v2); 378 n.normalize(); 379 if (n.z < 0.0) 380 n.z = -n.z; 381 normal.add(n); 382 count++; 383 } 384 385 // Average the four normals 386 normal.x /= count; 387 normal.y /= count; 388 normal.z /= count; 389 } 390 if (invertBumps) { 391 normal.x = -normal.x; 392 normal.y = -normal.y; 393 } 394 position.x = x; 395 396 if (normal.z >= 0) { 397 // Get the material colour at this point 398 if (colorSource == COLORS_FROM_IMAGE) 399 setFromRGB(diffuseColor, inPixels[index]); 400 else 401 setFromRGB(diffuseColor, material.diffuseColor); 402 if (reflectivity != 0 && environmentMap != null) { 403 //FIXME-too much normalizing going on here 404 tmpv2.set(viewpoint); 405 tmpv2.sub(position); 406 tmpv2.normalize(); 407 tmpv.set(normal); 408 tmpv.normalize(); 409 410 // Reflect 411 tmpv.scale( 2.0f*tmpv.dot(tmpv2) ); 412 tmpv.sub(v); 413 414 tmpv.normalize(); 415 setFromRGB(envColor, getEnvironmentMap(tmpv, inPixels, width, height));//FIXME-interpolate() 416 diffuseColor.x = reflectivity*envColor.x + areflectivity*diffuseColor.x; 417 diffuseColor.y = reflectivity*envColor.y + areflectivity*diffuseColor.y; 418 diffuseColor.z = reflectivity*envColor.z + areflectivity*diffuseColor.z; 419 } 420 // Shade the pixel 421 Color4f c = phongShade(position, viewpoint, normal, diffuseColor, specularColor, material, lightsArray); 422 int alpha = inPixels[index] & 0xff000000; 423 int rgb = ((int)(c.x * 255) << 16) | ((int)(c.y * 255) << 8) | (int)(c.z * 255); 424 outPixels[index++] = alpha | rgb; 425 } else 426 outPixels[index++] = 0; 427 } 428 float[] t = heightWindow[0]; 429 heightWindow[0] = heightWindow[1]; 430 heightWindow[1] = heightWindow[2]; 431 heightWindow[2] = t; 432 } 433 return outPixels; 434 } 435 436 protected Color4f phongShade(Vector3f position, Vector3f viewpoint, Vector3f normal, Color4f diffuseColor, Color4f specularColor, Material material, Light[] lightsArray) { 437 shadedColor.set(diffuseColor); 438 shadedColor.scale(material.ambientIntensity); 439 440 for (int i = 0; i < lightsArray.length; i++) { 441 Light light = lightsArray[i]; 442 n.set(normal); 443 l.set(light.position); 444 if (light.type != DISTANT) 445 l.sub(position); 446 l.normalize(); 447 float nDotL = n.dot(l); 448 if (nDotL >= 0.0) { 449 float dDotL = 0; 450 451 v.set(viewpoint); 452 v.sub(position); 453 v.normalize(); 454 455 // Spotlight 456 if (light.type == SPOT) { 457 dDotL = light.direction.dot(l); 458 if (dDotL < light.cosConeAngle) 459 continue; 460 } 461 462 n.scale(2.0f * nDotL); 463 n.sub(l); 464 float rDotV = n.dot(v); 465 466 float rv; 467 if (rDotV < 0.0) 468 rv = 0.0f; 469 else 470// rv = (float)Math.pow(rDotV, material.highlight); 471 rv = rDotV / (material.highlight - material.highlight*rDotV + rDotV); // Fast approximation to pow 472 473 // Spotlight 474 if (light.type == SPOT) { 475 dDotL = light.cosConeAngle/dDotL; 476 float e = dDotL; 477 e *= e; 478 e *= e; 479 e *= e; 480 e = (float)Math.pow(dDotL, light.focus*10)*(1 - e); 481 rv *= e; 482 nDotL *= e; 483 } 484 485 diffuse_color.set(diffuseColor); 486 diffuse_color.scale(material.diffuseReflectivity); 487 diffuse_color.x *= light.realColor.x * nDotL; 488 diffuse_color.y *= light.realColor.y * nDotL; 489 diffuse_color.z *= light.realColor.z * nDotL; 490 specular_color.set(specularColor); 491 specular_color.scale(material.specularReflectivity); 492 specular_color.x *= light.realColor.x * rv; 493 specular_color.y *= light.realColor.y * rv; 494 specular_color.z *= light.realColor.z * rv; 495 diffuse_color.add(specular_color); 496 diffuse_color.clamp( 0, 1 ); 497 shadedColor.add(diffuse_color); 498 } 499 } 500 shadedColor.clamp( 0, 1 ); 501 return shadedColor; 502 } 503 504 private int getEnvironmentMap(Vector3f normal, int[] inPixels, int width, int height) { 505 if (environmentMap != null) { 506 float angle = (float)Math.acos(-normal.y); 507 508 float x, y; 509 y = angle/ImageMath.PI; 510 511 if (y == 0.0f || y == 1.0f) 512 x = 0.0f; 513 else { 514 float f = normal.x/(float)Math.sin(angle); 515 516 if (f > 1.0f) 517 f = 1.0f; 518 else if (f < -1.0f) 519 f = -1.0f; 520 521 x = (float)Math.acos(f)/ImageMath.PI; 522 } 523 // A bit of empirical scaling.... 524 x = ImageMath.clamp(x * envWidth, 0, envWidth-1); 525 y = ImageMath.clamp(y * envHeight, 0, envHeight-1); 526 int ix = (int)x; 527 int iy = (int)y; 528 529 float xWeight = x-ix; 530 float yWeight = y-iy; 531 int i = envWidth*iy + ix; 532 int dx = ix == envWidth-1 ? 0 : 1; 533 int dy = iy == envHeight-1 ? 0 : envWidth; 534 return ImageMath.bilinearInterpolate( xWeight, yWeight, envPixels[i], envPixels[i+dx], envPixels[i+dy], envPixels[i+dx+dy] ); 535 } 536 return 0; 537 } 538 539 public String toString() { 540 return "Stylize/Light Effects..."; 541 } 542 543 /** 544 * A class representing material properties. 545 */ 546 public static class Material { 547 int diffuseColor; 548 int specularColor; 549 float ambientIntensity; 550 float diffuseReflectivity; 551 float specularReflectivity; 552 float highlight; 553 float reflectivity; 554 float opacity = 1; 555 556 public Material() { 557 ambientIntensity = 0.5f; 558 diffuseReflectivity = 1.0f; 559 specularReflectivity = 1.0f; 560 highlight = 3.0f; 561 reflectivity = 0.0f; 562 diffuseColor = 0xff888888; 563 specularColor = 0xffffffff; 564 } 565 566 public void setDiffuseColor(int diffuseColor) { 567 this.diffuseColor = diffuseColor; 568 } 569 570 public int getDiffuseColor() { 571 return diffuseColor; 572 } 573 574 public void setOpacity( float opacity ) { 575 this.opacity = opacity; 576 } 577 578 public float getOpacity() { 579 return opacity; 580 } 581 582 } 583 584 public final static int AMBIENT = 0; 585 public final static int DISTANT = 1; 586 public final static int POINT = 2; 587 public final static int SPOT = 3; 588 589 /** 590 * A class representing a light. 591 */ 592 public static class Light implements Cloneable { 593 594 int type = AMBIENT; 595 Vector3f position; 596 Vector3f direction; 597 Color4f realColor = new Color4f(); 598 int color = 0xffffffff; 599 float intensity; 600 float azimuth; 601 float elevation; 602 float focus = 0.5f; 603 float centreX = 0.5f, centreY = 0.5f; 604 float coneAngle = ImageMath.PI/6; 605 float cosConeAngle; 606 float distance = 100.0f; 607 608 public Light() { 609 this(270*ImageMath.PI/180.0f, 0.5235987755982988f, 1.0f); 610 } 611 612 public Light(float azimuth, float elevation, float intensity) { 613 this.azimuth = azimuth; 614 this.elevation = elevation; 615 this.intensity = intensity; 616 } 617 618 public void setAzimuth(float azimuth) { 619 this.azimuth = azimuth; 620 } 621 622 public float getAzimuth() { 623 return azimuth; 624 } 625 626 public void setElevation(float elevation) { 627 this.elevation = elevation; 628 } 629 630 public float getElevation() { 631 return elevation; 632 } 633 634 public void setDistance(float distance) { 635 this.distance = distance; 636 } 637 638 public float getDistance() { 639 return distance; 640 } 641 642 public void setIntensity(float intensity) { 643 this.intensity = intensity; 644 } 645 646 public float getIntensity() { 647 return intensity; 648 } 649 650 public void setConeAngle(float coneAngle) { 651 this.coneAngle = coneAngle; 652 } 653 654 public float getConeAngle() { 655 return coneAngle; 656 } 657 658 public void setFocus(float focus) { 659 this.focus = focus; 660 } 661 662 public float getFocus() { 663 return focus; 664 } 665 666 public void setColor(int color) { 667 this.color = color; 668 } 669 670 public int getColor() { 671 return color; 672 } 673 674 /** 675 * Set the centre of the light in the X direction as a proportion of the image size. 676 * @param centreX the center 677 * @see #getCentreX 678 */ 679 public void setCentreX(float x) { 680 centreX = x; 681 } 682 683 /** 684 * Get the centre of the light in the X direction as a proportion of the image size. 685 * @return the center 686 * @see #setCentreX 687 */ 688 public float getCentreX() { 689 return centreX; 690 } 691 692 /** 693 * Set the centre of the light in the Y direction as a proportion of the image size. 694 * @param centreY the center 695 * @see #getCentreY 696 */ 697 public void setCentreY(float y) { 698 centreY = y; 699 } 700 701 /** 702 * Get the centre of the light in the Y direction as a proportion of the image size. 703 * @return the center 704 * @see #setCentreY 705 */ 706 public float getCentreY() { 707 return centreY; 708 } 709 710 /** 711 * Prepare the light for rendering. 712 * @param width the output image width 713 * @param height the output image height 714 */ 715 public void prepare(int width, int height) { 716 float lx = (float)(Math.cos(azimuth) * Math.cos(elevation)); 717 float ly = (float)(Math.sin(azimuth) * Math.cos(elevation)); 718 float lz = (float)Math.sin(elevation); 719 direction = new Vector3f(lx, ly, lz); 720 direction.normalize(); 721 if (type != DISTANT) { 722 lx *= distance; 723 ly *= distance; 724 lz *= distance; 725 lx += width * centreX; 726 ly += height * centreY; 727 } 728 position = new Vector3f(lx, ly, lz); 729 realColor.set( new Color(color) ); 730 realColor.scale(intensity); 731 cosConeAngle = (float)Math.cos(coneAngle); 732 } 733 734 public Object clone() { 735 try { 736 Light copy = (Light)super.clone(); 737 return copy; 738 } 739 catch (CloneNotSupportedException e) { 740 return null; 741 } 742 } 743 744 public String toString() { 745 return "Light"; 746 } 747 748 } 749 750 public class AmbientLight extends Light { 751 public String toString() { 752 return "Ambient Light"; 753 } 754 } 755 756 public class PointLight extends Light { 757 public PointLight() { 758 type = POINT; 759 } 760 761 public String toString() { 762 return "Point Light"; 763 } 764 } 765 766 public class DistantLight extends Light { 767 public DistantLight() { 768 type = DISTANT; 769 } 770 771 public String toString() { 772 return "Distant Light"; 773 } 774 } 775 776 public class SpotLight extends Light { 777 public SpotLight() { 778 type = SPOT; 779 } 780 781 public String toString() { 782 return "Spotlight"; 783 } 784 } 785 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 786 Object o; 787 if((o=parameters.removeEL(KeyImpl.init("ColorSource")))!=null)setColorSource(ImageFilterUtil.toColorRGB(o,"ColorSource")); 788 if((o=parameters.removeEL(KeyImpl.init("Material")))!=null)setMaterial(ImageFilterUtil.toLightFilter$Material(o,"Material")); 789 if((o=parameters.removeEL(KeyImpl.init("BumpFunction")))!=null)setBumpFunction(ImageFilterUtil.toFunction2D(o,"BumpFunction")); 790 if((o=parameters.removeEL(KeyImpl.init("BumpHeight")))!=null)setBumpHeight(ImageFilterUtil.toFloatValue(o,"BumpHeight")); 791 if((o=parameters.removeEL(KeyImpl.init("BumpSoftness")))!=null)setBumpSoftness(ImageFilterUtil.toFloatValue(o,"BumpSoftness")); 792 if((o=parameters.removeEL(KeyImpl.init("BumpShape")))!=null)setBumpShape(ImageFilterUtil.toIntValue(o,"BumpShape")); 793 if((o=parameters.removeEL(KeyImpl.init("ViewDistance")))!=null)setViewDistance(ImageFilterUtil.toFloatValue(o,"ViewDistance")); 794 if((o=parameters.removeEL(KeyImpl.init("EnvironmentMap")))!=null)setEnvironmentMap(ImageFilterUtil.toBufferedImage(o,"EnvironmentMap")); 795 if((o=parameters.removeEL(KeyImpl.init("BumpSource")))!=null)setBumpSource(ImageFilterUtil.toIntValue(o,"BumpSource")); 796 if((o=parameters.removeEL(KeyImpl.init("DiffuseColor")))!=null)setDiffuseColor(ImageFilterUtil.toColorRGB(o,"DiffuseColor")); 797 798 // check for arguments not supported 799 if(parameters.size()>0) { 800 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 [ColorSource, Material, BumpFunction, BumpHeight, BumpSoftness, BumpShape, ViewDistance, EnvironmentMap, BumpSource, DiffuseColor]"); 801 } 802 803 return filter(src, dst); 804 } 805}