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