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.Color;
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.type.KeyImpl;
043import lucee.runtime.type.Struct;
044import lucee.runtime.type.util.CollectionUtil;
045
046/**
047 * A filter which produces a rubber-stamp type of effect by performing a thresholded blur.
048 */
049public class StampFilter extends PointFilter  implements DynFiltering {
050
051        private float threshold;
052        private float softness = 0;
053    private float radius = 5;
054        private float lowerThreshold3;
055        private float upperThreshold3;
056        private int white = 0xffffffff;
057        private int black = 0xff000000;
058
059        /**
060     * Construct a StampFilter.
061     */
062        public StampFilter() {
063                this(0.5f);
064        }
065
066        /**
067     * Construct a StampFilter.
068     * @param threshold the threshold value
069     */
070        public StampFilter( float threshold ) {
071                setThreshold( threshold );
072        }
073
074        /**
075         * Set the radius of the effect.
076         * @param radius the radius
077     * @min-value 0
078     * @see #getRadius
079         */
080        public void setRadius(float radius) {
081                this.radius = radius;
082        }
083        
084        /**
085         * Get the radius of the effect.
086         * @return the radius
087     * @see #setRadius
088         */
089        public float getRadius() {
090                return radius;
091        }
092
093        /**
094     * Set the threshold value.
095     * @param threshold the threshold value
096     * @see #getThreshold
097     */
098        public void setThreshold(float threshold) {
099                this.threshold = threshold;
100        }
101        
102        /**
103     * Get the threshold value.
104     * @return the threshold value
105     * @see #setThreshold
106     */
107        public float getThreshold() {
108                return threshold;
109        }
110        
111        /**
112         * Set the softness of the effect in the range 0..1.
113         * @param softness the softness
114     * @min-value 0
115     * @max-value 1
116     * @see #getSoftness
117         */
118        public void setSoftness(float softness) {
119                this.softness = softness;
120        }
121
122        /**
123         * Get the softness of the effect.
124         * @return the softness
125     * @see #setSoftness
126         */
127        public float getSoftness() {
128                return softness;
129        }
130
131        /**
132     * Set the color to be used for pixels above the upper threshold.
133     * @param white the color
134     * @see #getWhite
135     */
136        public void setWhite(Color white) {
137                this.white = white.getRGB();
138        }
139
140        /**
141     * Get the color to be used for pixels above the upper threshold.
142     * @return the color
143     * @see #setWhite
144     */
145        public int getWhite() {
146                return white;
147        }
148
149        /**
150     * Set the color to be used for pixels below the lower threshold.
151     * @param black the color
152     * @see #getBlack
153     */
154        public void setBlack(Color black) {
155                this.black = black.getRGB();
156        }
157
158        /**
159     * Set the color to be used for pixels below the lower threshold.
160     * @return the color
161     * @see #setBlack
162     */
163        public int getBlack() {
164                return black;
165        }
166
167    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
168        dst = new GaussianFilter( (int)radius ).filter( src, (BufferedImage)null );
169        lowerThreshold3 = 255*3*(threshold - softness*0.5f);
170        upperThreshold3 = 255*3*(threshold + softness*0.5f);
171                return super.filter(dst, dst);
172        }
173
174        public int filterRGB(int x, int y, int rgb) {
175                //int a = rgb & 0xff000000;
176                int r = (rgb >> 16) & 0xff;
177                int g = (rgb >> 8) & 0xff;
178                int b = rgb & 0xff;
179                int l = r + g + b;
180                float f = ImageMath.smoothStep(lowerThreshold3, upperThreshold3, l);
181        return ImageMath.mixColors(f, black, white);
182        }
183
184        public String toString() {
185                return "Stylize/Stamp...";
186        }
187        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
188                Object o;
189                if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toFloatValue(o,"Radius"));
190                if((o=parameters.removeEL(KeyImpl.init("Softness")))!=null)setSoftness(ImageFilterUtil.toFloatValue(o,"Softness"));
191                if((o=parameters.removeEL(KeyImpl.init("White")))!=null)setWhite(ImageFilterUtil.toColor(o,"White"));
192                if((o=parameters.removeEL(KeyImpl.init("Black")))!=null)setBlack(ImageFilterUtil.toColor(o,"Black"));
193                if((o=parameters.removeEL(KeyImpl.init("Threshold")))!=null)setThreshold(ImageFilterUtil.toFloatValue(o,"Threshold"));
194                if((o=parameters.removeEL(KeyImpl.init("Dimensions")))!=null){
195                        int[] dim=ImageFilterUtil.toDimensions(o,"Dimensions");
196                        setDimensions(dim[0],dim[1]);
197                }
198
199                // check for arguments not supported
200                if(parameters.size()>0) {
201                        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, Softness, White, Black, Threshold, Dimensions]");
202                }
203
204                return filter(src, dst);
205        }
206}