FileDocCategorySizeDatePackage
AnimatedPanel.javaAPI DocExample6778Mon Jan 09 11:02:00 GMT 2006None

AnimatedPanel.java

/**
 * 18:05 17/02/2005
 * Romain Guy <romain.guy@jext.org>
 * Subject to the BSD license.
 */

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;

import javax.swing.ImageIcon;
import javax.swing.JPanel;

/**
 * Display an animated panel. The panel contains a picture and a text message.
 * As soon as <code>start()</code> is called, the pictures and the text glow
 * in cycles. The animation can be stopped at anytime by calling
 * <code>stop()</code>. You can set the font and its color by calling
 * <code>setFont()</code> and <code>setForeground()</code>.
 * 
 * @author Romain Guy
 */
public class AnimatedPanel extends JPanel {

    protected float gradient;
    protected String message;
    protected Thread animator;
    protected BufferedImage convolvedImage;
    protected BufferedImage originalImage;
    protected static AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER);

    /**
     * Creates an animated panel with a message and a picture.
     * 
     * @param message The message to display, cannot be null nor empty.
     * @param icon The picture to display
     */
    public AnimatedPanel(String message, ImageIcon icon) {
        this.message = message;

        Image image = icon.getImage();
        originalImage = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
        convolvedImage = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics g = originalImage.createGraphics();
        g.drawImage(image, 0, 0, this);
        g.dispose();

        setBrightness(1.0f);
        setOpaque(false);
    }

    public Dimension getPreferredSize() {
        FontRenderContext context = ((Graphics2D) getGraphics()).getFontRenderContext();
        TextLayout layout = new TextLayout(message, getFont(), context);
        Rectangle2D bounds = layout.getBounds();
        return new Dimension(originalImage.getWidth() + 12,
                             (int) (originalImage.getHeight() + bounds.getHeight() + layout.getAscent() + 12));
    }

    /**
     * Changes the displayed message at runtime.
     *
     * @param text The message to be displayed. Can be null or empty.
     */
    public void setText(String text)
    {
        this.message = text;
        repaint();
    }

    /**
     * Returns the current displayed message.
     */
    public String getText()
    {
        return message;
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        if (convolvedImage != null) {
            int width = getWidth();
            int height = getHeight();

            synchronized (convolvedImage) {
                Graphics2D g2 = (Graphics2D) g;
                g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);

                FontRenderContext context = g2.getFontRenderContext();
                TextLayout layout = new TextLayout(message, getFont(), context);
                Rectangle2D bounds = layout.getBounds();

                int x = (width - convolvedImage.getWidth(null)) / 2;
                int y = (int) (height - (convolvedImage.getHeight(null) + bounds.getHeight() + layout.getAscent())) / 2;

                g2.drawImage(convolvedImage, x, y, this);
                Color foreground = getForeground();
                g2.setColor(new Color(foreground.getRed(), foreground.getGreen(), foreground.getBlue(),
                                      (int) (gradient * 255)));
                layout.draw(g2, (float) (width - bounds.getWidth()) / 2,
                    (float) (y + convolvedImage.getHeight(null) + bounds.getHeight() + layout.getAscent()));
            }
        }
    }
    
    /**
     * Changes the image luminosity.
     */
    private void setBrightness(float multiple) {
        float[] brightKernel = { multiple };
        RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        BufferedImageOp bright = new ConvolveOp(new Kernel(1, 1, brightKernel), ConvolveOp.EDGE_NO_OP, hints);
        bright.filter(originalImage, convolvedImage);
        repaint();
    }

    /**
     * Changes the text gradient control value.
     */
    private void setGradientFactor(float gradient) {
        this.gradient = gradient;
    }

    /**
     * Starts the animation. A thread called "Highlighter" is spawned and can be
     * interrupted at anytime by invoking <code>stop()</code>.
     */
    public void start() {
        this.animator = new Thread(new HighlightCycler(), "Highlighter");
        this.animator.start();
    }

    /**
     * Safely stops the animation.
     */
    public void stop() {
        if (this.animator != null) {
            setBrightness(1.0f);
            setGradientFactor(0.0f);
            this.animator.interrupt();
        }
        this.animator = null;
    }

    /**
     * Makes the image luminosity and the text gradient to cycle.
     */
    class HighlightCycler implements Runnable {

        private int way = 1;
        private final int LOWER_BOUND = 10;
        private final int UPPER_BOUND = 35;
        private int value = LOWER_BOUND;

        public void run() {
            while (true) {
                try {
                    Thread.sleep(1000 / (UPPER_BOUND - LOWER_BOUND));
                } catch (InterruptedException e) {
                    return;
                }

                value += this.way;
                if (value > UPPER_BOUND) {
                    value = UPPER_BOUND;
                    this.way = -1;
                } else if (value < LOWER_BOUND) {
                    value = LOWER_BOUND;
                    this.way = 1;
                }

                synchronized (convolvedImage) {
                    setBrightness((float) value / 10);
                    setGradientFactor((float) value / UPPER_BOUND);
                }
            }
        }
    }
}