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 distorts an image by twisting it from the centre out.
025 * The twisting is centred at the centre of the image and extends out to the smallest of
026 * the width and height. Pixels outside this radius are unaffected.
027 */
028public class TwirlFilter extends TransformFilter {
029        
030        private float angle = 0;
031        private float centreX = 0.5f;
032        private float centreY = 0.5f;
033        private float radius = 100;
034
035        private float radius2 = 0;
036        private float icentreX;
037        private float icentreY;
038
039        /**
040         * Construct a TwirlFilter with no distortion.
041         */
042        public TwirlFilter() {
043                setEdgeAction( CLAMP );
044        }
045
046        /**
047         * Set the angle of twirl in radians. 0 means no distortion.
048         * @param angle the angle of twirl. This is the angle by which pixels at the nearest edge of the image will move.
049     * @see #getAngle
050         */
051        public void setAngle(float angle) {
052                this.angle = angle;
053        }
054        
055        /**
056         * Get the angle of twist.
057         * @return the angle in radians.
058     * @see #setAngle
059         */
060        public float getAngle() {
061                return angle;
062        }
063        
064        /**
065         * Set the centre of the effect in the X direction as a proportion of the image size.
066         * @param centreX the center
067     * @see #getCentreX
068         */
069        public void setCentreX( float centreX ) {
070                this.centreX = centreX;
071        }
072
073        /**
074         * Get the centre of the effect in the X direction as a proportion of the image size.
075         * @return the center
076     * @see #setCentreX
077         */
078        public float getCentreX() {
079                return centreX;
080        }
081        
082        /**
083         * Set the centre of the effect in the Y direction as a proportion of the image size.
084         * @param centreY the center
085     * @see #getCentreY
086         */
087        public void setCentreY( float centreY ) {
088                this.centreY = centreY;
089        }
090
091        /**
092         * Get the centre of the effect in the Y direction as a proportion of the image size.
093         * @return the center
094     * @see #setCentreY
095         */
096        public float getCentreY() {
097                return centreY;
098        }
099        
100        /**
101         * Set the centre of the effect as a proportion of the image size.
102         * @param centre the center
103     * @see #getCentre
104         */
105        public void setCentre( Point2D centre ) {
106                this.centreX = (float)centre.getX();
107                this.centreY = (float)centre.getY();
108        }
109
110        /**
111         * Get the centre of the effect as a proportion of the image size.
112         * @return the center
113     * @see #setCentre
114         */
115        public Point2D getCentre() {
116                return new Point2D.Float( centreX, centreY );
117        }
118        
119        /**
120         * Set the radius of the effect.
121         * @param radius the radius
122     * @min-value 0
123     * @see #getRadius
124         */
125        public void setRadius(float radius) {
126                this.radius = radius;
127        }
128
129        /**
130         * Get the radius of the effect.
131         * @return the radius
132     * @see #setRadius
133         */
134        public float getRadius() {
135                return radius;
136        }
137
138    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
139                icentreX = src.getWidth() * centreX;
140                icentreY = src.getHeight() * centreY;
141                if ( radius == 0 )
142                        radius = Math.min(icentreX, icentreY);
143                radius2 = radius*radius;
144                return super.filter( src, dst );
145        }
146        
147        protected void transformInverse(int x, int y, float[] out) {
148                float dx = x-icentreX;
149                float dy = y-icentreY;
150                float distance = dx*dx + dy*dy;
151                if (distance > radius2) {
152                        out[0] = x;
153                        out[1] = y;
154                } else {
155                        distance = (float)Math.sqrt(distance);
156                        float a = (float)Math.atan2(dy, dx) + angle * (radius-distance) / radius;
157                        out[0] = icentreX + distance*(float)Math.cos(a);
158                        out[1] = icentreY + distance*(float)Math.sin(a);
159                }
160        }
161
162        public String toString() {
163                return "Distort/Twirl...";
164        }
165
166}