001/*
002Copyright 2006 Jerry Huxtable
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008   http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015*/
016
017package com.jhlabs.image;
018
019import java.awt.geom.*;
020import java.awt.image.*;
021import java.util.*;
022import com.jhlabs.math.*;
023
024/**
025 * An experimental filter for rendering lens flares.
026 */
027public class FlareFilter extends PointFilter {
028
029        private int rays = 50;
030        private float radius;
031        private float baseAmount = 1.0f;
032        private float ringAmount = 0.2f;
033        private float rayAmount = 0.1f;
034        private int color = 0xffffffff;
035        private int width, height;
036        private float centreX = 0.5f, centreY = 0.5f;
037        private float ringWidth = 1.6f;
038        
039        private float linear = 0.03f;
040        private float gauss = 0.006f;
041        private float mix = 0.50f;
042        private float falloff = 6.0f;
043        private float sigma;
044
045        private float icentreX, icentreY;
046
047        public FlareFilter() {
048                setRadius(50.0f);
049        }
050
051        public void setColor(int color) {
052                this.color = color;
053        }
054
055        public int getColor() {
056                return color;
057        }
058
059        public void setRingWidth(float ringWidth) {
060                this.ringWidth = ringWidth;
061        }
062
063        public float getRingWidth() {
064                return ringWidth;
065        }
066        
067        public void setBaseAmount(float baseAmount) {
068                this.baseAmount = baseAmount;
069        }
070
071        public float getBaseAmount() {
072                return baseAmount;
073        }
074
075        public void setRingAmount(float ringAmount) {
076                this.ringAmount = ringAmount;
077        }
078
079        public float getRingAmount() {
080                return ringAmount;
081        }
082
083        public void setRayAmount(float rayAmount) {
084                this.rayAmount = rayAmount;
085        }
086
087        public float getRayAmount() {
088                return rayAmount;
089        }
090
091        public void setCentre( Point2D centre ) {
092                this.centreX = (float)centre.getX();
093                this.centreY = (float)centre.getY();
094        }
095
096        public Point2D getCentre() {
097                return new Point2D.Float( centreX, centreY );
098        }
099        
100        /**
101         * Set the radius of the effect.
102         * @param radius the radius
103     * @min-value 0
104     * @see #getRadius
105         */
106        public void setRadius(float radius) {
107                this.radius = radius;
108                sigma = radius/3;
109        }
110
111        /**
112         * Get the radius of the effect.
113         * @return the radius
114     * @see #setRadius
115         */
116        public float getRadius() {
117                return radius;
118        }
119
120        public void setDimensions(int width, int height) {
121                this.width = width;
122                this.height = height;
123                icentreX = centreX*width;
124                icentreY = centreY*height;
125                super.setDimensions(width, height);
126        }
127        
128        public int filterRGB(int x, int y, int rgb) {
129                float dx = x-icentreX;
130                float dy = y-icentreY;
131                float distance = (float)Math.sqrt(dx*dx+dy*dy);
132                float a = (float)Math.exp(-distance*distance*gauss)*mix + (float)Math.exp(-distance*linear)*(1-mix);
133                float ring;
134
135                a *= baseAmount;
136
137                if (distance > radius + ringWidth)
138                        a = ImageMath.lerp((distance - (radius + ringWidth))/falloff, a, 0);
139
140                if (distance < radius - ringWidth || distance > radius + ringWidth)
141                        ring = 0;
142                else {
143                ring = Math.abs(distance-radius)/ringWidth;
144                ring = 1 - ring*ring*(3 - 2*ring);
145                ring *= ringAmount;
146                }
147
148                a += ring;
149
150                float angle = (float)Math.atan2(dx, dy)+ImageMath.PI;
151                angle = (ImageMath.mod(angle/ImageMath.PI*17 + 1.0f + Noise.noise1(angle*10), 1.0f) - 0.5f)*2;
152                angle = Math.abs(angle);
153                angle = (float)Math.pow(angle, 5.0);
154
155                float b = rayAmount * angle / (1 + distance*0.1f);
156                a += b;
157
158                a = ImageMath.clamp(a, 0, 1);
159                return ImageMath.mixColors(a, rgb, color);
160        }
161
162        public String toString() {
163                return "Stylize/Flare...";
164        }
165}