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