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.*;
021
022/**
023 * A class which warps an image using a field Warp algorithm.
024 */
025public class FieldWarpFilter extends TransformFilter {
026
027        public static class Line {
028                public int x1, y1, x2, y2;
029                public int dx, dy;
030                public float length, lengthSquared;
031                
032                public Line(int x1, int y1, int x2, int y2) {
033                        this.x1 = x1;
034                        this.y1 = y1;
035                        this.x2 = x2;
036                        this.y2 = y2;
037                }
038                
039                public void setup() {
040                        dx = x2-x1;
041                        dy = y2-y1;
042                        lengthSquared = dx*dx + dy*dy;
043                        length = (float)Math.sqrt(lengthSquared);
044                }
045        }
046
047        private float amount = 1.0f;
048        private float power = 1.0f;
049        private float strength = 2.0f;
050        private Line[] inLines;
051        private Line[] outLines;
052        private Line[] intermediateLines;
053        private float width, height;
054
055        public FieldWarpFilter() {
056        }
057
058        /**
059         * Set the amount of warp.
060         * @param amount the amount
061     * @min-value 0
062     * @max-value 1
063     * @see #getAmount
064         */
065        public void setAmount(float amount) {
066                this.amount = amount;
067        }
068        
069        /**
070         * Get the amount of warp.
071         * @return the amount
072     * @see #setAmount
073         */
074        public float getAmount() {
075                return amount;
076        }
077        
078        public void setPower(float power) {
079                this.power = power;
080        }
081        
082        public float getPower() {
083                return power;
084        }
085        
086        public void setStrength(float strength) {
087                this.strength = strength;
088        }
089        
090        public float getStrength() {
091                return strength;
092        }
093        
094        public void setInLines( Line[] inLines ) {
095                this.inLines = inLines;
096        }
097        
098        public Line[] getInLines() {
099                return inLines;
100        }
101        
102        public void setOutLines( Line[] outLines ) {
103                this.outLines = outLines;
104        }
105        
106        public Line[] getOutLines() {
107                return outLines;
108        }
109        
110        protected void transform(int x, int y, Point out) {
111        }
112
113        protected void transformInverse(int x, int y, float[] out) {
114                float u = 0, v = 0;
115                float fraction = 0;
116                float distance;
117                float fdist;
118                float weight;
119                float a = 0.001f;
120                float b = 1.5f*strength + 0.5f;
121                float p = power;
122
123                float totalWeight = 0.0f;
124                float sumX = 0.0f;
125                float sumY = 0.0f;
126
127                for (int line = 0; line < inLines.length; line++) {
128                        Line l1 = inLines[line];
129                        Line l = intermediateLines[line];
130                        float dx = x - l.x1;
131                        float dy = y - l.y1;
132
133                        fraction = (dx * l.dx + dy * l.dy) / l.lengthSquared;
134                        fdist = (dy * l.dx - dx * l.dy) / l.length;
135                        if (fraction <= 0)
136                                distance = (float)Math.sqrt(dx*dx + dy*dy);
137                        else if (fraction >= 1) {
138                                dx = x - l.x2;
139                                dy = y - l.y2;
140                                distance = (float)Math.sqrt(dx*dx + dy*dy);
141                        } else if (fdist >= 0)
142                                distance = fdist;
143                        else
144                                distance = -fdist;
145                        u = l1.x1 + fraction * l1.dx - fdist * l1.dy / l1.length;
146                        v = l1.y1 + fraction * l1.dy + fdist * l1.dx / l1.length;
147
148                        weight = (float)Math.pow(Math.pow(l.length, p) / (a + distance), b);
149
150                        sumX += (u - x) * weight;
151                        sumY += (v - y) * weight;
152//if (x % 10 == 0&&y == 20)System.out.println("distance="+distance+" weight="+weight+" sumX="+sumX+" sumY="+sumY+" u="+u+" v="+v);
153                        totalWeight += weight;
154                }
155
156//              out[0] = ImageMath.clamp(x + sumX / totalWeight + 0.5f, 0, width-1);
157//              out[1] = ImageMath.clamp(y + sumY / totalWeight + 0.5f, 0, height-1);
158                out[0] = x + sumX / totalWeight + 0.5f;
159                out[1] = y + sumY / totalWeight + 0.5f;
160        }
161
162    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
163                this.width = width;
164                this.height = height;
165                if ( inLines != null && outLines != null ) {
166                        intermediateLines = new Line[inLines.length];
167                        for (int line = 0; line < inLines.length; line++) {
168                                Line l = intermediateLines[line] = new Line(
169                                        ImageMath.lerp(amount, inLines[line].x1, outLines[line].x1),
170                                        ImageMath.lerp(amount, inLines[line].y1, outLines[line].y1),
171                                        ImageMath.lerp(amount, inLines[line].x2, outLines[line].x2),
172                                        ImageMath.lerp(amount, inLines[line].y2, outLines[line].y2)
173                                );
174                                l.setup();
175                                inLines[line].setup();
176                        }
177                        dst = super.filter( src, dst );
178                        intermediateLines = null;
179                        return dst;
180                }
181                return src;
182        }
183
184        public String toString() {
185                return "Distort/Field Warp...";
186        }
187
188}