/* Copyright 2006 Jerry Huxtable Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package com.jhlabs.image; import java.awt.AlphaComposite; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; /** * A filter which produces motion blur the faster, but lower-quality way. */ public class MotionBlurOp extends AbstractBufferedImageOp implements MotionBlur { private float centreX = 0.5f, centreY = 0.5f; private float distance; private float angle; private float rotation; private float zoom; /** * Construct a MotionBlurOp. */ public MotionBlurOp(String filterName) { super(filterName); } /** * Specifies the angle of blur. * * @param angle the angle of blur. * @angle * @see #getAngle */ @Override public void setAngle(float angle) { this.angle = angle; } /** * Returns the angle of blur. * * @return the angle of blur. * @see #setAngle */ @Override public float getAngle() { return angle; } /** * Set the distance of blur. * * @param distance the distance of blur. * @see #getDistance */ @Override public void setDistance(float distance) { this.distance = distance; } /** * Get the distance of blur. * * @return the distance of blur. * @see #setDistance */ @Override public float getDistance() { return distance; } /** * Set the blur rotation. * * @param rotation the angle of rotation. * @see #getRotation */ @Override public void setRotation(float rotation) { this.rotation = rotation; } /** * Get the blur rotation. * * @return the angle of rotation. * @see #setRotation */ @Override public float getRotation() { return rotation; } /** * Set the blur zoom. * * @param zoom the zoom factor. * @see #getZoom */ @Override public void setZoom(float zoom) { this.zoom = zoom; } /** * Get the blur zoom. * * @return the zoom factor. * @see #setZoom */ @Override public float getZoom() { return zoom; } /** * Set the centre of the effect in the X direction as a proportion of the image size. * * @param centreX the center * @see #getCentreX */ @Override public void setCentreX(float centreX) { this.centreX = centreX; } /** * Get the centre of the effect in the X direction as a proportion of the image size. * * @return the center * @see #setCentreX */ @Override public float getCentreX() { return centreX; } /** * Set the centre of the effect in the Y direction as a proportion of the image size. * * @param centreY the center * @see #getCentreY */ @Override public void setCentreY(float centreY) { this.centreY = centreY; } /** * Get the centre of the effect in the Y direction as a proportion of the image size. * * @return the center * @see #setCentreY */ @Override public float getCentreY() { return centreY; } /** * Set the centre of the effect as a proportion of the image size. * * @param centre the center * @see #getCentre */ @Override public void setCentre(Point2D centre) { this.centreX = (float) centre.getX(); this.centreY = (float) centre.getY(); } /** * Get the centre of the effect as a proportion of the image size. * * @return the center * @see #setCentre */ @Override public Point2D getCentre() { return new Point2D.Float(centreX, centreY); } private static int log2(int n) { int m = 1; int log2n = 0; while (m < n) { m *= 2; log2n++; } return log2n; } @Override public BufferedImage filter(BufferedImage src, BufferedImage dst) { if (dst == null) { dst = createCompatibleDestImage(src, null); } BufferedImage tsrc = src; float cx = (float) src.getWidth() * centreX; float cy = (float) src.getHeight() * centreY; float imageRadius = (float) Math.sqrt(cx * cx + cy * cy); float translateX = (float) (distance * Math.cos(angle)); float translateY = (float) (distance * -Math.sin(angle)); float scale = zoom; float rotate = rotation; float maxDistance = distance + Math.abs(rotation * imageRadius) + zoom * imageRadius; int steps = log2((int) maxDistance); translateX /= maxDistance; translateY /= maxDistance; scale /= maxDistance; rotate /= maxDistance; if (steps == 0) { Graphics2D g = dst.createGraphics(); g.drawRenderedImage(src, null); g.dispose(); return dst; } pt = createProgressTracker(steps); AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f); BufferedImage tmp = createCompatibleDestImage(src, null); for (int i = 0; i < steps; i++) { Graphics2D g = tmp.createGraphics(); g.drawImage(tsrc, null, null); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.setComposite(alphaComposite); g.translate(cx + translateX, cy + translateY); g.scale(1.0001 + scale, 1.0001 + scale); // The .0001 works round a bug on Windows where drawImage throws an ArrayIndexOutofBoundException if (rotation != 0) { g.rotate(rotate); } g.translate(-cx, -cy); g.drawImage(dst, null, null); g.dispose(); BufferedImage ti = dst; dst = tmp; tmp = ti; tsrc = dst; translateX *= 2; translateY *= 2; scale *= 2; rotate *= 2; pt.unitDone(); } finishProgressTracker(); return dst; } public String toString() { return "Blur/Faster Motion Blur..."; } }