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.Rectangle; 018 import java.awt.RenderingHints; 019 import java.awt.geom.Point2D; 020 import java.awt.geom.Rectangle2D; 021 import java.awt.image.BufferedImage; 022 import java.awt.image.ColorModel; 023 024 import railo.runtime.engine.ThreadLocalPageContext; 025 import railo.runtime.exp.FunctionException; 026 import railo.runtime.exp.PageException; 027 import railo.runtime.img.ImageUtil; 028 import railo.runtime.type.KeyImpl; 029 import railo.runtime.type.Struct; 030 import railo.runtime.type.util.CollectionUtil; 031 032 /** 033 * A filter which performs a box blur with a different blur radius at each pixel. The radius can either be specified by 034 * providing a blur mask image or by overriding the blurRadiusAt method. 035 */ 036 public class VariableBlurFilter extends AbstractBufferedImageOp implements DynFiltering { 037 038 private int hRadius = 1; 039 private int vRadius = 1; 040 private int iterations = 1; 041 private BufferedImage blurMask; 042 private boolean premultiplyAlpha = true; 043 044 /** 045 * Set whether to premultiply the alpha channel. 046 * @param premultiplyAlpha true to premultiply the alpha 047 * @see #getPremultiplyAlpha 048 */ 049 public void setPremultiplyAlpha( boolean premultiplyAlpha ) { 050 this.premultiplyAlpha = premultiplyAlpha; 051 } 052 053 /** 054 * Get whether to premultiply the alpha channel. 055 * @return true to premultiply the alpha 056 * @see #setPremultiplyAlpha 057 */ 058 public boolean getPremultiplyAlpha() { 059 return premultiplyAlpha; 060 } 061 062 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 063 int width = src.getWidth(); 064 int height = src.getHeight(); 065 066 if ( dst == null ) 067 dst = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB ); 068 069 int[] inPixels = new int[width*height]; 070 int[] outPixels = new int[width*height]; 071 getRGB( src, 0, 0, width, height, inPixels ); 072 073 if ( premultiplyAlpha ) 074 ImageMath.premultiply( inPixels, 0, inPixels.length ); 075 for (int i = 0; i < iterations; i++ ) { 076 blur( inPixels, outPixels, width, height, hRadius, 1 ); 077 blur( outPixels, inPixels, height, width, vRadius, 2 ); 078 } 079 if ( premultiplyAlpha ) 080 ImageMath.unpremultiply( inPixels, 0, inPixels.length ); 081 082 setRGB( dst, 0, 0, width, height, inPixels ); 083 return dst; 084 } 085 086 public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) { 087 if ( dstCM == null ) 088 dstCM = src.getColorModel(); 089 return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(src.getWidth(), src.getHeight()), dstCM.isAlphaPremultiplied(), null); 090 } 091 092 public Rectangle2D getBounds2D( BufferedImage src ) { 093 return new Rectangle(0, 0, src.getWidth(), src.getHeight()); 094 } 095 096 public Point2D getPoint2D( Point2D srcPt, Point2D dstPt ) { 097 if ( dstPt == null ) 098 dstPt = new Point2D.Double(); 099 dstPt.setLocation( srcPt.getX(), srcPt.getY() ); 100 return dstPt; 101 } 102 103 public RenderingHints getRenderingHints() { 104 return null; 105 } 106 107 public void blur( int[] in, int[] out, int width, int height, int radius, int pass ) { 108 int widthMinus1 = width-1; 109 int[] r = new int[width]; 110 int[] g = new int[width]; 111 int[] b = new int[width]; 112 int[] a = new int[width]; 113 int[] mask = new int[width]; 114 115 int inIndex = 0; 116 117 for ( int y = 0; y < height; y++ ) { 118 int outIndex = y; 119 120 if ( blurMask != null ) { 121 if ( pass == 1 ) 122 getRGB( blurMask, 0, y, width, 1, mask ); 123 else 124 getRGB( blurMask, y, 0, 1, width, mask ); 125 } 126 127 for ( int x = 0; x < width; x++ ) { 128 int argb = in[inIndex+x]; 129 a[x] = (argb >> 24) & 0xff; 130 r[x] = (argb >> 16) & 0xff; 131 g[x] = (argb >> 8) & 0xff; 132 b[x] = argb & 0xff; 133 if ( x != 0 ) { 134 a[x] += a[x-1]; 135 r[x] += r[x-1]; 136 g[x] += g[x-1]; 137 b[x] += b[x-1]; 138 } 139 } 140 141 for ( int x = 0; x < width; x++ ) { 142 // Get the blur radius at x, y 143 int ra; 144 if ( blurMask != null ) { 145 if ( pass == 1 ) 146 ra = (int)((mask[x] & 0xff)*hRadius/255f); 147 else 148 ra = (int)((mask[x] & 0xff)*vRadius/255f); 149 } else { 150 if ( pass == 1 ) 151 ra = (int)(blurRadiusAt( x, y, width, height ) * hRadius); 152 else 153 ra = (int)(blurRadiusAt( y, x, height, width ) * vRadius); 154 } 155 156 int divisor = 2*ra+1; 157 int ta = 0, tr = 0, tg = 0, tb = 0; 158 int i1 = x+ra; 159 if ( i1 > widthMinus1 ) { 160 int f = i1-widthMinus1; 161 int l = widthMinus1; 162 ta += (a[l]-a[l-1]) * f; 163 tr += (r[l]-r[l-1]) * f; 164 tg += (g[l]-g[l-1]) * f; 165 tb += (b[l]-b[l-1]) * f; 166 i1 = widthMinus1; 167 } 168 int i2 = x-ra-1; 169 if ( i2 < 0 ) { 170 ta -= a[0] * i2; 171 tr -= r[0] * i2; 172 tg -= g[0] * i2; 173 tb -= b[0] * i2; 174 i2 = 0; 175 } 176 177 ta += a[i1] - a[i2]; 178 tr += r[i1] - r[i2]; 179 tg += g[i1] - g[i2]; 180 tb += b[i1] - b[i2]; 181 out[ outIndex ] = ((ta/divisor) << 24) | ((tr/divisor) << 16) | ((tg/divisor) << 8) | (tb/divisor); 182 183 outIndex += height; 184 } 185 inIndex += width; 186 } 187 } 188 189 /** 190 * Override this to get a different blur radius at eahc point. 191 * @param x the x coordinate 192 * @param y the y coordinate 193 * @param width the width of the image 194 * @param height the height of the image 195 * @return the blur radius 196 */ 197 protected float blurRadiusAt( int x, int y, int width, int height ) { 198 return (float)x/width; 199 } 200 201 /** 202 * Set the horizontal size of the blur. 203 * @param hRadius the radius of the blur in the horizontal direction 204 * @min-value 0 205 * @see #getHRadius 206 */ 207 public void setHRadius(int hRadius) { 208 this.hRadius = hRadius; 209 } 210 211 /** 212 * Get the horizontal size of the blur. 213 * @return the radius of the blur in the horizontal direction 214 * @see #setHRadius 215 */ 216 public int getHRadius() { 217 return hRadius; 218 } 219 220 /** 221 * Set the vertical size of the blur. 222 * @param vRadius the radius of the blur in the vertical direction 223 * @min-value 0 224 * @see #getVRadius 225 */ 226 public void setVRadius(int vRadius) { 227 this.vRadius = vRadius; 228 } 229 230 /** 231 * Get the vertical size of the blur. 232 * @return the radius of the blur in the vertical direction 233 * @see #setVRadius 234 */ 235 public int getVRadius() { 236 return vRadius; 237 } 238 239 /** 240 * Set the radius of the effect. 241 * @param radius the radius 242 * @min-value 0 243 * @see #getRadius 244 */ 245 public void setRadius(int radius) { 246 this.hRadius = this.vRadius = radius; 247 } 248 249 /** 250 * Get the radius of the effect. 251 * @return the radius 252 * @see #setRadius 253 */ 254 public int getRadius() { 255 return hRadius; 256 } 257 258 /** 259 * Set the number of iterations the blur is performed. 260 * @param iterations the number of iterations 261 * @min-value 0 262 * @see #getIterations 263 */ 264 public void setIterations(int iterations) { 265 this.iterations = iterations; 266 } 267 268 /** 269 * Get the number of iterations the blur is performed. 270 * @return the number of iterations 271 * @see #setIterations 272 */ 273 public int getIterations() { 274 return iterations; 275 } 276 277 /** 278 * Set the mask used to give the amount of blur at each point. 279 * @param blurMask the mask 280 * @see #getBlurMask 281 */ 282 public void setBlurMask(BufferedImage blurMask) { 283 this.blurMask = blurMask; 284 } 285 286 /** 287 * Get the mask used to give the amount of blur at each point. 288 * @return the mask 289 * @see #setBlurMask 290 */ 291 public BufferedImage getBlurMask() { 292 return blurMask; 293 } 294 295 public String toString() { 296 return "Blur/Variable Blur..."; 297 } 298 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 299 Object o; 300 if((o=parameters.removeEL(KeyImpl.init("PremultiplyAlpha")))!=null)setPremultiplyAlpha(ImageFilterUtil.toBooleanValue(o,"PremultiplyAlpha")); 301 if((o=parameters.removeEL(KeyImpl.init("Iterations")))!=null)setIterations(ImageFilterUtil.toIntValue(o,"Iterations")); 302 if((o=parameters.removeEL(KeyImpl.init("HRadius")))!=null)setHRadius(ImageFilterUtil.toIntValue(o,"HRadius")); 303 if((o=parameters.removeEL(KeyImpl.init("VRadius")))!=null)setVRadius(ImageFilterUtil.toIntValue(o,"VRadius")); 304 if((o=parameters.removeEL(KeyImpl.init("Radius")))!=null)setRadius(ImageFilterUtil.toIntValue(o,"Radius")); 305 if((o=parameters.removeEL(KeyImpl.init("BlurMask")))!=null)setBlurMask(ImageFilterUtil.toBufferedImage(o,"BlurMask")); 306 307 // check for arguments not supported 308 if(parameters.size()>0) { 309 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 [PremultiplyAlpha, Iterations, HRadius, VRadius, Radius, BlurMask]"); 310 } 311 312 return filter(src, dst); 313 } 314 }