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.geom.Point2D; 018 import java.awt.image.BufferedImage; 019 020 import railo.runtime.engine.ThreadLocalPageContext; 021 import railo.runtime.exp.FunctionException; 022 import railo.runtime.exp.PageException; 023 import railo.runtime.img.ImageUtil; 024 import railo.runtime.img.math.Noise; 025 import railo.runtime.type.KeyImpl; 026 import railo.runtime.type.List; 027 import railo.runtime.type.Struct; 028 029 /** 030 * An experimental filter for rendering lens flares. 031 */ 032 public class FlareFilter extends PointFilter implements DynFiltering { 033 034 private int rays = 50; 035 private float radius; 036 private float baseAmount = 1.0f; 037 private float ringAmount = 0.2f; 038 private float rayAmount = 0.1f; 039 private int color = 0xffffffff; 040 private int width, height; 041 private float centreX = 0.5f, centreY = 0.5f; 042 private float ringWidth = 1.6f; 043 044 private float linear = 0.03f; 045 private float gauss = 0.006f; 046 private float mix = 0.50f; 047 private float falloff = 6.0f; 048 private float sigma; 049 050 private float icentreX, icentreY; 051 052 public FlareFilter() { 053 setRadius(50.0f); 054 } 055 056 public void setColor(int color) { 057 this.color = color; 058 } 059 060 public int getColor() { 061 return color; 062 } 063 064 public void setRingWidth(float ringWidth) { 065 this.ringWidth = ringWidth; 066 } 067 068 public float getRingWidth() { 069 return ringWidth; 070 } 071 072 public void setBaseAmount(float baseAmount) { 073 this.baseAmount = baseAmount; 074 } 075 076 public float getBaseAmount() { 077 return baseAmount; 078 } 079 080 public void setRingAmount(float ringAmount) { 081 this.ringAmount = ringAmount; 082 } 083 084 public float getRingAmount() { 085 return ringAmount; 086 } 087 088 public void setRayAmount(float rayAmount) { 089 this.rayAmount = rayAmount; 090 } 091 092 public float getRayAmount() { 093 return rayAmount; 094 } 095 096 public void setCentreY( float centreY ) { 097 this.centreY = centreY; 098 } 099 100 public void setCentreX( float centreX ) { 101 this.centreX = centreX; 102 } 103 104 /*public void setCentre( Point2D centre ) { 105 this.centreX = (float)centre.getX(); 106 this.centreY = (float)centre.getY(); 107 }*/ 108 109 public Point2D getCentre() { 110 return new Point2D.Float( centreX, centreY ); 111 } 112 113 /** 114 * Set the radius of the effect. 115 * @param radius the radius 116 * @min-value 0 117 * @see #getRadius 118 */ 119 public void setRadius(float radius) { 120 this.radius = radius; 121 sigma = radius/3; 122 } 123 124 /** 125 * Get the radius of the effect. 126 * @return the radius 127 * @see #setRadius 128 */ 129 public float getRadius() { 130 return radius; 131 } 132 133 public void setDimensions(int width, int height) { 134 this.width = width; 135 this.height = height; 136 icentreX = centreX*width; 137 icentreY = centreY*height; 138 super.setDimensions(width, height); 139 } 140 141 public int filterRGB(int x, int y, int rgb) { 142 float dx = x-icentreX; 143 float dy = y-icentreY; 144 float distance = (float)Math.sqrt(dx*dx+dy*dy); 145 float a = (float)Math.exp(-distance*distance*gauss)*mix + (float)Math.exp(-distance*linear)*(1-mix); 146 float ring; 147 148 a *= baseAmount; 149 150 if (distance > radius + ringWidth) 151 a = ImageMath.lerp((distance - (radius + ringWidth))/falloff, a, 0); 152 153 if (distance < radius - ringWidth || distance > radius + ringWidth) 154 ring = 0; 155 else { 156 ring = Math.abs(distance-radius)/ringWidth; 157 ring = 1 - ring*ring*(3 - 2*ring); 158 ring *= ringAmount; 159 } 160 161 a += ring; 162 163 float angle = (float)Math.atan2(dx, dy)+ImageMath.PI; 164 angle = (ImageMath.mod(angle/ImageMath.PI*17 + 1.0f + Noise.noise1(angle*10), 1.0f) - 0.5f)*2; 165 angle = Math.abs(angle); 166 angle = (float)Math.pow(angle, 5.0); 167 168 float b = rayAmount * angle / (1 + distance*0.1f); 169 a += b; 170 171 a = ImageMath.clamp(a, 0, 1); 172 return ImageMath.mixColors(a, rgb, color); 173 } 174 175 public String toString() { 176 return "Stylize/Flare..."; 177 } 178 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 179 Object o; 180 if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toFloatValue(o,"Radius")); 181 //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre")); 182 if((o=parameters.removeEL(KeyImpl.init("RingWidth")))!=null)setRingWidth(ImageFilterUtil.toFloatValue(o,"RingWidth")); 183 if((o=parameters.removeEL(KeyImpl.init("BaseAmount")))!=null)setBaseAmount(ImageFilterUtil.toFloatValue(o,"BaseAmount")); 184 if((o=parameters.removeEL(KeyImpl.init("RingAmount")))!=null)setRingAmount(ImageFilterUtil.toFloatValue(o,"RingAmount")); 185 if((o=parameters.removeEL(KeyImpl.init("RayAmount")))!=null)setRayAmount(ImageFilterUtil.toFloatValue(o,"RayAmount")); 186 if((o=parameters.removeEL(KeyImpl.init("Color")))!=null)setColor(ImageFilterUtil.toColorRGB(o,"Color")); 187 if((o=parameters.removeEL(KeyImpl.init("Dimensions")))!=null){ 188 int[] dim=ImageFilterUtil.toDimensions(o,"Dimensions"); 189 setDimensions(dim[0],dim[1]); 190 } 191 192 // check for arguments not supported 193 if(parameters.size()>0) { 194 throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+List.arrayToList(parameters.keysAsString(),", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [Radius, Centre, RingWidth, BaseAmount, RingAmount, RayAmount, Color, Dimensions]"); 195 } 196 197 return filter(src, dst); 198 } 199 }