Tuesday, January 19, 2010

Using animated GIF in CLabel

Have you ever tried to use animated GIF in Eclipse RCP? Unlike using regular image you have to provide your own mechanism that switches between image frames when using animated GIF (more about this is here). Fortunately, you can save in implementing your own thread by using org.eclipse.ui.internal.ImageCycleFeedbackBase and org.eclipse.ui.internal.AnimationEngine (though these classes are still in internal package). Let's say we need to add an animated image to a CLabel. First of all, we extend ImageCycleFeedbackBase:

public class AnimatedLabelFeedback extends ImageCycleFeedbackBase {
 private CLabel label;

 public AnimatedLabelFeedback(Shell parentShell, CLabel item, Image[] images) {
  super(parentShell, images);
  label = item;
 }

 public void initialize(AnimationEngine engine) {
  background = label.getParent().getBackground();
  display = label.getParent().getDisplay();
 }

 public void saveStoppedImage() {
  stoppedImage = label.getImage();
 }

 public void setStoppedImage(Image image) {
  label.setImage(image);
 }

 public void showImage(Image image) {
  if (!label.isDisposed()) {
   label.setImage(image);
  }
 }
}

This is how we attach animated GIF to the newly created CLabel:
label = new CLabel(parent, SWT.SHADOW_NONE);
Image[] images = loadAnimatedGIF(parent.getDisplay(), "icons/obj16/animated.gif");
AnimatedLabelFeedback feedback = new AnimatedLabelFeedback(parent.getShell(), label, images));
animation = new AnimationEngine(feedback,
  -1, // endless
  100 // delay between frames in milliseconds);
animation.schedule();

loadAnimatedGIF() is implemented as follows:
private void loadAnimatedGIF(Display display, String imagePath) {
  URL url = FileLocator.find(Activator.getDefault().getBundle(),
    new Path(imagePath), null);
  ImageLoader imageLoader = new ImageLoader();
  imageLoader.load(url.openStream());
  images = new Image[imageLoader.data.length];
  for (int i = 0; i < imageLoader.data.length; ++i) {
    ImageData nextFrameData = imageLoader.data[i];
    images[i] = new Image(display, nextFrameData);
  }
}
Dispose animation engine when it's not needed anymore (usually in dispose() method):
animation.cancelAnimation();

PS: @since 3.3