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