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.event.*;
021import java.awt.image.*;
022
023/**
024 * A filter for warping images using the gridwarp algorithm.
025 * You need to supply two warp grids, one for the source image and
026 * one for the destination image. The image will be warped so that
027 * a point in the source grid moves to its counterpart in the destination
028 * grid.
029 */
030public class WarpFilter extends WholeImageFilter {
031
032        private WarpGrid sourceGrid;
033        private WarpGrid destGrid;
034        private int frames = 1;
035
036        private BufferedImage morphImage;
037        private float time;
038
039        /**
040         * Create a WarpFilter.
041         */
042        public WarpFilter() {
043        }
044        
045        /**
046         * Create a WarpFilter with two warp grids.
047         * @param sourceGrid the source grid
048         * @param destGrid the destination grid
049         */
050        public WarpFilter(WarpGrid sourceGrid, WarpGrid destGrid) {
051                this.sourceGrid = sourceGrid;
052                this.destGrid = destGrid;               
053        }
054        
055        /**
056         * Set the source warp grid.
057         * @param sourceGrid the source grid
058     * @see #getSourceGrid
059         */
060        public void setSourceGrid(WarpGrid sourceGrid) {
061                this.sourceGrid = sourceGrid;
062        }
063
064        /**
065         * Get the source warp grid.
066         * @return the source grid
067     * @see #setSourceGrid
068         */
069        public WarpGrid getSourceGrid() {
070                return sourceGrid;
071        }
072
073        /**
074         * Set the destination warp grid.
075         * @param destGrid the destination grid
076     * @see #getDestGrid
077         */
078        public void setDestGrid(WarpGrid destGrid) {
079                this.destGrid = destGrid;
080        }
081
082        /**
083         * Get the destination warp grid.
084         * @return the destination grid
085     * @see #setDestGrid
086         */
087        public WarpGrid getDestGrid() {
088                return destGrid;
089        }
090
091        public void setFrames(int frames) {
092                this.frames = frames;
093        }
094
095        public int getFrames() {
096                return frames;
097        }
098
099        /**
100         * For morphing, sets the image we're morphing to. If not, set then we're just warping.
101         */
102        public void setMorphImage(BufferedImage morphImage) {
103                this.morphImage = morphImage;
104        }
105
106        public BufferedImage getMorphImage() {
107                return morphImage;
108        }
109
110        public void setTime(float time) {
111                this.time = time;
112        }
113
114        public float getTime() {
115                return time;
116        }
117
118        protected void transformSpace(Rectangle r) {
119                r.width *= frames;
120        }
121
122        protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
123                int[] outPixels = new int[width * height];
124                
125                if ( morphImage != null ) {
126                        int[] morphPixels = getRGB( morphImage, 0, 0, width, height, null );
127                        morph( inPixels, morphPixels, outPixels, sourceGrid, destGrid, width, height, time );
128                } else if (frames <= 1) {
129                        sourceGrid.warp(inPixels, width, height, sourceGrid, destGrid, outPixels);
130                } else {
131                        WarpGrid newGrid = new WarpGrid(sourceGrid.rows, sourceGrid.cols, width, height);
132                        for (int i = 0; i < frames; i++) {
133                                float t = (float)i/(frames-1);
134                                sourceGrid.lerp(t, destGrid, newGrid);
135                                sourceGrid.warp(inPixels, width, height, sourceGrid, newGrid, outPixels);
136                        }
137                }
138                return outPixels;
139        }
140
141        public void morph(int[] srcPixels, int[] destPixels, int[] outPixels, WarpGrid srcGrid, WarpGrid destGrid, int width, int height, float t) {
142                WarpGrid newGrid = new WarpGrid(srcGrid.rows, srcGrid.cols, width, height);
143                srcGrid.lerp(t, destGrid, newGrid);
144                srcGrid.warp(srcPixels, width, height, srcGrid, newGrid, outPixels);
145                int[] destPixels2 = new int[width * height];
146                destGrid.warp(destPixels, width, height, destGrid, newGrid, destPixels2);
147                crossDissolve(outPixels, destPixels2, width, height, t);
148        }
149
150        public void crossDissolve(int[] pixels1, int[] pixels2, int width, int height, float t) {
151                int index = 0;
152                for (int y = 0; y < height; y++) {
153                        for (int x = 0; x < width; x++) {
154                                pixels1[index] = ImageMath.mixColors(t, pixels1[index], pixels2[index]);
155                                index++;
156                        }
157                }
158        }
159        
160        public String toString() {
161                return "Distort/Mesh Warp...";
162        }
163
164}
165