001/*
002Copyright 2006 Jerry Huxtable
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008   http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015*/
016
017package com.jhlabs.image;
018
019import java.awt.*;
020import java.awt.image.*;
021import com.jhlabs.math.*;
022
023/**
024 * A filter which simulates the appearance of looking through glass. A separate grayscale displacement image is provided and
025 * pixels in the source image are displaced according to the gradient of the displacement map.
026 */
027public class DisplaceFilter extends TransformFilter {
028
029        private float amount = 1;
030        private BufferedImage displacementMap = null;
031        private int[] xmap, ymap;
032        private int dw, dh;
033
034        public DisplaceFilter() {
035        }
036        
037        /**
038         * Set the displacement map.
039         * @param displacementMap an image representing the displacment at each point
040     * @see #getDisplacementMap
041         */
042        public void setDisplacementMap(BufferedImage displacementMap) {
043                this.displacementMap = displacementMap;
044        }
045
046        /**
047         * Get the displacement map.
048         * @return an image representing the displacment at each point
049     * @see #setDisplacementMap
050         */
051        public BufferedImage getDisplacementMap() {
052                return displacementMap;
053        }
054
055        /**
056         * Set the amount of distortion.
057         * @param amount the amount
058     * @min-value 0
059     * @max-value 1
060     * @see #getAmount
061         */
062        public void setAmount(float amount) {
063                this.amount = amount;
064        }
065        
066        /**
067         * Get the amount of distortion.
068         * @return the amount
069     * @see #setAmount
070         */
071        public float getAmount() {
072                return amount;
073        }
074        
075    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
076                int w = src.getWidth();
077                int h = src.getHeight();
078
079                BufferedImage dm = displacementMap != null ? displacementMap : src;
080
081                dw = dm.getWidth();
082                dh = dm.getHeight();
083                
084                int[] mapPixels = new int[dw*dh];
085                getRGB( dm, 0, 0, dw, dh, mapPixels );
086                xmap = new int[dw*dh];
087                ymap = new int[dw*dh];
088                
089                int i = 0;
090                for ( int y = 0; y < dh; y++ ) {
091                        for ( int x = 0; x < dw; x++ ) {
092                                int rgb = mapPixels[i];
093                                int r = (rgb >> 16) & 0xff;
094                                int g = (rgb >> 8) & 0xff;
095                                int b = rgb & 0xff;
096                                mapPixels[i] = (r+g+b) / 8; // An arbitrary scaling factor which gives a good range for "amount"
097                                i++;
098                        }
099                }
100
101                i = 0;
102                for ( int y = 0; y < dh; y++ ) {
103                        int j1 = ((y+dh-1) % dh) * dw;
104                        int j2 = y*dw;
105                        int j3 = ((y+1) % dh) * dw;
106                        for ( int x = 0; x < dw; x++ ) {
107                                int k1 = (x+dw-1) % dw;
108                                int k2 = x;
109                                int k3 = (x+1) % dw;
110                                xmap[i] = mapPixels[k1+j1] + mapPixels[k1+j2] + mapPixels[k1+j3] - mapPixels[k3+j1] - mapPixels[k3+j2] - mapPixels[k3+j3];
111                                ymap[i] = mapPixels[k1+j3] + mapPixels[k2+j3] + mapPixels[k3+j3] - mapPixels[k1+j1] - mapPixels[k2+j1] - mapPixels[k3+j1];
112                                i++;
113                        }
114                }
115                mapPixels = null;
116                dst = super.filter( src, dst );
117                xmap = ymap = null;
118                return dst;
119        }
120        
121        protected void transformInverse(int x, int y, float[] out) {
122                float xDisplacement, yDisplacement;
123                float nx = x;
124                float ny = y;
125                int i = (y % dh)*dw + x % dw;
126                out[0] = x + amount * xmap[i];
127                out[1] = y + amount * ymap[i];
128        }
129
130        public String toString() {
131                return "Distort/Displace...";
132        }
133}