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}