/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
package de.cismet.layout;
import java.awt.AlphaComposite;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;
import javax.swing.event.EventListenerList;
/**
* Implements a Component that fades from one image to an other in a given time.
*
* @author jruiz
* @version $Revision$, $Date$
*/
public class FadingPanel extends Component {
//~ Instance fields --------------------------------------------------------
private BufferedImage fadeFromImage;
private BufferedImage fadeToImage;
private EventListenerList fadeListenerList;
private long fadeDuration;
private long startTime = 0;
//~ Constructors -----------------------------------------------------------
/**
* Standard construtor.
*/
public FadingPanel() {
fadeListenerList = new EventListenerList();
}
//~ Methods ----------------------------------------------------------------
/**
* Register a fadingpanel listener.
*
* @param listener DOCUMENT ME!
*/
public void addFadingPanelListener(final FadingPanelListener listener) {
fadeListenerList.add(FadingPanelListener.class, listener);
}
/**
* informs the listener that the fade animation is finished.
*/
private void fireFadeFinished() {
for (final FadingPanelListener listener : fadeListenerList.getListeners(FadingPanelListener.class)) {
listener.fadeFinished();
}
}
/**
* Returns the fade status of the component.
*
* @return true is the component is actualy fading, else false
*/
public boolean isFading() {
return startTime > 0;
}
/**
* Fades from one component to an other, by first creating an image from both components. Then @see
* startFading(BufferedImage, BufferedImage, long)
*
* @param fadeFromComponent DOCUMENT ME!
* @param fadeToComponent DOCUMENT ME!
* @param fadeDuration DOCUMENT ME!
*/
public void startFading(final Component fadeFromComponent,
final Component fadeToComponent,
final long fadeDuration) {
startFading(
createImageFromComponent(fadeFromComponent),
createImageFromComponent(fadeToComponent),
fadeDuration);
}
/**
* Fades an image to an other, in a specific duration of time. If the Component is still fading, then the timer is
* reseted. Else a new fade thread will be started.
*
* @param fadeFromImage the image to fade from
* @param fadeToImage the image to fade to
* @param fadeDuration the duration in ms for the fade animation
*/
public void startFading(final BufferedImage fadeFromImage,
final BufferedImage fadeToImage,
final long fadeDuration) {
this.fadeFromImage = fadeFromImage;
this.fadeToImage = fadeToImage;
this.fadeDuration = fadeDuration;
// schon am faden?
if (isFading()) {
// dann einfach den timer zurücksetzen
resetStartTime();
} else {
// sonst einen neuen fade-thread starten
final SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
// als erstes Zeit zurücksetzen
resetStartTime();
// faden bis fadeDuration erreicht wurde
while (ellapsedTime() < fadeDuration) {
// neu zeichnen (repaint ist thread-safe)
repaint();
}
// Startzeit wieder auf 0 setzen
// dies ist wichtig damit das Objekt weiß, dass das Faden
// beendet wurde (isFading)
startTime = 0;
return null;
}
@Override
protected void done() {
try {
get();
} catch (InterruptedException ex) {
// Exceptions.printStackTrace(ex);
} catch (ExecutionException ex) {
// Exceptions.printStackTrace(ex);
}
// Arbeit fertig, listener informieren
fireFadeFinished();
}
};
worker.execute();
}
}
/**
* DOCUMENT ME!
*
* @param graphics DOCUMENT ME!
*/
@Override
public void update(final Graphics graphics) {
paint(graphics);
}
/**
* DOCUMENT ME!
*
* @param graphics DOCUMENT ME!
*/
@Override
public synchronized void paint(final Graphics graphics) {
// wenn keine Bilder
if ((fadeFromImage == null) || (fadeToImage == null)) {
// dann nix zum Faden
return;
}
float alpha;
// alpha-Faktor berechnen
if (fadeDuration == 0) {
// division durch 0 verhindern
alpha = 1;
} else {
// je mehr Zeit vergeht, desto größer wird alpha
alpha = (float)ellapsedTime() / fadeDuration;
// alpha ist minimal 0 wenn noch keine Zeit vergangen ist,
alpha = (alpha < 0) ? 0 : alpha;
// und maximal 1 wenn die fadeDauer erreicht wurde
alpha = (alpha > 1) ? 1 : alpha;
}
final Graphics2D graphics2d = (Graphics2D)graphics;
// alte Composite sichern
final Composite oldComposite = graphics2d.getComposite();
// "from" immer mehr durchsichtig malen
graphics2d.setComposite(AlphaComposite.SrcOver.derive(1 - alpha));
graphics2d.drawImage(fadeFromImage, null, 0, 0);
// "to" immer weniger durchsichtig malen
graphics2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
graphics2d.drawImage(fadeToImage, null, 0, 0);
// alte Composite wieder setzen
graphics2d.setComposite(oldComposite);
}
/**
* Returns the ellapsed time between now and the last start time.
*
* @return ellapsed time in ms
*/
public long ellapsedTime() {
return System.currentTimeMillis() - startTime;
}
/**
* Resets the start time to now.
*/
public void resetStartTime() {
startTime = System.currentTimeMillis();
}
/**
* Creates an "alpha-compatible" buffered image from a given component.
*
* @param component the component
*
* @return the bufferd image
*/
public static BufferedImage createImageFromComponent(final Component component) {
// keine Komponente?
if (component == null) {
// dann kein Bild
return null;
}
// Standart-Grafik-Konfiguration des Bildschirms holen
final GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration();
// Höhe und Breite der Komponente schonmal merken
final int width = component.getSize().width;
final int height = component.getSize().height;
// Image anhand der Grafik-Einstellungen erzeugen lassen
BufferedImage image;
// unterstützt das verwendete Farbmodel Durchsichtigkeit?
if (graphicsConfiguration.getColorModel().hasAlpha()) {
// dann Image erzeugen lassen
image = graphicsConfiguration.createCompatibleImage(width, height);
} else {
// sonst, Image selbst erzeugen
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
}
// Image wurde richtig erzeugt?
if (image != null) {
// dann Objekt zum Malen in das Image holen
final Graphics graphics = image.getGraphics();
// und die Komponente sich darin malen lassen
component.paint(graphics);
// ressource freigeben
graphics.dispose();
// fertiges Bild zurückliefern
return image;
} else {
// sonst kein Bild
return null;
}
}
}