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.jhlabs.image;
018
019import java.awt.*;
020import java.awt.image.*;
021import java.awt.geom.*;
022
023/**
024 * A filter which performs a box blur with a different blur radius at each pixel. The radius can either be specified by
025 * providing a blur mask image or by overriding the blurRadiusAt method.
026 */
027public class VariableBlurFilter extends AbstractBufferedImageOp {
028
029        private int hRadius = 1;
030        private int vRadius = 1;
031        private int iterations = 1;
032        private BufferedImage blurMask;
033        private boolean premultiplyAlpha = true;
034
035    /**
036     * Set whether to premultiply the alpha channel.
037     * @param premultiplyAlpha true to premultiply the alpha
038     * @see #getPremultiplyAlpha
039     */
040        public void setPremultiplyAlpha( boolean premultiplyAlpha ) {
041                this.premultiplyAlpha = premultiplyAlpha;
042        }
043
044    /**
045     * Get whether to premultiply the alpha channel.
046     * @return true to premultiply the alpha
047     * @see #setPremultiplyAlpha
048     */
049        public boolean getPremultiplyAlpha() {
050                return premultiplyAlpha;
051        }
052
053    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
054                int width = src.getWidth();
055        int height = src.getHeight();
056
057        if ( dst == null )
058            dst = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB );
059
060        int[] inPixels = new int[width*height];
061        int[] outPixels = new int[width*height];
062        getRGB( src, 0, 0, width, height, inPixels );
063
064        if ( premultiplyAlpha )
065                        ImageMath.premultiply( inPixels, 0, inPixels.length );
066        for (int i = 0; i < iterations; i++ ) {
067            blur( inPixels, outPixels, width, height, hRadius, 1 );
068            blur( outPixels, inPixels, height, width, vRadius, 2 );
069        }
070        if ( premultiplyAlpha )
071                        ImageMath.unpremultiply( inPixels, 0, inPixels.length );
072
073        setRGB( dst, 0, 0, width, height, inPixels );
074        return dst;
075    }
076
077    public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) {
078        if ( dstCM == null )
079            dstCM = src.getColorModel();
080        return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(src.getWidth(), src.getHeight()), dstCM.isAlphaPremultiplied(), null);
081    }
082    
083    public Rectangle2D getBounds2D( BufferedImage src ) {
084        return new Rectangle(0, 0, src.getWidth(), src.getHeight());
085    }
086    
087    public Point2D getPoint2D( Point2D srcPt, Point2D dstPt ) {
088        if ( dstPt == null )
089            dstPt = new Point2D.Double();
090        dstPt.setLocation( srcPt.getX(), srcPt.getY() );
091        return dstPt;
092    }
093
094    public RenderingHints getRenderingHints() {
095        return null;
096    }
097
098    public void blur( int[] in, int[] out, int width, int height, int radius, int pass ) {
099        int widthMinus1 = width-1;
100        int[] r = new int[width];
101        int[] g = new int[width];
102        int[] b = new int[width];
103        int[] a = new int[width];
104        int[] mask = new int[width];
105
106                int inIndex = 0;
107
108        for ( int y = 0; y < height; y++ ) {
109            int outIndex = y;
110
111                        if ( blurMask != null ) {
112                                if ( pass == 1 )
113                                        getRGB( blurMask, 0, y, width, 1, mask );
114                                else
115                                        getRGB( blurMask, y, 0, 1, width, mask );
116                        }
117
118            for ( int x = 0; x < width; x++ ) {
119                                int argb = in[inIndex+x];
120                                a[x] = (argb >> 24) & 0xff;
121                r[x] = (argb >> 16) & 0xff;
122                g[x] = (argb >> 8) & 0xff;
123                b[x] = argb & 0xff;
124                if ( x != 0 ) {
125                    a[x] += a[x-1];
126                    r[x] += r[x-1];
127                    g[x] += g[x-1];
128                    b[x] += b[x-1];
129                }
130                        }
131
132            for ( int x = 0; x < width; x++ ) {
133                                // Get the blur radius at x, y
134                                int ra;
135                                if ( blurMask != null ) {
136                                        if ( pass == 1 )
137                                                ra = (int)((mask[x] & 0xff)*hRadius/255f);
138                                        else
139                                                ra = (int)((mask[x] & 0xff)*vRadius/255f);
140                                } else {
141                                        if ( pass == 1 )
142                                                ra = (int)(blurRadiusAt( x, y, width, height ) * hRadius);
143                                        else
144                                                ra = (int)(blurRadiusAt( y, x, height, width ) * vRadius);
145                                }
146
147                int divisor = 2*ra+1;
148                int ta = 0, tr = 0, tg = 0, tb = 0;
149                                int i1 = x+ra;
150                if ( i1 > widthMinus1 ) {
151                    int f = i1-widthMinus1;
152                                        int l = widthMinus1;
153                                        ta += (a[l]-a[l-1]) * f;
154                                        tr += (r[l]-r[l-1]) * f;
155                                        tg += (g[l]-g[l-1]) * f;
156                                        tb += (b[l]-b[l-1]) * f;
157                                        i1 = widthMinus1;
158                }
159                                int i2 = x-ra-1;
160                if ( i2 < 0 ) {
161                                        ta -= a[0] * i2;
162                                        tr -= r[0] * i2;
163                                        tg -= g[0] * i2;
164                                        tb -= b[0] * i2;
165                    i2 = 0;
166                                }
167                
168                ta += a[i1] - a[i2];
169                tr += r[i1] - r[i2];
170                tg += g[i1] - g[i2];
171                tb += b[i1] - b[i2];
172                out[ outIndex ] = ((ta/divisor) << 24) | ((tr/divisor) << 16) | ((tg/divisor) << 8) | (tb/divisor);
173
174                outIndex += height;
175            }
176                        inIndex += width;
177        }
178    }
179    
180        /**
181     * Override this to get a different blur radius at eahc point.
182     * @param x the x coordinate
183     * @param y the y coordinate
184     * @param width the width of the image
185     * @param height the height of the image
186     * @return the blur radius
187     */
188        protected float blurRadiusAt( int x, int y, int width, int height ) {
189                return (float)x/width;
190        }
191
192        /**
193         * Set the horizontal size of the blur.
194         * @param hRadius the radius of the blur in the horizontal direction
195     * @min-value 0
196     * @see #getHRadius
197         */
198        public void setHRadius(int hRadius) {
199                this.hRadius = hRadius;
200        }
201        
202        /**
203         * Get the horizontal size of the blur.
204         * @return the radius of the blur in the horizontal direction
205     * @see #setHRadius
206         */
207        public int getHRadius() {
208                return hRadius;
209        }
210        
211        /**
212         * Set the vertical size of the blur.
213         * @param vRadius the radius of the blur in the vertical direction
214     * @min-value 0
215     * @see #getVRadius
216         */
217        public void setVRadius(int vRadius) {
218                this.vRadius = vRadius;
219        }
220        
221        /**
222         * Get the vertical size of the blur.
223         * @return the radius of the blur in the vertical direction
224     * @see #setVRadius
225         */
226        public int getVRadius() {
227                return vRadius;
228        }
229        
230        /**
231         * Set the radius of the effect.
232         * @param radius the radius
233     * @min-value 0
234     * @see #getRadius
235         */
236        public void setRadius(int radius) {
237                this.hRadius = this.vRadius = radius;
238        }
239        
240        /**
241         * Get the radius of the effect.
242         * @return the radius
243     * @see #setRadius
244         */
245        public int getRadius() {
246                return hRadius;
247        }
248        
249        /**
250         * Set the number of iterations the blur is performed.
251         * @param iterations the number of iterations
252     * @min-value 0
253     * @see #getIterations
254         */
255        public void setIterations(int iterations) {
256                this.iterations = iterations;
257        }
258        
259        /**
260         * Get the number of iterations the blur is performed.
261         * @return the number of iterations
262     * @see #setIterations
263         */
264        public int getIterations() {
265                return iterations;
266        }
267        
268        /**
269         * Set the mask used to give the amount of blur at each point.
270         * @param blurMask the mask
271     * @see #getBlurMask
272         */
273        public void setBlurMask(BufferedImage blurMask) {
274                this.blurMask = blurMask;
275        }
276        
277        /**
278         * Get the mask used to give the amount of blur at each point.
279         * @return the mask
280     * @see #setBlurMask
281         */
282        public BufferedImage getBlurMask() {
283                return blurMask;
284        }
285        
286        public String toString() {
287                return "Blur/Variable Blur...";
288        }
289}