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.AlphaComposite;
036import java.awt.Graphics2D;
037import java.awt.image.BufferedImage;
038
039import lucee.runtime.engine.ThreadLocalPageContext;
040import lucee.runtime.exp.FunctionException;
041import lucee.runtime.exp.PageException;
042import lucee.runtime.img.ImageUtil;
043import lucee.runtime.img.composite.MiscComposite;
044import lucee.runtime.type.KeyImpl;
045import lucee.runtime.type.Struct;
046import lucee.runtime.type.util.CollectionUtil;
047
048/**
049 * A filter which produces the effect of light rays shining out of an image.
050 */
051public class RaysFilter extends MotionBlurOp  implements DynFiltering {
052
053    private float opacity = 1.0f;
054    private float threshold = 0.0f;
055    private float strength = 0.5f;
056        private boolean raysOnly = false;
057        private Colormap colormap=new GrayscaleColormap();
058
059    public RaysFilter() {
060        }
061        
062        /**
063     * Set the opacity of the rays.
064     * @param opacity the opacity.
065     * @see #getOpacity
066     */
067        public void setOpacity(float opacity) {
068                this.opacity = opacity;
069        }
070
071        /**
072     * Get the opacity of the rays.
073     * @return the opacity.
074     * @see #setOpacity
075     */
076        public float getOpacity() {
077                return opacity;
078        }
079
080        /**
081     * Set the threshold value.
082     * @param threshold the threshold value
083     * @see #getThreshold
084     */
085        public void setThreshold( float threshold ) {
086                this.threshold = threshold;
087        }
088        
089        /**
090     * Get the threshold value.
091     * @return the threshold value
092     * @see #setThreshold
093     */
094        public float getThreshold() {
095                return threshold;
096        }
097        
098        /**
099     * Set the strength of the rays.
100     * @param strength the strength.
101     * @see #getStrength
102     */
103        public void setStrength( float strength ) {
104                this.strength = strength;
105        }
106        
107        /**
108     * Get the strength of the rays.
109     * @return the strength.
110     * @see #setStrength
111     */
112        public float getStrength() {
113                return strength;
114        }
115        
116        /**
117     * Set whether to render only the rays.
118     * @param raysOnly true to render rays only.
119     * @see #getRaysOnly
120     */
121        public void setRaysOnly(boolean raysOnly) {
122                this.raysOnly = raysOnly;
123        }
124
125        /**
126     * Get whether to render only the rays.
127     * @return true to render rays only.
128     * @see #setRaysOnly
129     */
130        public boolean getRaysOnly() {
131                return raysOnly;
132        }
133
134    /**
135     * Set the colormap to be used for the filter.
136     * @param colormap the colormap
137     * @see #getColormap
138     */
139        public void setColormap(Colormap colormap) {
140                this.colormap = colormap;
141        }
142        
143    /**
144     * Get the colormap to be used for the filter.
145     * @return the colormap
146     * @see #setColormap
147     */
148        public Colormap getColormap() {
149                return colormap;
150        }
151        
152    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
153        int width = src.getWidth();
154        int height = src.getHeight();
155                int[] pixels = new int[width];
156                int[] srcPixels = new int[width];
157
158        BufferedImage rays = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
159
160                int threshold3 = (int)(threshold*3*255);
161                for ( int y = 0; y < height; y++ ) {
162                        getRGB( src, 0, y, width, 1, pixels );
163                        for ( int x = 0; x < width; x++ ) {
164                                int rgb = pixels[x];
165                                int a = rgb & 0xff000000;
166                                int r = (rgb >> 16) & 0xff;
167                                int g = (rgb >> 8) & 0xff;
168                                int b = rgb & 0xff;
169                                int l = r + g + b;
170                                if (l < threshold3)
171                                        pixels[x] = 0xff000000;
172                                else {
173                                        l /= 3;
174                                        pixels[x] = a | (l << 16) | (l << 8) | l;
175                                }
176                        }
177                        setRGB( rays, 0, y, width, 1, pixels );
178                }
179
180                rays = super.filter( rays, null );
181
182                for ( int y = 0; y < height; y++ ) {
183                        getRGB( rays, 0, y, width, 1, pixels );
184                        getRGB( src, 0, y, width, 1, srcPixels );
185                        for ( int x = 0; x < width; x++ ) {
186                                int rgb = pixels[x];
187                                int a = rgb & 0xff000000;
188                                int r = (rgb >> 16) & 0xff;
189                                int g = (rgb >> 8) & 0xff;
190                                int b = rgb & 0xff;
191                                
192                                if ( colormap != null ) {
193                                        int l = r + g + b;
194                                        rgb = colormap.getColor( l * strength * (1/3f) );
195                                } else {
196                                        r = PixelUtils.clamp((int)(r * strength));
197                                        g = PixelUtils.clamp((int)(g * strength));
198                                        b = PixelUtils.clamp((int)(b * strength));
199                                        rgb = a | (r << 16) | (g << 8) | b;
200                                }
201
202                                pixels[x] = rgb;
203                        }
204                        setRGB( rays, 0, y, width, 1, pixels );
205                }
206
207        if ( dst == null )
208            dst = createCompatibleDestImage( src, null );
209
210                Graphics2D g = dst.createGraphics();
211                if ( !raysOnly ) {
212                        g.setComposite( AlphaComposite.SrcOver );
213                        g.drawRenderedImage( src, null );
214                }
215                g.setComposite( MiscComposite.getInstance( MiscComposite.ADD, opacity ) );
216                g.drawRenderedImage( rays, null );
217                g.dispose();
218
219        return dst;
220    }
221    
222        public String toString() {
223                return "Stylize/Rays...";
224        }
225        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
226                Object o;
227                if((o=parameters.removeEL(KeyImpl.init("Colormap")))!=null)setColormap(ImageFilterUtil.toColormap(o,"Colormap"));
228                if((o=parameters.removeEL(KeyImpl.init("Strength")))!=null)setStrength(ImageFilterUtil.toFloatValue(o,"Strength"));
229                if((o=parameters.removeEL(KeyImpl.init("Opacity")))!=null)setOpacity(ImageFilterUtil.toFloatValue(o,"Opacity"));
230                if((o=parameters.removeEL(KeyImpl.init("RaysOnly")))!=null)setRaysOnly(ImageFilterUtil.toBooleanValue(o,"RaysOnly"));
231                if((o=parameters.removeEL(KeyImpl.init("Threshold")))!=null)setThreshold(ImageFilterUtil.toFloatValue(o,"Threshold"));
232                if((o=parameters.removeEL(KeyImpl.init("Angle")))!=null)setAngle(ImageFilterUtil.toFloatValue(o,"Angle"));
233                if((o=parameters.removeEL(KeyImpl.init("CentreX")))!=null)setCentreX(ImageFilterUtil.toFloatValue(o,"CentreX"));
234                if((o=parameters.removeEL(KeyImpl.init("CentreY")))!=null)setCentreY(ImageFilterUtil.toFloatValue(o,"CentreY"));
235                //if((o=parameters.removeEL(KeyImpl.init("Centre")))!=null)setCentre(ImageFilterUtil.toPoint2D(o,"Centre"));
236                if((o=parameters.removeEL(KeyImpl.init("Distance")))!=null)setDistance(ImageFilterUtil.toFloatValue(o,"Distance"));
237                if((o=parameters.removeEL(KeyImpl.init("Rotation")))!=null)setRotation(ImageFilterUtil.toFloatValue(o,"Rotation"));
238                if((o=parameters.removeEL(KeyImpl.init("Zoom")))!=null)setZoom(ImageFilterUtil.toFloatValue(o,"Zoom"));
239
240                // check for arguments not supported
241                if(parameters.size()>0) {
242                        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 [Colormap, Strength, Opacity, RaysOnly, Threshold, Angle, CentreX, CentreY, Centre, Distance, Rotation, Zoom]");
243                }
244
245                return filter(src, dst);
246        }
247}