import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Checkbox;
import java.awt.Choice;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Label;
import java.awt.MediaTracker;
import java.awt.Panel;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.text.NumberFormat;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TextBouncer extends JPanel implements Runnable {
private boolean trucking = true;
private long[] previousTimes; // milliseconds
private int previousIndex;
private boolean previousFilled;
private double frameRate; // frames per second
private Image image = null;
public static void main(String[] args) {
String s = "Java Source and Support";
final int size = 64;
if (args.length > 0)
s = args[0];
Panel controls = new Panel();
final Choice choice = new Choice();
GraphicsEnvironment ge = GraphicsEnvironment
.getLocalGraphicsEnvironment();
Font[] allFonts = ge.getAllFonts();
for (int i = 0; i < allFonts.length; i++)
choice.addItem(allFonts[i].getName());
Font defaultFont = new Font(allFonts[0].getName(), Font.PLAIN, size);
final TextBouncer bouncer = new TextBouncer(s, defaultFont);
Frame f = new AnimationFrame(bouncer);
f.setFont(new Font("Serif", Font.PLAIN, 12));
controls.add(bouncer.createCheckbox("Antialiasing",
TextBouncer.ANTIALIASING));
controls.add(bouncer.createCheckbox("Gradient", TextBouncer.GRADIENT));
controls.add(bouncer.createCheckbox("Shear", TextBouncer.SHEAR));
controls.add(bouncer.createCheckbox("Rotate", TextBouncer.ROTATE));
controls.add(bouncer.createCheckbox("Axes", TextBouncer.AXES));
Panel fontControls = new Panel();
choice.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent ie) {
Font font = new Font(choice.getSelectedItem(), Font.PLAIN, size);
bouncer.setFont(font);
}
});
fontControls.add(choice);
Panel allControls = new Panel(new GridLayout(2, 1));
allControls.add(controls);
allControls.add(fontControls);
f.add(allControls, BorderLayout.NORTH);
f.setSize(300,300);
f.setVisible(true);
}
private boolean mAntialiasing = false, mGradient = false;
private boolean mShear = false, mRotate = false, mAxes = false;
public static final int ANTIALIASING = 0;
public static final int GRADIENT = 1;
public static final int SHEAR = 2;
public static final int ROTATE = 3;
public static final int AXES = 5;
private float mDeltaX, mDeltaY;
private float mX, mY, mWidth, mHeight;
private float mTheta;
private float mShearX, mShearY, mShearDeltaX, mShearDeltaY;
private String mString;
public TextBouncer(String s, Font f) {
previousTimes = new long[128];
previousTimes[0] = System.currentTimeMillis();
previousIndex = 1;
previousFilled = false;
mString = s;
setFont(f);
Random random = new Random();
mX = random.nextFloat() * 500;
mY = random.nextFloat() * 500;
mDeltaX = random.nextFloat() * 3;
mDeltaY = random.nextFloat() * 3;
mShearX = random.nextFloat() / 2;
mShearY = random.nextFloat() / 2;
mShearDeltaX = mShearDeltaY = .05f;
FontRenderContext frc = new FontRenderContext(null, true, false);
Rectangle2D bounds = getFont().getStringBounds(mString, frc);
mWidth = (float) bounds.getWidth();
mHeight = (float) bounds.getHeight();
// Make sure points are within range.
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent ce) {
Dimension d = getSize();
if (mX < 0)
mX = 0;
else if (mX + mWidth >= d.width)
mX = d.width - mWidth - 1;
if (mY < 0)
mY = 0;
else if (mY + mHeight >= d.height)
mY = d.height - mHeight - 1;
}
});
}
public void setSwitch(int item, boolean value) {
switch (item) {
case ANTIALIASING:
mAntialiasing = value;
break;
case GRADIENT:
mGradient = value;
break;
case SHEAR:
mShear = value;
break;
case ROTATE:
mRotate = value;
break;
case AXES:
mAxes = value;
break;
default:
break;
}
}
protected Checkbox createCheckbox(String label, final int item) {
Checkbox check = new Checkbox(label);
check.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent ie) {
setSwitch(item, (ie.getStateChange() == ie.SELECTED));
}
});
return check;
}
public void timeStep() {
Dimension d = getSize();
if (mX + mDeltaX < 0)
mDeltaX = -mDeltaX;
else if (mX + mWidth + mDeltaX >= d.width)
mDeltaX = -mDeltaX;
if (mY + mDeltaY < 0)
mDeltaY = -mDeltaY;
else if (mY + mHeight + mDeltaY >= d.height)
mDeltaY = -mDeltaY;
mX += mDeltaX;
mY += mDeltaY;
mTheta += Math.PI / 192;
if (mTheta > (2 * Math.PI))
mTheta -= (2 * Math.PI);
if (mShearX + mShearDeltaX > .5)
mShearDeltaX = -mShearDeltaX;
else if (mShearX + mShearDeltaX < -.5)
mShearDeltaX = -mShearDeltaX;
if (mShearY + mShearDeltaY > .5)
mShearDeltaY = -mShearDeltaY;
else if (mShearY + mShearDeltaY < -.5)
mShearDeltaY = -mShearDeltaY;
mShearX += mShearDeltaX;
mShearY += mShearDeltaY;
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
setAntialiasing(g2);
setTransform(g2);
setPaint(g2);
// Draw the string.
g2.setFont(getFont());
g2.drawString(mString, mX, mY + mHeight);
drawAxes(g2);
}
protected void setAntialiasing(Graphics2D g2) {
if (mAntialiasing == false)
return;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
protected void setTransform(Graphics2D g2) {
Dimension d = getSize();
int cx = d.width / 2;
int cy = d.height / 2;
g2.translate(cx, cy);
if (mShear)
g2.shear(mShearX, mShearY);
if (mRotate)
g2.rotate(mTheta);
g2.translate(-cx, -cy);
}
protected void setPaint(Graphics2D g2) {
if (mGradient) {
GradientPaint gp = new GradientPaint(0, 0, Color.blue, 50, 25,
Color.green, true);
g2.setPaint(gp);
} else
g2.setPaint(Color.orange);
}
protected void drawAxes(Graphics2D g2) {
if (mAxes == false)
return;
g2.setPaint(getForeground());
g2.setStroke(new BasicStroke());
Dimension d = getSize();
int side = 20;
int arrow = 4;
int w = d.width / 2, h = d.height / 2;
g2.drawLine(w - side, h, w + side, h);
g2.drawLine(w + side - arrow, h - arrow, w + side, h);
g2.drawLine(w, h - side, w, h + side);
g2.drawLine(w + arrow, h + side - arrow, w, h + side);
}
public void run() {
while (trucking) {
render();
timeStep();
calculateFrameRate();
}
}
protected void render() {
Graphics g = getGraphics();
if (g != null) {
Dimension d = getSize();
if (checkImage(d)) {
Graphics imageGraphics = image.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(image, 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 (image == null || image.getWidth(null) != d.width
|| image.getHeight(null) != d.height) {
image = createImage(d.width, d.height);
}
return true;
}
protected void calculateFrameRate() {
// Measure the frame rate
long now = System.currentTimeMillis();
int numberOfFrames = previousTimes.length;
double newRate;
// Use the more stable method if a history is available.
if (previousFilled)
newRate = (double) numberOfFrames
/ (double) (now - previousTimes[previousIndex]) * 1000.0;
else
newRate = 1000.0 / (double) (now - previousTimes[numberOfFrames - 1]);
firePropertyChange("frameRate", frameRate, newRate);
frameRate = newRate;
// Update the history.
previousTimes[previousIndex] = now;
previousIndex++;
if (previousIndex >= numberOfFrames) {
previousIndex = 0;
previousFilled = true;
}
}
public double getFrameRate() {
return frameRate;
}
// Property change support.
private transient AnimationFrame mRateListener;
public void setRateListener(AnimationFrame af) {
mRateListener = af;
}
public void firePropertyChange(String name, double oldValue, double newValue) {
mRateListener.rateChanged(newValue);
}
private static Component sComponent = new Component() {
};
private static final MediaTracker sTracker = new MediaTracker(sComponent);
private static int sID = 0;
public static boolean waitForImage(Image image) {
int id;
synchronized (sComponent) {
id = sID++;
}
sTracker.addImage(image, id);
try {
sTracker.waitForID(id);
} catch (InterruptedException ie) {
return false;
}
if (sTracker.isErrorID(id))
return false;
return true;
}
public Image blockingLoad(String path) {
Image image = Toolkit.getDefaultToolkit().getImage(path);
if (waitForImage(image) == false)
return null;
return image;
}
public static Image blockingLoad(URL url) {
Image image = Toolkit.getDefaultToolkit().getImage(url);
if (waitForImage(image) == false)
return null;
return image;
}
public BufferedImage makeBufferedImage(Image image) {
return makeBufferedImage(image, BufferedImage.TYPE_INT_RGB);
}
public BufferedImage makeBufferedImage(Image image, int imageType) {
if (waitForImage(image) == false)
return null;
BufferedImage bufferedImage = new BufferedImage(image.getWidth(null),
image.getHeight(null), imageType);
Graphics2D g2 = bufferedImage.createGraphics();
g2.drawImage(image, null, null);
return bufferedImage;
}
}
class AnimationFrame extends JFrame {
private Label mStatusLabel;
private NumberFormat mFormat;
public AnimationFrame(TextBouncer ac) {
super();
setLayout(new BorderLayout());
add(ac, BorderLayout.CENTER);
add(mStatusLabel = new Label(), BorderLayout.SOUTH);
// Create a number formatter.
mFormat = NumberFormat.getInstance();
mFormat.setMaximumFractionDigits(1);
// Listen for the frame rate changes.
ac.setRateListener(this);
// Kick off the animation.
Thread t = new Thread(ac);
t.start();
}
public void rateChanged(double frameRate) {
mStatusLabel.setText(mFormat.format(frameRate) + " fps");
}
}
|