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}