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;
036
037import java.awt.image.BufferedImage;
038
039import lucee.runtime.engine.ThreadLocalPageContext;
040import lucee.runtime.exp.FunctionException;
041import lucee.runtime.exp.PageException;
042import lucee.runtime.type.KeyImpl;
043import lucee.runtime.type.Struct;
044import lucee.runtime.type.util.CollectionUtil;
045
046/**
047 * A filter which simulates the appearance of looking through glass. A separate grayscale displacement image is provided and
048 * pixels in the source image are displaced according to the gradient of the displacement map.
049 */
050public class DisplaceFilter extends TransformFilter  implements DynFiltering {
051
052        private float amount = 1;
053        private BufferedImage displacementMap = null;
054        private int[] xmap, ymap;
055        private int dw, dh;
056
057        public DisplaceFilter() {
058        }
059        
060        /**
061         * Set the displacement map.
062         * @param displacementMap an image representing the displacment at each point
063     * @see #getDisplacementMap
064         */
065        public void setDisplacementMap(BufferedImage displacementMap) {
066                this.displacementMap = displacementMap;
067        }
068
069        /**
070         * Get the displacement map.
071         * @return an image representing the displacment at each point
072     * @see #setDisplacementMap
073         */
074        public BufferedImage getDisplacementMap() {
075                return displacementMap;
076        }
077
078        /**
079         * Set the amount of distortion.
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 distortion.
091         * @return the amount
092     * @see #setAmount
093         */
094        public float getAmount() {
095                return amount;
096        }
097        
098    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
099                //int w = src.getWidth();
100                //int h = src.getHeight();
101
102                BufferedImage dm = displacementMap != null ? displacementMap : src;
103
104                dw = dm.getWidth();
105                dh = dm.getHeight();
106                
107                int[] mapPixels = new int[dw*dh];
108                getRGB( dm, 0, 0, dw, dh, mapPixels );
109                xmap = new int[dw*dh];
110                ymap = new int[dw*dh];
111                
112                int i = 0;
113                for ( int y = 0; y < dh; y++ ) {
114                        for ( int x = 0; x < dw; x++ ) {
115                                int rgb = mapPixels[i];
116                                int r = (rgb >> 16) & 0xff;
117                                int g = (rgb >> 8) & 0xff;
118                                int b = rgb & 0xff;
119                                mapPixels[i] = (r+g+b) / 8; // An arbitrary scaling factor which gives a good range for "amount"
120                                i++;
121                        }
122                }
123
124                i = 0;
125                for ( int y = 0; y < dh; y++ ) {
126                        int j1 = ((y+dh-1) % dh) * dw;
127                        int j2 = y*dw;
128                        int j3 = ((y+1) % dh) * dw;
129                        for ( int x = 0; x < dw; x++ ) {
130                                int k1 = (x+dw-1) % dw;
131                                int k2 = x;
132                                int k3 = (x+1) % dw;
133                                xmap[i] = mapPixels[k1+j1] + mapPixels[k1+j2] + mapPixels[k1+j3] - mapPixels[k3+j1] - mapPixels[k3+j2] - mapPixels[k3+j3];
134                                ymap[i] = mapPixels[k1+j3] + mapPixels[k2+j3] + mapPixels[k3+j3] - mapPixels[k1+j1] - mapPixels[k2+j1] - mapPixels[k3+j1];
135                                i++;
136                        }
137                }
138                mapPixels = null;
139                dst = super.filter( src, dst );
140                xmap = ymap = null;
141                return dst;
142        }
143        
144        protected void transformInverse(int x, int y, float[] out) {
145                //float xDisplacement, yDisplacement;
146                //float nx = x;
147                //float ny = y;
148                int i = (y % dh)*dw + x % dw;
149                out[0] = x + amount * xmap[i];
150                out[1] = y + amount * ymap[i];
151        }
152
153        public String toString() {
154                return "Distort/Displace...";
155        }
156        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=null;//ImageUtil.createBufferedImage(src);
157                Object o;
158                if((o=parameters.removeEL(KeyImpl.init("Amount")))!=null)setAmount(ImageFilterUtil.toFloatValue(o,"Amount"));
159                if((o=parameters.removeEL(KeyImpl.init("DisplacementMap")))!=null)setDisplacementMap(ImageFilterUtil.toBufferedImage(o,"DisplacementMap"));
160                if((o=parameters.removeEL(KeyImpl.init("EdgeAction")))!=null)setEdgeAction(ImageFilterUtil.toString(o,"EdgeAction"));
161                if((o=parameters.removeEL(KeyImpl.init("Interpolation")))!=null)setInterpolation(ImageFilterUtil.toString(o,"Interpolation"));
162
163                // check for arguments not supported
164                if(parameters.size()>0) {
165                        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, DisplacementMap, EdgeAction, Interpolation]");
166                }
167
168                return filter(src, dst);
169        }
170}