Eclipse RCP不支持Gif格式动画,手动实现(支持Gif和其他静态格式图片)

446 阅读2分钟

Eclipse RCP 是没有原生API支持Gif动画播放,需要自己手动实现。
大家都知道Gif动画由多帧image组合而成。所以顺利实现Gif播放就是读取到Gif源文件并将包含的多帧image完全读取出来,并按一定时间间隔播放即可。
** 源代码如下:**
借鉴了 喜来乐哈哈代码,特此声明引用。

/**
  * 负责显示各种格式的图片
  * 
  * @author 喜来乐哈哈
  */

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.draw2d.TextUtilities;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Pattern;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

public class ImageViewer extends Composite {

	private static final String NO_PICTURE = "No Picture!";
	String key;// 为每个gif格式图片做标识的key,一般使用图片名称
	String imageKey;// 为每个图片都做内存缓存

	Map<String, Image[]> gifCache = new ConcurrentHashMap<>(10);
	Map<String, Image> imageCache = new ConcurrentHashMap<>(10);
	/**
	 * 图片堆叠类型
	 */
	int picType = 0; // 0 居中 1 平铺 2拉伸

	protected Point origin = new Point(0, 0);
	protected Image image;
	protected ImageData[] imageDatas;
	protected Image[] images;
	protected int current;

	private int repeatCount;
	private Runnable animationTimer;
	private Color bg;
	private Display display;

	public ImageViewer(Composite parent) {
		super(parent, SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.TRANSPARENT);

		bg = getBackground();
		display = parent.getDisplay();
		addListeners();
	}

	int getPicType() {
		return picType;
	}

	void setPicType(int picType) {
		if (this.picType != picType) {
			this.picType = picType;
		}
	}

	public void setImage(String imageKey, ImageData imageData) {
		this.imageKey = imageKey;
		checkWidget();

		stopAnimationTimer();

		if (Objects.nonNull(imageCache.get(imageKey))) {
			this.image = imageCache.get(imageKey);
		} else {
			this.image = new Image(display, imageData);
			if (!imageCache.containsKey(imageKey)) {
				imageCache.put(imageKey, image);
				System.out.println(imageKey + " put to imageMap cache!!!");
			}
		}
		this.imageDatas = null;
		this.images = null;
		redraw();
	}

	/**
	 * @param repeatCount 0 forever
	 */
	public void setImages(String key, ImageData[] imageDatas, int repeatCount) {
		this.key = key;
		checkWidget();
		this.current = 0;
		this.image = null;
		this.imageDatas = imageDatas;
		this.repeatCount = repeatCount;

		convertImageDatasToImages();
		startAnimationTimer();
		redraw();
	}

	@Override
	public Point computeSize(int wHint, int hHint, boolean changed) {
		checkWidget();

		Image image = getCurrentImage();
		if (image != null) {
			Rectangle rect = image.getBounds();
			Rectangle trim = computeTrim(0, 0, rect.width, rect.height);
			return new Point(trim.width, trim.height);
		}

		return new Point(wHint, hHint);
	}

	@Override
	public void dispose() {
		
		if (image != null)
			image.dispose();

		if (images != null)
			for (int i = 0; i < images.length; i++)
				images[i].dispose();

		if (!gifCache.isEmpty()) {
			gifCache.clear();
		}
		if (null != bg)
			bg.dispose();
		super.dispose();
	}

	protected void paint(Event e) {
		Image image = getCurrentImage();
		GC gc = e.gc;
		if (image == null) {
			// 画个空白文字在图片上
			FontData fntData = gc.getFont().getFontData()[0];
			String text = new String(NO_PICTURE);
			// Label label = new Label(this.getParent(),SWT.NONE);
			// int textWidth = Utility.getWidth(text,label);
			int textWidth = TextUtilities.INSTANCE.getTextExtents(text, gc.getFont()).width;
			// label.dispose();
			gc.drawText(NO_PICTURE, getBounds().width / 2 - textWidth / 2,
					getBounds().height / 2 - fntData.getHeight() / 2, SWT.DRAW_TRANSPARENT | SWT.DRAW_MNEMONIC);

			return;
		}
		int picType = this.getPicType();

		/////////////////////////////////////////////////////////////////////
		int width = this.getBounds().width;
		int height = this.getBounds().height;
		int imageWidth = image.getBounds().width;
		int imageHeight = image.getBounds().height;
		switch (picType) {
		case 1: {// 平铺
			Pattern ptn = new Pattern(null, image);
			gc.setBackgroundPattern(ptn);
			gc.fillRectangle(0, 0, width, height);
			ptn.dispose();
		}
			break;
		case 2: { // 拉伸
			gc.drawImage(image, 0, 0, imageWidth, imageHeight, 0, 0, width, height);
		}
			break;
		case 0:// 居中
		{
			gc.drawImage(image, imageWidth > width ? (imageWidth - width) / 2 : 0,
					imageHeight > height ? (imageHeight - height) / 2 : 0, Math.min(imageWidth, width),
					Math.min(imageHeight, height), width > imageWidth ? (width - imageWidth) / 2 : 0,
					height > imageHeight ? (height - imageHeight) / 2 : 0, Math.min(imageWidth, width),
					Math.min(imageHeight, height));
		}
			break;

		default:
			break;
		}
	}

	private void patternDispose(Pattern ptn) {
		if (Objects.nonNull(ptn)) {
			ptn.dispose();
			ptn = null;
		}
	}

	void addListeners() {
		addListener(SWT.Paint, new Listener() {
			public void handleEvent(Event e) {
				paint(e);
			}
		});
	}

	void convertImageDatasToImages() {
		int length = imageDatas.length;
		images = new Image[length];

		// Step 1: Determine the size of the resulting images.
		int width = imageDatas[0].width;
		int height = imageDatas[0].height;

		// Step 2: Construct each image.
		int transition = SWT.DM_FILL_BACKGROUND;
		// 首先从缓存中查找当前key有没有images[]列表,如果有,直接遍历即可。如果没有,重新生成,并将当前image放入cache 中。
		if (Objects.nonNull(gifCache.get(key))) {
			images = gifCache.get(key);

			for (int i = 0; i < length; i++) {
				ImageData id = imageDatas[i];
				GC gc = new GC(images[i]);

				transition = drawImage(width, height, transition, i, id, gc);
			}

		} else {
			if (!gifCache.containsKey(key)) {
				this.gifCache.put(key, images);
				System.out.println(key + " put to cacheMap");
			}
			for (int i = 0; i < length; i++) {
				ImageData id = imageDatas[i];
				images[i] = new Image(display, width, height);
				GC gc = new GC(images[i]);
				transition = drawImage(width, height, transition, i, id, gc);
			}
		}

	}

	private int drawImage(int width, int height, int transition, int i, ImageData id, GC gc) {
		// Do the transition from the previous image.
		switch (transition) {
		case SWT.DM_FILL_NONE:
		case SWT.DM_UNSPECIFIED:
			// Start from last image.
			gc.drawImage(images[i - 1], 0, 0);
			break;
		case SWT.DM_FILL_PREVIOUS:
			// Start from second last image.
			gc.drawImage(images[i - 2], 0, 0);
			break;
		default:
			// DM_FILL_BACKGROUND or anything else,
			// just fill with default background.
			gc.setBackground(bg);
			gc.fillRectangle(0, 0, width, height);
			break;
		}

		// Draw the current image and clean up.
		Image img = new Image(display, id);
		gc.drawImage(img, 0, 0, id.width, id.height, id.x, id.y, id.width, id.height);
		img.dispose();
		gc.dispose();

		// Compute the next transition.
		// Special case: Can't do DM_FILL_PREVIOUS on the
		// second image since there is no "second last"
		// image to use.
		transition = id.disposalMethod;
		if (i == 0 && transition == SWT.DM_FILL_PREVIOUS)
			transition = SWT.DM_FILL_NONE;
		return transition;
	}

	Image getCurrentImage() {
		if (image != null)
			return image;

		if (images == null)
			return null;

		current = current >= images.length ? (images.length - 1) : current;
		return images[current];
	}

	void startAnimationTimer() {
		int length = images.length;
		if (images == null || length < 2)
			return;
		// 将原来的timertask stop dispose
		stopAnimationTimer();
		final int delay = imageDatas[current].delayTime * 10;

		display.timerExec(delay, animationTimer = new Runnable() {
			public void run() {
				if (isDisposed())
					return;
				current = (current + 1) % length;
				redraw();

				if (current + 1 == length && repeatCount != 0 && --repeatCount <= 0)
					return;
				display.timerExec(delay, this);
			}
		});
	}

	void stopAnimationTimer() {
		if (animationTimer != null)
			display.timerExec(-1, animationTimer);
	}}

下面写一个main方法来测试一下是否支持Gif及其他静态图片。

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;

public   class  ImageCanvasTest {
     public   static   void  main(String[] args) {
        Display display  =   new  Display();
         final  Shell shell  =   new  Shell(display);
        ImageViewer ic  =   new  ImageViewer(shell);
 
        shell.setLayout( new  FillLayout());
        FileDialog dialog  =   new  FileDialog(shell, SWT.OPEN);
        dialog.setText( " Open an image file or cancel " );
        String string  =  dialog.open();
 
        ImageLoader loader  =   new  ImageLoader();
        ImageData[] imageDatas  =  loader.load(string);
         if  (imageDatas.length  ==   0 )
             return ;
         else   if  (imageDatas.length  ==   1 ) {
            ic.setImage("aabbcc",imageDatas[ 0 ]);
        }  else  {
            ic.setImages("aabbcc",imageDatas, loader.repeatCount);
        }
 
        ic.pack();
        shell.pack();
        shell.open();
         while  ( ! shell.isDisposed()) {
             if  ( ! display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }}

代码贴完了,解释一下
ImageViewer类就是实现了读取静态图片和Gif图片的实现类。支持图片的居中,拉伸和平铺等三种展示方式,默认为居中显示。
ImageCanvasTest类实现了打开图片窗口,并使用ImageLoader类来读取图片资源。该类支持Gif格式。会将Gif格式图片读取为ImageData[]数组。
还有一个注意的地方:loader.pepeatcount=0.0为无限次循环,即Gif图片会一直循环播放。如果设定了次数,则只会播放设定的次数。