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    }