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 on an image. The horizontal and vertical blurs can be specified separately 025 * and a number of iterations can be given which allows an approximation to Gaussian blur. 026 */ 027public class BoxBlurFilter extends AbstractBufferedImageOp { 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 BoxBlurFilter() { 038 } 039 040 /** 041 * Construct a BoxBlurFilter. 042 * @param hRadius the horizontal radius of blur 043 * @param vRadius the vertical radius of blur 044 * @param iterations the number of time to iterate the blur 045 */ 046 public BoxBlurFilter( float hRadius, float vRadius, int iterations ) { 047 this.hRadius = hRadius; 048 this.vRadius = vRadius; 049 this.iterations = iterations; 050 } 051 052 /** 053 * Set whether to premultiply the alpha channel. 054 * @param premultiplyAlpha true to premultiply the alpha 055 * @see #getPremultiplyAlpha 056 */ 057 public void setPremultiplyAlpha( boolean premultiplyAlpha ) { 058 this.premultiplyAlpha = premultiplyAlpha; 059 } 060 061 /** 062 * Get whether to premultiply the alpha channel. 063 * @return true to premultiply the alpha 064 * @see #setPremultiplyAlpha 065 */ 066 public boolean getPremultiplyAlpha() { 067 return premultiplyAlpha; 068 } 069 070 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 071 int width = src.getWidth(); 072 int height = src.getHeight(); 073 074 if ( dst == null ) 075 dst = createCompatibleDestImage( src, null ); 076 077 int[] inPixels = new int[width*height]; 078 int[] outPixels = new int[width*height]; 079 getRGB( src, 0, 0, width, height, inPixels ); 080 081 if ( premultiplyAlpha ) 082 ImageMath.premultiply( inPixels, 0, inPixels.length ); 083 for (int i = 0; i < iterations; i++ ) { 084 blur( inPixels, outPixels, width, height, hRadius ); 085 blur( outPixels, inPixels, height, width, vRadius ); 086 } 087 blurFractional( inPixels, outPixels, width, height, hRadius ); 088 blurFractional( outPixels, inPixels, height, width, vRadius ); 089 if ( premultiplyAlpha ) 090 ImageMath.unpremultiply( inPixels, 0, inPixels.length ); 091 092 setRGB( dst, 0, 0, width, height, inPixels ); 093 return dst; 094 } 095 096 /** 097 * Blur and transpose a block of ARGB pixels. 098 * @param in the input pixels 099 * @param out the output pixels 100 * @param width the width of the pixel array 101 * @param height the height of the pixel array 102 * @param radius the radius of blur 103 */ 104 public static void blur( int[] in, int[] out, int width, int height, float radius ) { 105 int widthMinus1 = width-1; 106 int r = (int)radius; 107 int tableSize = 2*r+1; 108 int divide[] = new int[256*tableSize]; 109 110 for ( int i = 0; i < 256*tableSize; i++ ) 111 divide[i] = i/tableSize; 112 113 int inIndex = 0; 114 115 for ( int y = 0; y < height; y++ ) { 116 int outIndex = y; 117 int ta = 0, tr = 0, tg = 0, tb = 0; 118 119 for ( int i = -r; i <= r; i++ ) { 120 int rgb = in[inIndex + ImageMath.clamp(i, 0, width-1)]; 121 ta += (rgb >> 24) & 0xff; 122 tr += (rgb >> 16) & 0xff; 123 tg += (rgb >> 8) & 0xff; 124 tb += rgb & 0xff; 125 } 126 127 for ( int x = 0; x < width; x++ ) { 128 out[ outIndex ] = (divide[ta] << 24) | (divide[tr] << 16) | (divide[tg] << 8) | divide[tb]; 129 130 int i1 = x+r+1; 131 if ( i1 > widthMinus1 ) 132 i1 = widthMinus1; 133 int i2 = x-r; 134 if ( i2 < 0 ) 135 i2 = 0; 136 int rgb1 = in[inIndex+i1]; 137 int rgb2 = in[inIndex+i2]; 138 139 ta += ((rgb1 >> 24) & 0xff)-((rgb2 >> 24) & 0xff); 140 tr += ((rgb1 & 0xff0000)-(rgb2 & 0xff0000)) >> 16; 141 tg += ((rgb1 & 0xff00)-(rgb2 & 0xff00)) >> 8; 142 tb += (rgb1 & 0xff)-(rgb2 & 0xff); 143 outIndex += height; 144 } 145 inIndex += width; 146 } 147 } 148 149 public static void blurFractional( int[] in, int[] out, int width, int height, float radius ) { 150 radius -= (int)radius; 151 float f = 1.0f/(1+2*radius); 152 int inIndex = 0; 153 154 for ( int y = 0; y < height; y++ ) { 155 int outIndex = y; 156 157 out[ outIndex ] = in[0]; 158 outIndex += height; 159 for ( int x = 1; x < width-1; x++ ) { 160 int i = inIndex+x; 161 int rgb1 = in[i-1]; 162 int rgb2 = in[i]; 163 int rgb3 = in[i+1]; 164 165 int a1 = (rgb1 >> 24) & 0xff; 166 int r1 = (rgb1 >> 16) & 0xff; 167 int g1 = (rgb1 >> 8) & 0xff; 168 int b1 = rgb1 & 0xff; 169 int a2 = (rgb2 >> 24) & 0xff; 170 int r2 = (rgb2 >> 16) & 0xff; 171 int g2 = (rgb2 >> 8) & 0xff; 172 int b2 = rgb2 & 0xff; 173 int a3 = (rgb3 >> 24) & 0xff; 174 int r3 = (rgb3 >> 16) & 0xff; 175 int g3 = (rgb3 >> 8) & 0xff; 176 int b3 = rgb3 & 0xff; 177 a1 = a2 + (int)((a1 + a3) * radius); 178 r1 = r2 + (int)((r1 + r3) * radius); 179 g1 = g2 + (int)((g1 + g3) * radius); 180 b1 = b2 + (int)((b1 + b3) * radius); 181 a1 *= f; 182 r1 *= f; 183 g1 *= f; 184 b1 *= f; 185 out[ outIndex ] = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1; 186 outIndex += height; 187 } 188 out[ outIndex ] = in[width-1]; 189 inIndex += width; 190 } 191 } 192 193 /** 194 * Set the horizontal size of the blur. 195 * @param hRadius the radius of the blur in the horizontal direction 196 * @min-value 0 197 * @see #getHRadius 198 */ 199 public void setHRadius(float hRadius) { 200 this.hRadius = hRadius; 201 } 202 203 /** 204 * Get the horizontal size of the blur. 205 * @return the radius of the blur in the horizontal direction 206 * @see #setHRadius 207 */ 208 public float getHRadius() { 209 return hRadius; 210 } 211 212 /** 213 * Set the vertical size of the blur. 214 * @param vRadius the radius of the blur in the vertical direction 215 * @min-value 0 216 * @see #getVRadius 217 */ 218 public void setVRadius(float vRadius) { 219 this.vRadius = vRadius; 220 } 221 222 /** 223 * Get the vertical size of the blur. 224 * @return the radius of the blur in the vertical direction 225 * @see #setVRadius 226 */ 227 public float getVRadius() { 228 return vRadius; 229 } 230 231 /** 232 * Set both the horizontal and vertical sizes of the blur. 233 * @param radius the radius of the blur in both directions 234 * @min-value 0 235 * @see #getRadius 236 */ 237 public void setRadius(float radius) { 238 this.hRadius = this.vRadius = radius; 239 } 240 241 /** 242 * Get the size of the blur. 243 * @return the radius of the blur in the horizontal direction 244 * @see #setRadius 245 */ 246 public float getRadius() { 247 return hRadius; 248 } 249 250 /** 251 * Set the number of iterations the blur is performed. 252 * @param iterations the number of iterations 253 * @min-value 0 254 * @see #getIterations 255 */ 256 public void setIterations(int iterations) { 257 this.iterations = iterations; 258 } 259 260 /** 261 * Get the number of iterations the blur is performed. 262 * @return the number of iterations 263 * @see #setIterations 264 */ 265 public int getIterations() { 266 return iterations; 267 } 268 269 public String toString() { 270 return "Blur/Box Blur..."; 271 } 272}