001/*
002Copyright 2006 Jerry Huxtable
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008   http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015*/
016
017package com.jhlabs.image;
018
019import java.util.*;
020import java.awt.*;
021import java.awt.image.*;
022import com.jhlabs.math.*;
023
024public class SmearFilter extends WholeImageFilter {
025        
026        public final static int CROSSES = 0;
027        public final static int LINES = 1;
028        public final static int CIRCLES = 2;
029        public final static int SQUARES = 3;
030
031        private Colormap colormap = new LinearColormap();
032        private float angle = 0;
033        private float density = 0.5f;
034        private float scatter = 0.0f;
035        private int distance = 8;
036        private Random randomGenerator;
037        private long seed = 567;
038        private int shape = LINES;
039        private float mix = 0.5f;
040        private int fadeout = 0;
041        private boolean background = false;
042
043        public SmearFilter() {
044                randomGenerator = new Random();
045        }
046
047        public void setShape(int shape) {
048                this.shape = shape;
049        }
050
051        public int getShape() {
052                return shape;
053        }
054
055        public void setDistance(int distance) {
056                this.distance = distance;
057        }
058
059        public int getDistance() {
060                return distance;
061        }
062
063        public void setDensity(float density) {
064                this.density = density;
065        }
066
067        public float getDensity() {
068                return density;
069        }
070
071        public void setScatter(float scatter) {
072                this.scatter = scatter;
073        }
074
075        public float getScatter() {
076                return scatter;
077        }
078
079        /**
080     * Specifies the angle of the texture.
081     * @param angle the angle of the texture.
082     * @angle
083     * @see #getAngle
084     */
085        public void setAngle(float angle) {
086                this.angle = angle;
087        }
088
089        /**
090     * Returns the angle of the texture.
091     * @return the angle of the texture.
092     * @see #setAngle
093     */
094        public float getAngle() {
095                return angle;
096        }
097
098        public void setMix(float mix) {
099                this.mix = mix;
100        }
101
102        public float getMix() {
103                return mix;
104        }
105
106        public void setFadeout(int fadeout) {
107                this.fadeout = fadeout;
108        }
109
110        public int getFadeout() {
111                return fadeout;
112        }
113
114        public void setBackground(boolean background) {
115                this.background = background;
116        }
117
118        public boolean getBackground() {
119                return background;
120        }
121
122        public void randomize() {
123                seed = new Date().getTime();
124        }
125        
126        private float random(float low, float high) {
127                return low+(high-low) * randomGenerator.nextFloat();
128        }
129        
130        protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
131                int[] outPixels = new int[width * height];
132
133                randomGenerator.setSeed(seed);
134                float sinAngle = (float)Math.sin(angle);
135                float cosAngle = (float)Math.cos(angle);
136
137                int i = 0;
138                int numShapes;
139
140                for (int y = 0; y < height; y++)
141                        for (int x = 0; x < width; x++) {
142                                outPixels[i] = background ? 0xffffffff : inPixels[i];
143                                i++;
144                        }
145
146                switch (shape) {
147                case CROSSES:
148                        //Crosses
149                        numShapes = (int)(2*density*width * height / (distance + 1));
150                        for (i = 0; i < numShapes; i++) {
151                                int x = (randomGenerator.nextInt() & 0x7fffffff) % width;
152                                int y = (randomGenerator.nextInt() & 0x7fffffff) % height;
153                                int length = randomGenerator.nextInt() % distance + 1;
154                                int rgb = inPixels[y*width+x];
155                                for (int x1 = x - length; x1 < x + length + 1; x1++) {
156                                        if (x1 >= 0 && x1 < width) {
157                                                int rgb2 = background ? 0xffffffff : outPixels[y*width+x1];
158                                                outPixels[y*width+x1] = ImageMath.mixColors(mix, rgb2, rgb);
159                                        }
160                                }
161                                for (int y1 = y - length; y1 < y + length + 1; y1++) {
162                                        if (y1 >= 0 && y1 < height) {
163                                                int rgb2 = background ? 0xffffffff : outPixels[y1*width+x];
164                                                outPixels[y1*width+x] = ImageMath.mixColors(mix, rgb2, rgb);
165                                        }
166                                }
167                        }
168                        break;
169                case LINES:
170                        numShapes = (int)(2*density*width * height / 2);
171
172                        for (i = 0; i < numShapes; i++) {
173                                int sx = (randomGenerator.nextInt() & 0x7fffffff) % width;
174                                int sy = (randomGenerator.nextInt() & 0x7fffffff) % height;
175                                int rgb = inPixels[sy*width+sx];
176                                int length = (randomGenerator.nextInt() & 0x7fffffff) % distance;
177                                int dx = (int)(length*cosAngle);
178                                int dy = (int)(length*sinAngle);
179
180                                int x0 = sx-dx;
181                                int y0 = sy-dy;
182                                int x1 = sx+dx;
183                                int y1 = sy+dy;
184                                int x, y, d, incrE, incrNE, ddx, ddy;
185                                
186                                if (x1 < x0)
187                                        ddx = -1;
188                                else
189                                        ddx = 1;
190                                if (y1 < y0)
191                                        ddy = -1;
192                                else
193                                        ddy = 1;
194                                dx = x1-x0;
195                                dy = y1-y0;
196                                dx = Math.abs(dx);
197                                dy = Math.abs(dy);
198                                x = x0;
199                                y = y0;
200
201                                if (x < width && x >= 0 && y < height && y >= 0) {
202                                        int rgb2 = background ? 0xffffffff : outPixels[y*width+x];
203                                        outPixels[y*width+x] = ImageMath.mixColors(mix, rgb2, rgb);
204                                }
205                                if (Math.abs(dx) > Math.abs(dy)) {
206                                        d = 2*dy-dx;
207                                        incrE = 2*dy;
208                                        incrNE = 2*(dy-dx);
209
210                                        while (x != x1) {
211                                                if (d <= 0)
212                                                        d += incrE;
213                                                else {
214                                                        d += incrNE;
215                                                        y += ddy;
216                                                }
217                                                x += ddx;
218                                                if (x < width && x >= 0 && y < height && y >= 0) {
219                                                        int rgb2 = background ? 0xffffffff : outPixels[y*width+x];
220                                                        outPixels[y*width+x] = ImageMath.mixColors(mix, rgb2, rgb);
221                                                }
222                                        }
223                                } else {
224                                        d = 2*dx-dy;
225                                        incrE = 2*dx;
226                                        incrNE = 2*(dx-dy);
227
228                                        while (y != y1) {
229                                                if (d <= 0)
230                                                        d += incrE;
231                                                else {
232                                                        d += incrNE;
233                                                        x += ddx;
234                                                }
235                                                y += ddy;
236                                                if (x < width && x >= 0 && y < height && y >= 0) {
237                                                        int rgb2 = background ? 0xffffffff : outPixels[y*width+x];
238                                                        outPixels[y*width+x] = ImageMath.mixColors(mix, rgb2, rgb);
239                                                }
240                                        }
241                                }
242                        }
243                        break;
244                case SQUARES:
245                case CIRCLES:
246                        int radius = distance+1;
247                        int radius2 = radius * radius;
248                        numShapes = (int)(2*density*width * height / radius);
249                        for (i = 0; i < numShapes; i++) {
250                                int sx = (randomGenerator.nextInt() & 0x7fffffff) % width;
251                                int sy = (randomGenerator.nextInt() & 0x7fffffff) % height;
252                                int rgb = inPixels[sy*width+sx];
253                                for (int x = sx - radius; x < sx + radius + 1; x++) {
254                                        for (int y = sy - radius; y < sy + radius + 1; y++) {
255                                                int f;
256                                                if (shape == CIRCLES)
257                                                        f = (x - sx) * (x - sx) + (y - sy) * (y - sy);
258                                                else
259                                                        f = 0;
260                                                if (x >= 0 && x < width && y >= 0 && y < height && f <= radius2) {
261                                                        int rgb2 = background ? 0xffffffff : outPixels[y*width+x];
262                                                        outPixels[y*width+x] = ImageMath.mixColors(mix, rgb2, rgb);
263                                                }
264                                        }
265                                }
266                        }
267                }
268
269                return outPixels;
270        }
271
272        public String toString() {
273                return "Effects/Smear...";
274        }
275        
276}