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.List;
026    import railo.runtime.type.Struct;
027    
028    /**
029     * A filter which draws contours on an image at given brightness levels.
030     */
031    public class ContourFilter extends WholeImageFilter  implements DynFiltering {
032    
033            private float levels = 5;
034            private float scale = 1;
035            private float offset = 0;
036            private int contourColor = 0xff000000;
037            
038            public ContourFilter() {
039            }
040    
041            public void setLevels( float levels ) {
042                    this.levels = levels;
043            }
044            
045            public float getLevels() {
046                    return levels;
047            }
048            
049            /**
050         * Specifies the scale of the contours.
051         * @param scale the scale of the contours.
052         * @min-value 0
053         * @max-value 1
054         * @see #getScale
055         */
056            public void setScale( float scale ) {
057                    this.scale = scale;
058            }
059            
060            /**
061         * Returns the scale of the contours.
062         * @return the scale of the contours.
063         * @see #setScale
064         */
065            public float getScale() {
066                    return scale;
067            }
068            
069            public void setOffset( float offset ) {
070                    this.offset = offset;
071            }
072            
073            public float getOffset() {
074                    return offset;
075            }
076            
077            public void setContourColor( int contourColor ) {
078                    this.contourColor = contourColor;
079            }
080            
081            public int getContourColor() {
082                    return contourColor;
083            }
084            
085            protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
086                    int index = 0;
087                    short[][] r = new short[3][width];
088                    int[] outPixels = new int[width * height];
089    
090                    short[] table = new short[256];
091                    int offsetl = (int)(offset * 256 / levels);
092                    for ( int i = 0; i < 256; i++ )
093                            table[i] = (short)PixelUtils.clamp( (int)(255 * Math.floor(levels*(i+offsetl) / 256) / (levels-1) - offsetl) );
094    
095                    for (int x = 0; x < width; x++) {
096                            int rgb = inPixels[x];
097                            r[1][x] = (short)PixelUtils.brightness( rgb );
098                    }
099                    for (int y = 0; y < height; y++) {
100                            boolean yIn = y > 0 && y < height-1;
101                            int nextRowIndex = index+width;
102                            if ( y < height-1) {
103                                    for (int x = 0; x < width; x++) {
104                                            int rgb = inPixels[nextRowIndex++];
105                                            r[2][x] = (short)PixelUtils.brightness( rgb );
106                                    }
107                            }
108                            for (int x = 0; x < width; x++) {
109                                    boolean xIn = x > 0 && x < width-1;
110                                    int w = x-1;
111                                    int e = x+1;
112                                    int v = 0;
113                                    
114                                    if ( yIn && xIn ) {
115                                            short nwb = r[0][w];
116                                            short neb = r[0][x];
117                                            short swb = r[1][w];
118                                            short seb = r[1][x];
119                                            short nw = table[nwb];
120                                            short ne = table[neb];
121                                            short sw = table[swb];
122                                            short se = table[seb];
123    
124                                            if (nw != ne || nw != sw || ne != se || sw != se) {
125                                                    v = (int)(scale * (Math.abs(nwb - neb) + Math.abs(nwb - swb) + Math.abs(neb - seb) + Math.abs(swb - seb)));
126    //                                              v /= 255;
127                                                    if (v > 255)
128                                                            v = 255;
129                                            }
130                                    }
131    
132                                    if ( v != 0 )
133                                            outPixels[index] = PixelUtils.combinePixels( inPixels[index], contourColor, PixelUtils.NORMAL, v );
134    //                                      outPixels[index] = PixelUtils.combinePixels( (contourColor & 0xff)|(v << 24), inPixels[index], PixelUtils.NORMAL );
135                                    else
136                                            outPixels[index] = inPixels[index];
137                                    index++;
138                            }
139                            short[] t;
140                            t = r[0];
141                            r[0] = r[1];
142                            r[1] = r[2];
143                            r[2] = t;
144                    }
145            
146                    return outPixels;
147            }
148    
149            public String toString() {
150                    return "Stylize/Contour...";
151            }
152    
153            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
154                    Object o;
155                    if((o=parameters.removeEL(KeyImpl.init("Levels")))!=null)setLevels(ImageFilterUtil.toFloatValue(o,"Levels"));
156                    if((o=parameters.removeEL(KeyImpl.init("ContourColor")))!=null)setContourColor(ImageFilterUtil.toColorRGB(o,"ContourColor"));
157                    if((o=parameters.removeEL(KeyImpl.init("Offset")))!=null)setOffset(ImageFilterUtil.toFloatValue(o,"Offset"));
158                    if((o=parameters.removeEL(KeyImpl.init("Scale")))!=null)setScale(ImageFilterUtil.toFloatValue(o,"Scale"));
159    
160                    // check for arguments not supported
161                    if(parameters.size()>0) {
162                            throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+List.arrayToList(parameters.keysAsString(),", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [Levels, ContourColor, Offset, Scale]");
163                    }
164    
165                    return filter(src, dst);
166            }
167    }
168