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.List; 028 import railo.runtime.type.Struct; 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":"")+" ["+List.arrayToList(parameters.keysAsString(),", ")+"] "+(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 }