001/* 002 * $Id: GaussianBlurFilter.java 4082 2011-11-15 18:39:43Z kschaefe $ 003 * 004 * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). 005 * 006 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, 007 * Santa Clara, California 95054, U.S.A. All rights reserved. 008 * 009 * Copyright (c) 2006 Romain Guy <romain.guy@mac.com> 010 * All rights reserved. 011 * 012 * Redistribution and use in source and binary forms, with or without 013 * modification, are permitted provided that the following conditions 014 * are met: 015 * 1. Redistributions of source code must retain the above copyright 016 * notice, this list of conditions and the following disclaimer. 017 * 2. Redistributions in binary form must reproduce the above copyright 018 * notice, this list of conditions and the following disclaimer in the 019 * documentation and/or other materials provided with the distribution. 020 * 3. The name of the author may not be used to endorse or promote products 021 * derived from this software without specific prior written permission. 022 * 023 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 024 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 025 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 026 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 027 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 028 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 029 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 030 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 031 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 032 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 033 */ 034 035package org.jdesktop.swingx.image; 036 037import java.awt.image.BufferedImage; 038 039import org.jdesktop.swingx.util.GraphicsUtilities; 040 041public class GaussianBlurFilter extends AbstractFilter { 042 private final int radius; 043 044 /** 045 * <p>Creates a new blur filter with a default radius of 3.</p> 046 */ 047 public GaussianBlurFilter() { 048 this(3); 049 } 050 051 /** 052 * <p>Creates a new blur filter with the specified radius. If the radius 053 * is lower than 0, a radius of 0.1 will be used automatically.</p> 054 * 055 * @param radius the radius, in pixels, of the blur 056 */ 057 public GaussianBlurFilter(int radius) { 058 if (radius < 1) { 059 radius = 1; 060 } 061 062 this.radius = radius; 063 } 064 065 /** 066 * <p>Returns the radius used by this filter, in pixels.</p> 067 * 068 * @return the radius of the blur 069 */ 070 public int getRadius() { 071 return radius; 072 } 073 074 /** 075 * {@inheritDoc} 076 */ 077 @Override 078 public BufferedImage filter(BufferedImage src, BufferedImage dst) { 079 int width = src.getWidth(); 080 int height = src.getHeight(); 081 082 if (dst == null) { 083 dst = createCompatibleDestImage(src, null); 084 } 085 086 int[] srcPixels = new int[width * height]; 087 int[] dstPixels = new int[width * height]; 088 089 float[] kernel = createGaussianKernel(radius); 090 091 GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels); 092 // horizontal pass 093 blur(srcPixels, dstPixels, width, height, kernel, radius); 094 // vertical pass 095 blur(dstPixels, srcPixels, height, width, kernel, radius); 096 // the result is now stored in srcPixels due to the 2nd pass 097 GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels); 098 099 return dst; 100 } 101 102 /** 103 * <p>Blurs the source pixels into the destination pixels. The force of 104 * the blur is specified by the radius which must be greater than 0.</p> 105 * <p>The source and destination pixels arrays are expected to be in the 106 * INT_ARGB format.</p> 107 * <p>After this method is executed, dstPixels contains a transposed and 108 * filtered copy of srcPixels.</p> 109 * 110 * @param srcPixels the source pixels 111 * @param dstPixels the destination pixels 112 * @param width the width of the source picture 113 * @param height the height of the source picture 114 * @param kernel the kernel of the blur effect 115 * @param radius the radius of the blur effect 116 */ 117 static void blur(int[] srcPixels, int[] dstPixels, 118 int width, int height, 119 float[] kernel, int radius) { 120 float a; 121 float r; 122 float g; 123 float b; 124 125 int ca; 126 int cr; 127 int cg; 128 int cb; 129 130 for (int y = 0; y < height; y++) { 131 int index = y; 132 int offset = y * width; 133 134 for (int x = 0; x < width; x++) { 135 a = r = g = b = 0.0f; 136 137 for (int i = -radius; i <= radius; i++) { 138 int subOffset = x + i; 139 if (subOffset < 0 || subOffset >= width) { 140 subOffset = (x + width) % width; 141 } 142 143 int pixel = srcPixels[offset + subOffset]; 144 float blurFactor = kernel[radius + i]; 145 146 a += blurFactor * ((pixel >> 24) & 0xFF); 147 r += blurFactor * ((pixel >> 16) & 0xFF); 148 g += blurFactor * ((pixel >> 8) & 0xFF); 149 b += blurFactor * ((pixel ) & 0xFF); 150 } 151 152 ca = (int) (a + 0.5f); 153 cr = (int) (r + 0.5f); 154 cg = (int) (g + 0.5f); 155 cb = (int) (b + 0.5f); 156 157 dstPixels[index] = ((ca > 255 ? 255 : ca) << 24) | 158 ((cr > 255 ? 255 : cr) << 16) | 159 ((cg > 255 ? 255 : cg) << 8) | 160 (cb > 255 ? 255 : cb); 161 index += height; 162 } 163 } 164 } 165 166 static float[] createGaussianKernel(int radius) { 167 if (radius < 1) { 168 throw new IllegalArgumentException("Radius must be >= 1"); 169 } 170 171 float[] data = new float[radius * 2 + 1]; 172 173 float sigma = radius / 3.0f; 174 float twoSigmaSquare = 2.0f * sigma * sigma; 175 float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI); 176 float total = 0.0f; 177 178 for (int i = -radius; i <= radius; i++) { 179 float distance = i * i; 180 int index = i + radius; 181 data[index] = (float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot; 182 total += data[index]; 183 } 184 185 for (int i = 0; i < data.length; i++) { 186 data[i] /= total; 187 } 188 189 return data; 190 } 191}