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.math.Noise;
023    import railo.runtime.type.KeyImpl;
024    import railo.runtime.type.List;
025    import railo.runtime.type.Struct;
026    
027    /**
028     * This filter applies a marbling effect to an image, displacing pixels by random amounts.
029     */
030    public class MarbleFilter extends TransformFilter  implements DynFiltering {
031    
032            private float[] sinTable, cosTable;
033            private float xScale = 4;
034            private float yScale = 4;
035            private float amount = 1;
036            private float turbulence = 1;
037            
038            public MarbleFilter() {
039                    super(ConvolveFilter.CLAMP_EDGES);
040            }
041            
042            /**
043         * Set the X scale of the effect.
044         * @param xScale the scale.
045         * @see #getXScale
046         */
047            public void setXScale(float xScale) {
048                    this.xScale = xScale;
049            }
050    
051            /**
052         * Get the X scale of the effect.
053         * @return the scale.
054         * @see #setXScale
055         */
056            public float getXScale() {
057                    return xScale;
058            }
059    
060            /**
061         * Set the Y scale of the effect.
062         * @param yScale the scale.
063         * @see #getYScale
064         */
065            public void setYScale(float yScale) {
066                    this.yScale = yScale;
067            }
068    
069            /**
070         * Get the Y scale of the effect.
071         * @return the scale.
072         * @see #setYScale
073         */
074            public float getYScale() {
075                    return yScale;
076            }
077    
078            /**
079             * Set the amount of effect.
080             * @param amount the amount
081         * @min-value 0
082         * @max-value 1
083         * @see #getAmount
084             */
085            public void setAmount(float amount) {
086                    this.amount = amount;
087            }
088    
089            /**
090             * Get the amount of effect.
091             * @return the amount
092         * @see #setAmount
093             */
094            public float getAmount() {
095                    return amount;
096            }
097    
098            /**
099         * Specifies the turbulence of the effect.
100         * @param turbulence the turbulence of the effect.
101         * @min-value 0
102         * @max-value 1
103         * @see #getTurbulence
104         */
105            public void setTurbulence(float turbulence) {
106                    this.turbulence = turbulence;
107            }
108    
109            /**
110         * Returns the turbulence of the effect.
111         * @return the turbulence of the effect.
112         * @see #setTurbulence
113         */
114            public float getTurbulence() {
115                    return turbulence;
116            }
117    
118            private void initialize() {
119                    sinTable = new float[256];
120                    cosTable = new float[256];
121                    for (int i = 0; i < 256; i++) {
122                            float angle = ImageMath.TWO_PI*i/256f*turbulence;
123                            sinTable[i] = (float)(-yScale*Math.sin(angle));
124                            cosTable[i] = (float)(yScale*Math.cos(angle));
125                    }
126            }
127    
128            private int displacementMap(int x, int y) {
129                    return PixelUtils.clamp((int)(127 * (1+Noise.noise2(x / xScale, y / xScale))));
130            }
131            
132            protected void transformInverse(int x, int y, float[] out) {
133                    int displacement = displacementMap(x, y);
134                    out[0] = x + sinTable[displacement];
135                    out[1] = y + cosTable[displacement];
136            }
137    
138        public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
139                    initialize();
140                    return super.filter( src, dst );
141            }
142    
143            public String toString() {
144                    return "Distort/Marble...";
145            }
146            public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src);
147                    Object o;
148                    if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount"));
149                    if((o=parameters.removeEL(KeyImpl.init("Turbulence")))!=null)setTurbulence(ImageFilterUtil.toFloatValue(o,"Turbulence"));
150                    if((o=parameters.removeEL(KeyImpl.init("XScale")))!=null)setXScale(ImageFilterUtil.toFloatValue(o,"XScale"));
151                    if((o=parameters.removeEL(KeyImpl.init("YScale")))!=null)setYScale(ImageFilterUtil.toFloatValue(o,"YScale"));
152                    if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction"));
153                    if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation"));
154    
155                    // check for arguments not supported
156                    if(parameters.size()>0) {
157                            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 [Amount, Turbulence, XScale, YScale, EdgeAction, Interpolation]");
158                    }
159    
160                    return filter(src, dst);
161            }
162    }