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.image.BufferedImage;
036
037import lucee.runtime.engine.ThreadLocalPageContext;
038import lucee.runtime.exp.FunctionException;
039import lucee.runtime.exp.PageException;
040import lucee.runtime.img.ImageUtil;
041import lucee.runtime.type.KeyImpl;
042import lucee.runtime.type.Struct;
043import lucee.runtime.type.util.CollectionUtil;
044
045/**
046 * A filter which subtracts Gaussian blur from an image, sharpening it.
047 *
048 */
049public class UnsharpFilter extends GaussianFilter  implements DynFiltering {
050
051        private float amount = 0.5f;
052        private int threshold = 1;
053        
054        public UnsharpFilter() {
055                radius = 2;
056        }
057        
058        /**
059     * Set the threshold value.
060     * @param threshold the threshold value
061     * @see #getThreshold
062     */
063        public void setThreshold( int threshold ) {
064                this.threshold = threshold;
065        }
066        
067        /**
068     * Get the threshold value.
069     * @return the threshold value
070     * @see #setThreshold
071     */
072        public int getThreshold() {
073                return threshold;
074        }
075        
076        /**
077         * Set the amount of sharpening.
078         * @param amount the amount
079     * @min-value 0
080     * @max-value 1
081     * @see #getAmount
082         */
083        public void setAmount( float amount ) {
084                this.amount = amount;
085        }
086        
087        /**
088         * Get the amount of sharpening.
089         * @return the amount
090     * @see #setAmount
091         */
092        public float getAmount() {
093                return amount;
094        }
095        
096    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
097        int width = src.getWidth();
098        int height = src.getHeight();
099
100        if ( dst == null )
101            dst = createCompatibleDestImage( src, null );
102
103        int[] inPixels = new int[width*height];
104        int[] outPixels = new int[width*height];
105        src.getRGB( 0, 0, width, height, inPixels, 0, width );
106
107                if ( radius > 0 ) {
108                        convolveAndTranspose(kernel, inPixels, outPixels, width, height, alpha, alpha && premultiplyAlpha, false, CLAMP_EDGES);
109                        convolveAndTranspose(kernel, outPixels, inPixels, height, width, alpha, false, alpha && premultiplyAlpha, CLAMP_EDGES);
110                }
111
112        src.getRGB( 0, 0, width, height, outPixels, 0, width );
113
114                float a = 4*amount;
115
116                int index = 0;
117                for ( int y = 0; y < height; y++ ) {
118                        for ( int x = 0; x < width; x++ ) {
119                                int rgb1 = outPixels[index];
120                                int r1 = (rgb1 >> 16) & 0xff;
121                                int g1 = (rgb1 >> 8) & 0xff;
122                                int b1 = rgb1 & 0xff;
123
124                                int rgb2 = inPixels[index];
125                                int r2 = (rgb2 >> 16) & 0xff;
126                                int g2 = (rgb2 >> 8) & 0xff;
127                                int b2 = rgb2 & 0xff;
128
129                                if ( Math.abs( r1 -  r2 ) >= threshold )
130                                        r1 = PixelUtils.clamp( (int)((a+1) * (r1-r2) + r2) );
131                                if ( Math.abs( g1 -  g2 ) >= threshold )
132                                        g1 = PixelUtils.clamp( (int)((a+1) * (g1-g2) + g2) );
133                                if ( Math.abs( b1 -  b2 ) >= threshold )
134                                        b1 = PixelUtils.clamp( (int)((a+1) * (b1-b2) + b2) );
135
136                                inPixels[index] = (rgb1 & 0xff000000) | (r1 << 16) | (g1 << 8) | b1;
137                                index++;
138                        }
139                }
140
141        dst.setRGB( 0, 0, width, height, inPixels, 0, width );
142        return dst;
143    }
144
145        public String toString() {
146                return "Blur/Unsharp Mask...";
147        }
148        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
149                Object o;
150                if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount"));
151                if((o=parameters.removeEL(KeyImpl.init("Threshold")))!=null)setThreshold(ImageFilterUtil.toIntValue(o,"Threshold"));
152                if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toFloatValue(o,"Radius"));
153                if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction"));
154                if((o=parameters.removeEL(KeyImpl.init("UseAlpha")))!=null)setUseAlpha(ImageFilterUtil.toBooleanValue(o,"UseAlpha"));
155                if((o=parameters.removeEL(KeyImpl.init("PremultiplyAlpha")))!=null)setPremultiplyAlpha(ImageFilterUtil.toBooleanValue(o,"PremultiplyAlpha"));
156
157                // check for arguments not supported
158                if(parameters.size()>0) {
159                        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 [Amount, Threshold, Radius, Kernel, EdgeAction, UseAlpha, PremultiplyAlpha]");
160                }
161
162                return filter(src, dst);
163        }
164}