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