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 allows levels adjustment on an image.
030     */
031    public class LevelsFilter extends WholeImageFilter  implements DynFiltering {
032    
033            private int[][] lut;
034        private float lowLevel = 0;
035        private float highLevel = 1;
036        private float lowOutputLevel = 0;
037        private float highOutputLevel = 1;
038    
039            public LevelsFilter() {
040            }
041    
042        public void setLowLevel( float lowLevel ) {
043            this.lowLevel = lowLevel;
044        }
045        
046        public float getLowLevel() {
047            return lowLevel;
048        }
049        
050        public void setHighLevel( float highLevel ) {
051            this.highLevel = highLevel;
052        }
053        
054        public float getHighLevel() {
055            return highLevel;
056        }
057        
058        public void setLowOutputLevel( float lowOutputLevel ) {
059            this.lowOutputLevel = lowOutputLevel;
060        }
061        
062        public float getLowOutputLevel() {
063            return lowOutputLevel;
064        }
065        
066        public void setHighOutputLevel( float highOutputLevel ) {
067            this.highOutputLevel = highOutputLevel;
068        }
069        
070        public float getHighOutputLevel() {
071            return highOutputLevel;
072        }
073        
074            protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
075                    Histogram histogram = new Histogram(inPixels, width, height, 0, width);
076    
077                    int i, j;
078    
079                    if (histogram.getNumSamples() > 0) {
080                            float scale = 255.0f / histogram.getNumSamples();
081                            lut = new int[3][256];
082    
083                float low = lowLevel * 255;
084                float high = highLevel * 255;
085                if ( low == high )
086                    high++;
087                            for (i = 0; i < 3; i++) {
088                                    for (j = 0; j < 256; j++)
089                                            lut[i][j] = PixelUtils.clamp( (int)(255 * (lowOutputLevel + (highOutputLevel-lowOutputLevel) * (j-low)/(high-low))) );
090                            }
091                    } else
092                            lut = null;
093    
094                    i = 0;
095                    for (int y = 0; y < height; y++)
096                            for (int x = 0; x < width; x++) {
097                                    inPixels[i] = filterRGB(x, y, inPixels[i]);
098                                    i++;
099                            }
100                    lut = null;
101                    
102                    return inPixels;
103            }
104    
105            public int filterRGB(int x, int y, int rgb) {
106                    if (lut != null) {
107                            int a = rgb & 0xff000000;
108                            int r = lut[Histogram.RED][(rgb >> 16) & 0xff];
109                            int g = lut[Histogram.GREEN][(rgb >> 8) & 0xff];
110                            int b = lut[Histogram.BLUE][rgb & 0xff];
111    
112                            return a | (r << 16) | (g << 8) | b;
113                    }
114                    return rgb;
115            }
116    
117            public String toString() {
118                    return "Colors/Levels...";
119            }
120            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
121                    Object o;
122                    if((o=parameters.removeEL(KeyImpl.init("LowLevel")))!=null)setLowLevel(ImageFilterUtil.toFloatValue(o,"LowLevel"));
123                    if((o=parameters.removeEL(KeyImpl.init("HighLevel")))!=null)setHighLevel(ImageFilterUtil.toFloatValue(o,"HighLevel"));
124                    if((o=parameters.removeEL(KeyImpl.init("LowOutputLevel")))!=null)setLowOutputLevel(ImageFilterUtil.toFloatValue(o,"LowOutputLevel"));
125                    if((o=parameters.removeEL(KeyImpl.init("HighOutputLevel")))!=null)setHighOutputLevel(ImageFilterUtil.toFloatValue(o,"HighOutputLevel"));
126    
127                    // check for arguments not supported
128                    if(parameters.size()>0) {
129                            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 [LowLevel, HighLevel, LowOutputLevel, HighOutputLevel]");
130                    }
131    
132                    return filter(src, dst);
133            }
134    }