今天美工做了一幅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; } }
原文参考:http://space.itpub.net/13685345/viewspace-514315