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}