001 /* 002 * 003 004 Licensed under the Apache License, Version 2.0 (the "License"); 005 you may not use this file except in compliance with the License. 006 You may obtain a copy of the License at 007 008 http://www.apache.org/licenses/LICENSE-2.0 009 010 Unless required by applicable law or agreed to in writing, software 011 distributed under the License is distributed on an "AS IS" BASIS, 012 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 See the License for the specific language governing permissions and 014 limitations under the License. 015 */ 016 017 package railo.runtime.img.filter;import java.awt.image.BufferedImage; 018 import java.awt.image.Kernel; 019 020 import railo.runtime.engine.ThreadLocalPageContext; 021 import railo.runtime.exp.FunctionException; 022 import railo.runtime.exp.PageException; 023 import railo.runtime.img.ImageUtil; 024 import railo.runtime.type.KeyImpl; 025 import railo.runtime.type.Struct; 026 import railo.runtime.type.util.CollectionUtil; 027 028 /** 029 * A filter which performs a "smart blur". i.e. a blur which blurs smotth parts of the image while preserving edges. 030 */ 031 public class SmartBlurFilter extends AbstractBufferedImageOp implements DynFiltering { 032 033 private int hRadius = 5; 034 private int vRadius = 5; 035 private int threshold = 10; 036 037 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 038 int width = src.getWidth(); 039 int height = src.getHeight(); 040 041 if ( dst == null ) 042 dst = createCompatibleDestImage( src, null ); 043 044 int[] inPixels = new int[width*height]; 045 int[] outPixels = new int[width*height]; 046 getRGB( src, 0, 0, width, height, inPixels ); 047 048 Kernel kernel = GaussianFilter.makeKernel(hRadius); 049 thresholdBlur( kernel, inPixels, outPixels, width, height, true ); 050 thresholdBlur( kernel, outPixels, inPixels, height, width, true ); 051 052 setRGB( dst, 0, 0, width, height, inPixels ); 053 return dst; 054 } 055 056 /** 057 * Convolve with a kernel consisting of one row 058 */ 059 private void thresholdBlur(Kernel kernel, int[] inPixels, int[] outPixels, int width, int height, boolean alpha) { 060 //int index = 0; 061 float[] matrix = kernel.getKernelData( null ); 062 int cols = kernel.getWidth(); 063 int cols2 = cols/2; 064 065 for (int y = 0; y < height; y++) { 066 int ioffset = y*width; 067 int outIndex = y; 068 for (int x = 0; x < width; x++) { 069 float r = 0, g = 0, b = 0, a = 0; 070 int moffset = cols2; 071 072 int rgb1 = inPixels[ioffset+x]; 073 int a1 = (rgb1 >> 24) & 0xff; 074 int r1 = (rgb1 >> 16) & 0xff; 075 int g1 = (rgb1 >> 8) & 0xff; 076 int b1 = rgb1 & 0xff; 077 float af = 0, rf = 0, gf = 0, bf = 0; 078 for (int col = -cols2; col <= cols2; col++) { 079 float f = matrix[moffset+col]; 080 081 if (f != 0) { 082 int ix = x+col; 083 if (!(0 <= ix && ix < width)) 084 ix = x; 085 int rgb2 = inPixels[ioffset+ix]; 086 int a2 = (rgb2 >> 24) & 0xff; 087 int r2 = (rgb2 >> 16) & 0xff; 088 int g2 = (rgb2 >> 8) & 0xff; 089 int b2 = rgb2 & 0xff; 090 091 int d; 092 d = a1-a2; 093 if ( d >= -threshold && d <= threshold ) { 094 a += f * a2; 095 af += f; 096 } 097 d = r1-r2; 098 if ( d >= -threshold && d <= threshold ) { 099 r += f * r2; 100 rf += f; 101 } 102 d = g1-g2; 103 if ( d >= -threshold && d <= threshold ) { 104 g += f * g2; 105 gf += f; 106 } 107 d = b1-b2; 108 if ( d >= -threshold && d <= threshold ) { 109 b += f * b2; 110 bf += f; 111 } 112 } 113 } 114 a = af == 0 ? a1 : a/af; 115 r = rf == 0 ? r1 : r/rf; 116 g = gf == 0 ? g1 : g/gf; 117 b = bf == 0 ? b1 : b/bf; 118 int ia = alpha ? PixelUtils.clamp((int)(a+0.5)) : 0xff; 119 int ir = PixelUtils.clamp((int)(r+0.5)); 120 int ig = PixelUtils.clamp((int)(g+0.5)); 121 int ib = PixelUtils.clamp((int)(b+0.5)); 122 outPixels[outIndex] = (ia << 24) | (ir << 16) | (ig << 8) | ib; 123 outIndex += height; 124 } 125 } 126 } 127 128 /** 129 * Set the horizontal size of the blur. 130 * @param hRadius the radius of the blur in the horizontal direction 131 * @min-value 0 132 * @see #getHRadius 133 */ 134 public void setHRadius(int hRadius) { 135 this.hRadius = hRadius; 136 } 137 138 /** 139 * Get the horizontal size of the blur. 140 * @return the radius of the blur in the horizontal direction 141 * @see #setHRadius 142 */ 143 public int getHRadius() { 144 return hRadius; 145 } 146 147 /** 148 * Set the vertical size of the blur. 149 * @param vRadius the radius of the blur in the vertical direction 150 * @min-value 0 151 * @see #getVRadius 152 */ 153 public void setVRadius(int vRadius) { 154 this.vRadius = vRadius; 155 } 156 157 /** 158 * Get the vertical size of the blur. 159 * @return the radius of the blur in the vertical direction 160 * @see #setVRadius 161 */ 162 public int getVRadius() { 163 return vRadius; 164 } 165 166 /** 167 * Set the radius of the effect. 168 * @param radius the radius 169 * @min-value 0 170 * @see #getRadius 171 */ 172 public void setRadius(int radius) { 173 this.hRadius = this.vRadius = radius; 174 } 175 176 /** 177 * Get the radius of the effect. 178 * @return the radius 179 * @see #setRadius 180 */ 181 public int getRadius() { 182 return hRadius; 183 } 184 185 /** 186 * Set the threshold value. 187 * @param threshold the threshold value 188 * @see #getThreshold 189 */ 190 public void setThreshold(int threshold) { 191 this.threshold = threshold; 192 } 193 194 /** 195 * Get the threshold value. 196 * @return the threshold value 197 * @see #setThreshold 198 */ 199 public int getThreshold() { 200 return threshold; 201 } 202 203 public String toString() { 204 return "Blur/Smart Blur..."; 205 } 206 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 207 Object o; 208 if((o=parameters.removeEL(KeyImpl.init("HRadius")))!=null)setHRadius(ImageFilterUtil.toIntValue(o,"HRadius")); 209 if((o=parameters.removeEL(KeyImpl.init("VRadius")))!=null)setVRadius(ImageFilterUtil.toIntValue(o,"VRadius")); 210 if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toIntValue(o,"Radius")); 211 if((o=parameters.removeEL(KeyImpl.init("Threshold")))!=null)setThreshold(ImageFilterUtil.toIntValue(o,"Threshold")); 212 213 // check for arguments not supported 214 if(parameters.size()>0) { 215 throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+CollectionUtil.getKeyList(parameters,", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [HRadius, VRadius, Radius, Threshold]"); 216 } 217 218 return filter(src, dst); 219 } 220 }