package com.hphoto.image; import java.awt.image.*; /** * A filter which applies Gaussian blur to an image. This is a subclass of ConvolveFilter * which simply creates a kernel with a Gaussian distribution for blurring. * @author Jerry Huxtable */ public class GaussianFilter extends ConvolveFilter { /** * The blur radius. */ protected float radius; /** * The convolution kernel. */ protected Kernel kernel; /** * Construct a Gaussian filter. */ public GaussianFilter() { this(2); } /** * Construct a Gaussian filter. * @param radius blur radius in pixels */ public GaussianFilter(float radius) { setRadius(radius); } /** * Set the radius of the kernel, and hence the amount of blur. The bigger the radius, the longer this filter will take. * @param radius the radius of the blur in pixels. * @min-value 0 * @max-value 100+ * @see #getRadius */ public void setRadius(float radius) { this.radius = radius; kernel = makeKernel(radius); } /** * Get the radius of the kernel. * @return the radius * @see #setRadius */ public float getRadius() { return radius; } public BufferedImage filter( BufferedImage src, BufferedImage dst ) { int width = src.getWidth(); int height = src.getHeight(); if ( dst == null ) dst = createCompatibleDestImage( src, null ); int[] inPixels = new int[width*height]; int[] outPixels = new int[width*height]; src.getRGB( 0, 0, width, height, inPixels, 0, width ); if ( radius > 0 ) { convolveAndTranspose(kernel, inPixels, outPixels, width, height, alpha, alpha && premultiplyAlpha, false, CLAMP_EDGES); convolveAndTranspose(kernel, outPixels, inPixels, height, width, alpha, false, alpha && premultiplyAlpha, CLAMP_EDGES); } dst.setRGB( 0, 0, width, height, inPixels, 0, width ); return dst; } /** * Blur and transpose a block of ARGB pixels. * @param kernel the blur kernel * @param inPixels the input pixels * @param outPixels the output pixels * @param width the width of the pixel array * @param height the height of the pixel array * @param alpha whether to blur the alpha channel * @param edgeAction what to do at the edges */ public static void convolveAndTranspose(Kernel kernel, int[] inPixels, int[] outPixels, int width, int height, boolean alpha, boolean premultiply, boolean unpremultiply, int edgeAction) { float[] matrix = kernel.getKernelData( null ); int cols = kernel.getWidth(); int cols2 = cols/2; for (int y = 0; y < height; y++) { int index = y; int ioffset = y*width; for (int x = 0; x < width; x++) { float r = 0, g = 0, b = 0, a = 0; int moffset = cols2; for (int col = -cols2; col <= cols2; col++) { float f = matrix[moffset+col]; if (f != 0) { int ix = x+col; if ( ix < 0 ) { if ( edgeAction == CLAMP_EDGES ) ix = 0; else if ( edgeAction == WRAP_EDGES ) ix = (x+width) % width; } else if ( ix >= width) { if ( edgeAction == CLAMP_EDGES ) ix = width-1; else if ( edgeAction == WRAP_EDGES ) ix = (x+width) % width; } int rgb = inPixels[ioffset+ix]; int pa = (rgb >> 24) & 0xff; int pr = (rgb >> 16) & 0xff; int pg = (rgb >> 8) & 0xff; int pb = rgb & 0xff; if ( premultiply ) { float a255 = pa * (1.0f / 255.0f); pr *= a255; pg *= a255; pb *= a255; } a += f * pa; r += f * pr; g += f * pg; b += f * pb; } } if ( unpremultiply && a != 0 && a != 255 ) { float f = 255.0f / a; r *= f; g *= f; b *= f; } int ia = alpha ? PixelUtils.clamp((int)(a+0.5)) : 0xff; int ir = PixelUtils.clamp((int)(r+0.5)); int ig = PixelUtils.clamp((int)(g+0.5)); int ib = PixelUtils.clamp((int)(b+0.5)); outPixels[index] = (ia << 24) | (ir << 16) | (ig << 8) | ib; index += height; } } } /** * Make a Gaussian blur kernel. * @param radius the blur radius * @return the kernel */ public static Kernel makeKernel(float radius) { int r = (int)Math.ceil(radius); int rows = r*2+1; float[] matrix = new float[rows]; float sigma = radius/3; float sigma22 = 2*sigma*sigma; float sigmaPi2 = 2*ImageMath.PI*sigma; float sqrtSigmaPi2 = (float)Math.sqrt(sigmaPi2); float radius2 = radius*radius; float total = 0; int index = 0; for (int row = -r; row <= r; row++) { float distance = row*row; if (distance > radius2) matrix[index] = 0; else matrix[index] = (float)Math.exp(-(distance)/sigma22) / sqrtSigmaPi2; total += matrix[index]; index++; } for (int i = 0; i < rows; i++) matrix[i] /= total; return new Kernel(rows, 1, matrix); } public String toString() { return "Blur/Gaussian Blur..."; } }