001    /*
002    *
003    
004    Licensed under the Apache License, Version 2.0 (the "License");
005    you may not use this file except in compliance with the License.
006    You may obtain a copy of the License at
007    
008       http://www.apache.org/licenses/LICENSE-2.0
009    
010    Unless required by applicable law or agreed to in writing, software
011    distributed under the License is distributed on an "AS IS" BASIS,
012    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013    See the License for the specific language governing permissions and
014    limitations under the License.
015    */
016    
017    package railo.runtime.img.filter;import java.awt.AlphaComposite;
018    import java.awt.Graphics2D;
019    import java.awt.RenderingHints;
020    import java.awt.geom.Point2D;
021    import java.awt.image.BufferedImage;
022    
023    /**
024     * A filter which produces motion blur the faster, but lower-quality way.
025     */
026    public class MotionBlurOp extends AbstractBufferedImageOp {
027    
028        private float centreX = 0.5f, centreY = 0.5f;
029        private float distance;
030        private float angle;
031        private float rotation;
032        private float zoom;
033    
034        /**
035         * Construct a MotionBlurOp.
036         */
037        public MotionBlurOp() {
038            }
039            
040        /**
041         * Construct a MotionBlurOp.
042         * @param distance the distance of blur.
043         * @param angle the angle of blur.
044         * @param rotation the angle of rotation.
045         * @param zoom the zoom factor.
046         */
047            public MotionBlurOp( float distance, float angle, float rotation, float zoom ) {
048            this.distance = distance;
049            this.angle = angle;
050            this.rotation = rotation;
051            this.zoom = zoom;
052        }
053        
054            /**
055         * Specifies the angle of blur.
056         * @param angle the angle of blur.
057         * @angle
058         * @see #getAngle
059         */
060            public void setAngle( float angle ) {
061                    this.angle = angle;
062            }
063    
064            /**
065         * Returns the angle of blur.
066         * @return the angle of blur.
067         * @see #setAngle
068         */
069            public float getAngle() {
070                    return angle;
071            }
072            
073            /**
074         * Set the distance of blur.
075         * @param distance the distance of blur.
076         * @see #getDistance
077         */
078            public void setDistance( float distance ) {
079                    this.distance = distance;
080            }
081    
082            /**
083         * Get the distance of blur.
084         * @return the distance of blur.
085         * @see #setDistance
086         */
087            public float getDistance() {
088                    return distance;
089            }
090            
091            /**
092         * Set the blur rotation.
093         * @param rotation the angle of rotation.
094         * @see #getRotation
095         */
096            public void setRotation( float rotation ) {
097                    this.rotation = rotation;
098            }
099    
100            /**
101         * Get the blur rotation.
102         * @return the angle of rotation.
103         * @see #setRotation
104         */
105            public float getRotation() {
106                    return rotation;
107            }
108            
109            /**
110         * Set the blur zoom.
111         * @param zoom the zoom factor.
112         * @see #getZoom
113         */
114            public void setZoom( float zoom ) {
115                    this.zoom = zoom;
116            }
117    
118            /**
119         * Get the blur zoom.
120         * @return the zoom factor.
121         * @see #setZoom
122         */
123            public float getZoom() {
124                    return zoom;
125            }
126            
127            /**
128             * Set the centre of the effect in the X direction as a proportion of the image size.
129             * @param centreX the center
130         * @see #getCentreX
131             */
132            public void setCentreX( float centreX ) {
133                    this.centreX = centreX;
134            }
135    
136            /**
137             * Get the centre of the effect in the X direction as a proportion of the image size.
138             * @return the center
139         * @see #setCentreX
140             */
141            public float getCentreX() {
142                    return centreX;
143            }
144            
145            /**
146             * Set the centre of the effect in the Y direction as a proportion of the image size.
147             * @param centreY the center
148         * @see #getCentreY
149             */
150            public void setCentreY( float centreY ) {
151                    this.centreY = centreY;
152            }
153    
154            /**
155             * Get the centre of the effect in the Y direction as a proportion of the image size.
156             * @return the center
157         * @see #setCentreY
158             */
159            public float getCentreY() {
160                    return centreY;
161            }
162    
163            /**
164             * Get the centre of the effect as a proportion of the image size.
165             * @return the center
166         * @see #setCentre
167             */
168            public Point2D getCentre() {
169                    return new Point2D.Float( centreX, centreY );
170            }
171            
172        private int log2( int n ) {
173            int m = 1;
174            int log2n = 0;
175    
176            while (m < n) {
177                m *= 2;
178                log2n++;
179            }
180            return log2n;
181        }
182    
183        public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
184            if ( dst == null )
185                dst = createCompatibleDestImage( src, null );
186            BufferedImage tsrc = src;
187            float cx = src.getWidth() * centreX;
188            float cy = src.getHeight() * centreY;
189            float imageRadius = (float)Math.sqrt( cx*cx + cy*cy );
190            float translateX = (float)(distance * Math.cos( angle ));
191            float translateY = (float)(distance * -Math.sin( angle ));
192            float scale = zoom;
193            float rotate = rotation;
194            float maxDistance = distance + Math.abs(rotation*imageRadius) + zoom*imageRadius;
195            int steps = log2((int)maxDistance);
196    
197                    translateX /= maxDistance;
198                    translateY /= maxDistance;
199                    scale /= maxDistance;
200                    rotate /= maxDistance;
201                    
202            if ( steps == 0 ) {
203                Graphics2D g = dst.createGraphics();
204                g.drawRenderedImage( src, null );
205                g.dispose();
206                return dst;
207            }
208            
209            BufferedImage tmp = createCompatibleDestImage( src, null );
210            for ( int i = 0; i < steps; i++ ) {
211                Graphics2D g = tmp.createGraphics();
212                g.drawImage( tsrc, null, null );
213                            g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
214                            g.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR );
215                            g.setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.5f ) );
216    
217                g.translate( cx+translateX, cy+translateY );
218                g.scale( 1.0001+scale, 1.0001+scale );  // The .0001 works round a bug on Windows where drawImage throws an ArrayIndexOutofBoundException
219                if ( rotation != 0 )
220                    g.rotate( rotate );
221                g.translate( -cx, -cy );
222    
223                g.drawImage( dst, null, null );
224                g.dispose();
225                BufferedImage ti = dst;
226                dst = tmp;
227                tmp = ti;
228                tsrc = dst;
229    
230                translateX *= 2;
231                translateY *= 2;
232                scale *= 2;
233                rotate *= 2;
234            }
235            return dst;
236        }
237        
238            public String toString() {
239                    return "Blur/Faster Motion Blur...";
240            }
241    }