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.image.BufferedImage;
036import java.util.Random;
037
038import lucee.runtime.engine.ThreadLocalPageContext;
039import lucee.runtime.exp.FunctionException;
040import lucee.runtime.exp.PageException;
041import lucee.runtime.img.ImageUtil;
042import lucee.runtime.type.KeyImpl;
043import lucee.runtime.type.Struct;
044import lucee.runtime.type.util.CollectionUtil;
045
046public class SparkleFilter extends PointFilter  implements DynFiltering {
047        
048        private int rays = 50;
049        private int radius = 25;
050        private int amount = 50;
051        private int color = 0xffffffff;
052        private int randomness = 25;
053        private int width, height;
054        private int centreX, centreY;
055        private long seed = 371;
056        private float[] rayLengths;
057        private Random randomNumbers = new Random();
058        
059        public SparkleFilter() {
060        }
061
062        public void setColor(int color) {
063                this.color = color;
064        }
065
066        public int getColor() {
067                return color;
068        }
069
070        public void setRandomness(int randomness) {
071                this.randomness = randomness;
072        }
073
074        public int getRandomness() {
075                return randomness;
076        }
077
078        /**
079         * Set the amount of sparkle.
080         * @param amount the amount
081     * @min-value 0
082     * @max-value 1
083     * @see #getAmount
084         */
085        public void setAmount(int amount) {
086                this.amount = amount;
087        }
088        
089        /**
090         * Get the amount of sparkle.
091         * @return the amount
092     * @see #setAmount
093         */
094        public int getAmount() {
095                return amount;
096        }
097        
098        public void setRays(int rays) {
099                this.rays = rays;
100        }
101
102        public int getRays() {
103                return rays;
104        }
105
106        /**
107         * Set the radius of the effect.
108         * @param radius the radius
109     * @min-value 0
110     * @see #getRadius
111         */
112        public void setRadius(int radius) {
113                this.radius = radius;
114        }
115
116        /**
117         * Get the radius of the effect.
118         * @return the radius
119     * @see #setRadius
120         */
121        public int getRadius() {
122                return radius;
123        }
124
125        public void setDimensions(int width, int height) {
126                this.width = width;
127                this.height = height;
128                centreX = width/2;
129                centreY = height/2;
130                super.setDimensions(width, height);
131                randomNumbers.setSeed(seed);
132                rayLengths = new float[rays];
133                for (int i = 0; i < rays; i++)
134                        rayLengths[i] = radius + randomness / 100.0f * radius * (float)randomNumbers.nextGaussian();
135        }
136        
137        public int filterRGB(int x, int y, int rgb) {
138                float dx = x-centreX;
139                float dy = y-centreY;
140                float distance = dx*dx+dy*dy;
141                float angle = (float)Math.atan2(dy, dx);
142                float d = (angle+ImageMath.PI) / (ImageMath.TWO_PI) * rays;
143                int i = (int)d;
144                float f = d - i;
145
146                if (radius != 0) {
147                        float length = ImageMath.lerp(f, rayLengths[i % rays], rayLengths[(i+1) % rays]);
148                        float g = length*length / (distance+0.0001f);
149                        g = (float)Math.pow(g, (100-amount) / 50.0);
150                        f -= 0.5f;
151//                      f *= amount/50.0f;
152                        f = 1 - f*f;
153                        f *= g;
154                }
155                f = ImageMath.clamp(f, 0, 1);
156                return ImageMath.mixColors(f, rgb, color);
157        }
158
159        public String toString() {
160                return "Stylize/Sparkle...";
161        }
162        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
163                Object o;
164                if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toIntValue(o,"Radius"));
165                if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toIntValue(o,"Amount"));
166                if((o=parameters.removeEL(KeyImpl.init("Randomness")))!=null)setRandomness(ImageFilterUtil.toIntValue(o,"Randomness"));
167                if((o=parameters.removeEL(KeyImpl.init("Rays")))!=null)setRays(ImageFilterUtil.toIntValue(o,"Rays"));
168                if((o=parameters.removeEL(KeyImpl.init("Color")))!=null)setColor(ImageFilterUtil.toColorRGB(o,"Color"));
169                if((o=parameters.removeEL(KeyImpl.init("Dimensions")))!=null){
170                        int[] dim=ImageFilterUtil.toDimensions(o,"Dimensions");
171                        setDimensions(dim[0],dim[1]);
172                }
173
174                // check for arguments not supported
175                if(parameters.size()>0) {
176                        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, Amount, Randomness, Rays, Color, Dimensions]");
177                }
178
179                return filter(src, dst);
180        }
181}