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