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.Rectangle;
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 "oil-painting" effect.
030     */
031    public class OilFilter extends WholeImageFilter  implements DynFiltering {
032            
033            private int range = 3;
034            private int levels = 256;
035            
036            public OilFilter() {
037            }
038    
039        /**
040         * Set the range of the effect in pixels.
041         * @param range the range
042         * @see #getRange
043         */
044            public void setRange( int range ) {
045                    this.range = range;
046            }
047            
048        /**
049         * Get the range of the effect in pixels.
050         * @return the range
051         * @see #setRange
052         */
053            public int getRange() {
054                    return range;
055            }
056            
057        /**
058         * Set the number of levels for the effect.
059         * @param levels the number of levels
060         * @see #getLevels
061         */
062            public void setLevels( int levels ) {
063                    this.levels = levels;
064            }
065            
066        /**
067         * Get the number of levels for the effect.
068         * @return the number of levels
069         * @see #setLevels
070         */
071            public int getLevels() {
072                    return levels;
073            }
074            
075            protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
076                    int index = 0;
077                    int[] rHistogram = new int[levels];
078                    int[] gHistogram = new int[levels];
079                    int[] bHistogram = new int[levels];
080                    int[] rTotal = new int[levels];
081                    int[] gTotal = new int[levels];
082                    int[] bTotal = new int[levels];
083                    int[] outPixels = new int[width * height];
084    
085                    for (int y = 0; y < height; y++) {
086                            for (int x = 0; x < width; x++) {
087                                    for (int i = 0; i < levels; i++)
088                                        rHistogram[i] = gHistogram[i] = bHistogram[i] = rTotal[i] = gTotal[i] = bTotal[i] = 0;
089    
090                                    for (int row = -range; row <= range; row++) {
091                                            int iy = y+row;
092                                            int ioffset;
093                                            if (0 <= iy && iy < height) {
094                                                    ioffset = iy*width;
095                                                    for (int col = -range; col <= range; col++) {
096                                                            int ix = x+col;
097                                                            if (0 <= ix && ix < width) {
098                                                                    int rgb = inPixels[ioffset+ix];
099                                                                    int r = (rgb >> 16) & 0xff;
100                                                                    int g = (rgb >> 8) & 0xff;
101                                                                    int b = rgb & 0xff;
102                                                                    int ri = r*levels/256;
103                                                                    int gi = g*levels/256;
104                                                                    int bi = b*levels/256;
105                                                                    rTotal[ri] += r;
106                                                                    gTotal[gi] += g;
107                                                                    bTotal[bi] += b;
108                                                                    rHistogram[ri]++;
109                                                                    gHistogram[gi]++;
110                                                                    bHistogram[bi]++;
111                                                            }
112                                                    }
113                                            }
114                                    }
115                                    
116                                    int r = 0, g = 0, b = 0;
117                                    for (int i = 1; i < levels; i++) {
118                                            if (rHistogram[i] > rHistogram[r])
119                                                    r = i;
120                                            if (gHistogram[i] > gHistogram[g])
121                                                    g = i;
122                                            if (bHistogram[i] > bHistogram[b])
123                                                    b = i;
124                                    }
125                                    r = rTotal[r] / rHistogram[r];
126                                    g = gTotal[g] / gHistogram[g];
127                                    b = bTotal[b] / bHistogram[b];
128                                    outPixels[index++] = 0xff000000 | ( r << 16 ) | ( g << 8 ) | b;
129                            }
130                    }
131                    return outPixels;
132            }
133    
134            public String toString() {
135                    return "Stylize/Oil...";
136            }
137    
138            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
139                    Object o;
140                    if((o=parameters.removeEL(KeyImpl.init("Levels")))!=null)setLevels(ImageFilterUtil.toIntValue(o,"Levels"));
141                    if((o=parameters.removeEL(KeyImpl.init("Range")))!=null)setRange(ImageFilterUtil.toIntValue(o,"Range"));
142    
143                    // check for arguments not supported
144                    if(parameters.size()>0) {
145                            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 [Levels, Range]");
146                    }
147    
148                    return filter(src, dst);
149            }
150    }
151