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.geom.*;
021import java.awt.image.*;
022
023/**
024 * A filter which performs the popular whirl-and-pinch distortion effect.
025 */
026public class PinchFilter extends TransformFilter {
027
028        private float angle = 0;
029        private float centreX = 0.5f;
030        private float centreY = 0.5f;
031        private float radius = 100;
032        private float amount = 0.5f;
033
034        private float radius2 = 0;
035        private float icentreX;
036        private float icentreY;
037        private float width;
038        private float height;
039        
040        public PinchFilter() {
041        }
042
043        /**
044         * Set the angle of twirl in radians. 0 means no distortion.
045         * @param angle the angle of twirl. This is the angle by which pixels at the nearest edge of the image will move.
046     * @see #getAngle
047         */
048        public void setAngle(float angle) {
049                this.angle = angle;
050        }
051        
052        /**
053         * Get the angle of twist.
054         * @return the angle in radians.
055     * @see #setAngle
056         */
057        public float getAngle() {
058                return angle;
059        }
060        
061        /**
062         * Set the centre of the effect in the X direction as a proportion of the image size.
063         * @param centreX the center
064     * @see #getCentreX
065         */
066        public void setCentreX( float centreX ) {
067                this.centreX = centreX;
068        }
069
070        /**
071         * Get the centre of the effect in the X direction as a proportion of the image size.
072         * @return the center
073     * @see #setCentreX
074         */
075        public float getCentreX() {
076                return centreX;
077        }
078        
079        /**
080         * Set the centre of the effect in the Y direction as a proportion of the image size.
081         * @param centreY the center
082     * @see #getCentreY
083         */
084        public void setCentreY( float centreY ) {
085                this.centreY = centreY;
086        }
087
088        /**
089         * Get the centre of the effect in the Y direction as a proportion of the image size.
090         * @return the center
091     * @see #setCentreY
092         */
093        public float getCentreY() {
094                return centreY;
095        }
096        
097        /**
098         * Set the centre of the effect as a proportion of the image size.
099         * @param centre the center
100     * @see #getCentre
101         */
102        public void setCentre( Point2D centre ) {
103                this.centreX = (float)centre.getX();
104                this.centreY = (float)centre.getY();
105        }
106
107        /**
108         * Get the centre of the effect as a proportion of the image size.
109         * @return the center
110     * @see #setCentre
111         */
112        public Point2D getCentre() {
113                return new Point2D.Float( centreX, centreY );
114        }
115        
116        /**
117         * Set the radius of the effect.
118         * @param radius the radius
119     * @min-value 0
120     * @see #getRadius
121         */
122        public void setRadius(float radius) {
123                this.radius = radius;
124        }
125
126        /**
127         * Get the radius of the effect.
128         * @return the radius
129     * @see #setRadius
130         */
131        public float getRadius() {
132                return radius;
133        }
134
135        /**
136         * Set the amount of pinch.
137         * @param amount the amount
138     * @min-value -1
139     * @max-value 1
140     * @see #getAmount
141         */
142        public void setAmount(float amount) {
143                this.amount = amount;
144        }
145
146        /**
147         * Get the amount of pinch.
148         * @return the amount
149     * @see #setAmount
150         */
151        public float getAmount() {
152                return amount;
153        }
154
155    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
156                width = src.getWidth();
157                height = src.getHeight();
158                icentreX = width * centreX;
159                icentreY = height * centreY;
160                if ( radius == 0 )
161                        radius = Math.min(icentreX, icentreY);
162                radius2 = radius*radius;
163                return super.filter( src, dst );
164        }
165        
166        protected void transformInverse(int x, int y, float[] out) {
167                float dx = x-icentreX;
168                float dy = y-icentreY;
169                float distance = dx*dx + dy*dy;
170
171                if ( distance > radius2 || distance == 0 ) {
172                        out[0] = x;
173                        out[1] = y;
174                } else {
175                        float d = (float)Math.sqrt( distance / radius2 );
176                        float t = (float)Math.pow( Math.sin( Math.PI*0.5 * d ), -amount);
177
178                        dx *= t;
179                        dy *= t;
180
181                        float e = 1 - d;
182                        float a = angle * e * e;
183
184                        float s = (float)Math.sin( a );
185                        float c = (float)Math.cos( a );
186
187                        out[0] = icentreX + c*dx - s*dy;
188                        out[1] = icentreY + s*dx + c*dy;
189                }
190        }
191
192        public String toString() {
193                return "Distort/Pinch...";
194        }
195
196}