今天美工做了一幅gif的图片,我放到JLabel里面去,gif图片闪动得特别厉害。以为美工提供的图片有问题,又双击用IE打开,却显示正常。心想应该是java组建对gif图片显示的问题。网上查了下swing对gif的显示。果不其然,是swing对gif的显示问题。下面整理下处理swing正确显示gif图片的问题。
首先需要对Gif这种图片格式有一些基本认识。
第一:Gif由一系列Image组成,也就是桢,Gif动画就是连续地显示这些桢,但是这还不够。
第二:无论某一时刻轮循到哪一桢,第1桢,总是要当作背景画出来,而且第1桢也是所有桢当中最长最高的,它的尺寸也是整个Gif图象的尺寸,位置从(0, 0)开始,其余各桢可能只是描述与相临各桢变化的部分,所以长和高要小且不完整,起始位置是该桢相对整体背景的位置。(这点SWT也是这样做的)
第三:Gif动画连续显示不一定是各个桢轮循单独显示,而是不仅仅显示当前该显示的桢,还要向前追溯到"第一桢",从"第一桢"开始到当前应该显示的桢组成的连续一系列"桢簇",所以某一时刻单单显示背景和当前桢是不够的,而是显示背景和当前"桢簇"。""桢簇""是我自己取的名字,而且我看SWT轮循的例子中并没有用到"桢簇",而是传统的单桢轮循。但是同样的方法对Swing不奏效,现在我对此还不得其解。关于"第一桢",是和com.sun.imageio.plugins.gif.GIFImageMetadata类的disposalMethod属性有关,在SWT中这个属性是org.eclipse.swt.graphics.ImageData.disposalMethod。disposalMethod据我的研究是描述处理桢的方法,常见的disposalMethod取值有none(取值0,不处理)、Background(取值2,背景)两种,所谓的当前桢的"第一桢"就是向前追溯到最近的disposalMethod取值为2的那一桢的下一桢,也就是说或者"第一桢"的前一桢的disposalMethod取值为2,或者"第一桢"就是Gif索引为2的桢,因为Gif的第1桢总要当背景显示。
第四:桢的元数据在SWT中用org.eclipse.swt.graphics.ImageData类封装,在Swing中对应的是com.sun.imageio.plugins.gif.GIFImageMetadata(可是截止到JDK6.0 u11,这个类的版本号还是0.5,有些另人失望:(),可以通过次类获取到delayTime这个属性,也就是下一桢的间隔时间,但是有很多Gif,这个值总是0,所以Swing显示频率相当的快。
下面为处理代码:
public class GifComponent extends JComponent {
private static final long serialVersionUID = 1L;
private GifBean[] gifBeans;
private Map<Integer, Integer[]> gifBeanMap = new HashMap<Integer, Integer[]>();
private int index = 0;
private int delayFactor;
private Timer timer;
/**
*
* @param gifFile
* @param delayFactor
* 显示gif每帧图片的时间因子
*/
public GifComponent(File gifFile, int delayFactor) {
setDelayFactor(delayFactor);
setGifFile(gifFile);
}
/**
* 设置Gif文件
*
* @param gifFile
*/
public void setGifFile(File gifFile) {
ImageReader reader = null;
try {
ImageInputStream imageIn = ImageIO.createImageInputStream(gifFile);
Iterator<ImageReader> iter = ImageIO
.getImageReadersByFormatName("gif");
if (iter.hasNext()) {
reader = iter.next();
}
reader.setInput(imageIn, false);
gifBeanMap.clear();
gifBeans = new GifBean[reader.getNumImages(true)];
GIFImageMetadata meta = null;
for (int i = 0; i < gifBeans.length; i++) {
meta = (GIFImageMetadata) reader.getImageMetadata(i);
gifBeans[i] = new GifBean();
gifBeans[i].image = reader.read(i);
gifBeans[i].x = meta.imageLeftPosition;
gifBeans[i].y = meta.imageTopPosition;
gifBeans[i].width = meta.imageWidth;
gifBeans[i].height = meta.imageHeight;
gifBeans[i].disposalMethod = meta.disposalMethod;
gifBeans[i].delayTime = meta.delayTime == 0 ? 1
: meta.delayTime;
}
for (int i = 1; i < gifBeans.length; i++) {
if (gifBeans[i].disposalMethod == 2) {
gifBeanMap.put(new Integer(i), new Integer[] { i });
continue;
}
int firstIndex = getFirstIndex(i);
List<Integer> list = new ArrayList<Integer>();
for (int j = firstIndex; j <= i; j++) {
list.add(j);
}
gifBeanMap.put(new Integer(i), list.toArray(new Integer[] {}));
}
} catch (IOException e) {
e.printStackTrace();
}
setTimer();
}
private synchronized void setTimer() {
if (timer != null) {
timer.cancel();
}
timer = new Timer("show gif");
timer.schedule(new TimerTask() {
@Override
public void run() {
repaint();
try {
Thread.sleep(gifBeans[index].delayTime * delayFactor);
} catch (InterruptedException e) {
}
index++;
if (index >= gifBeans.length) {
index = 0;
}
}
}, 0, 1);
}
/**
* 设置时间因子
*
* @param delayFactor
*/
public void setDelayFactor(int delayFactor) {
this.delayFactor = delayFactor;
}
@Override
protected void paintComponent(Graphics g) {
g.drawImage(gifBeans[0].image, gifBeans[0].x, gifBeans[0].y, this);
if (index > 0) {
Integer[] array = gifBeanMap.get(index);
for (Integer i : array) {
g.drawImage(gifBeans[i].image, gifBeans[i].x, gifBeans[i].y,
this);
}
}
}
private int getFirstIndex(int index) {
int tempIndex = index;
while (tempIndex > 1) {
if (tempIndex - 1 > 0
&& gifBeans[tempIndex - 1].disposalMethod == 2) {
return index;
}
tempIndex--;
}
return tempIndex;
}
/**
* 用于保持gif每帧图片的信息
*/
public class GifBean {
public BufferedImage image;
public int x;
public int y;
public int width;
public int height;
public int disposalMethod;
public int delayTime;
}
}