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
046/**
047 * A filter which adds random noise into an image.
048 */
049public class NoiseFilter extends PointFilter  implements DynFiltering {
050        
051    /**
052     * Gaussian distribution for the noise.
053     */
054        public final static int GAUSSIAN = 0;
055
056    /**
057     * Uniform distribution for the noise.
058     */
059        public final static int UNIFORM = 1;
060        
061        private int amount = 25;
062        private int distribution = UNIFORM;
063        private boolean monochrome = false;
064        private float density = 1;
065        private Random randomNumbers = new Random();
066        
067        public NoiseFilter() {
068        }
069
070        /**
071         * Set the amount of effect.
072         * @param amount the amount
073     * @min-value 0
074     * @max-value 1
075     * @see #getAmount
076         */
077        public void setAmount(int amount) {
078                this.amount = amount;
079        }
080        
081        /**
082         * Get the amount of noise.
083         * @return the amount
084     * @see #setAmount
085         */
086        public int getAmount() {
087                return amount;
088        }
089        
090        /**
091         * Set the distribution of the noise.
092         * @param distribution the distribution
093     * @see #getDistribution
094         */
095        public void setDistribution( int distribution ) {
096                this.distribution = distribution;
097        }
098        
099        /**
100         * Get the distribution of the noise.
101         * @return the distribution
102     * @see #setDistribution
103         */
104        public int getDistribution() {
105                return distribution;
106        }
107        
108        /**
109         * Set whether to use monochrome noise.
110         * @param monochrome true for monochrome noise
111     * @see #getMonochrome
112         */
113        public void setMonochrome(boolean monochrome) {
114                this.monochrome = monochrome;
115        }
116        
117        /**
118         * Get whether to use monochrome noise.
119         * @return true for monochrome noise
120     * @see #setMonochrome
121         */
122        public boolean getMonochrome() {
123                return monochrome;
124        }
125        
126        /**
127         * Set the density of the noise.
128         * @param density the density
129     * @see #getDensity
130         */
131        public void setDensity( float density ) {
132                this.density = density;
133        }
134        
135        /**
136         * Get the density of the noise.
137         * @return the density
138     * @see #setDensity
139         */
140        public float getDensity() {
141                return density;
142        }
143        
144        private int random(int x) {
145                x += (int)(((distribution == GAUSSIAN ? randomNumbers.nextGaussian() : 2*randomNumbers.nextFloat() - 1)) * amount);
146                if (x < 0)
147                        x = 0;
148                else if (x > 0xff)
149                        x = 0xff;
150                return x;
151        }
152        
153        public int filterRGB(int x, int y, int rgb) {
154                if ( randomNumbers.nextFloat() <= density ) {
155                        int a = rgb & 0xff000000;
156                        int r = (rgb >> 16) & 0xff;
157                        int g = (rgb >> 8) & 0xff;
158                        int b = rgb & 0xff;
159                        if (monochrome) {
160                                int n = (int)(((distribution == GAUSSIAN ? randomNumbers.nextGaussian() : 2*randomNumbers.nextFloat() - 1)) * amount);
161                                r = PixelUtils.clamp(r+n);
162                                g = PixelUtils.clamp(g+n);
163                                b = PixelUtils.clamp(b+n);
164                        } else {
165                                r = random(r);
166                                g = random(g);
167                                b = random(b);
168                        }
169                        return a | (r << 16) | (g << 8) | b;
170                }
171                return rgb;
172        }
173
174        public String toString() {
175                return "Stylize/Add Noise...";
176        }
177        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
178                Object o;
179                if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toIntValue(o,"Amount"));
180                if((o=parameters.removeEL(KeyImpl.init("Monochrome")))!=null)setMonochrome(ImageFilterUtil.toBooleanValue(o,"Monochrome"));
181                if((o=parameters.removeEL(KeyImpl.init("Density")))!=null)setDensity(ImageFilterUtil.toFloatValue(o,"Density"));
182                if((o=parameters.removeEL(KeyImpl.init("Distribution")))!=null)setDistribution(ImageFilterUtil.toIntValue(o,"Distribution"));
183                if((o=parameters.removeEL(KeyImpl.init("Dimensions")))!=null){
184                        int[] dim=ImageFilterUtil.toDimensions(o,"Dimensions");
185                        setDimensions(dim[0],dim[1]);
186                }
187
188                // check for arguments not supported
189                if(parameters.size()>0) {
190                        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 [Amount, Monochrome, Density, Distribution, Dimensions]");
191                }
192
193                return filter(src, dst);
194        }
195}