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.util.Random; 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.img.math.CellularFunction2D; 025 import railo.runtime.img.math.FBM; 026 import railo.runtime.img.math.Function2D; 027 import railo.runtime.img.math.Noise; 028 import railo.runtime.img.math.RidgedFBM; 029 import railo.runtime.img.math.SCNoise; 030 import railo.runtime.img.math.VLNoise; 031 import railo.runtime.type.KeyImpl; 032 import railo.runtime.type.Struct; 033 import railo.runtime.type.util.CollectionUtil; 034 035 /** 036 * A filter which produces textures from fractal Brownian motion. 037 */ 038 public class FBMFilter extends PointFilter implements Cloneable, DynFiltering { 039 040 public final static int NOISE = 0; 041 public final static int RIDGED = 1; 042 public final static int VLNOISE = 2; 043 public final static int SCNOISE = 3; 044 public final static int CELLULAR = 4; 045 046 private float scale = 32; 047 private float stretch = 1.0f; 048 private float angle = 0.0f; 049 private float amount = 1.0f; 050 private float H = 1.0f; 051 private float octaves = 4.0f; 052 private float lacunarity = 2.0f; 053 private float gain = 0.5f; 054 private float bias = 0.5f; 055 private int operation; 056 private float m00 = 1.0f; 057 private float m01 = 0.0f; 058 private float m10 = 0.0f; 059 private float m11 = 1.0f; 060 private float min; 061 private float max; 062 private Colormap colormap = new Gradient(); 063 //private boolean ridged; 064 private FBM fBm; 065 protected Random random = new Random(); 066 private int basisType = NOISE; 067 private Function2D basis; 068 069 public FBMFilter() { 070 setBasisType(NOISE); 071 } 072 073 /** 074 * Set the amount of effect. 075 * @param amount the amount 076 * @min-value 0 077 * @max-value 1 078 * @see #getAmount 079 */ 080 public void setAmount(float amount) { 081 this.amount = amount; 082 } 083 084 /** 085 * Get the amount of texture. 086 * @return the amount 087 * @see #setAmount 088 */ 089 public float getAmount() { 090 return amount; 091 } 092 093 public void setOperation(int operation) { 094 this.operation = operation; 095 } 096 097 public int getOperation() { 098 return operation; 099 } 100 101 /** 102 * Specifies the scale of the texture. 103 * @param scale the scale of the texture. 104 * @min-value 1 105 * @max-value 300+ 106 * @see #getScale 107 */ 108 public void setScale(float scale) { 109 this.scale = scale; 110 } 111 112 /** 113 * Returns the scale of the texture. 114 * @return the scale of the texture. 115 * @see #setScale 116 */ 117 public float getScale() { 118 return scale; 119 } 120 121 /** 122 * Specifies the stretch factor of the texture. 123 * @param stretch the stretch factor of the texture. 124 * @min-value 1 125 * @max-value 50+ 126 * @see #getStretch 127 */ 128 public void setStretch(float stretch) { 129 this.stretch = stretch; 130 } 131 132 /** 133 * Returns the stretch factor of the texture. 134 * @return the stretch factor of the texture. 135 * @see #setStretch 136 */ 137 public float getStretch() { 138 return stretch; 139 } 140 141 /** 142 * Specifies the angle of the texture. 143 * @param angle the angle of the texture. 144 * @angle 145 * @see #getAngle 146 */ 147 public void setAngle(float angle) { 148 this.angle = angle; 149 float cos = (float)Math.cos(this.angle); 150 float sin = (float)Math.sin(this.angle); 151 m00 = cos; 152 m01 = sin; 153 m10 = -sin; 154 m11 = cos; 155 } 156 157 /** 158 * Returns the angle of the texture. 159 * @return the angle of the texture. 160 * @see #setAngle 161 */ 162 public float getAngle() { 163 return angle; 164 } 165 166 public void setOctaves(float octaves) { 167 this.octaves = octaves; 168 } 169 170 public float getOctaves() { 171 return octaves; 172 } 173 174 public void setH(float H) { 175 this.H = H; 176 } 177 178 public float getH() { 179 return H; 180 } 181 182 public void setLacunarity(float lacunarity) { 183 this.lacunarity = lacunarity; 184 } 185 186 public float getLacunarity() { 187 return lacunarity; 188 } 189 190 public void setGain(float gain) { 191 this.gain = gain; 192 } 193 194 public float getGain() { 195 return gain; 196 } 197 198 public void setBias(float bias) { 199 this.bias = bias; 200 } 201 202 public float getBias() { 203 return bias; 204 } 205 206 /** 207 * Set the colormap to be used for the filter. 208 * @param colormap the colormap 209 * @see #getColormap 210 */ 211 public void setColormap(Colormap colormap) { 212 this.colormap = colormap; 213 } 214 215 /** 216 * Get the colormap to be used for the filter. 217 * @return the colormap 218 * @see #setColormap 219 */ 220 public Colormap getColormap() { 221 return colormap; 222 } 223 224 public void setBasisType(int basisType) { 225 this.basisType = basisType; 226 switch (basisType) { 227 default: 228 case NOISE: 229 basis = new Noise(); 230 break; 231 case RIDGED: 232 basis = new RidgedFBM(); 233 break; 234 case VLNOISE: 235 basis = new VLNoise(); 236 break; 237 case SCNOISE: 238 basis = new SCNoise(); 239 break; 240 case CELLULAR: 241 basis = new CellularFunction2D(); 242 break; 243 } 244 } 245 246 public int getBasisType() { 247 return basisType; 248 } 249 250 public void setBasis(Function2D basis) { 251 this.basis = basis; 252 } 253 254 public Function2D getBasis() { 255 return basis; 256 } 257 258 protected FBM makeFBM(float H, float lacunarity, float octaves) { 259 FBM fbm = new FBM(H, lacunarity, octaves, basis); 260 float[] minmax = Noise.findRange(fbm, null); 261 min = minmax[0]; 262 max = minmax[1]; 263 return fbm; 264 } 265 266 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 267 fBm = makeFBM(H, lacunarity, octaves); 268 return super.filter( src, dst ); 269 } 270 271 public int filterRGB(int x, int y, int rgb) { 272 float nx = m00*x + m01*y; 273 float ny = m10*x + m11*y; 274 nx /= scale; 275 ny /= scale * stretch; 276 float f = fBm.evaluate(nx, ny); 277 // Normalize to 0..1 278 f = (f-min)/(max-min); 279 f = ImageMath.gain(f, gain); 280 f = ImageMath.bias(f, bias); 281 f *= amount; 282 int a = rgb & 0xff000000; 283 int v; 284 if (colormap != null) 285 v = colormap.getColor(f); 286 else { 287 v = PixelUtils.clamp((int)(f*255)); 288 int r = v << 16; 289 int g = v << 8; 290 int b = v; 291 v = a|r|g|b; 292 } 293 if (operation != PixelUtils.REPLACE) 294 v = PixelUtils.combinePixels(rgb, v, operation); 295 return v; 296 } 297 298 public String toString() { 299 return "Texture/Fractal Brownian Motion..."; 300 } 301 302 public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src); 303 Object o; 304 if((o=parameters.removeEL(KeyImpl.init("Colormap")))!=null)setColormap(ImageFilterUtil.toColormap(o,"Colormap")); 305 if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount")); 306 if((o=parameters.removeEL(KeyImpl.init("Stretch")))!=null)setStretch(ImageFilterUtil.toFloatValue(o,"Stretch")); 307 if((o=parameters.removeEL(KeyImpl.init("Angle")))!=null)setAngle(ImageFilterUtil.toFloatValue(o,"Angle")); 308 if((o=parameters.removeEL(KeyImpl.init("BasisType")))!=null)setBasisType(ImageFilterUtil.toIntValue(o,"BasisType")); 309 if((o=parameters.removeEL(KeyImpl.init("Operation")))!=null)setOperation(ImageFilterUtil.toIntValue(o,"Operation")); 310 if((o=parameters.removeEL(KeyImpl.init("Octaves")))!=null)setOctaves(ImageFilterUtil.toFloatValue(o,"Octaves")); 311 if((o=parameters.removeEL(KeyImpl.init("H")))!=null)setH(ImageFilterUtil.toFloatValue(o,"H")); 312 if((o=parameters.removeEL(KeyImpl.init("Lacunarity")))!=null)setLacunarity(ImageFilterUtil.toFloatValue(o,"Lacunarity")); 313 if((o=parameters.removeEL(KeyImpl.init("Gain")))!=null)setGain(ImageFilterUtil.toFloatValue(o,"Gain")); 314 if((o=parameters.removeEL(KeyImpl.init("Bias")))!=null)setBias(ImageFilterUtil.toFloatValue(o,"Bias")); 315 if((o=parameters.removeEL(KeyImpl.init("Basis")))!=null)setBasis(ImageFilterUtil.toFunction2D(o,"Basis")); 316 if((o=parameters.removeEL(KeyImpl.init("Scale")))!=null)setScale(ImageFilterUtil.toFloatValue(o,"Scale")); 317 if((o=parameters.removeEL(KeyImpl.init("Dimensions")))!=null){ 318 int[] dim=ImageFilterUtil.toDimensions(o,"Dimensions"); 319 setDimensions(dim[0],dim[1]); 320 } 321 322 // check for arguments not supported 323 if(parameters.size()>0) { 324 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 [Colormap, Amount, Stretch, Angle, BasisType, Operation, Octaves, H, Lacunarity, Gain, Bias, Basis, Scale, Dimensions]"); 325 } 326 327 return filter(src, dst); 328 } 329 }