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 020 import railo.runtime.engine.ThreadLocalPageContext; 021 import railo.runtime.exp.FunctionException; 022 import railo.runtime.exp.PageException; 023 import railo.runtime.img.ImageUtil; 024 import railo.runtime.type.KeyImpl; 025 import railo.runtime.type.Struct; 026 import railo.runtime.type.util.CollectionUtil; 027 028 public class ShapeFilter extends WholeImageFilter implements DynFiltering { 029 030 public final static int LINEAR = 0; 031 public final static int CIRCLE_UP = 1; 032 public final static int CIRCLE_DOWN = 2; 033 public final static int SMOOTH = 3; 034 035 private float factor = 1.0f; 036 protected Colormap colormap; 037 private boolean useAlpha = true; 038 private boolean invert = false; 039 private boolean merge = false; 040 private int type; 041 042 private final static int one = 41; 043 private final static int sqrt2 = (int)(41*Math.sqrt(2)); 044 private final static int sqrt5 = (int)(41*Math.sqrt(5)); 045 046 public ShapeFilter() { 047 colormap = new LinearColormap(); 048 } 049 050 public void setFactor(float factor) { 051 this.factor = factor; 052 } 053 054 public float getFactor() { 055 return factor; 056 } 057 058 /** 059 * Set the colormap to be used for the filter. 060 * @param colormap the colormap 061 * @see #getColormap 062 */ 063 public void setColormap(Colormap colormap) { 064 this.colormap = colormap; 065 } 066 067 /** 068 * Get the colormap to be used for the filter. 069 * @return the colormap 070 * @see #setColormap 071 */ 072 public Colormap getColormap() { 073 return colormap; 074 } 075 076 public void setUseAlpha(boolean useAlpha) { 077 this.useAlpha = useAlpha; 078 } 079 080 public boolean getUseAlpha() { 081 return useAlpha; 082 } 083 084 public void setType(int type) { 085 this.type = type; 086 } 087 088 public int getType() { 089 return type; 090 } 091 092 public void setInvert(boolean invert) { 093 this.invert = invert; 094 } 095 096 public boolean getInvert() { 097 return invert; 098 } 099 100 public void setMerge(boolean merge) { 101 this.merge = merge; 102 } 103 104 public boolean getMerge() { 105 return merge; 106 } 107 108 protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) { 109 int[] map = new int[width * height]; 110 makeMap(inPixels, map, width, height); 111 int max = distanceMap(map, width, height); 112 applyMap(map, inPixels, width, height, max); 113 114 return inPixels; 115 } 116 117 public int distanceMap(int[] map, int width, int height) { 118 int xmax = width - 3; 119 int ymax = height - 3; 120 int max = 0; 121 int v; 122 123 for (int y = 0; y < height; y++) { 124 for (int x = 0; x < width; x++) { 125 int offset = x + y * width; 126 if (map[offset] > 0) { 127 if (x < 2 || x > xmax || y < 2 || y > ymax) 128 v = setEdgeValue(x, y, map, width, offset, xmax, ymax); 129 else 130 v = setValue(map, width, offset); 131 if (v > max) 132 max = v; 133 } 134 } 135 } 136 for (int y = height-1; y >= 0; y--) { 137 for (int x = width-1; x >= 0; x--) { 138 int offset = x + y * width; 139 if (map[offset] > 0) { 140 if (x < 2 || x > xmax || y < 2 || y > ymax) 141 v = setEdgeValue(x, y, map, width, offset, xmax, ymax); 142 else 143 v = setValue(map, width, offset); 144 if (v > max) 145 max = v; 146 } 147 } 148 } 149 return max; 150 } 151 152 private void makeMap(int[] pixels, int[] map, int width, int height) { 153 for (int y = 0; y < height; y++) { 154 for (int x = 0; x < width; x++) { 155 int offset = x + y * width; 156 int b = useAlpha ? (pixels[offset] >> 24) & 0xff : PixelUtils.brightness(pixels[offset]); 157 // map[offset] = b * one; 158 map[offset] = b * one / 10; 159 } 160 } 161 } 162 163 private void applyMap(int[] map, int[] pixels, int width, int height, int max) { 164 if (max == 0) 165 max = 1; 166 for (int y = 0; y < height; y++) { 167 for (int x = 0; x < width; x++) { 168 int offset = x + y * width; 169 int m = map[offset]; 170 float v = 0; 171 int sa = 0, sr = 0, sg = 0, sb = 0; 172 173 if (m == 0) { 174 // default color 175 sa = sr = sg = sb = 0; 176 sa = (pixels[offset] >> 24) & 0xff; 177 } else { 178 // get V from map 179 v = ImageMath.clamp(factor * m / max, 0, 1); 180 switch (type) { 181 case CIRCLE_UP : 182 v = (ImageMath.circleUp(v)); 183 break; 184 case CIRCLE_DOWN : 185 v = (ImageMath.circleDown(v)); 186 break; 187 case SMOOTH : 188 v = (ImageMath.smoothStep(0, 1, v)); 189 break; 190 } 191 192 if (colormap == null) { 193 sr = sg = sb = (int)(v*255); 194 } else { 195 int c = (colormap.getColor(v)); 196 197 sr = (c >> 16) & 0xFF; 198 sg = (c >> 8) & 0xFF; 199 sb = (c) & 0xFF; 200 } 201 202 sa = useAlpha ? (pixels[offset] >> 24) & 0xff : PixelUtils.brightness(pixels[offset]); 203 204 // invert v if necessary 205 if (invert) { 206 sr = 255-sr; 207 sg = 255-sg; 208 sb = 255-sb; 209 } 210 } 211 212 // write results 213 if (merge) { 214 // merge with source 215 int transp = 255; 216 int col = pixels[offset]; 217 218 int a = (col & 0xFF000000) >> 24; 219 int r = (col & 0xFF0000) >> 16; 220 int g = (col & 0xFF00) >> 8; 221 int b = (col & 0xFF); 222 223 r = ((sr*r/transp)); 224 g = ((sg*g/transp)); 225 b = ((sb*b/transp)); 226 227 // clip colors 228 if (r < 0) 229 r = 0; 230 if (r > 255) 231 r = 255; 232 if (g < 0) 233 g = 0; 234 if (g > 255) 235 g = 255; 236 if (b < 0) 237 b = 0; 238 if (b > 255) 239 b = 255; 240 241 pixels[offset] = (a << 24) | (r << 16) | (g << 8) | b; 242 } else { 243 // write gray shades 244 pixels[offset] = (sa << 24) | (sr << 16) | (sg << 8) | sb; 245 } 246 } 247 } 248 } 249 250 private int setEdgeValue(int x, int y, int[] map, int width, int offset, int xmax, int ymax) { 251 int min, v; 252 int r1, r2, r3, r4, r5; 253 254 r1 = offset - width - width - 2; 255 r2 = r1 + width; 256 r3 = r2 + width; 257 r4 = r3 + width; 258 r5 = r4 + width; 259 260 if (y == 0 || x == 0 || y == ymax+2 || x == xmax+2) 261 return map[offset] = one; 262 263 v = map[r2 + 2] + one; 264 min = v; 265 266 v = map[r3 + 1] + one; 267 if (v < min) 268 min = v; 269 270 v = map[r3 + 3] + one; 271 if (v < min) 272 min = v; 273 274 v = map[r4 + 2] + one; 275 if (v < min) 276 min = v; 277 278 v = map[r2 + 1] + sqrt2; 279 if (v < min) 280 min = v; 281 282 v = map[r2 + 3] + sqrt2; 283 if (v < min) 284 min = v; 285 286 v = map[r4 + 1] + sqrt2; 287 if (v < min) 288 min = v; 289 290 v = map[r4 + 3] + sqrt2; 291 if (v < min) 292 min = v; 293 294 if (y == 1 || x == 1 || y == ymax+1 || x == xmax+1) 295 return map[offset] = min; 296 297 v = map[r1 + 1] + sqrt5; 298 if (v < min) 299 min = v; 300 301 v = map[r1 + 3] + sqrt5; 302 if (v < min) 303 min = v; 304 305 v = map[r2 + 4] + sqrt5; 306 if (v < min) 307 min = v; 308 309 v = map[r4 + 4] + sqrt5; 310 if (v < min) 311 min = v; 312 313 v = map[r5 + 3] + sqrt5; 314 if (v < min) 315 min = v; 316 317 v = map[r5 + 1] + sqrt5; 318 if (v < min) 319 min = v; 320 321 v = map[r4] + sqrt5; 322 if (v < min) 323 min = v; 324 325 v = map[r2] + sqrt5; 326 if (v < min) 327 min = v; 328 329 return map[offset] = min; 330 } 331 332 private int setValue(int[] map, int width, int offset) { 333 int min, v; 334 int r1, r2, r3, r4, r5; 335 336 r1 = offset - width - width - 2; 337 r2 = r1 + width; 338 r3 = r2 + width; 339 r4 = r3 + width; 340 r5 = r4 + width; 341 342 v = map[r2 + 2] + one; 343 min = v; 344 v = map[r3 + 1] + one; 345 if (v < min) 346 min = v; 347 v = map[r3 + 3] + one; 348 if (v < min) 349 min = v; 350 v = map[r4 + 2] + one; 351 if (v < min) 352 min = v; 353 354 v = map[r2 + 1] + sqrt2; 355 if (v < min) 356 min = v; 357 v = map[r2 + 3] + sqrt2; 358 if (v < min) 359 min = v; 360 v = map[r4 + 1] + sqrt2; 361 if (v < min) 362 min = v; 363 v = map[r4 + 3] + sqrt2; 364 if (v < min) 365 min = v; 366 367 v = map[r1 + 1] + sqrt5; 368 if (v < min) 369 min = v; 370 v = map[r1 + 3] + sqrt5; 371 if (v < min) 372 min = v; 373 v = map[r2 + 4] + sqrt5; 374 if (v < min) 375 min = v; 376 v = map[r4 + 4] + sqrt5; 377 if (v < min) 378 min = v; 379 v = map[r5 + 3] + sqrt5; 380 if (v < min) 381 min = v; 382 v = map[r5 + 1] + sqrt5; 383 if (v < min) 384 min = v; 385 v = map[r4] + sqrt5; 386 if (v < min) 387 min = v; 388 v = map[r2] + sqrt5; 389 if (v < min) 390 min = v; 391 392 return map[offset] = min; 393 } 394 395 public String toString() { 396 return "Stylize/Shapeburst..."; 397 } 398 399 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 400 Object o; 401 if((o=parameters.removeEL(KeyImpl.init("UseAlpha")))!=null)setUseAlpha(ImageFilterUtil.toBooleanValue(o,"UseAlpha")); 402 if((o=parameters.removeEL(KeyImpl.init("Colormap")))!=null)setColormap(ImageFilterUtil.toColormap(o,"Colormap")); 403 if((o=parameters.removeEL(KeyImpl.init("Invert")))!=null)setInvert(ImageFilterUtil.toBooleanValue(o,"Invert")); 404 if((o=parameters.removeEL(KeyImpl.init("Factor")))!=null)setFactor(ImageFilterUtil.toFloatValue(o,"Factor")); 405 if((o=parameters.removeEL(KeyImpl.init("Merge")))!=null)setMerge(ImageFilterUtil.toBooleanValue(o,"Merge")); 406 if((o=parameters.removeEL(KeyImpl.init("Type")))!=null)setType(ImageFilterUtil.toIntValue(o,"Type")); 407 408 // check for arguments not supported 409 if(parameters.size()>0) { 410 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 [UseAlpha, Colormap, Invert, Factor, Merge, Type]"); 411 } 412 413 return filter(src, dst); 414 } 415 }