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
024 * blurs can be specified separately and a number of iterations can be given
025 * which allows an approximation to 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                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}