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 /** 028 * A filter which renders "glints" on bright parts of the image. 029 */ 030 public class GlintFilter extends AbstractBufferedImageOp implements DynFiltering { 031 032 private float threshold = 1.0f; 033 private int length = 5; 034 private float blur = 0.0f; 035 private float amount = 0.1f; 036 private boolean glintOnly = false; 037 private Colormap colormap = new LinearColormap( 0xffffffff, 0xff000000 ); 038 039 public GlintFilter() { 040 } 041 042 /** 043 * Set the threshold value. 044 * @param threshold the threshold value 045 * @see #getThreshold 046 */ 047 public void setThreshold( float threshold ) { 048 this.threshold = threshold; 049 } 050 051 /** 052 * Get the threshold value. 053 * @return the threshold value 054 * @see #setThreshold 055 */ 056 public float getThreshold() { 057 return threshold; 058 } 059 060 /** 061 * Set the amount of glint. 062 * @param amount the amount 063 * @min-value 0 064 * @max-value 1 065 * @see #getAmount 066 */ 067 public void setAmount( float amount ) { 068 this.amount = amount; 069 } 070 071 /** 072 * Get the amount of glint. 073 * @return the amount 074 * @see #setAmount 075 */ 076 public float getAmount() { 077 return amount; 078 } 079 080 /** 081 * Set the length of the stars. 082 * @param length the length 083 * @see #getLength 084 */ 085 public void setLength( int length ) { 086 this.length = length; 087 } 088 089 /** 090 * Get the length of the stars. 091 * @return the length 092 * @see #setLength 093 */ 094 public int getLength() { 095 return length; 096 } 097 098 /** 099 * Set the blur that is applied before thresholding. 100 * @param blur the blur radius 101 * @see #getBlur 102 */ 103 public void setBlur(float blur) { 104 this.blur = blur; 105 } 106 107 /** 108 * Set the blur that is applied before thresholding. 109 * @return the blur radius 110 * @see #setBlur 111 */ 112 public float getBlur() { 113 return blur; 114 } 115 116 /** 117 * Set whether to render the stars and the image or only the stars. 118 * @param glintOnly true to render only stars 119 * @see #getGlintOnly 120 */ 121 public void setGlintOnly(boolean glintOnly) { 122 this.glintOnly = glintOnly; 123 } 124 125 /** 126 * Get whether to render the stars and the image or only the stars. 127 * @return true to render only stars 128 * @see #setGlintOnly 129 */ 130 public boolean getGlintOnly() { 131 return glintOnly; 132 } 133 134 /** 135 * Set the colormap to be used for the filter. 136 * @param colormap the colormap 137 * @see #getColormap 138 */ 139 public void setColormap(Colormap colormap) { 140 this.colormap = colormap; 141 } 142 143 /** 144 * Get the colormap to be used for the filter. 145 * @return the colormap 146 * @see #setColormap 147 */ 148 public Colormap getColormap() { 149 return colormap; 150 } 151 152 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 153 int width = src.getWidth(); 154 int height = src.getHeight(); 155 int[] pixels = new int[width]; 156 int length2 = (int)(length / 1.414f); 157 int[] colors = new int[length+1]; 158 int[] colors2 = new int[length2+1]; 159 160 if ( colormap != null ) { 161 for (int i = 0; i <= length; i++) { 162 int argb = colormap.getColor( (float)i/length ); 163 int r = (argb >> 16) & 0xff; 164 int g = (argb >> 8) & 0xff; 165 int b = argb & 0xff; 166 argb = (argb & 0xff000000) | ((int)(amount*r) << 16) | ((int)(amount*g) << 8) | (int)(amount*b); 167 colors[i] = argb; 168 } 169 for (int i = 0; i <= length2; i++) { 170 int argb = colormap.getColor( (float)i/length2 ); 171 int r = (argb >> 16) & 0xff; 172 int g = (argb >> 8) & 0xff; 173 int b = argb & 0xff; 174 argb = (argb & 0xff000000) | ((int)(amount*r) << 16) | ((int)(amount*g) << 8) | (int)(amount*b); 175 colors2[i] = argb; 176 } 177 } 178 179 BufferedImage mask = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 180 181 int threshold3 = (int)(threshold*3*255); 182 for ( int y = 0; y < height; y++ ) { 183 getRGB( src, 0, y, width, 1, pixels ); 184 for ( int x = 0; x < width; x++ ) { 185 int rgb = pixels[x]; 186 int a = rgb & 0xff000000; 187 int r = (rgb >> 16) & 0xff; 188 int g = (rgb >> 8) & 0xff; 189 int b = rgb & 0xff; 190 int l = r + g + b; 191 if (l < threshold3) 192 pixels[x] = 0xff000000; 193 else { 194 l /= 3; 195 pixels[x] = a | (l << 16) | (l << 8) | l; 196 } 197 } 198 setRGB( mask, 0, y, width, 1, pixels ); 199 } 200 201 if ( blur != 0 ) 202 mask = new GaussianFilter(blur).filter( mask, (BufferedImage)null ); 203 204 if ( dst == null ) 205 dst = createCompatibleDestImage( src, null ); 206 int[] dstPixels; 207 if ( glintOnly ) 208 dstPixels = new int[width*height]; 209 else 210 dstPixels = getRGB( src, 0, 0, width, height, null );//FIXME - only need 2*length 211 212 for ( int y = 0; y < height; y++ ) { 213 int index = y*width; 214 getRGB( mask, 0, y, width, 1, pixels ); 215 int ymin = Math.max( y-length, 0 )-y; 216 int ymax = Math.min( y+length, height-1 )-y; 217 int ymin2 = Math.max( y-length2, 0 )-y; 218 int ymax2 = Math.min( y+length2, height-1 )-y; 219 for ( int x = 0; x < width; x++ ) { 220 if ( (pixels[x] & 0xff) > threshold*255 ) { 221 int xmin = Math.max( x-length, 0 )-x; 222 int xmax = Math.min( x+length, width-1 )-x; 223 int xmin2 = Math.max( x-length2, 0 )-x; 224 int xmax2 = Math.min( x+length2, width-1 )-x; 225 226 // Horizontal 227 for ( int i = 0, k = 0; i <= xmax; i++, k++ ) 228 dstPixels[index+i] = PixelUtils.combinePixels( dstPixels[index+i], colors[k], PixelUtils.ADD ); 229 for ( int i = -1, k = 1; i >= xmin; i--, k++ ) 230 dstPixels[index+i] = PixelUtils.combinePixels( dstPixels[index+i], colors[k], PixelUtils.ADD ); 231 // Vertical 232 for ( int i = 1, j = index+width, k = 0; i <= ymax; i++, j += width, k++ ) 233 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors[k], PixelUtils.ADD ); 234 for ( int i = -1, j = index-width, k = 0; i >= ymin; i--, j -= width, k++ ) 235 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors[k], PixelUtils.ADD ); 236 237 // Diagonals 238 int xymin = Math.max( xmin2, ymin2 ); 239 int xymax = Math.min( xmax2, ymax2 ); 240 // SE 241 int count = Math.min( xmax2, ymax2 ); 242 for ( int i = 1, j = index+width+1, k = 0; i <= count; i++, j += width+1, k++ ) 243 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors2[k], PixelUtils.ADD ); 244 // NW 245 count = Math.min( -xmin2, -ymin2 ); 246 for ( int i = 1, j = index-width-1, k = 0; i <= count; i++, j -= width+1, k++ ) 247 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors2[k], PixelUtils.ADD ); 248 // NE 249 count = Math.min( xmax2, -ymin2 ); 250 for ( int i = 1, j = index-width+1, k = 0; i <= count; i++, j += -width+1, k++ ) 251 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors2[k], PixelUtils.ADD ); 252 // SW 253 count = Math.min( -xmin2, ymax2 ); 254 for ( int i = 1, j = index+width-1, k = 0; i <= count; i++, j += width-1, k++ ) 255 dstPixels[j] = PixelUtils.combinePixels( dstPixels[j], colors2[k], PixelUtils.ADD ); 256 } 257 index++; 258 } 259 } 260 setRGB( dst, 0, 0, width, height, dstPixels ); 261 262 return dst; 263 } 264 265 public String toString() { 266 return "Effects/Glint..."; 267 } 268 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 269 Object o; 270 if((o=parameters.removeEL(KeyImpl.init("Colormap")))!=null)setColormap(ImageFilterUtil.toColormap(o,"Colormap")); 271 if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount")); 272 if((o=parameters.removeEL(KeyImpl.init("Blur")))!=null)setBlur(ImageFilterUtil.toFloatValue(o,"Blur")); 273 if((o=parameters.removeEL(KeyImpl.init("GlintOnly")))!=null)setGlintOnly(ImageFilterUtil.toBooleanValue(o,"GlintOnly")); 274 if((o=parameters.removeEL(KeyImpl.init("Length")))!=null)setLength(ImageFilterUtil.toIntValue(o,"Length")); 275 if((o=parameters.removeEL(KeyImpl.init("Threshold")))!=null)setThreshold(ImageFilterUtil.toFloatValue(o,"Threshold")); 276 277 // check for arguments not supported 278 if(parameters.size()>0) { 279 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 [Colormap, Amount, Blur, GlintOnly, Length, Threshold]"); 280 } 281 282 return filter(src, dst); 283 } 284 }