最近闲来无事,折腾了一下关于gif图片在Android上的显示(大家都知道,Android本身不支持gif图片的显示,当然通过Media还是能够实现gif的播放的)。网上找到的实现gif图片展示的主要是两种方式:使用java实现解码,或者使用编辑工具将gif图片拆分为多张图片,并编写xml文件,以帧动画的形式播放,另外还有个牛人,直接修改了Android框架层的源码,让android系统支持gif解码的。
最后,我参考了一个android的开源项目,gifView,实现了一个基于native层的gif解码。
以下是我参考的资料:
解码的算法是直接抄袭了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的解码无关,只是写代码时的中间产物。