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.List;
033    import railo.runtime.type.Struct;
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":"")+" ["+List.arrayToList(parameters.keysAsString(),", ")+"] "+(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    }