/*
* Created on Mar 4, 2005
*/
package edu.umd.cs.piccolox.nodes;
import java.awt.Color;
import java.awt.GraphicsEnvironment;
import java.awt.Paint;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PRoot;
import edu.umd.cs.piccolo.activities.PTransformActivity;
import edu.umd.cs.piccolo.util.PAffineTransform;
import edu.umd.cs.piccolo.util.PBounds;
import edu.umd.cs.piccolo.util.PDimension;
import edu.umd.cs.piccolo.util.PPaintContext;
import edu.umd.cs.piccolo.util.PUtil;
/**
* An extension to PCamera that provides a fast image based animationToCenterBounds method
*
* @author Lance Good
*/
public class PCacheCamera extends PCamera {
private BufferedImage paintBuffer;
private boolean imageAnimate;
private PBounds imageAnimateBounds;
/**
* Get the buffer used to provide fast image based animation
*/
protected BufferedImage getPaintBuffer() {
PBounds fRef = getFullBoundsReference();
if (paintBuffer == null || paintBuffer.getWidth() < fRef.getWidth() || paintBuffer.getHeight() < fRef.getHeight()) {
paintBuffer = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage((int)Math.ceil(fRef.getWidth()),(int)Math.ceil(fRef.getHeight()));
}
return paintBuffer;
}
/**
* Caches the information necessary to animate from the current view bounds to the
* specified centerBounds
*/
private AffineTransform cacheViewBounds(Rectangle2D centerBounds, boolean scaleToFit) {
PBounds viewBounds = getViewBounds();
// Initialize the image to the union of the current and destination bounds
PBounds imageBounds = new PBounds(viewBounds);
imageBounds.add(centerBounds);
animateViewToCenterBounds(imageBounds,scaleToFit,0);
imageAnimateBounds = getViewBounds();
// Now create the actual cache image that we will use to animate fast
BufferedImage buffer = getPaintBuffer();
Paint fPaint = Color.white;
if (getPaint() != null) {
fPaint = getPaint();
}
toImage(buffer,fPaint);
// Do this after the painting above!
imageAnimate = true;
// Return the bounds to the previous viewbounds
animateViewToCenterBounds(viewBounds,scaleToFit,0);
// The code below is just copied from animateViewToCenterBounds to create the
// correct transform to center the specified bounds
PDimension delta = viewBounds.deltaRequiredToCenter(centerBounds);
PAffineTransform newTransform = getViewTransform();
newTransform.translate(delta.width, delta.height);
if (scaleToFit) {
double s = Math.min(viewBounds.getWidth() / centerBounds.getWidth(), viewBounds.getHeight() / centerBounds.getHeight());
newTransform.scaleAboutPoint(s, centerBounds.getCenterX(), centerBounds.getCenterY());
}
return newTransform;
}
/**
* Turns off the fast image animation and does any other applicable cleanup
*/
private void clearViewCache() {
imageAnimate = false;
imageAnimateBounds = null;
}
/**
* Mimics the standard animateViewToCenterBounds but uses a cached image for performance
* rather than re-rendering the scene at each step
*/
public PTransformActivity animateStaticViewToCenterBoundsFast(Rectangle2D centerBounds, boolean shouldScaleToFit, long duration) {
if (duration == 0) {
return animateViewToCenterBounds(centerBounds,shouldScaleToFit,duration);
}
AffineTransform newViewTransform = cacheViewBounds(centerBounds,shouldScaleToFit);
return animateStaticViewToTransformFast(newViewTransform, duration);
}
/**
* This copies the behavior of the standard animateViewToTransform but clears the cache
* when it is done
*/
protected PTransformActivity animateStaticViewToTransformFast(AffineTransform destination, long duration) {
if (duration == 0) {
setViewTransform(destination);
return null;
}
PTransformActivity.Target t = new PTransformActivity.Target() {
public void setTransform(AffineTransform aTransform) {
PCacheCamera.this.setViewTransform(aTransform);
}
public void getSourceMatrix(double[] aSource) {
getViewTransformReference().getMatrix(aSource);
}
};
PTransformActivity ta = new PTransformActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, t, destination) {
protected void activityFinished() {
clearViewCache();
repaint();
super.activityFinished();
}
};
PRoot r = getRoot();
if (r != null) {
r.getActivityScheduler().addActivity(ta);
}
return ta;
}
/**
* Overrides the camera's full paint method to do the fast rendering when possible
*/
public void fullPaint(PPaintContext paintContext) {
if (imageAnimate) {
PBounds fRef = getFullBoundsReference();
PBounds viewBounds = getViewBounds();
double scale = getFullBoundsReference().getWidth()/imageAnimateBounds.getWidth();
double xOffset = (viewBounds.getX()-imageAnimateBounds.getX())*scale;
double yOffset = (viewBounds.getY()-imageAnimateBounds.getY())*scale;
double scaleW = viewBounds.getWidth()*scale;
double scaleH = viewBounds.getHeight()*scale;
paintContext.getGraphics().drawImage(paintBuffer,0,0,(int)Math.ceil(fRef.getWidth()),(int)Math.ceil(fRef.getHeight()),
(int)Math.floor(xOffset),(int)Math.floor(yOffset),(int)Math.ceil(xOffset+scaleW),(int)Math.ceil(yOffset+scaleH),null);
}
else {
super.fullPaint(paintContext);
}
}
}