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}