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