001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019/*
020*
021
022Licensed under the Apache License, Version 2.0 (the "License");
023you may not use this file except in compliance with the License.
024You may obtain a copy of the License at
025
026   http://www.apache.org/licenses/LICENSE-2.0
027
028Unless required by applicable law or agreed to in writing, software
029distributed under the License is distributed on an "AS IS" BASIS,
030WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
031See the License for the specific language governing permissions and
032limitations under the License.
033*/
034
035package lucee.runtime.img.filter;import java.awt.image.BufferedImage;
036
037import lucee.runtime.engine.ThreadLocalPageContext;
038import lucee.runtime.exp.FunctionException;
039import lucee.runtime.exp.PageException;
040import lucee.runtime.img.math.Noise;
041import lucee.runtime.type.KeyImpl;
042import lucee.runtime.type.Struct;
043import lucee.runtime.type.util.CollectionUtil;
044
045/**
046 * This filter applies a marbling effect to an image, displacing pixels by random amounts.
047 */
048public class MarbleFilter extends TransformFilter  implements DynFiltering {
049
050        private float[] sinTable, cosTable;
051        private float xScale = 4;
052        private float yScale = 4;
053        private float amount = 1;
054        private float turbulence = 1;
055        
056        public MarbleFilter() {
057                super(ConvolveFilter.CLAMP_EDGES);
058        }
059        
060        /**
061     * Set the X scale of the effect.
062     * @param xScale the scale.
063     * @see #getXScale
064     */
065        public void setXScale(float xScale) {
066                this.xScale = xScale;
067        }
068
069        /**
070     * Get the X scale of the effect.
071     * @return the scale.
072     * @see #setXScale
073     */
074        public float getXScale() {
075                return xScale;
076        }
077
078        /**
079     * Set the Y scale of the effect.
080     * @param yScale the scale.
081     * @see #getYScale
082     */
083        public void setYScale(float yScale) {
084                this.yScale = yScale;
085        }
086
087        /**
088     * Get the Y scale of the effect.
089     * @return the scale.
090     * @see #setYScale
091     */
092        public float getYScale() {
093                return yScale;
094        }
095
096        /**
097         * Set the amount of effect.
098         * @param amount the amount
099     * @min-value 0
100     * @max-value 1
101     * @see #getAmount
102         */
103        public void setAmount(float amount) {
104                this.amount = amount;
105        }
106
107        /**
108         * Get the amount of effect.
109         * @return the amount
110     * @see #setAmount
111         */
112        public float getAmount() {
113                return amount;
114        }
115
116        /**
117     * Specifies the turbulence of the effect.
118     * @param turbulence the turbulence of the effect.
119     * @min-value 0
120     * @max-value 1
121     * @see #getTurbulence
122     */
123        public void setTurbulence(float turbulence) {
124                this.turbulence = turbulence;
125        }
126
127        /**
128     * Returns the turbulence of the effect.
129     * @return the turbulence of the effect.
130     * @see #setTurbulence
131     */
132        public float getTurbulence() {
133                return turbulence;
134        }
135
136        private void initialize() {
137                sinTable = new float[256];
138                cosTable = new float[256];
139                for (int i = 0; i < 256; i++) {
140                        float angle = ImageMath.TWO_PI*i/256f*turbulence;
141                        sinTable[i] = (float)(-yScale*Math.sin(angle));
142                        cosTable[i] = (float)(yScale*Math.cos(angle));
143                }
144        }
145
146        private int displacementMap(int x, int y) {
147                return PixelUtils.clamp((int)(127 * (1+Noise.noise2(x / xScale, y / xScale))));
148        }
149        
150        protected void transformInverse(int x, int y, float[] out) {
151                int displacement = displacementMap(x, y);
152                out[0] = x + sinTable[displacement];
153                out[1] = y + cosTable[displacement];
154        }
155
156    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
157                initialize();
158                return super.filter( src, dst );
159        }
160
161        public String toString() {
162                return "Distort/Marble...";
163        }
164        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src);
165                Object o;
166                if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount"));
167                if((o=parameters.removeEL(KeyImpl.init("Turbulence")))!=null)setTurbulence(ImageFilterUtil.toFloatValue(o,"Turbulence"));
168                if((o=parameters.removeEL(KeyImpl.init("XScale")))!=null)setXScale(ImageFilterUtil.toFloatValue(o,"XScale"));
169                if((o=parameters.removeEL(KeyImpl.init("YScale")))!=null)setYScale(ImageFilterUtil.toFloatValue(o,"YScale"));
170                if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction"));
171                if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation"));
172
173                // check for arguments not supported
174                if(parameters.size()>0) {
175                        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 [Amount, Turbulence, XScale, YScale, EdgeAction, Interpolation]");
176                }
177
178                return filter(src, dst);
179        }
180}