最近闲来无事,折腾了一下关于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的解码无关,只是写代码时的中间产物。
点击下载源码