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 019 import railo.runtime.engine.ThreadLocalPageContext; 020 import railo.runtime.exp.FunctionException; 021 import railo.runtime.exp.PageException; 022 import railo.runtime.img.ImageUtil; 023 import railo.runtime.type.KeyImpl; 024 import railo.runtime.type.List; 025 import railo.runtime.type.Struct; 026 /** 027 * A filter which performs a box blur on an image. The horizontal and vertical blurs can be specified separately 028 * and a number of iterations can be given which allows an approximation to Gaussian blur. 029 */ 030 public class BoxBlurFilter extends AbstractBufferedImageOp implements DynFiltering { 031 032 private float hRadius; 033 private float vRadius; 034 private int iterations = 1; 035 private boolean premultiplyAlpha = true; 036 037 /** 038 * Construct a default BoxBlurFilter. 039 */ 040 public BoxBlurFilter() { 041 } 042 043 /** 044 * Construct a BoxBlurFilter. 045 * @param hRadius the horizontal radius of blur 046 * @param vRadius the vertical radius of blur 047 * @param iterations the number of time to iterate the blur 048 */ 049 public BoxBlurFilter( float hRadius, float vRadius, int iterations ) { 050 this.hRadius = hRadius; 051 this.vRadius = vRadius; 052 this.iterations = iterations; 053 } 054 055 /** 056 * Set whether to premultiply the alpha channel. 057 * @param premultiplyAlpha true to premultiply the alpha 058 * @see #getPremultiplyAlpha 059 */ 060 public void setPremultiplyAlpha( boolean premultiplyAlpha ) { 061 this.premultiplyAlpha = premultiplyAlpha; 062 } 063 064 /** 065 * Get whether to premultiply the alpha channel. 066 * @return true to premultiply the alpha 067 * @see #setPremultiplyAlpha 068 */ 069 public boolean getPremultiplyAlpha() { 070 return premultiplyAlpha; 071 } 072 073 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 074 int width = src.getWidth(); 075 int height = src.getHeight(); 076 077 if ( dst == null ) 078 dst = createCompatibleDestImage( src, null ); 079 080 int[] inPixels = new int[width*height]; 081 int[] outPixels = new int[width*height]; 082 getRGB( src, 0, 0, width, height, inPixels ); 083 084 if ( premultiplyAlpha ) 085 ImageMath.premultiply( inPixels, 0, inPixels.length ); 086 for (int i = 0; i < iterations; i++ ) { 087 blur( inPixels, outPixels, width, height, hRadius ); 088 blur( outPixels, inPixels, height, width, vRadius ); 089 } 090 blurFractional( inPixels, outPixels, width, height, hRadius ); 091 blurFractional( outPixels, inPixels, height, width, vRadius ); 092 if ( premultiplyAlpha ) 093 ImageMath.unpremultiply( inPixels, 0, inPixels.length ); 094 095 setRGB( dst, 0, 0, width, height, inPixels ); 096 return dst; 097 } 098 099 /** 100 * Blur and transpose a block of ARGB pixels. 101 * @param in the input pixels 102 * @param out the output pixels 103 * @param width the width of the pixel array 104 * @param height the height of the pixel array 105 * @param radius the radius of blur 106 */ 107 public static void blur( int[] in, int[] out, int width, int height, float radius ) { 108 int widthMinus1 = width-1; 109 int r = (int)radius; 110 int tableSize = 2*r+1; 111 int divide[] = new int[256*tableSize]; 112 113 for ( int i = 0; i < 256*tableSize; i++ ) 114 divide[i] = i/tableSize; 115 116 int inIndex = 0; 117 118 for ( int y = 0; y < height; y++ ) { 119 int outIndex = y; 120 int ta = 0, tr = 0, tg = 0, tb = 0; 121 122 for ( int i = -r; i <= r; i++ ) { 123 int rgb = in[inIndex + ImageMath.clamp(i, 0, width-1)]; 124 ta += (rgb >> 24) & 0xff; 125 tr += (rgb >> 16) & 0xff; 126 tg += (rgb >> 8) & 0xff; 127 tb += rgb & 0xff; 128 } 129 130 for ( int x = 0; x < width; x++ ) { 131 out[ outIndex ] = (divide[ta] << 24) | (divide[tr] << 16) | (divide[tg] << 8) | divide[tb]; 132 133 int i1 = x+r+1; 134 if ( i1 > widthMinus1 ) 135 i1 = widthMinus1; 136 int i2 = x-r; 137 if ( i2 < 0 ) 138 i2 = 0; 139 int rgb1 = in[inIndex+i1]; 140 int rgb2 = in[inIndex+i2]; 141 142 ta += ((rgb1 >> 24) & 0xff)-((rgb2 >> 24) & 0xff); 143 tr += ((rgb1 & 0xff0000)-(rgb2 & 0xff0000)) >> 16; 144 tg += ((rgb1 & 0xff00)-(rgb2 & 0xff00)) >> 8; 145 tb += (rgb1 & 0xff)-(rgb2 & 0xff); 146 outIndex += height; 147 } 148 inIndex += width; 149 } 150 } 151 152 public static void blurFractional( int[] in, int[] out, int width, int height, float radius ) { 153 radius -= (int)radius; 154 float f = 1.0f/(1+2*radius); 155 int inIndex = 0; 156 157 for ( int y = 0; y < height; y++ ) { 158 int outIndex = y; 159 160 out[ outIndex ] = in[0]; 161 outIndex += height; 162 for ( int x = 1; x < width-1; x++ ) { 163 int i = inIndex+x; 164 int rgb1 = in[i-1]; 165 int rgb2 = in[i]; 166 int rgb3 = in[i+1]; 167 168 int a1 = (rgb1 >> 24) & 0xff; 169 int r1 = (rgb1 >> 16) & 0xff; 170 int g1 = (rgb1 >> 8) & 0xff; 171 int b1 = rgb1 & 0xff; 172 int a2 = (rgb2 >> 24) & 0xff; 173 int r2 = (rgb2 >> 16) & 0xff; 174 int g2 = (rgb2 >> 8) & 0xff; 175 int b2 = rgb2 & 0xff; 176 int a3 = (rgb3 >> 24) & 0xff; 177 int r3 = (rgb3 >> 16) & 0xff; 178 int g3 = (rgb3 >> 8) & 0xff; 179 int b3 = rgb3 & 0xff; 180 a1 = a2 + (int)((a1 + a3) * radius); 181 r1 = r2 + (int)((r1 + r3) * radius); 182 g1 = g2 + (int)((g1 + g3) * radius); 183 b1 = b2 + (int)((b1 + b3) * radius); 184 a1 *= f; 185 r1 *= f; 186 g1 *= f; 187 b1 *= f; 188 out[ outIndex ] = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1; 189 outIndex += height; 190 } 191 out[ outIndex ] = in[width-1]; 192 inIndex += width; 193 } 194 } 195 196 /** 197 * Set the horizontal size of the blur. 198 * @param hRadius the radius of the blur in the horizontal direction 199 * @min-value 0 200 * @see #getHRadius 201 */ 202 public void setHRadius(float hRadius) { 203 this.hRadius = hRadius; 204 } 205 206 /** 207 * Get the horizontal size of the blur. 208 * @return the radius of the blur in the horizontal direction 209 * @see #setHRadius 210 */ 211 public float getHRadius() { 212 return hRadius; 213 } 214 215 /** 216 * Set the vertical size of the blur. 217 * @param vRadius the radius of the blur in the vertical direction 218 * @min-value 0 219 * @see #getVRadius 220 */ 221 public void setVRadius(float vRadius) { 222 this.vRadius = vRadius; 223 } 224 225 /** 226 * Get the vertical size of the blur. 227 * @return the radius of the blur in the vertical direction 228 * @see #setVRadius 229 */ 230 public float getVRadius() { 231 return vRadius; 232 } 233 234 /** 235 * Set both the horizontal and vertical sizes of the blur. 236 * @param radius the radius of the blur in both directions 237 * @min-value 0 238 * @see #getRadius 239 */ 240 public void setRadius(float radius) { 241 this.hRadius = this.vRadius = radius; 242 } 243 244 /** 245 * Get the size of the blur. 246 * @return the radius of the blur in the horizontal direction 247 * @see #setRadius 248 */ 249 public float getRadius() { 250 return hRadius; 251 } 252 253 /** 254 * Set the number of iterations the blur is performed. 255 * @param iterations the number of iterations 256 * @min-value 0 257 * @see #getIterations 258 */ 259 public void setIterations(int iterations) { 260 this.iterations = iterations; 261 } 262 263 /** 264 * Get the number of iterations the blur is performed. 265 * @return the number of iterations 266 * @see #setIterations 267 */ 268 public int getIterations() { 269 return iterations; 270 } 271 272 public String toString() { 273 return "Blur/Box Blur..."; 274 } 275 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 276 Object o; 277 if((o=parameters.removeEL(KeyImpl.init("PremultiplyAlpha")))!=null)setPremultiplyAlpha(ImageFilterUtil.toBooleanValue(o,"PremultiplyAlpha")); 278 if((o=parameters.removeEL(KeyImpl.init("Iterations")))!=null)setIterations(ImageFilterUtil.toIntValue(o,"Iterations")); 279 if((o=parameters.removeEL(KeyImpl.init("HRadius")))!=null)setHRadius(ImageFilterUtil.toFloatValue(o,"HRadius")); 280 if((o=parameters.removeEL(KeyImpl.init("VRadius")))!=null)setVRadius(ImageFilterUtil.toFloatValue(o,"VRadius")); 281 if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toFloatValue(o,"Radius")); 282 283 // check for arguments not supported 284 if(parameters.size()>0) { 285 throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+List.arrayToList(parameters.keysAsString(),", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported [PremultiplyAlpha, Iterations, HRadius, VRadius, Radius]"); 286 } 287 288 return filter(src, dst); 289 } 290 }