Android中Gif图片的显示

         最近闲来无事,折腾了一下关于gif图片在Android上的显示(大家都知道,Android本身不支持gif图片的显示,当然通过Media还是能够实现gif的播放的)。网上找到的实现gif图片展示的主要是两种方式:使用java实现解码,或者使用编辑工具将gif图片拆分为多张图片,并编写xml文件,以帧动画的形式播放,另外还有个牛人,直接修改了Android框架层的源码,让android系统支持gif解码的。

        最后,我参考了一个android的开源项目,gifView,实现了一个基于native层的gif解码。

        以下是我参考的资料:

         gif文件格式说明

         LZW编码

         LZW算法和GIF数据压缩

         gifView项目

          解码的算法是直接抄袭了GifView,基本上就是C++语言重新实现了一下,解码重写了一下SurfaceView控件来实现gif的播放。以下贴上部分的核心代码:

          Gif.java

package com.ray.test.gif;

import java.util.ArrayList;

public class Gif {
	public class Frame {
		private int delayTime;
		private Bitmap image;
		private boolean userInput = false;

		public Frame(int delay, int[] color) {
			delayTime = delay;
			image = Bitmap.createBitmap(color, mWidth, mHeight, Config.RGB_565);
		}

		private Frame setUserInput() {
			userInput = true;
			return this;
		}

		public int getDelay() {
			return delayTime;
		}

		public Bitmap getImage() {
			return image;
		}

		public boolean isUserInput() {
			return userInput;
		}
	}

	private int mWidth;
	private int mHeight;
	private List<Frame> mFrames = new ArrayList<Frame>();

	public Gif(int width, int height) {
		mWidth = width;
		mHeight = height;
	}

	public int getWidth() {
		return mWidth;
	}

	public int getHeight() {
		return mHeight;
	}

	public void addFrame(int delay, int[] color, boolean userInput) {
		synchronized (mFrames) {
			if (!userInput)
				mFrames.add(new Frame(delay, color));
			else
				mFrames.add(new Frame(delay, color).setUserInput());
		}
	}

	public int getFrameCount() {
		synchronized (mFrames) {
			return mFrames.size();
		}
	}

	public Frame getFrame(int idx) {
		synchronized (mFrames) {
			if (idx < 0 || idx >= mFrames.size())
				return null;
			return mFrames.get(idx);
		}
	}
}

GifDecoder.java

package com.ray.test.gif;

import java.io.File;

public class GifDecoder {

	private static final String MYTAG = "Ray";
	private static final String CLASS_NAME = "GifDecoder";

	public interface DecodeResult {
		public void onDecodeFinished(int count);
	}

	private static Gif sGif;
	private static DecodeResult sListener;
	private static boolean sIsReady = false;

	static void decode(String filePath, DecodeResult result) throws FileNotFoundException {
		File f = new File(filePath);
		if (f.exists()) {
			sListener = result;
			sIsReady = false;
			sGif = null;
			WorkThread thread = new WorkThread(filePath);
			thread.start();
		} else
			throw new FileNotFoundException("can not find file:" + filePath);
	}

	static Gif getImage() {
		return sGif;
	}

	private static void onDecodeFinished(String count) {
		Log.d(MYTAG, CLASS_NAME + ": onDecodeFinished, count = " + count);
		int c = Integer.parseInt(count);
		getFrames(c);
		if(c == 0)
			mHandler.obtainMessage(c).sendToTarget();
	}

	private static void getFrames(int idx) {
		if(idx == 0)
			sGif = new Gif(getWidth(), getHeight());
		sGif.addFrame(getDelay(idx), getColors(idx), getUserInput(idx));
	}

	private static class WorkThread extends Thread {
		private String mPath;

		public WorkThread(String path) {
			mPath = path;
		}

		@Override
		public void run() {
			doDecode(mPath);
		}
	}

	private static Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			sListener.onDecodeFinished(msg.what);
		}
	};

	private static native void doDecode(String path);

	private static native int getWidth();

	private static native int getHeight();

	private static native int getDelay(int index);

	private static native boolean getUserInput(int index);

	private static native int[] getColors(int index);
}


GifVIewer.java

package com.ray.test.gif;

import android.content.Context;

public class GifView extends SurfaceView implements SurfaceHolder.Callback {

	private SurfaceHolder mHolder;
	private Gif mGif;
	private boolean isReady = false;
	private Rect mdRc;
	private Rect msRc;
	private Paint mPaint;
	GestureDetector mGestureDetector;
	private int mCurrentImage;

	public GifView(Context context) {
		super(context);
		mHolder = this.getHolder();
		mHolder.addCallback(this);
		mPaint = new Paint();
		mGestureDetector = new GestureDetector(gestureListener);
	}

	public void setImages(Gif gif) {
		this.mGif = gif;
		init();
	}

	private void init() {
		if (isReady && mGif != null) {
			msRc = new Rect(0, 0, mGif.getWidth(), mGif.getHeight());
			Rect vRc = new Rect();
			getLocalVisibleRect(vRc);
			mdRc = getDstRc(msRc, vRc);
			mHandler.removeCallbacksAndMessages(null);
			mHandler.sendEmptyMessage(0);
		}
	}

	private Rect getDstRc(Rect image, Rect view) {
		double xRate = view.width() * 1.0 / image.width();
		double yRate = view.height() * 1.0 / image.height();
		if (xRate > yRate) {
			return new Rect((int) (view.width() - image.width() * yRate) / 2, 0, (int) (image.width() * yRate) + (int) (view.width() - image.width() * yRate)
					/ 2, (int) (image.height() * yRate));
		} else {
			return new Rect(0, (int) (view.height() - image.height() * xRate) / 2, (int) (image.width() * xRate), (int) (image.height() * xRate)
					+ (int) (view.height() - image.height() * xRate) / 2);
		}
	}

	private Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			int idx = msg.what;
			if (idx < 0 || idx >= mGif.getFrameCount())
				idx = 0;
			mCurrentImage = idx;
			Frame f = mGif.getFrame(idx);
			if(f == null){
				Log.e("Ray", "f = null when idx = " + idx);
				this.sendEmptyMessageDelayed(idx, 100);
				return;
			}
			Rect rc = new Rect(mdRc);
			Canvas cv = mHolder.lockCanvas(rc);
			if (rc.equals(mdRc)) {
				cv.drawBitmap(f.getImage(), msRc, mdRc, mPaint);
				mHolder.unlockCanvasAndPost(cv);
				this.sendEmptyMessageDelayed(++idx, f.getDelay());
			} else {
				mHolder.unlockCanvasAndPost(cv);
				this.sendEmptyMessageDelayed(idx, 100);
			}
		}
	};

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		init();
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		isReady = true;
		init();
		this.setOnTouchListener(new OnTouchListener(){

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				return mGestureDetector.onTouchEvent(event);
			}});
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		isReady = false;
		mHandler.removeCallbacksAndMessages(null);
	}
	
	private GestureDetector.OnGestureListener gestureListener = new GestureDetector.OnGestureListener() {
		
		@Override
		public boolean onSingleTapUp(MotionEvent e) {
			if(mGif.getFrame(mCurrentImage).isUserInput())
			{
				mHandler.removeMessages(mCurrentImage + 1);
				mHandler.handleMessage(mHandler.obtainMessage(mCurrentImage + 1));
			}
			return true;
		}
		
		@Override
		public void onShowPress(MotionEvent e) {
			;
		}
		
		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
			return true;
		}
		
		@Override
		public void onLongPress(MotionEvent e) {	
			;
		}
		
		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
			return true;
		}
		
		@Override
		public boolean onDown(MotionEvent e) {
			return true;
		}
	};
}


GifUtils.cpp

#include "../head/GifUtils.h"
#include <cstring>
#include <iostream>
#include <typeinfo>

extern void showIntLog(string str, int num);

extern void showStringLog(string text);

extern void decodeFinished(int count);

unsigned short getShort(char* data, int idx) {
	return *((unsigned short*) (data + idx));
}

GifUtils::GifUtils(string path) {
	this->mInStream.open(path.c_str());
	this->mFileSize = mInStream.gcount();
}

GifUtils::~GifUtils() {
	if (this->mGlobalColorTable != NULL) {
		delete[] this->mGlobalColorTable;
		this->mGlobalColorTable = NULL;
	}
}

void GifUtils::doWork(){
	showStringLog("Start decode");
	this->readFile();
	showStringLog("end decode");
}

int GifUtils::readShort() {
	return (unsigned short) (this->mInStream.get() | this->mInStream.get() << 8);
}

void GifUtils::readFile() {
	this->mStatus = DECODING;
	this->readHead();
	if (!this->is89) //本类只支持gif89版本
		return;
	this->readLogicalScreenDescriptor();

	int imageId = 0;
	while (this->mStatus == DECODING) {
		char flag = this->mInStream.get();
		if (flag == BLOCK_FLAG_IMAGE_DESCRIPTOR)
		{
			this->readImageDescriptor();
		} else if (((unsigned char) flag) == BLOCK_FLAG_EXTENSION) {
			flag = this->mInStream.get();
			switch (flag) {
			case EXTENSION_FLAG_CONTROL:
				this->readControlExtension(imageId);
				imageId++;
				break;
			case EXTENSION_FLAG_COMMEMT:
				this->readCommentExtension();
				break;
			case EXTENSION_FLAG_TEXT:
				this->readTextExtension();
				break;
			case EXTENSION_FLAG_APPLICATION:
				this->readApplicationExtension();
				break;
			default:
				cout << "invalidate EXTENSION FLAG" << endl;
				this->mStatus = DATA_ERROR;
				break;
			}
		} else if (flag == BLOCK_FLAG_END) {
			this->mStatus = FINISHED;
			break;
		} else {
			cout << "invalidate block head" << endl;
			this->mStatus = DATA_ERROR;
			break;
		}
	}

	for(int i = 0; i < this->mFrames.size(); i++){
		delete[] this->mFrames[i].getColors();
	}
}

/**
 * 读取头文件
 */
void GifUtils::readHead() {
	char head[6];
	for (int i = 0; i < 6; i++)
		mInStream >> head[i];
	if (0 == strcmp(head, FILE_HEAD_GIF_89A)) {
		this->is89 = true;
	} else {
		this->is89 = false;
	}
}

/**
 * 读取逻辑屏幕标识符
 */
void GifUtils::readLogicalScreenDescriptor() {
	char data[7];
	this->mInStream.read(data, 7);
	this->mGlobalWidth = getShort(data, 0);
	this->mGlobalHeight = getShort(data, 2);
	this->mHasGlobalColorTable = (data[4] & 0x80);
	this->mColorResolution = ((data[4] & 0x70) >> 4) + 1;
	this->mSortFlag = data[4] & 8;
	this->mGlobalColorTableSize = 2 << (data[4] & 7);
	this->mGlobalBackGroundColorIndex = data[5];
	this->mRateOfHeightWithWidth = data[6];
	if (this->mHasGlobalColorTable)
		this->readGlobalColorTable();
}

/**
 * 读取全局调色板
 */
void GifUtils::readGlobalColorTable() {
	int* table = new int[this->mGlobalColorTableSize];
	for (int i = 0; i < this->mGlobalColorTableSize; i++) {
		table[i] = (unsigned char) this->mInStream.get();
		table[i] <<= 8;
		table[i] += (unsigned char) this->mInStream.get();
		table[i] <<= 8;
		table[i] += (unsigned char) this->mInStream.get();
		table[i] |= 0xFF000000;
	}
	this->mGlobalColorTable = table;
}

unsigned char GifUtils::readOneBlock(char* &data) {
	unsigned char length = this->mInStream.get();
	if (length != 0) {
		data = new char[length];
		this->mInStream.read(data, length);
	}
	return length;
}

/**
 * 读取局部调色板
 */
void GifUtils::readLocalColorTable(FrameInfo& frame) {
	int size = frame.getLocalColorTableSize();
	int* table = new int[size];
	for (int i = 0; i < size; i++) {
		table[i] = (unsigned char) this->mInStream.get();
		table[i] <<= 8;
		table[i] += (unsigned char) this->mInStream.get();
		table[i] <<= 8;
		table[i] += (unsigned char) this->mInStream.get();
		table[i] |= 0xFF000000;
	}
	frame.setLocalColorTable(table);
}

int* cloneColors(int* colors, int length) {
	int* data = new int[length];
	if (colors == NULL) {
		memset(data, length*4, 0);
	} else {
		for (int i = 0; i < length; i++)
			data[i] = colors[i];
	}
	return data;
}

/**
 * 读取图像标识符
 */
void GifUtils::readImageDescriptor() {
	FrameInfo frame = this->mFrames.back();
	this->mFrames.pop_back();
	frame.setXoffset(this->readShort());
	frame.setYOffset(this->readShort());
	frame.setWidth(this->readShort());
	frame.setHeight(this->readShort());

	char flag = this->mInStream.get();
	frame.setInterlaceFlag(flag & 0x40);
	frame.setSortFlag(flag & 0x20);
	if (flag & 0x80) {
		frame.setLocalColorTableSize(2 << (flag & 7));
		this->readLocalColorTable(frame);
	}

	int codeSize = this->mInStream.get();
	vector<char> data;
	char* block = NULL;
	int blockSize = this->readOneBlock(block);
	while (blockSize) {
		for (int i = 0; i < blockSize; i++)
			data.push_back(block[i]);
		delete[] block;
		blockSize = this->readOneBlock(block);
	}

	int length = this->mGlobalHeight * this->mGlobalWidth;
	int* lastColors = NULL;
	if (frame.getAction() == FrameInfo::RETURN_PRE
			&& this->mFrames.size() > 1) {
		lastColors = cloneColors(
				this->mFrames[this->mFrames.size() - 2].getColors(), length);
	} else if (frame.getAction() == FrameInfo::NONE
			|| frame.getAction() == FrameInfo::REMOVE_IMAGE) {
		if (!this->mFrames.empty())
			lastColors = cloneColors(
					this->mFrames[this->mFrames.size() - 1].getColors(),
					length);
	} else if (frame.getAction() == FrameInfo::RETURN_BG) {
		if (!this->mFrames.empty()) {
			lastColors = cloneColors(
					this->mFrames[this->mFrames.size() - 1].getColors(),
					length);
			int bgColor = 0;
			if (frame.hasTransparentIdx()) {
				int transparentIdx = frame.getTransparentIdx();
				if (frame.hasLocalColorTable())
					bgColor = frame.getLocalColorTable()[transparentIdx];
				else
					bgColor = this->mGlobalColorTable[transparentIdx];
			}

			int yStart = frame.getOffsetY();
			int yEnd = yStart + frame.getHeight();
			int y = yStart, xStart, xEnd, x;
			while (y < yEnd) {
				xStart = this->mGlobalWidth * y + frame.getOffsetX();
				xEnd = xStart + frame.getWidth();
				x = xStart;
				while (x < xEnd) {
					lastColors[x] = bgColor;
				}
			}
		}
	}

	if (lastColors == NULL
		)
		lastColors = cloneColors(NULL, length);

	frame.decocde(this->mGlobalColorTable, this->mGlobalColorTableSize,
			this->mGlobalBackGroundColorIndex, codeSize, &data[0], data.size(),
			lastColors, this->mGlobalWidth, this->mGlobalHeight);
	this->mFrames.push_back(frame);
	decodeFinished(this->mFrames.size()-1);
	showIntLog("Image Ready :", this->mFrames.size());
}

void GifUtils::readControlExtension(int idx) {
	char* data = NULL;
	this->readOneBlock(data);
	char tmp[10] = { 0 };
	sprintf(tmp, "%d", idx);
	FrameInfo frame(tmp);
	frame.setAction((data[0] & 0x3C) >> 2);
	frame.setUserInput(data[0] & 2);
	if (data[0] & 1)
		frame.setTransparentIndex(data[3]);
	frame.setDelayTime(getShort(data, 1));
	if (!this->mInStream.get())
		this->mFrames.push_back(frame);
	else
		this->mStatus = DATA_ERROR;
}

void GifUtils::readCommentExtension() {
	int blockSize = 1;
	char * data = NULL;
	while (blockSize) {
		if (data != NULL
			)
			delete[] data;
		blockSize = this->readOneBlock(data);
	}

}

void GifUtils::readTextExtension() {
	this->readCommentExtension();

//	unsigned char blockSize = this->mInStream.get();
//	unsigned short locationX = this->readShort();
//	unsigned short locationY = this->readShort();
//	unsigned short width = this->readShort();
//	unsigned short height = this->readShort();
//	char cellWidth = this->mInStream.get();
//	char cellHeight = this->mInStream.get();
//	char foreColorIdx = this->mInStream.get();
//	char bgColorIdx = this->mInStream.get();
//	char * data = NULL;
//	while(blockSize){
//		if(data != NULL)
//			delete[] data;
//		blockSize = this->readOneBlock(data);
//	}
}

void GifUtils::readNETSCAPFile() {
	unsigned char blockSize;
	char* data = NULL;
	blockSize = this->readOneBlock(data);
	while (blockSize != 0) {
		if (data[0] == 1)
			this->mLoopCount = getShort(data, 1);
		delete[] data;
		data = NULL;
		blockSize = this->readOneBlock(data);
	}
}

void GifUtils::skip() {
	unsigned char blockSize;
	char* data = NULL;
	blockSize = this->readOneBlock(data);
	while (blockSize != 0) {
		delete[] data;
		data = NULL;
		blockSize = this->readOneBlock(data);
	}
}

void GifUtils::readApplicationExtension() {
	char * data = NULL;
	this->readOneBlock(data);
	char applicationIdentifier[8]; //应用程序标志符
	for (int i = 0; i < 8; i++)
		applicationIdentifier[i] = data[i];
	char applicationAuthenticationCode[3]; //应用程序验证码
	for (int i = 0; i < 3; i++)
		applicationAuthenticationCode[i] = data[i + 8];
	delete[] data;
	data = NULL;

	bool ap = strcmp(applicationIdentifier, "NETSCAPE");
	bool code = strcmp(applicationAuthenticationCode, "2.0");
	if (!ap && !code) {
		this->readNETSCAPFile();
	} else
		this->skip();
}

/**
 * 获得图片帧数
 */
int GifUtils::getImageCount() {
	return this->mFrames.size();
}

/**
 * 获得X方向偏移量
 */
int GifUtils::getOffsetX(int idx) {
	int re = this->mFrames[idx].getOffsetX();
	return re;
}

/**
 * 获得Y方向偏移量
 */
int GifUtils::getOffsetY(int idx) {
	return this->mFrames[idx].getOffsetY();
}

/**
 * 获得宽度
 */
int GifUtils::getWidth(int idx) {
	return this->mFrames[idx].getWidth();
}

/**
 * 获得高度
 */
int GifUtils::getHeight(int idx) {
	return this->mFrames[idx].getHeight();
}

/**
 * 获得等待时间
 */
int GifUtils::getDelayTime(int idx) {
	return this->mFrames[idx].getDelayTime();
}

/**
 * 获得动作
 */
int GifUtils::getAction(int idx) {
	return this->mFrames[idx].getAction();
}

/**
 * 获得用户操作
 */
bool GifUtils::getUserInput(int idx){
	return this->mFrames[idx].getUserInput();
}
/**
 * 获得颜色数据
 */
int* GifUtils::getColors(int idx) {
	return this->mFrames[idx].getColors();
}

int GifUtils::getGlobalWidth(){
	return this->mGlobalWidth;
}

int GifUtils::getGlobalHeight(){
	return this->mGlobalHeight;
}


FrameInfo.cpp

#include "../head/FrameInfo.h"
#include "../head/LZWDecoder.h"
//#include "../head/Bitmap.h"
#include <iostream>
#include <cstring>
#include <cstdio>

FrameInfo::FrameInfo(string name) {
	this->mName = name;
	this->mTransparentFlag = false;
	this->mHasLocalColorTable = false;
}

void FrameInfo::setAction(char action) {
	if (action < 4)
		this->mAction = FRAME_ACTION(action);
	else
		this->mAction = OTHER;
}

void FrameInfo::setUserInput(bool flag) {
	this->mUserInputFlag = flag;
}

void FrameInfo::setDelayTime(unsigned char delay) {
	this->mDelayTime = delay;
}

void FrameInfo::setTransparentIndex(unsigned char idx) {
	this->mTransparentFlag = true;
	this->mTransparentColorIndex = idx;
}

bool FrameInfo::isUserInput() {
	return this->mUserInputFlag;
}

bool FrameInfo::hasTransparentIdx() {
	return this->mTransparentFlag;
}

bool FrameInfo::hasLocalColorTable() {
	return this->mHasLocalColorTable;
}

int FrameInfo::getLocalColorTableSize() {
	return this->mLocalColorTableSize;
}

int* FrameInfo::getLocalColorTable() {
	return this->mLocalColorTable;
}

unsigned char FrameInfo::getTransparentIdx() {
	return this->mTransparentColorIndex;
}

void FrameInfo::setXoffset(int x) {
	this->mXoffset = x;
}

void FrameInfo::setYOffset(int y) {
	this->mYoffset = y;
}

void FrameInfo::setWidth(int width) {
	this->mWidth = width;
}

void FrameInfo::setHeight(int height) {
	this->mHeight = height;
}

void FrameInfo::setInterlaceFlag(bool interlace) {
	this->mInterlaceFlag = interlace;
}

void FrameInfo::setSortFlag(bool sort) {
	this->mSortFlag = sort;
}

void FrameInfo::setLocalColorTableSize(int size) {
	this->mHasLocalColorTable = true;
	this->mLocalColorTableSize = size;
}

void FrameInfo::setLocalColorTable(int* table) {
	this->mLocalColorTable = table;
}

void FrameInfo::decocde(int* globalColorTable, int tableSize, int bgIdx,
		unsigned char codeSize, char* data, int length, int* lastColors,
		int gWidth, int gHeight) {

	LZWDecoder decoder(codeSize, this->mWidth, this->mHeight);
	decoder.setData(data, length);
	unsigned char* decodedData = decoder.doDecode();

//	char tmp[10];
//	int dataSize = this->mWidth * this->mHeight;
//	string decoded = "decoded " + this->mName;
//	FILE* file1 = fopen(decoded.c_str(), "w+");
//	for(int i = 0; i < dataSize; i++)
//	{
//		sprintf(tmp,"%d ", decodedData[i]);
//		fwrite(tmp,1,strlen(tmp), file1);
//	}
//	fflush(file1);
//	fclose(file1);
//	file1 = NULL;

	int* activeColorTable =
			this->mHasLocalColorTable ?
					this->mLocalColorTable : globalColorTable;
	int activeColorTableSize =
			this->mHasLocalColorTable ? this->mLocalColorTableSize : tableSize;

	int savedColor;
	if (this->mTransparentFlag) {
		savedColor = activeColorTable[this->mTransparentColorIndex];
		if (!strcmp(this->mName.c_str(), "0")) {
			activeColorTable[this->mTransparentColorIndex] = activeColorTable[bgIdx];
		} else {
			activeColorTable[this->mTransparentColorIndex] = 0;
		}
	}

	//make bitmap below
//	this->mColors = new int[this->mWidth * this->mHeight];
//	char picData[this->mWidth * this->mHeight];
	this->mColors = lastColors;

	int pass = 1;
	int inc = 8;
	int iline = 0, line = 0;
	for (int i = 0; i < this->mHeight; i++) {
		line = i;
		if (this->mInterlaceFlag) {
			if (iline >= this->mHeight) {
				pass++;
				switch (pass) {
				case 2:
					iline = 4;
					break;
				case 3:
					iline = 2;
					inc = 4;
					break;
				case 4:
					iline = 1;
					inc = 2;
					break;
				}
			}
			line = iline;
			iline += inc;
		}
		int sIdx = i * this->mWidth;
		int dIdx = (this->getOffsetY() + line) * gWidth + this->getOffsetX();
		for (int x = 0; x < this->mWidth; x++) {
//			picData[dIdx + x] = decodedData[sIdx + x];
			if(activeColorTable[decodedData[sIdx + x]] != 0)
			this->mColors[dIdx + x] = activeColorTable[decodedData[sIdx + x]];
		}
	}
//	FILE* file = fopen(this->mName.c_str(), "w+");
//	fwrite(this->mColors, 4, this->mWidth * this->mHeight, file);
//	fflush(file);
//	fclose(file);

//	BitmapUtils bitmap(this->mWidth, this->mHeight);
//	bitmap.setColorTable(activeColorTable, activeColorTableSize);
//	bitmap.addData(picData, (int) (this->mWidth * this->mHeight));
//	bitmap.save(this->mName);

	if (this->mTransparentFlag && !this->mHasLocalColorTable)
		globalColorTable[this->mTransparentColorIndex] = savedColor;
}
/**
 * 获得X方向偏移量
 */
int FrameInfo::getOffsetX() {
	return this->mXoffset;
}

/**
 * 获得Y方向偏移量
 */
int FrameInfo::getOffsetY() {
	return this->mYoffset;
}

/**
 *	获得宽度
 */
int FrameInfo::getWidth() {
	return this->mWidth;
}

/**
 * 获得高度
 */
int FrameInfo::getHeight() {
	return this->mHeight;
}

/**
 * 获得延迟时间
 */
int FrameInfo::getDelayTime() {
	return (int) this->mDelayTime * 10;
}

/**
 * 获取帧动作
 */
int FrameInfo::getAction() {
	return this->mAction;
}

/**
 * 是否接受用户输入
 */
bool FrameInfo::getUserInput(){
	return this->mUserInputFlag;
}

/**
 * 获取色彩数据
 */
int* FrameInfo::getColors() {
	return this->mColors;
}


LZWDecoder.cpp

#include "../head/LZWDecoder.h"
#include <stdio.h>
#include <cstring>
#include <cstdlib>
#include <iostream>
using namespace std;

LZWDecoder::LZWDecoder(unsigned char codeSize, int width, int height) {
	this->mOrignalCodeSize = codeSize + 1;
	int orignalCodeTableSize = 1 << codeSize;
	this->mIdxClear = orignalCodeTableSize;
	this->mIdxEnd = orignalCodeTableSize + 1;

	this->mOutData = new unsigned char[width * height];this
	->mCodeTable = new unsigned char*[MAX_CODE_TABLE_SIZE];
	for (int i = 0; i < MAX_CODE_TABLE_SIZE; i++)
		this->mCodeTable[i] = NULL;
	for (int i = 0; i < orignalCodeTableSize; i++) {
		this->mCodeTable[i] = new unsigned char[2];
		this->mCodeTable[i][0] = 1;
		this->mCodeTable[i][1] = i;
	}
	this->mFinishedNumber = 0;
}

/**
 * 设定待解码的数据
 */
void LZWDecoder::setData(char* data, int length) {
	this->mInData = data;
	this->mDataLength = length;
}

unsigned char* LZWDecoder::doDecode() {

	int codeSize = this->mOrignalCodeSize;
	int codeMask = (1 << codeSize) - 1;
	int availableIdx = this->mIdxClear + 2;
	int preCode = NULL_CODE, inCode = 0, code = 0;
	int readedBits = 0, readedCode = 0;
	int readBytes = 0, top = 0, first = 0;

	unsigned short* prefix = new unsigned short[MAX_CODE_TABLE_SIZE];
	unsigned char* suffix = new unsigned char[MAX_CODE_TABLE_SIZE];
	unsigned char* pixelStack = new unsigned char[MAX_CODE_TABLE_SIZE + 1];
	for(int i =0; i < this->mIdxClear; i++){
		prefix[i] = 0;
		suffix[i] = i & 0xFF;
	}

	while (readBytes < this->mDataLength) {
		if (!top) {
			if (readedBits < codeSize) { //如果现有的数据长度不足已构成一个编码,那么继续读取
				readedCode += (((int) this->mInData[readBytes]) & 0xFF)
						<< readedBits;
				readedBits += 8;
				readBytes++;
				continue;
			}

			//从读取的数据中获取一个编码
			inCode = readedCode & codeMask;
			readedCode >>= codeSize;
			readedBits -= codeSize;

			if (inCode > availableIdx || inCode == this->mIdxEnd) {
				break;
			}

			if (inCode == this->mIdxClear) {
				codeSize = this->mOrignalCodeSize;
				codeMask = (1 << codeSize) - 1;
				availableIdx = this->mIdxClear + 2;
				preCode = NULL_CODE;
				continue;
			}

			if (preCode == NULL_CODE) {
				pixelStack[top++] = suffix[inCode];
				preCode = inCode;
				first = inCode;
				continue;
			}
			code = inCode;
			if (inCode == availableIdx) {
				pixelStack[top++] = first;
				inCode = preCode;
			}

			while (inCode > this->mIdxClear) {
				pixelStack[top++] = suffix[inCode];
				inCode = prefix[inCode];
			}

			first = suffix[inCode];
			if (availableIdx >= MAX_CODE_TABLE_SIZE) {
				cout << "availableIdx = MAX_CODE_TABLE_SIZE" << endl;
				break;
			}

			pixelStack[top++] = first;
			prefix[availableIdx] = preCode;
			suffix[availableIdx] = first;
			availableIdx++;

			if (((availableIdx & codeMask) == 0)
					&& availableIdx < MAX_CODE_TABLE_SIZE) {
				codeSize++;
				codeMask += availableIdx;
			}
			preCode = code;
		}
		top--;
		this->mOutData[this->mFinishedNumber++] = pixelStack[top];
	}
	delete[] prefix;
	delete[] suffix;
	delete[] pixelStack;
	return this->mOutData;
}



       经过我自己的初步测试,使用C++实现的解码器,比起JAVA的实现,基本上可以节约1/3到1/2的解码时间,内存占用上稍有优势,但不明显。

       PS:源码中的Bitmap.h和BitmapUtils.cpp的作用是将解码出来的gif帧保存为bitmap文件,与gif的解码无关,只是写代码时的中间产物。

      点击下载源码



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值