001/* 002 * $Id: FastBlurFilter.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 041/** 042 * <p>A fast blur filter can be used to blur pictures quickly. This filter is an 043 * implementation of the box blur algorithm. The blurs generated by this 044 * algorithm might show square artifacts, especially on pictures containing 045 * straight lines (rectangles, text, etc.) On most pictures though, the 046 * result will look very good.</p> 047 * <p>The force of the blur can be controlled with a radius and the 048 * default radius is 3. Since the blur clamps values on the edges of the 049 * source picture, you might need to provide a picture with empty borders 050 * to avoid artifacts at the edges. The performance of this filter are 051 * independent from the radius.</p> 052 * 053 * @author Romain Guy <romain.guy@mac.com> 054 */ 055public class FastBlurFilter extends AbstractFilter { 056 private final int radius; 057 058 /** 059 * <p>Creates a new blur filter with a default radius of 3.</p> 060 */ 061 public FastBlurFilter() { 062 this(3); 063 } 064 065 /** 066 * <p>Creates a new blur filter with the specified radius. If the radius 067 * is lower than 1, a radius of 1 will be used automatically.</p> 068 * 069 * @param radius the radius, in pixels, of the blur 070 */ 071 public FastBlurFilter(int radius) { 072 if (radius < 1) { 073 radius = 1; 074 } 075 076 this.radius = radius; 077 } 078 079 /** 080 * <p>Returns the radius used by this filter, in pixels.</p> 081 * 082 * @return the radius of the blur 083 */ 084 public int getRadius() { 085 return radius; 086 } 087 088 /** 089 * {@inheritDoc} 090 */ 091 @Override 092 public BufferedImage filter(BufferedImage src, BufferedImage dst) { 093 int width = src.getWidth(); 094 int height = src.getHeight(); 095 096 if (dst == null) { 097 dst = createCompatibleDestImage(src, null); 098 } 099 100 int[] srcPixels = new int[width * height]; 101 int[] dstPixels = new int[width * height]; 102 103 GraphicsUtilities.getPixels(src, 0, 0, width, height, srcPixels); 104 // horizontal pass 105 blur(srcPixels, dstPixels, width, height, radius); 106 // vertical pass 107 //noinspection SuspiciousNameCombination 108 blur(dstPixels, srcPixels, height, width, radius); 109 // the result is now stored in srcPixels due to the 2nd pass 110 GraphicsUtilities.setPixels(dst, 0, 0, width, height, srcPixels); 111 112 return dst; 113 } 114 115 /** 116 * <p>Blurs the source pixels into the destination pixels. The force of 117 * the blur is specified by the radius which must be greater than 0.</p> 118 * <p>The source and destination pixels arrays are expected to be in the 119 * INT_ARGB format.</p> 120 * <p>After this method is executed, dstPixels contains a transposed and 121 * filtered copy of srcPixels.</p> 122 * 123 * @param srcPixels the source pixels 124 * @param dstPixels the destination pixels 125 * @param width the width of the source picture 126 * @param height the height of the source picture 127 * @param radius the radius of the blur effect 128 */ 129 static void blur(int[] srcPixels, int[] dstPixels, 130 int width, int height, int radius) { 131 final int windowSize = radius * 2 + 1; 132 final int radiusPlusOne = radius + 1; 133 134 int sumAlpha; 135 int sumRed; 136 int sumGreen; 137 int sumBlue; 138 139 int srcIndex = 0; 140 int dstIndex; 141 int pixel; 142 143 int[] sumLookupTable = new int[256 * windowSize]; 144 for (int i = 0; i < sumLookupTable.length; i++) { 145 sumLookupTable[i] = i / windowSize; 146 } 147 148 int[] indexLookupTable = new int[radiusPlusOne]; 149 if (radius < width) { 150 for (int i = 0; i < indexLookupTable.length; i++) { 151 indexLookupTable[i] = i; 152 } 153 } else { 154 for (int i = 0; i < width; i++) { 155 indexLookupTable[i] = i; 156 } 157 for (int i = width; i < indexLookupTable.length; i++) { 158 indexLookupTable[i] = width - 1; 159 } 160 } 161 162 for (int y = 0; y < height; y++) { 163 sumAlpha = sumRed = sumGreen = sumBlue = 0; 164 dstIndex = y; 165 166 pixel = srcPixels[srcIndex]; 167 sumAlpha += radiusPlusOne * ((pixel >> 24) & 0xFF); 168 sumRed += radiusPlusOne * ((pixel >> 16) & 0xFF); 169 sumGreen += radiusPlusOne * ((pixel >> 8) & 0xFF); 170 sumBlue += radiusPlusOne * ( pixel & 0xFF); 171 172 for (int i = 1; i <= radius; i++) { 173 pixel = srcPixels[srcIndex + indexLookupTable[i]]; 174 sumAlpha += (pixel >> 24) & 0xFF; 175 sumRed += (pixel >> 16) & 0xFF; 176 sumGreen += (pixel >> 8) & 0xFF; 177 sumBlue += pixel & 0xFF; 178 } 179 180 for (int x = 0; x < width; x++) { 181 dstPixels[dstIndex] = sumLookupTable[sumAlpha] << 24 | 182 sumLookupTable[sumRed] << 16 | 183 sumLookupTable[sumGreen] << 8 | 184 sumLookupTable[sumBlue]; 185 dstIndex += height; 186 187 int nextPixelIndex = x + radiusPlusOne; 188 if (nextPixelIndex >= width) { 189 nextPixelIndex = width - 1; 190 } 191 192 int previousPixelIndex = x - radius; 193 if (previousPixelIndex < 0) { 194 previousPixelIndex = 0; 195 } 196 197 int nextPixel = srcPixels[srcIndex + nextPixelIndex]; 198 int previousPixel = srcPixels[srcIndex + previousPixelIndex]; 199 200 sumAlpha += (nextPixel >> 24) & 0xFF; 201 sumAlpha -= (previousPixel >> 24) & 0xFF; 202 203 sumRed += (nextPixel >> 16) & 0xFF; 204 sumRed -= (previousPixel >> 16) & 0xFF; 205 206 sumGreen += (nextPixel >> 8) & 0xFF; 207 sumGreen -= (previousPixel >> 8) & 0xFF; 208 209 sumBlue += nextPixel & 0xFF; 210 sumBlue -= previousPixel & 0xFF; 211 } 212 213 srcIndex += width; 214 } 215 } 216}