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