001/* 002Copyright 2006 Jerry Huxtable 003 004Licensed under the Apache License, Version 2.0 (the "License"); 005you may not use this file except in compliance with the License. 006You may obtain a copy of the License at 007 008 http://www.apache.org/licenses/LICENSE-2.0 009 010Unless required by applicable law or agreed to in writing, software 011distributed under the License is distributed on an "AS IS" BASIS, 012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013See the License for the specific language governing permissions and 014limitations under the License. 015*/ 016 017package com.jhlabs.image; 018 019import java.awt.image.*; 020import java.util.*; 021import com.jhlabs.math.*; 022 023/** 024 * A filter which produces textures from fractal Brownian motion. 025 */ 026public class FBMFilter extends PointFilter implements Cloneable { 027 028 public final static int NOISE = 0; 029 public final static int RIDGED = 1; 030 public final static int VLNOISE = 2; 031 public final static int SCNOISE = 3; 032 public final static int CELLULAR = 4; 033 034 private float scale = 32; 035 private float stretch = 1.0f; 036 private float angle = 0.0f; 037 private float amount = 1.0f; 038 private float H = 1.0f; 039 private float octaves = 4.0f; 040 private float lacunarity = 2.0f; 041 private float gain = 0.5f; 042 private float bias = 0.5f; 043 private int operation; 044 private float m00 = 1.0f; 045 private float m01 = 0.0f; 046 private float m10 = 0.0f; 047 private float m11 = 1.0f; 048 private float min; 049 private float max; 050 private Colormap colormap = new Gradient(); 051 private boolean ridged; 052 private FBM fBm; 053 protected Random random = new Random(); 054 private int basisType = NOISE; 055 private Function2D basis; 056 057 public FBMFilter() { 058 setBasisType(NOISE); 059 } 060 061 /** 062 * Set the amount of effect. 063 * @param amount the amount 064 * @min-value 0 065 * @max-value 1 066 * @see #getAmount 067 */ 068 public void setAmount(float amount) { 069 this.amount = amount; 070 } 071 072 /** 073 * Get the amount of texture. 074 * @return the amount 075 * @see #setAmount 076 */ 077 public float getAmount() { 078 return amount; 079 } 080 081 public void setOperation(int operation) { 082 this.operation = operation; 083 } 084 085 public int getOperation() { 086 return operation; 087 } 088 089 /** 090 * Specifies the scale of the texture. 091 * @param scale the scale of the texture. 092 * @min-value 1 093 * @max-value 300+ 094 * @see #getScale 095 */ 096 public void setScale(float scale) { 097 this.scale = scale; 098 } 099 100 /** 101 * Returns the scale of the texture. 102 * @return the scale of the texture. 103 * @see #setScale 104 */ 105 public float getScale() { 106 return scale; 107 } 108 109 /** 110 * Specifies the stretch factor of the texture. 111 * @param stretch the stretch factor of the texture. 112 * @min-value 1 113 * @max-value 50+ 114 * @see #getStretch 115 */ 116 public void setStretch(float stretch) { 117 this.stretch = stretch; 118 } 119 120 /** 121 * Returns the stretch factor of the texture. 122 * @return the stretch factor of the texture. 123 * @see #setStretch 124 */ 125 public float getStretch() { 126 return stretch; 127 } 128 129 /** 130 * Specifies the angle of the texture. 131 * @param angle the angle of the texture. 132 * @angle 133 * @see #getAngle 134 */ 135 public void setAngle(float angle) { 136 this.angle = angle; 137 float cos = (float)Math.cos(this.angle); 138 float sin = (float)Math.sin(this.angle); 139 m00 = cos; 140 m01 = sin; 141 m10 = -sin; 142 m11 = cos; 143 } 144 145 /** 146 * Returns the angle of the texture. 147 * @return the angle of the texture. 148 * @see #setAngle 149 */ 150 public float getAngle() { 151 return angle; 152 } 153 154 public void setOctaves(float octaves) { 155 this.octaves = octaves; 156 } 157 158 public float getOctaves() { 159 return octaves; 160 } 161 162 public void setH(float H) { 163 this.H = H; 164 } 165 166 public float getH() { 167 return H; 168 } 169 170 public void setLacunarity(float lacunarity) { 171 this.lacunarity = lacunarity; 172 } 173 174 public float getLacunarity() { 175 return lacunarity; 176 } 177 178 public void setGain(float gain) { 179 this.gain = gain; 180 } 181 182 public float getGain() { 183 return gain; 184 } 185 186 public void setBias(float bias) { 187 this.bias = bias; 188 } 189 190 public float getBias() { 191 return bias; 192 } 193 194 /** 195 * Set the colormap to be used for the filter. 196 * @param colormap the colormap 197 * @see #getColormap 198 */ 199 public void setColormap(Colormap colormap) { 200 this.colormap = colormap; 201 } 202 203 /** 204 * Get the colormap to be used for the filter. 205 * @return the colormap 206 * @see #setColormap 207 */ 208 public Colormap getColormap() { 209 return colormap; 210 } 211 212 public void setBasisType(int basisType) { 213 this.basisType = basisType; 214 switch (basisType) { 215 default: 216 case NOISE: 217 basis = new Noise(); 218 break; 219 case RIDGED: 220 basis = new RidgedFBM(); 221 break; 222 case VLNOISE: 223 basis = new VLNoise(); 224 break; 225 case SCNOISE: 226 basis = new SCNoise(); 227 break; 228 case CELLULAR: 229 basis = new CellularFunction2D(); 230 break; 231 } 232 } 233 234 public int getBasisType() { 235 return basisType; 236 } 237 238 public void setBasis(Function2D basis) { 239 this.basis = basis; 240 } 241 242 public Function2D getBasis() { 243 return basis; 244 } 245 246 protected FBM makeFBM(float H, float lacunarity, float octaves) { 247 FBM fbm = new FBM(H, lacunarity, octaves, basis); 248 float[] minmax = Noise.findRange(fbm, null); 249 min = minmax[0]; 250 max = minmax[1]; 251 return fbm; 252 } 253 254 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 255 fBm = makeFBM(H, lacunarity, octaves); 256 return super.filter( src, dst ); 257 } 258 259 public int filterRGB(int x, int y, int rgb) { 260 float nx = m00*x + m01*y; 261 float ny = m10*x + m11*y; 262 nx /= scale; 263 ny /= scale * stretch; 264 float f = fBm.evaluate(nx, ny); 265 // Normalize to 0..1 266 f = (f-min)/(max-min); 267 f = ImageMath.gain(f, gain); 268 f = ImageMath.bias(f, bias); 269 f *= amount; 270 int a = rgb & 0xff000000; 271 int v; 272 if (colormap != null) 273 v = colormap.getColor(f); 274 else { 275 v = PixelUtils.clamp((int)(f*255)); 276 int r = v << 16; 277 int g = v << 8; 278 int b = v; 279 v = a|r|g|b; 280 } 281 if (operation != PixelUtils.REPLACE) 282 v = PixelUtils.combinePixels(rgb, v, operation); 283 return v; 284 } 285 286 public String toString() { 287 return "Texture/Fractal Brownian Motion..."; 288 } 289 290}