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.BasicStroke;
036import java.awt.Color;
037import java.awt.Graphics2D;
038import java.awt.RenderingHints;
039import java.awt.geom.Line2D;
040import java.awt.image.BufferedImage;
041import java.util.ArrayList;
042import java.util.Random;
043
044import lucee.runtime.engine.ThreadLocalPageContext;
045import lucee.runtime.exp.FunctionException;
046import lucee.runtime.exp.PageException;
047import lucee.runtime.type.KeyImpl;
048import lucee.runtime.type.Struct;
049import lucee.runtime.type.util.CollectionUtil;
050
051public class ScratchFilter extends AbstractBufferedImageOp  implements DynFiltering {
052    private float density = 0.1f;
053    private float angle;
054    private float angleVariation = 1.0f;
055    private float width = 0.5f;
056    private float length = 0.5f;
057    private int color = 0xffffffff;
058    private int seed = 0;
059
060    public ScratchFilter() {
061        }
062        
063        public void setAngle( float angle ) {
064                this.angle = angle;
065        }
066
067        public float getAngle() {
068                return angle;
069        }
070        
071        public void setAngleVariation( float angleVariation ) {
072                this.angleVariation = angleVariation;
073        }
074
075        public float getAngleVariation() {
076                return angleVariation;
077        }
078        
079        public void setDensity( float density ) {
080                this.density = density;
081        }
082
083        public float getDensity() {
084                return density;
085        }
086        
087        public void setLength( float length ) {
088                this.length = length;
089        }
090
091        public float getLength() {
092                return length;
093        }
094        
095        public void setWidth( float width ) {
096                this.width = width;
097        }
098
099        public float getWidth() {
100                return width;
101        }
102        
103        public void setColor( int color ) {
104                this.color = color;
105        }
106
107        public int getColor() {
108                return color;
109        }
110
111        public void setSeed( int seed ) {
112                this.seed = seed;
113        }
114
115        public int getSeed() {
116                return seed;
117        }
118
119    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
120        if ( dst == null )
121            dst = createCompatibleDestImage( src, null );
122
123        int width = src.getWidth();
124        int height = src.getHeight();
125        int numScratches = (int)(density * width * height / 100);
126ArrayList lines = new ArrayList();
127{
128        float l = length * width;
129        Random random = new Random( seed );
130        Graphics2D g = dst.createGraphics();
131        g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
132        g.setColor( new Color( color ) );
133        g.setStroke( new BasicStroke( this.width ) );
134        for ( int i = 0; i < numScratches; i++ ) {
135            float x = width * random.nextFloat();
136            float y = height * random.nextFloat();
137            float a = angle + ImageMath.TWO_PI * (angleVariation * (random.nextFloat() - 0.5f));
138            float s = (float)Math.sin( a ) * l;
139            float c = (float)Math.cos( a ) * l;
140            float x1 = x-c;
141            float y1 = y-s;
142            float x2 = x+c;
143            float y2 = y+s;
144            g.drawLine( (int)x1, (int)y1, (int)x2, (int)y2 );
145lines.add( new Line2D.Float( x1, y1, x2, y2 ) );
146        }
147        g.dispose();
148}
149        
150if ( false ) {
151//              int[] inPixels = getRGB( src, 0, 0, width, height, null );
152                int[] inPixels = new int[width*height];
153        int index = 0;
154        for ( int y = 0; y < height; y++ ) {
155            for ( int x = 0; x < width; x++ ) {
156                float sx = x, sy = y;
157                for ( int i = 0; i < numScratches; i++ ) {
158                    Line2D.Float l = (Line2D.Float)lines.get( i );
159                    float dot = (l.x2-l.x1)*(sx-l.x1) + (l.y2-l.y1)*(sy-l.y1);
160                    if ( dot > 0 )
161                        inPixels[index] |= (1 << i );
162                }
163                index++;
164            }
165        }
166
167        Colormap colormap = new LinearColormap();
168        index = 0;
169        for ( int y = 0; y < height; y++ ) {
170            for ( int x = 0; x < width; x++ ) {
171                float f = (float)(inPixels[index] & 0x7fffffff) / 0x7fffffff;
172                inPixels[index] = colormap.getColor( f );
173                index++;
174            }
175        }
176                setRGB( dst, 0, 0, width, height, inPixels );
177}
178        return dst;
179    }
180    
181        public String toString() {
182                return "Render/Scratches...";
183        }
184        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=src;//ImageUtil.createBufferedImage(src);
185                Object o;
186                if((o=parameters.removeEL(KeyImpl.init("Angle")))!=null)setAngle(ImageFilterUtil.toFloatValue(o,"Angle"));
187                if((o=parameters.removeEL(KeyImpl.init("Density")))!=null)setDensity(ImageFilterUtil.toFloatValue(o,"Density"));
188                if((o=parameters.removeEL(KeyImpl.init("AngleVariation")))!=null)setAngleVariation(ImageFilterUtil.toFloatValue(o,"AngleVariation"));
189                if((o=parameters.removeEL(KeyImpl.init("Length")))!=null)setLength(ImageFilterUtil.toFloatValue(o,"Length"));
190                if((o=parameters.removeEL(KeyImpl.init("Seed")))!=null)setSeed(ImageFilterUtil.toIntValue(o,"Seed"));
191                if((o=parameters.removeEL(KeyImpl.init("Color")))!=null)setColor(ImageFilterUtil.toColorRGB(o,"Color"));
192                if((o=parameters.removeEL(KeyImpl.init("Width")))!=null)setWidth(ImageFilterUtil.toFloatValue(o,"Width"));
193
194                // check for arguments not supported
195                if(parameters.size()>0) {
196                        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 [Angle, Density, AngleVariation, Length, Seed, Color, Width]");
197                }
198
199                return filter(src, dst);
200        }
201}