001 /* 002 Copyright 2006 Jerry Huxtable 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 com.github.sarxos.webcam.util.jh; 018 019 import 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 */ 027 public 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. 200 * 201 * @param hRadius the radius of the blur in the horizontal direction 202 * @min-value 0 203 * @see #getHRadius 204 */ 205 public void setHRadius(float hRadius) { 206 this.hRadius = hRadius; 207 } 208 209 /** 210 * Get the horizontal size of the blur. 211 * 212 * @return the radius of the blur in the horizontal direction 213 * @see #setHRadius 214 */ 215 public float getHRadius() { 216 return hRadius; 217 } 218 219 /** 220 * Set the vertical size of the blur. 221 * 222 * @param vRadius the radius of the blur in the vertical direction 223 * @min-value 0 224 * @see #getVRadius 225 */ 226 public void setVRadius(float vRadius) { 227 this.vRadius = vRadius; 228 } 229 230 /** 231 * Get the vertical size of the blur. 232 * 233 * @return the radius of the blur in the vertical direction 234 * @see #setVRadius 235 */ 236 public float getVRadius() { 237 return vRadius; 238 } 239 240 /** 241 * Set both the horizontal and vertical sizes of the blur. 242 * 243 * @param radius the radius of the blur in both directions 244 * @min-value 0 245 * @see #getRadius 246 */ 247 public void setRadius(float radius) { 248 this.hRadius = this.vRadius = radius; 249 } 250 251 /** 252 * Get the size of the blur. 253 * 254 * @return the radius of the blur in the horizontal direction 255 * @see #setRadius 256 */ 257 public float getRadius() { 258 return hRadius; 259 } 260 261 /** 262 * Set the number of iterations the blur is performed. 263 * 264 * @param iterations the number of iterations 265 * @min-value 0 266 * @see #getIterations 267 */ 268 public void setIterations(int iterations) { 269 this.iterations = iterations; 270 } 271 272 /** 273 * Get the number of iterations the blur is performed. 274 * 275 * @return the number of iterations 276 * @see #setIterations 277 */ 278 public int getIterations() { 279 return iterations; 280 } 281 282 @Override 283 public String toString() { 284 return "Blur/Box Blur..."; 285 } 286 287 /** 288 * Premultiply a block of pixels 289 */ 290 public static void premultiply(int[] p, int offset, int length) { 291 length += offset; 292 for (int i = offset; i < length; i++) { 293 int rgb = p[i]; 294 int a = (rgb >> 24) & 0xff; 295 int r = (rgb >> 16) & 0xff; 296 int g = (rgb >> 8) & 0xff; 297 int b = rgb & 0xff; 298 float f = a * (1.0f / 255.0f); 299 r *= f; 300 g *= f; 301 b *= f; 302 p[i] = (a << 24) | (r << 16) | (g << 8) | b; 303 } 304 } 305 306 /** 307 * Premultiply a block of pixels 308 */ 309 public static void unpremultiply(int[] p, int offset, int length) { 310 length += offset; 311 for (int i = offset; i < length; i++) { 312 int rgb = p[i]; 313 int a = (rgb >> 24) & 0xff; 314 int r = (rgb >> 16) & 0xff; 315 int g = (rgb >> 8) & 0xff; 316 int b = rgb & 0xff; 317 if (a != 0 && a != 255) { 318 float f = 255.0f / a; 319 r *= f; 320 g *= f; 321 b *= f; 322 if (r > 255) 323 r = 255; 324 if (g > 255) 325 g = 255; 326 if (b > 255) 327 b = 255; 328 p[i] = (a << 24) | (r << 16) | (g << 8) | b; 329 } 330 } 331 } 332 333 /** 334 * Clamp a value to an interval. 335 * 336 * @param a the lower clamp threshold 337 * @param b the upper clamp threshold 338 * @param x the input parameter 339 * @return the clamped value 340 */ 341 public static int clamp(int x, int a, int b) { 342 return (x < a) ? a : (x > b) ? b : x; 343 } 344 }