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