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    }