import java.awt.*; public abstract class AnimationComponent extends Container implements Runnable { private boolean mTrucking = true; private long[] mPreviousTimes; // milliseconds private int mPreviousIndex; private boolean mPreviousFilled; private double mFrameRate; // frames per second private Image mImage; public AnimationComponent() { mPreviousTimes = new long[128]; mPreviousTimes[0] = System.currentTimeMillis(); mPreviousIndex = 1; mPreviousFilled = false; } public abstract void timeStep(); public void run() { while (mTrucking) { render(); timeStep(); calculateFrameRate(); } } protected void render() { Graphics g = getGraphics(); if (g != null) { Dimension d = getSize(); if (checkImage(d)) { Graphics imageGraphics = mImage.getGraphics(); // Clear the image background. imageGraphics.setColor(getBackground()); imageGraphics.fillRect(0, 0, d.width, d.height); imageGraphics.setColor(getForeground()); // Draw this component offscreen. paint(imageGraphics); // Now put the offscreen image on the screen. g.drawImage(mImage, 0, 0, null); // Clean up. imageGraphics.dispose(); } g.dispose(); } } // Offscreen image. protected boolean checkImage(Dimension d) { if (d.width == 0 || d.height == 0) return false; if (mImage == null || mImage.getWidth(null) != d.width || mImage.getHeight(null) != d.height) { mImage = createImage(d.width, d.height); } return true; } protected void calculateFrameRate() { // Measure the frame rate long now = System.currentTimeMillis(); int numberOfFrames = mPreviousTimes.length; double newRate; // Use the more stable method if a history is available. if (mPreviousFilled) newRate = (double)numberOfFrames / (double)(now - mPreviousTimes[mPreviousIndex]) * 1000.0; else newRate = 1000.0 / (double)(now - mPreviousTimes[numberOfFrames - 1]); firePropertyChange("frameRate", mFrameRate, newRate); mFrameRate = newRate; // Update the history. mPreviousTimes[mPreviousIndex] = now; mPreviousIndex++; if (mPreviousIndex >= numberOfFrames) { mPreviousIndex = 0; mPreviousFilled = true; } } public double getFrameRate() { return mFrameRate; } // Property change support. private transient AnimationFrame mRateListener; public void setRateListener(AnimationFrame af) { mRateListener = af; } protected void firePropertyChange(String name, double oldValue, double newValue) { mRateListener.rateChanged(newValue); } }