GIFDecoder源码分析

Posted on 2017-03-13 09:03  xl_phoenix  阅读(1072)  评论(0编辑  收藏  举报

源码见:ddxxll2008/gifdecoder_java

run()

public void run(){
		if(in != null){
			readStream();
		}else if(gifData != null){
			readByte();
		}
	}
	
	private int readByte(){
		in = new ByteArrayInputStream(gifData);
		gifData = null;
		return readStream();
	}

GifDecoder的入口是run函数,里面包含了readStream()和readByte()两个方法,但是readByte()里也返回了一个readStream(),所以从readStream()方法进行分析。

readStream

private int readStream(){
		init();
		if(in != null){
			readHeader();
			if(!err()){
				readContents();
				if(frameCount < 0){
					status = STATUS_FORMAT_ERROR;
					action.parseOk(false,-1);
				}else{
					status = STATUS_FINISH;
					action.parseOk(true,-1);
				}
			}
			try {
				in.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}else {
			status = STATUS_OPEN_ERROR;
			action.parseOk(false,-1);
		}
		return status;
	}
	

readStream()里先进行一些参数的初始化,然后读取Gif头文件。

private void readHeader() {
		String id = "";
		for (int i = 0; i < 6; i++) {
			id += (char) read();
		}
		if (!id.startsWith("GIF")) {
			status = STATUS_FORMAT_ERROR;
			return;
		}
		readLSD();
		if (gctFlag && !err()) {
			gct = readColorTable(gctSize);
			bgColor = gct[bgIndex];
		}
	}
	
	private void readLSD() {
		// logical screen size
		width = readShort();
		height = readShort();
		// packed fields
		int packed = read();
		gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
		// 2-4 : color resolution
		// 5 : gct sort flag
		gctSize = 2 << (packed & 7); // 6-8 : gct size
		bgIndex = read(); // background color index
		pixelAspect = read(); // pixel aspect ratio
	}
	
	private int[] readColorTable(int ncolors) {
		int nbytes = 3 * ncolors;
		int[] tab = null;
		byte[] c = new byte[nbytes];
		int n = 0;
		try {
			n = in.read(c);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (n < nbytes) {
			status = STATUS_FORMAT_ERROR;
		} else {
			tab = new int[256]; // max size to avoid bounds checks
			int i = 0;
			int j = 0;
			while (i < ncolors) {
				int r = ((int) c[j++]) & 0xff;
				int g = ((int) c[j++]) & 0xff;
				int b = ((int) c[j++]) & 0xff;
				tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
			}
		}
		return tab;
	}

readHeader()里主要是判断是否是Gif文件,如果是的话,则在readLSD()里获得Gif的一些基本信息。readLSD()读取的信息包括长宽,颜色和颜色索引等。之后通过readColorTable(int ncolors)读取颜色表单。之后就是通过readContents()来获取Gif的内容了。

readContents()

private void readContents() {
		// read GIF file content blocks
		boolean done = false;
		while (!(done || err())) {
			int code = read();
			switch (code) {
				case 0x2C: // image separator
					readImage();
					break;
				case 0x21: // extension
					code = read();
					switch (code) {
						case 0xf9: // graphics control extension
							readGraphicControlExt();
							break;
						case 0xff: // application extension
							readBlock();
							String app = "";
							for (int i = 0; i < 11; i++) {
								app += (char) block[i];
							}
							if (app.equals("NETSCAPE2.0")) {
								readNetscapeExt();
							} else {
								skip(); // don't care
							}
							break;
						default: // uninteresting extension
							skip();
					}
					break;
				case 0x3b: // terminator
					done = true;
					break;
				case 0x00: // bad byte, but keep going and see what happens
					break;
				default:
					status = STATUS_FORMAT_ERROR;
			}
		}
	}

在readContents()里,只要没有发生错误,就会一直读取下去,直到读取完成。这里面的readImage()方法是用来获取图片信息的。通过decodeImageData()方法来获取各个像素点的数据,之后新建一个bitmap,通过setPixels()来设置像素点的信息。然后根据此bitmap生成一个GifFrame的对象,并通过action.parseOk(true, frameCount)返回解析成功以及解析成功的帧数。全部解析完成后,帧数为-1。

private void readImage() {
		ix = readShort(); // (sub)image position & size
		iy = readShort();
		iw = readShort();
		ih = readShort();
		int packed = read();
		lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
		interlace = (packed & 0x40) != 0; // 2 - interlace flag
		// 3 - sort flag
		// 4-5 - reserved
		lctSize = 2 << (packed & 7); // 6-8 - local color table size
		if (lctFlag) {
			lct = readColorTable(lctSize); // read table
			act = lct; // make local table active
		} else {
			act = gct; // make global table active
			if (bgIndex == transIndex) {
				bgColor = 0;
			}
		}
		int save = 0;
		if (transparency) {
			save = act[transIndex];
			act[transIndex] = 0; // set transparent color if specified
		}
		if (act == null) {
			status = STATUS_FORMAT_ERROR; // no color table defined
		}
		if (err()) {
			return;
		}
		decodeImageData(); // decode pixel data
		skip();
		if (err()) {
			return;
		}
		frameCount++;
		// create new image to receive frame data
		image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
		// createImage(width, height);
		setPixels(); // transfer pixel data to image
		if (gifFrame == null) {
			gifFrame = new GifFrame(image, delay);
			currentFrame = gifFrame;
		} else {
			GifFrame f = gifFrame;
			while(f.nextFrame != null){
				f = f.nextFrame;
			}
			f.nextFrame = new GifFrame(image, delay);
		}
		// frames.addElement(new GifFrame(image, delay)); // add image to frame
		// list
		if (transparency) {
			act[transIndex] = save;
		}
		resetFrame();
		action.parseOk(true, frameCount);
	}

readContents()里还有读取其他信息的函数,比如readGraphicControlExt(),readBlock()和readNetscapeExt()。这些都是获取一些信息用的函数。

使用方法

GIFDecoder里有很多方法,可以获取Gif的信息。先构造一个GifDecoder,然后调用gifDecoder.run()方法,就可以得到一个完整的GIFDecoder对象,之后便能通过GIFDecoder里的各种方法去获得Gif图片的信息了。

//解析gif图片
gifDecoder = new GifDecoder(fileInputStream, new GifAction() {
	@Override
	public void parseOk(boolean parseStatus, int frameIndex) {
		Logger.d(parseStatus + "  " + frameIndex);
	}
});
gifDecoder.run();

Copyright © 2024 xl_phoenix
Powered by .NET 9.0 on Kubernetes