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. 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 }