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.Rectangle;
018    import java.awt.image.BufferedImage;
019    import java.util.Date;
020    import java.util.Random;
021    
022    import railo.runtime.engine.ThreadLocalPageContext;
023    import railo.runtime.exp.FunctionException;
024    import railo.runtime.exp.PageException;
025    import railo.runtime.img.ImageUtil;
026    import railo.runtime.type.KeyImpl;
027    import railo.runtime.type.Struct;
028    import railo.runtime.type.util.CollectionUtil;
029    
030    public class SmearFilter extends WholeImageFilter  implements DynFiltering {
031            
032            public final static int CROSSES = 0;
033            public final static int LINES = 1;
034            public final static int CIRCLES = 2;
035            public final static int SQUARES = 3;
036    
037            private Colormap colormap = new LinearColormap();
038            private float angle = 0;
039            private float density = 0.5f;
040            private float scatter = 0.0f;
041            private int distance = 8;
042            private Random randomGenerator;
043            private long seed = 567;
044            private int shape = LINES;
045            private float mix = 0.5f;
046            private int fadeout = 0;
047            private boolean background = false;
048    
049            public SmearFilter() {
050                    randomGenerator = new Random();
051            }
052    
053            public void setShape(int shape) {
054                    this.shape = shape;
055            }
056    
057            public int getShape() {
058                    return shape;
059            }
060    
061            public void setDistance(int distance) {
062                    this.distance = distance;
063            }
064    
065            public int getDistance() {
066                    return distance;
067            }
068    
069            public void setDensity(float density) {
070                    this.density = density;
071            }
072    
073            public float getDensity() {
074                    return density;
075            }
076    
077            public void setScatter(float scatter) {
078                    this.scatter = scatter;
079            }
080    
081            public float getScatter() {
082                    return scatter;
083            }
084    
085            /**
086         * Specifies the angle of the texture.
087         * @param angle the angle of the texture.
088         * @angle
089         * @see #getAngle
090         */
091            public void setAngle(float angle) {
092                    this.angle = angle;
093            }
094    
095            /**
096         * Returns the angle of the texture.
097         * @return the angle of the texture.
098         * @see #setAngle
099         */
100            public float getAngle() {
101                    return angle;
102            }
103    
104            public void setMix(float mix) {
105                    this.mix = mix;
106            }
107    
108            public float getMix() {
109                    return mix;
110            }
111    
112            public void setFadeout(int fadeout) {
113                    this.fadeout = fadeout;
114            }
115    
116            public int getFadeout() {
117                    return fadeout;
118            }
119    
120            public void setBackground(boolean background) {
121                    this.background = background;
122            }
123    
124            public boolean getBackground() {
125                    return background;
126            }
127    
128            public void randomize() {
129                    seed = new Date().getTime();
130            }
131            
132            private float random(float low, float high) {
133                    return low+(high-low) * randomGenerator.nextFloat();
134            }
135            
136            protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
137                    int[] outPixels = new int[width * height];
138    
139                    randomGenerator.setSeed(seed);
140                    float sinAngle = (float)Math.sin(angle);
141                    float cosAngle = (float)Math.cos(angle);
142    
143                    int i = 0;
144                    int numShapes;
145    
146                    for (int y = 0; y < height; y++)
147                            for (int x = 0; x < width; x++) {
148                                    outPixels[i] = background ? 0xffffffff : inPixels[i];
149                                    i++;
150                            }
151    
152                    switch (shape) {
153                    case CROSSES:
154                            //Crosses
155                            numShapes = (int)(2*density*width * height / (distance + 1));
156                            for (i = 0; i < numShapes; i++) {
157                                    int x = (randomGenerator.nextInt() & 0x7fffffff) % width;
158                                    int y = (randomGenerator.nextInt() & 0x7fffffff) % height;
159                                    int length = randomGenerator.nextInt() % distance + 1;
160                                    int rgb = inPixels[y*width+x];
161                                    for (int x1 = x - length; x1 < x + length + 1; x1++) {
162                                            if (x1 >= 0 && x1 < width) {
163                                                    int rgb2 = background ? 0xffffffff : outPixels[y*width+x1];
164                                                    outPixels[y*width+x1] = ImageMath.mixColors(mix, rgb2, rgb);
165                                            }
166                                    }
167                                    for (int y1 = y - length; y1 < y + length + 1; y1++) {
168                                            if (y1 >= 0 && y1 < height) {
169                                                    int rgb2 = background ? 0xffffffff : outPixels[y1*width+x];
170                                                    outPixels[y1*width+x] = ImageMath.mixColors(mix, rgb2, rgb);
171                                            }
172                                    }
173                            }
174                            break;
175                    case LINES:
176                            numShapes = (int)(2*density*width * height / 2);
177    
178                            for (i = 0; i < numShapes; i++) {
179                                    int sx = (randomGenerator.nextInt() & 0x7fffffff) % width;
180                                    int sy = (randomGenerator.nextInt() & 0x7fffffff) % height;
181                                    int rgb = inPixels[sy*width+sx];
182                                    int length = (randomGenerator.nextInt() & 0x7fffffff) % distance;
183                                    int dx = (int)(length*cosAngle);
184                                    int dy = (int)(length*sinAngle);
185    
186                                    int x0 = sx-dx;
187                                    int y0 = sy-dy;
188                                    int x1 = sx+dx;
189                                    int y1 = sy+dy;
190                                    int x, y, d, incrE, incrNE, ddx, ddy;
191                                    
192                                    if (x1 < x0)
193                                            ddx = -1;
194                                    else
195                                            ddx = 1;
196                                    if (y1 < y0)
197                                            ddy = -1;
198                                    else
199                                            ddy = 1;
200                                    dx = x1-x0;
201                                    dy = y1-y0;
202                                    dx = Math.abs(dx);
203                                    dy = Math.abs(dy);
204                                    x = x0;
205                                    y = y0;
206    
207                                    if (x < width && x >= 0 && y < height && y >= 0) {
208                                            int rgb2 = background ? 0xffffffff : outPixels[y*width+x];
209                                            outPixels[y*width+x] = ImageMath.mixColors(mix, rgb2, rgb);
210                                    }
211                                    if (Math.abs(dx) > Math.abs(dy)) {
212                                            d = 2*dy-dx;
213                                            incrE = 2*dy;
214                                            incrNE = 2*(dy-dx);
215    
216                                            while (x != x1) {
217                                                    if (d <= 0)
218                                                            d += incrE;
219                                                    else {
220                                                            d += incrNE;
221                                                            y += ddy;
222                                                    }
223                                                    x += ddx;
224                                                    if (x < width && x >= 0 && y < height && y >= 0) {
225                                                            int rgb2 = background ? 0xffffffff : outPixels[y*width+x];
226                                                            outPixels[y*width+x] = ImageMath.mixColors(mix, rgb2, rgb);
227                                                    }
228                                            }
229                                    } else {
230                                            d = 2*dx-dy;
231                                            incrE = 2*dx;
232                                            incrNE = 2*(dx-dy);
233    
234                                            while (y != y1) {
235                                                    if (d <= 0)
236                                                            d += incrE;
237                                                    else {
238                                                            d += incrNE;
239                                                            x += ddx;
240                                                    }
241                                                    y += ddy;
242                                                    if (x < width && x >= 0 && y < height && y >= 0) {
243                                                            int rgb2 = background ? 0xffffffff : outPixels[y*width+x];
244                                                            outPixels[y*width+x] = ImageMath.mixColors(mix, rgb2, rgb);
245                                                    }
246                                            }
247                                    }
248                            }
249                            break;
250                    case SQUARES:
251                    case CIRCLES:
252                            int radius = distance+1;
253                            int radius2 = radius * radius;
254                            numShapes = (int)(2*density*width * height / radius);
255                            for (i = 0; i < numShapes; i++) {
256                                    int sx = (randomGenerator.nextInt() & 0x7fffffff) % width;
257                                    int sy = (randomGenerator.nextInt() & 0x7fffffff) % height;
258                                    int rgb = inPixels[sy*width+sx];
259                                    for (int x = sx - radius; x < sx + radius + 1; x++) {
260                                            for (int y = sy - radius; y < sy + radius + 1; y++) {
261                                                    int f;
262                                                    if (shape == CIRCLES)
263                                                            f = (x - sx) * (x - sx) + (y - sy) * (y - sy);
264                                                    else
265                                                            f = 0;
266                                                    if (x >= 0 && x < width && y >= 0 && y < height && f <= radius2) {
267                                                            int rgb2 = background ? 0xffffffff : outPixels[y*width+x];
268                                                            outPixels[y*width+x] = ImageMath.mixColors(mix, rgb2, rgb);
269                                                    }
270                                            }
271                                    }
272                            }
273                    }
274    
275                    return outPixels;
276            }
277    
278            public String toString() {
279                    return "Effects/Smear...";
280            }
281            
282            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
283                    Object o;
284                    if((o=parameters.removeEL(KeyImpl.init("Angle")))!=null)setAngle(ImageFilterUtil.toFloatValue(o,"Angle"));
285                    if((o=parameters.removeEL(KeyImpl.init("Density")))!=null)setDensity(ImageFilterUtil.toFloatValue(o,"Density"));
286                    if((o=parameters.removeEL(KeyImpl.init("Distance")))!=null)setDistance(ImageFilterUtil.toIntValue(o,"Distance"));
287                    if((o=parameters.removeEL(KeyImpl.init("Shape")))!=null)setShape(ImageFilterUtil.toIntValue(o,"Shape"));
288                    if((o=parameters.removeEL(KeyImpl.init("Scatter")))!=null)setScatter(ImageFilterUtil.toFloatValue(o,"Scatter"));
289                    if((o=parameters.removeEL(KeyImpl.init("Mix")))!=null)setMix(ImageFilterUtil.toFloatValue(o,"Mix"));
290                    if((o=parameters.removeEL(KeyImpl.init("Fadeout")))!=null)setFadeout(ImageFilterUtil.toIntValue(o,"Fadeout"));
291                    if((o=parameters.removeEL(KeyImpl.init("Background")))!=null)setBackground(ImageFilterUtil.toBooleanValue(o,"Background"));
292    
293                    // check for arguments not supported
294                    if(parameters.size()>0) {
295                            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, Distance, Shape, Scatter, Mix, Fadeout, Background]");
296                    }
297    
298                    return filter(src, dst);
299            }
300    }