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 "smart blur". i.e. a blur which blurs smotth parts of the image while preserving edges. 025 */ 026public class SmartBlurFilter extends AbstractBufferedImageOp { 027 028 private int hRadius = 5; 029 private int vRadius = 5; 030 private int threshold = 10; 031 032 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 033 int width = src.getWidth(); 034 int height = src.getHeight(); 035 036 if ( dst == null ) 037 dst = createCompatibleDestImage( src, null ); 038 039 int[] inPixels = new int[width*height]; 040 int[] outPixels = new int[width*height]; 041 getRGB( src, 0, 0, width, height, inPixels ); 042 043 Kernel kernel = GaussianFilter.makeKernel(hRadius); 044 thresholdBlur( kernel, inPixels, outPixels, width, height, true ); 045 thresholdBlur( kernel, outPixels, inPixels, height, width, true ); 046 047 setRGB( dst, 0, 0, width, height, inPixels ); 048 return dst; 049 } 050 051 /** 052 * Convolve with a kernel consisting of one row 053 */ 054 private void thresholdBlur(Kernel kernel, int[] inPixels, int[] outPixels, int width, int height, boolean alpha) { 055 int index = 0; 056 float[] matrix = kernel.getKernelData( null ); 057 int cols = kernel.getWidth(); 058 int cols2 = cols/2; 059 060 for (int y = 0; y < height; y++) { 061 int ioffset = y*width; 062 int outIndex = y; 063 for (int x = 0; x < width; x++) { 064 float r = 0, g = 0, b = 0, a = 0; 065 int moffset = cols2; 066 067 int rgb1 = inPixels[ioffset+x]; 068 int a1 = (rgb1 >> 24) & 0xff; 069 int r1 = (rgb1 >> 16) & 0xff; 070 int g1 = (rgb1 >> 8) & 0xff; 071 int b1 = rgb1 & 0xff; 072 float af = 0, rf = 0, gf = 0, bf = 0; 073 for (int col = -cols2; col <= cols2; col++) { 074 float f = matrix[moffset+col]; 075 076 if (f != 0) { 077 int ix = x+col; 078 if (!(0 <= ix && ix < width)) 079 ix = x; 080 int rgb2 = inPixels[ioffset+ix]; 081 int a2 = (rgb2 >> 24) & 0xff; 082 int r2 = (rgb2 >> 16) & 0xff; 083 int g2 = (rgb2 >> 8) & 0xff; 084 int b2 = rgb2 & 0xff; 085 086 int d; 087 d = a1-a2; 088 if ( d >= -threshold && d <= threshold ) { 089 a += f * a2; 090 af += f; 091 } 092 d = r1-r2; 093 if ( d >= -threshold && d <= threshold ) { 094 r += f * r2; 095 rf += f; 096 } 097 d = g1-g2; 098 if ( d >= -threshold && d <= threshold ) { 099 g += f * g2; 100 gf += f; 101 } 102 d = b1-b2; 103 if ( d >= -threshold && d <= threshold ) { 104 b += f * b2; 105 bf += f; 106 } 107 } 108 } 109 a = af == 0 ? a1 : a/af; 110 r = rf == 0 ? r1 : r/rf; 111 g = gf == 0 ? g1 : g/gf; 112 b = bf == 0 ? b1 : b/bf; 113 int ia = alpha ? PixelUtils.clamp((int)(a+0.5)) : 0xff; 114 int ir = PixelUtils.clamp((int)(r+0.5)); 115 int ig = PixelUtils.clamp((int)(g+0.5)); 116 int ib = PixelUtils.clamp((int)(b+0.5)); 117 outPixels[outIndex] = (ia << 24) | (ir << 16) | (ig << 8) | ib; 118 outIndex += height; 119 } 120 } 121 } 122 123 /** 124 * Set the horizontal size of the blur. 125 * @param hRadius the radius of the blur in the horizontal direction 126 * @min-value 0 127 * @see #getHRadius 128 */ 129 public void setHRadius(int hRadius) { 130 this.hRadius = hRadius; 131 } 132 133 /** 134 * Get the horizontal size of the blur. 135 * @return the radius of the blur in the horizontal direction 136 * @see #setHRadius 137 */ 138 public int getHRadius() { 139 return hRadius; 140 } 141 142 /** 143 * Set the vertical size of the blur. 144 * @param vRadius the radius of the blur in the vertical direction 145 * @min-value 0 146 * @see #getVRadius 147 */ 148 public void setVRadius(int vRadius) { 149 this.vRadius = vRadius; 150 } 151 152 /** 153 * Get the vertical size of the blur. 154 * @return the radius of the blur in the vertical direction 155 * @see #setVRadius 156 */ 157 public int getVRadius() { 158 return vRadius; 159 } 160 161 /** 162 * Set the radius of the effect. 163 * @param radius the radius 164 * @min-value 0 165 * @see #getRadius 166 */ 167 public void setRadius(int radius) { 168 this.hRadius = this.vRadius = radius; 169 } 170 171 /** 172 * Get the radius of the effect. 173 * @return the radius 174 * @see #setRadius 175 */ 176 public int getRadius() { 177 return hRadius; 178 } 179 180 /** 181 * Set the threshold value. 182 * @param threshold the threshold value 183 * @see #getThreshold 184 */ 185 public void setThreshold(int threshold) { 186 this.threshold = threshold; 187 } 188 189 /** 190 * Get the threshold value. 191 * @return the threshold value 192 * @see #setThreshold 193 */ 194 public int getThreshold() { 195 return threshold; 196 } 197 198 public String toString() { 199 return "Blur/Smart Blur..."; 200 } 201}