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 024 * blurs can be specified separately and a number of iterations can be given 025 * which allows an approximation to 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 int[] inPixels = new int[width * height]; 082 int[] outPixels = new int[width * height]; 083 getRGB(src, 0, 0, width, height, inPixels); 084 085 if (premultiplyAlpha) 086 premultiply(inPixels, 0, inPixels.length); 087 for (int i = 0; i < iterations; i++) { 088 blur(inPixels, outPixels, width, height, hRadius); 089 blur(outPixels, inPixels, height, width, vRadius); 090 } 091 blurFractional(inPixels, outPixels, width, height, hRadius); 092 blurFractional(outPixels, inPixels, height, width, vRadius); 093 if (premultiplyAlpha) 094 unpremultiply(inPixels, 0, inPixels.length); 095 096 setRGB(dst, 0, 0, width, height, inPixels); 097 return dst; 098 } 099 100 /** 101 * Blur and transpose a block of ARGB pixels. 102 * 103 * @param in the input pixels 104 * @param out the output pixels 105 * @param width the width of the pixel array 106 * @param height the height of the pixel array 107 * @param radius the radius of blur 108 */ 109 public static void blur(int[] in, int[] out, int width, int height, float radius) { 110 int widthMinus1 = width - 1; 111 int r = (int) radius; 112 int tableSize = 2 * r + 1; 113 int divide[] = new int[256 * tableSize]; 114 115 for (int i = 0; i < 256 * tableSize; i++) 116 divide[i] = i / tableSize; 117 118 int inIndex = 0; 119 120 for (int y = 0; y < height; y++) { 121 int outIndex = y; 122 int ta = 0, tr = 0, tg = 0, tb = 0; 123 124 for (int i = -r; i <= r; i++) { 125 int rgb = in[inIndex + clamp(i, 0, width - 1)]; 126 ta += (rgb >> 24) & 0xff; 127 tr += (rgb >> 16) & 0xff; 128 tg += (rgb >> 8) & 0xff; 129 tb += rgb & 0xff; 130 } 131 132 for (int x = 0; x < width; x++) { 133 out[outIndex] = (divide[ta] << 24) | (divide[tr] << 16) | (divide[tg] << 8) | divide[tb]; 134 135 int i1 = x + r + 1; 136 if (i1 > widthMinus1) 137 i1 = widthMinus1; 138 int i2 = x - r; 139 if (i2 < 0) 140 i2 = 0; 141 int rgb1 = in[inIndex + i1]; 142 int rgb2 = in[inIndex + i2]; 143 144 ta += ((rgb1 >> 24) & 0xff) - ((rgb2 >> 24) & 0xff); 145 tr += ((rgb1 & 0xff0000) - (rgb2 & 0xff0000)) >> 16; 146 tg += ((rgb1 & 0xff00) - (rgb2 & 0xff00)) >> 8; 147 tb += (rgb1 & 0xff) - (rgb2 & 0xff); 148 outIndex += height; 149 } 150 inIndex += width; 151 } 152 } 153 154 public static void blurFractional(int[] in, int[] out, int width, int height, float radius) { 155 radius -= (int) radius; 156 float f = 1.0f / (1 + 2 * radius); 157 int inIndex = 0; 158 159 for (int y = 0; y < height; y++) { 160 int outIndex = y; 161 162 out[outIndex] = in[0]; 163 outIndex += height; 164 for (int x = 1; x < width - 1; x++) { 165 int i = inIndex + x; 166 int rgb1 = in[i - 1]; 167 int rgb2 = in[i]; 168 int rgb3 = in[i + 1]; 169 170 int a1 = (rgb1 >> 24) & 0xff; 171 int r1 = (rgb1 >> 16) & 0xff; 172 int g1 = (rgb1 >> 8) & 0xff; 173 int b1 = rgb1 & 0xff; 174 int a2 = (rgb2 >> 24) & 0xff; 175 int r2 = (rgb2 >> 16) & 0xff; 176 int g2 = (rgb2 >> 8) & 0xff; 177 int b2 = rgb2 & 0xff; 178 int a3 = (rgb3 >> 24) & 0xff; 179 int r3 = (rgb3 >> 16) & 0xff; 180 int g3 = (rgb3 >> 8) & 0xff; 181 int b3 = rgb3 & 0xff; 182 a1 = a2 + (int) ((a1 + a3) * radius); 183 r1 = r2 + (int) ((r1 + r3) * radius); 184 g1 = g2 + (int) ((g1 + g3) * radius); 185 b1 = b2 + (int) ((b1 + b3) * radius); 186 a1 *= f; 187 r1 *= f; 188 g1 *= f; 189 b1 *= f; 190 out[outIndex] = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1; 191 outIndex += height; 192 } 193 out[outIndex] = in[width - 1]; 194 inIndex += width; 195 } 196 } 197 198 /** 199 * Set the horizontal size of the blur. Minimum hRadius value is 0. 200 * 201 * @param hRadius the radius of the blur in the horizontal direction 202 * @see #getHRadius 203 */ 204 public void setHRadius(float hRadius) { 205 this.hRadius = hRadius; 206 } 207 208 /** 209 * Get the horizontal size of the blur. 210 * 211 * @return the radius of the blur in the horizontal direction 212 * @see #setHRadius 213 */ 214 public float getHRadius() { 215 return hRadius; 216 } 217 218 /** 219 * Set the vertical size of the blur. Minimal vRadius value is 0. 220 * 221 * @param vRadius the radius of the blur in the vertical direction 222 * @see #getVRadius 223 */ 224 public void setVRadius(float vRadius) { 225 this.vRadius = vRadius; 226 } 227 228 /** 229 * Get the vertical size of the blur. 230 * 231 * @return the radius of the blur in the vertical direction 232 * @see #setVRadius 233 */ 234 public float getVRadius() { 235 return vRadius; 236 } 237 238 /** 239 * Set both the horizontal and vertical sizes of the blur. Minimum value is 240 * 0. 241 * 242 * @param radius the radius of the blur in both directions 243 * @see #getRadius 244 */ 245 public void setRadius(float radius) { 246 this.hRadius = this.vRadius = radius; 247 } 248 249 /** 250 * Get the size of the blur. 251 * 252 * @return the radius of the blur in the horizontal direction 253 * @see #setRadius 254 */ 255 public float getRadius() { 256 return hRadius; 257 } 258 259 /** 260 * Set the number of iterations the blur is performed. Minimum value is 0. 261 * 262 * @param iterations the number of iterations 263 * @see #getIterations 264 */ 265 public void setIterations(int iterations) { 266 this.iterations = iterations; 267 } 268 269 /** 270 * Get the number of iterations the blur is performed. 271 * 272 * @return the number of iterations 273 * @see #setIterations 274 */ 275 public int getIterations() { 276 return iterations; 277 } 278 279 @Override 280 public String toString() { 281 return "Blur/Box Blur..."; 282 } 283 284 /** 285 * Premultiply a block of pixels 286 */ 287 public static void premultiply(int[] p, int offset, int length) { 288 length += offset; 289 for (int i = offset; i < length; i++) { 290 int rgb = p[i]; 291 int a = (rgb >> 24) & 0xff; 292 int r = (rgb >> 16) & 0xff; 293 int g = (rgb >> 8) & 0xff; 294 int b = rgb & 0xff; 295 float f = a * (1.0f / 255.0f); 296 r *= f; 297 g *= f; 298 b *= f; 299 p[i] = (a << 24) | (r << 16) | (g << 8) | b; 300 } 301 } 302 303 /** 304 * Premultiply a block of pixels 305 */ 306 public static void unpremultiply(int[] p, int offset, int length) { 307 length += offset; 308 for (int i = offset; i < length; i++) { 309 int rgb = p[i]; 310 int a = (rgb >> 24) & 0xff; 311 int r = (rgb >> 16) & 0xff; 312 int g = (rgb >> 8) & 0xff; 313 int b = rgb & 0xff; 314 if (a != 0 && a != 255) { 315 float f = 255.0f / a; 316 r *= f; 317 g *= f; 318 b *= f; 319 if (r > 255) 320 r = 255; 321 if (g > 255) 322 g = 255; 323 if (b > 255) 324 b = 255; 325 p[i] = (a << 24) | (r << 16) | (g << 8) | b; 326 } 327 } 328 } 329 330 /** 331 * Clamp a value to an interval. 332 * 333 * @param a the lower clamp threshold 334 * @param b the upper clamp threshold 335 * @param x the input parameter 336 * @return the clamped value 337 */ 338 public static int clamp(int x, int a, int b) { 339 return (x < a) ? a : (x > b) ? b : x; 340 } 341}