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 }