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 simulates a lens placed over an image.
025 */
026public class SphereFilter extends TransformFilter {
027
028        private float a = 0;
029        private float b = 0;
030        private float a2 = 0;
031        private float b2 = 0;
032        private float centreX = 0.5f;
033        private float centreY = 0.5f;
034        private float refractionIndex = 1.5f;
035
036        private float icentreX;
037        private float icentreY;
038
039        public SphereFilter() {
040                setEdgeAction( CLAMP );
041                setRadius( 100.0f );
042        }
043
044        /**
045         * Set the index of refaction.
046         * @param refractionIndex the index of refaction
047     * @see #getRefractionIndex
048         */
049        public void setRefractionIndex(float refractionIndex) {
050                this.refractionIndex = refractionIndex;
051        }
052
053        /**
054         * Get the index of refaction.
055         * @return the index of refaction
056     * @see #setRefractionIndex
057         */
058        public float getRefractionIndex() {
059                return refractionIndex;
060        }
061
062        /**
063         * Set the radius of the effect.
064         * @param r the radius
065     * @min-value 0
066     * @see #getRadius
067         */
068        public void setRadius(float r) {
069                this.a = r;
070                this.b = r;
071        }
072
073        /**
074         * Get the radius of the effect.
075         * @return the radius
076     * @see #setRadius
077         */
078        public float getRadius() {
079                return a;
080        }
081
082        /**
083         * Set the centre of the effect in the X direction as a proportion of the image size.
084         * @param centreX the center
085     * @see #getCentreX
086         */
087        public void setCentreX( float centreX ) {
088                this.centreX = centreX;
089        }
090
091        public float getCentreX() {
092                return centreX;
093        }
094        
095        /**
096         * Set the centre of the effect in the Y direction as a proportion of the image size.
097         * @param centreY the center
098     * @see #getCentreY
099         */
100        public void setCentreY( float centreY ) {
101                this.centreY = centreY;
102        }
103
104        /**
105         * Get the centre of the effect in the Y direction as a proportion of the image size.
106         * @return the center
107     * @see #setCentreY
108         */
109        public float getCentreY() {
110                return centreY;
111        }
112        
113        /**
114         * Set the centre of the effect as a proportion of the image size.
115         * @param centre the center
116     * @see #getCentre
117         */
118        public void setCentre( Point2D centre ) {
119                this.centreX = (float)centre.getX();
120                this.centreY = (float)centre.getY();
121        }
122
123        /**
124         * Get the centre of the effect as a proportion of the image size.
125         * @return the center
126     * @see #setCentre
127         */
128        public Point2D getCentre() {
129                return new Point2D.Float( centreX, centreY );
130        }
131        
132    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
133                int width = src.getWidth();
134                int height = src.getHeight();
135                icentreX = width * centreX;
136                icentreY = height * centreY;
137                if (a == 0)
138                        a = width/2;
139                if (b == 0)
140                        b = height/2;
141                a2 = a*a;
142                b2 = b*b;
143                return super.filter( src, dst );
144        }
145        
146        protected void transformInverse(int x, int y, float[] out) {
147                float dx = x-icentreX;
148                float dy = y-icentreY;
149                float x2 = dx*dx;
150                float y2 = dy*dy;
151                if (y2 >= (b2 - (b2*x2)/a2)) {
152                        out[0] = x;
153                        out[1] = y;
154                } else {
155                        float rRefraction = 1.0f / refractionIndex;
156
157                        float z = (float)Math.sqrt((1.0f - x2/a2 - y2/b2) * (a*b));
158                        float z2 = z*z;
159
160                        float xAngle = (float)Math.acos(dx / Math.sqrt(x2+z2));
161                        float angle1 = ImageMath.HALF_PI - xAngle;
162                        float angle2 = (float)Math.asin(Math.sin(angle1)*rRefraction);
163                        angle2 = ImageMath.HALF_PI - xAngle - angle2;
164                        out[0] = x - (float)Math.tan(angle2)*z;
165
166                        float yAngle = (float)Math.acos(dy / Math.sqrt(y2+z2));
167                        angle1 = ImageMath.HALF_PI - yAngle;
168                        angle2 = (float)Math.asin(Math.sin(angle1)*rRefraction);
169                        angle2 = ImageMath.HALF_PI - yAngle - angle2;
170                        out[1] = y - (float)Math.tan(angle2)*z;
171                }
172        }
173
174        public String toString() {
175                return "Distort/Sphere...";
176        }
177
178}