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.github.sarxos.webcam.util.jh; 018 019import java.awt.image.BufferedImage; 020 021 022/** 023 * A filter which performs a box blur on an image. The horizontal and vertical blurs can be 024 * specified separately and a number of iterations can be given which allows an approximation to 025 * Gaussian blur. 026 */ 027public class JHBlurFilter extends JHFilter { 028 029 private float hRadius; 030 private float vRadius; 031 private int iterations = 1; 032 private boolean premultiplyAlpha = true; 033 034 /** 035 * Construct a default BoxBlurFilter. 036 */ 037 public JHBlurFilter() { 038 } 039 040 /** 041 * Construct a BoxBlurFilter. 042 * 043 * @param hRadius the horizontal radius of blur 044 * @param vRadius the vertical radius of blur 045 * @param iterations the number of time to iterate the blur 046 */ 047 public JHBlurFilter(float hRadius, float vRadius, int iterations) { 048 this.hRadius = hRadius; 049 this.vRadius = vRadius; 050 this.iterations = iterations; 051 } 052 053 /** 054 * Set whether to premultiply the alpha channel. 055 * 056 * @param premultiplyAlpha true to premultiply the alpha 057 * @see #getPremultiplyAlpha 058 */ 059 public void setPremultiplyAlpha(boolean premultiplyAlpha) { 060 this.premultiplyAlpha = premultiplyAlpha; 061 } 062 063 /** 064 * Get whether to premultiply the alpha channel. 065 * 066 * @return true to premultiply the alpha 067 * @see #setPremultiplyAlpha 068 */ 069 public boolean getPremultiplyAlpha() { 070 return premultiplyAlpha; 071 } 072 073 @Override 074 public BufferedImage filter(BufferedImage src, BufferedImage dst) { 075 int width = src.getWidth(); 076 int height = src.getHeight(); 077 078 if (dst == null) { 079 dst = createCompatibleDestImage(src, null); 080 } 081 082 int[] inPixels = new int[width * height]; 083 int[] outPixels = new int[width * height]; 084 getRGB(src, 0, 0, width, height, inPixels); 085 086 if (premultiplyAlpha) { 087 premultiply(inPixels, 0, inPixels.length); 088 } 089 for (int i = 0; i < iterations; i++) { 090 blur(inPixels, outPixels, width, height, hRadius); 091 blur(outPixels, inPixels, height, width, vRadius); 092 } 093 blurFractional(inPixels, outPixels, width, height, hRadius); 094 blurFractional(outPixels, inPixels, height, width, vRadius); 095 if (premultiplyAlpha) { 096 unpremultiply(inPixels, 0, inPixels.length); 097 } 098 099 setRGB(dst, 0, 0, width, height, inPixels); 100 return dst; 101 } 102 103 /** 104 * Blur and transpose a block of ARGB pixels. 105 * 106 * @param in the input pixels 107 * @param out the output pixels 108 * @param width the width of the pixel array 109 * @param height the height of the pixel array 110 * @param radius the radius of blur 111 */ 112 public static void blur(int[] in, int[] out, int width, int height, float radius) { 113 int widthMinus1 = width - 1; 114 int r = (int) radius; 115 int tableSize = 2 * r + 1; 116 int divide[] = new int[256 * tableSize]; 117 118 for (int i = 0; i < 256 * tableSize; i++) { 119 divide[i] = i / tableSize; 120 } 121 122 int inIndex = 0; 123 124 for (int y = 0; y < height; y++) { 125 int outIndex = y; 126 int ta = 0, tr = 0, tg = 0, tb = 0; 127 128 for (int i = -r; i <= r; i++) { 129 int rgb = in[inIndex + clamp(i, 0, width - 1)]; 130 ta += (rgb >> 24) & 0xff; 131 tr += (rgb >> 16) & 0xff; 132 tg += (rgb >> 8) & 0xff; 133 tb += rgb & 0xff; 134 } 135 136 for (int x = 0; x < width; x++) { 137 out[outIndex] = (divide[ta] << 24) | (divide[tr] << 16) | (divide[tg] << 8) | divide[tb]; 138 139 int i1 = x + r + 1; 140 if (i1 > widthMinus1) { 141 i1 = widthMinus1; 142 } 143 int i2 = x - r; 144 if (i2 < 0) { 145 i2 = 0; 146 } 147 int rgb1 = in[inIndex + i1]; 148 int rgb2 = in[inIndex + i2]; 149 150 ta += ((rgb1 >> 24) & 0xff) - ((rgb2 >> 24) & 0xff); 151 tr += ((rgb1 & 0xff0000) - (rgb2 & 0xff0000)) >> 16; 152 tg += ((rgb1 & 0xff00) - (rgb2 & 0xff00)) >> 8; 153 tb += (rgb1 & 0xff) - (rgb2 & 0xff); 154 outIndex += height; 155 } 156 inIndex += width; 157 } 158 } 159 160 public static void blurFractional(int[] in, int[] out, int width, int height, float radius) { 161 radius -= (int) radius; 162 float f = 1.0f / (1 + 2 * radius); 163 int inIndex = 0; 164 165 for (int y = 0; y < height; y++) { 166 int outIndex = y; 167 168 out[outIndex] = in[0]; 169 outIndex += height; 170 for (int x = 1; x < width - 1; x++) { 171 int i = inIndex + x; 172 int rgb1 = in[i - 1]; 173 int rgb2 = in[i]; 174 int rgb3 = in[i + 1]; 175 176 int a1 = (rgb1 >> 24) & 0xff; 177 int r1 = (rgb1 >> 16) & 0xff; 178 int g1 = (rgb1 >> 8) & 0xff; 179 int b1 = rgb1 & 0xff; 180 int a2 = (rgb2 >> 24) & 0xff; 181 int r2 = (rgb2 >> 16) & 0xff; 182 int g2 = (rgb2 >> 8) & 0xff; 183 int b2 = rgb2 & 0xff; 184 int a3 = (rgb3 >> 24) & 0xff; 185 int r3 = (rgb3 >> 16) & 0xff; 186 int g3 = (rgb3 >> 8) & 0xff; 187 int b3 = rgb3 & 0xff; 188 a1 = a2 + (int) ((a1 + a3) * radius); 189 r1 = r2 + (int) ((r1 + r3) * radius); 190 g1 = g2 + (int) ((g1 + g3) * radius); 191 b1 = b2 + (int) ((b1 + b3) * radius); 192 a1 *= f; 193 r1 *= f; 194 g1 *= f; 195 b1 *= f; 196 out[outIndex] = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1; 197 outIndex += height; 198 } 199 out[outIndex] = in[width - 1]; 200 inIndex += width; 201 } 202 } 203 204 /** 205 * Set the horizontal size of the blur. Minimum hRadius value is 0. 206 * 207 * @param hRadius the radius of the blur in the horizontal direction 208 * @see #getHRadius 209 */ 210 public void setHRadius(float hRadius) { 211 this.hRadius = hRadius; 212 } 213 214 /** 215 * Get the horizontal size of the blur. 216 * 217 * @return the radius of the blur in the horizontal direction 218 * @see #setHRadius 219 */ 220 public float getHRadius() { 221 return hRadius; 222 } 223 224 /** 225 * Set the vertical size of the blur. Minimal vRadius value is 0. 226 * 227 * @param vRadius the radius of the blur in the vertical direction 228 * @see #getVRadius 229 */ 230 public void setVRadius(float vRadius) { 231 this.vRadius = vRadius; 232 } 233 234 /** 235 * Get the vertical size of the blur. 236 * 237 * @return the radius of the blur in the vertical direction 238 * @see #setVRadius 239 */ 240 public float getVRadius() { 241 return vRadius; 242 } 243 244 /** 245 * Set both the horizontal and vertical sizes of the blur. Minimum value is 0. 246 * 247 * @param radius the radius of the blur in both directions 248 * @see #getRadius 249 */ 250 public void setRadius(float radius) { 251 this.hRadius = this.vRadius = radius; 252 } 253 254 /** 255 * Get the size of the blur. 256 * 257 * @return the radius of the blur in the horizontal direction 258 * @see #setRadius 259 */ 260 public float getRadius() { 261 return hRadius; 262 } 263 264 /** 265 * Set the number of iterations the blur is performed. Minimum value is 0. 266 * 267 * @param iterations the number of iterations 268 * @see #getIterations 269 */ 270 public void setIterations(int iterations) { 271 this.iterations = iterations; 272 } 273 274 /** 275 * Get the number of iterations the blur is performed. 276 * 277 * @return the number of iterations 278 * @see #setIterations 279 */ 280 public int getIterations() { 281 return iterations; 282 } 283 284 @Override 285 public String toString() { 286 return "Blur/Box Blur..."; 287 } 288 289 /** 290 * Premultiply a block of pixels 291 * 292 * @param p pixels 293 * @param offset the offset 294 * @param length the length 295 */ 296 public static void premultiply(int[] p, int offset, int length) { 297 length += offset; 298 for (int i = offset; i < length; i++) { 299 int rgb = p[i]; 300 int a = (rgb >> 24) & 0xff; 301 int r = (rgb >> 16) & 0xff; 302 int g = (rgb >> 8) & 0xff; 303 int b = rgb & 0xff; 304 float f = a * (1.0f / 255.0f); 305 r *= f; 306 g *= f; 307 b *= f; 308 p[i] = (a << 24) | (r << 16) | (g << 8) | b; 309 } 310 } 311 312 /** 313 * Premultiply a block of pixels 314 * 315 * @param p the pixels 316 * @param offset the offset 317 * @param length the length 318 */ 319 public static void unpremultiply(int[] p, int offset, int length) { 320 length += offset; 321 for (int i = offset; i < length; i++) { 322 int rgb = p[i]; 323 int a = (rgb >> 24) & 0xff; 324 int r = (rgb >> 16) & 0xff; 325 int g = (rgb >> 8) & 0xff; 326 int b = rgb & 0xff; 327 if (a != 0 && a != 255) { 328 float f = 255.0f / a; 329 r *= f; 330 g *= f; 331 b *= f; 332 if (r > 255) { 333 r = 255; 334 } 335 if (g > 255) { 336 g = 255; 337 } 338 if (b > 255) { 339 b = 255; 340 } 341 p[i] = (a << 24) | (r << 16) | (g << 8) | b; 342 } 343 } 344 } 345 346 /** 347 * Clamp a value to an interval. 348 * 349 * @param a the lower clamp threshold 350 * @param b the upper clamp threshold 351 * @param x the input parameter 352 * @return the clamped value 353 */ 354 public static int clamp(int x, int a, int b) { 355 return (x < a) ? a : (x > b) ? b : x; 356 } 357}