TextBouncer.java Source code

Java tutorial

Introduction

Here is the source code for TextBouncer.java

Source

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");
    }

}