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 produces motion blur the faster, but lower-quality way. 025 */ 026public 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 * Set the centre of the effect as a proportion of the image size. 165 * @param centre the center 166 * @see #getCentre 167 */ 168 public void setCentre( Point2D centre ) { 169 this.centreX = (float)centre.getX(); 170 this.centreY = (float)centre.getY(); 171 } 172 173 /** 174 * Get the centre of the effect as a proportion of the image size. 175 * @return the center 176 * @see #setCentre 177 */ 178 public Point2D getCentre() { 179 return new Point2D.Float( centreX, centreY ); 180 } 181 182 private int log2( int n ) { 183 int m = 1; 184 int log2n = 0; 185 186 while (m < n) { 187 m *= 2; 188 log2n++; 189 } 190 return log2n; 191 } 192 193 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 194 if ( dst == null ) 195 dst = createCompatibleDestImage( src, null ); 196 BufferedImage tsrc = src; 197 float cx = (float)src.getWidth() * centreX; 198 float cy = (float)src.getHeight() * centreY; 199 float imageRadius = (float)Math.sqrt( cx*cx + cy*cy ); 200 float translateX = (float)(distance * Math.cos( angle )); 201 float translateY = (float)(distance * -Math.sin( angle )); 202 float scale = zoom; 203 float rotate = rotation; 204 float maxDistance = distance + Math.abs(rotation*imageRadius) + zoom*imageRadius; 205 int steps = log2((int)maxDistance); 206 207 translateX /= maxDistance; 208 translateY /= maxDistance; 209 scale /= maxDistance; 210 rotate /= maxDistance; 211 212 if ( steps == 0 ) { 213 Graphics2D g = dst.createGraphics(); 214 g.drawRenderedImage( src, null ); 215 g.dispose(); 216 return dst; 217 } 218 219 BufferedImage tmp = createCompatibleDestImage( src, null ); 220 for ( int i = 0; i < steps; i++ ) { 221 Graphics2D g = tmp.createGraphics(); 222 g.drawImage( tsrc, null, null ); 223 g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); 224 g.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR ); 225 g.setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.5f ) ); 226 227 g.translate( cx+translateX, cy+translateY ); 228 g.scale( 1.0001+scale, 1.0001+scale ); // The .0001 works round a bug on Windows where drawImage throws an ArrayIndexOutofBoundException 229 if ( rotation != 0 ) 230 g.rotate( rotate ); 231 g.translate( -cx, -cy ); 232 233 g.drawImage( dst, null, null ); 234 g.dispose(); 235 BufferedImage ti = dst; 236 dst = tmp; 237 tmp = ti; 238 tsrc = dst; 239 240 translateX *= 2; 241 translateY *= 2; 242 scale *= 2; 243 rotate *= 2; 244 } 245 return dst; 246 } 247 248 public String toString() { 249 return "Blur/Faster Motion Blur..."; 250 } 251}