Gif的文件格式,可以参考另外两篇转载的博客:
项目中,会把Gif的解码、播放提交到一个线程池中执行,线程池的用法可以参考另一篇文章,ThreadPoolExecutor的使用。
以下范例代码来源于图库Gallery,首先把Gif解码的任务提交到线程池:
mThreadPool.submit(new GifDecoderJob(item), new GifDecoderListener(item.getPath()));
其中的Item表示媒体类型,可以自定义,GifDecoderListener用于解码完成的回调,也就是解码完成会由GifDecoderListener启动Gif播放。
private class GifDecoderJob implements Job<GifDecoder> {
private MediaItem mItem;
public GifDecoderJob(MediaItem item) {
mItem = item;
}
@Override
public GifDecoder run(JobContext jc) {
if (isTemporaryItem(mItem)) {
return null;
}
return new GifRequest(mItem.getContentUri(), mActivity).run(jc);
}
}
自定义类GifDecoderJob,处理Gif的decode。
public class GifRequest implements Job<GifDecoder> {
private static final String TAG = "GifRequest";
Uri itemUri;
Context mContext;
public GifRequest(Uri uri, Context context) {
itemUri = uri;
mContext = context;
}
private InputStream getInputStream(Uri uri) {
ContentResolver cr = mContext.getContentResolver();
InputStream input = null;
try {
input = cr.openInputStream(uri);
} catch (IOException e) {
Log.e(TAG, "catch exception:" + e);
}
return input;
}
@Override
public GifDecoder run(JobContext jc) {
InputStream input = getInputStream(itemUri);
if (input != null) {
return new GifDecoder(getInputStream(itemUri), null);
} else {
return null;
}
}
}
根据资源的uri,打开一个输入流,input = cr.openInputStream(uri);这个是调用frameworks/base/core/java/android/content/ContentResolver.java中的方法openInputStream();
真正执行解码的是GifDecoder 类,以下函数都来自于GifDecoder :
private InputStream mIS;
public GifDecoder(InputStream is, GifAction act) {
mIS = is;
mGifAction = act;
startDecoder();
}
startDecoder主要调用了readStream方法:
private int readStream() {
init();
if (mIS != null) {
readHeader();
if (!err()) {
readContents();
if (mFrameCount < 0) {
mStatus = STATUS_FORMAT_ERROR;
if (mGifAction != null) {
mGifAction.parseOk(false, -1);
}
} else {
mStatus = STATUS_FINISH;
if (mGifAction != null) {
mGifAction.parseOk(true, -1);
}
}
}
try {
mIS.close();
} catch (Exception e) {
e.printStackTrace();
}
} else {
mStatus = STATUS_OPEN_ERROR;
if (mGifAction != null) {
mGifAction.parseOk(false, -1);
}
}
return mStatus;
}
通过readHeader读取文件头,前3个字节是Gif署名,后三个字节是版本号:
private void readHeader() {
String id = "";
for (int i = 0; i < 6; i++) {
id += (char) read();
}
if (!id.startsWith("GIF")) {
mStatus = STATUS_FORMAT_ERROR;
return;
}
readLSD();
if (mGctFlag && !err()) {
mGct = readColorTable(mGctSize);
mBgColor = mGct[mBgIndex];
}
}
接着调用readLSD读取Gif的宽高、颜色方案、背景色索引、像素宽高比:
private void readLSD() {
// logical screen size
mWidth = readShort();
mHeight = readShort();
// packed fields
int packed = read();
mGctFlag = (packed & 0x80) != 0; // 1 : global color table flag
// 2-4 : color resolution
// 5 : gct sort flag
mGctSize = 2 << (packed & 7); // 6-8 : gct size
mBgIndex = read(); // background color index
mPixelAspect = read(); // pixel aspect ratio
}
接着读取内容:
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) mBlock[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:
mStatus = STATUS_FORMAT_ERROR;
}
}
}
通过readImage读取图像块部分,包括x、y方向偏移量,图像宽高,图像数据块:
private void readImage() {
mIx = readShort(); // (sub)image position & size
mIy = readShort();
mIw = readShort();
mIh = readShort();
int packed = read();
mLctFlag = (packed & 0x80) != 0; // 1 - local color table flag
mInterlace = (packed & 0x40) != 0; // 2 - interlace flag
// 3 - sort flag
// 4-5 - reserved
mLctSize = 2 << (packed & 7); // 6-8 - local color table size
if (mLctFlag) {
mLct = readColorTable(mLctSize); // read table
mAct = mLct; // make local table active
} else {
mAct = mGct; // make global table active
if (mBgIndex == mTransIndex) {
mBgColor = 0;
}
}
int save = 0;
if (mTransparency) {
save = mAct[mTransIndex];
mAct[mTransIndex] = 0; // set transparent color if specified
}
if (mAct == null) {
mStatus = STATUS_FORMAT_ERROR; // no color table defined
}
if (err()) {
return;
}
try {
decodeImageData(); // decode pixel data
skip();
if (err()) {
return;
}
mFrameCount++;
// create new image to receive frame data
mImage = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_4444);
// createImage(mWidth, mHeight);
setPixels(); // transfer pixel data to image
if (mGifFrame == null) {
mGifFrame = new GifFrame(mImage, mDelay, mDispose);
mCurrentFrame = mGifFrame;
} else {
GifFrame f = mGifFrame;
while (f.mNextFrame != null) {
f = f.mNextFrame;
}
f.mNextFrame = new GifFrame(mImage, mDelay, mDispose);
}
// frames.addElement(new GifFrame(image, delay)); // add image to
// frame
// list
if (mTransparency) {
mAct[mTransIndex] = save;
}
resetFrame();
if (mGifAction != null) {
mGifAction.parseOk(true, mFrameCount);
}
} catch (OutOfMemoryError e) {
Log.e("GifDecoder", ">>> log : " + e.toString());
e.printStackTrace();
}
}
由decodeImageData完成像素解码,然后将像素数据填充到Bitmap中,构建GifFrame对象。
private void decodeImageData() {
int NullCode = -1;
int npix = mIw * mIh;
int available, clear, code_mask, code_size, end_of_information, in_code, old_code,
bits, code, count, i, datum, data_size, first, top, bi, pi;
if ((mPixels == null) || (mPixels.length < npix)) {
mPixels = new byte[npix]; // allocate new pixel array
}
if (mPrefix == null) {
mPrefix = new short[MaxStackSize];
}
if (mSuffix == null) {
mSuffix = new byte[MaxStackSize];
}
if (mPixelStack == null) {
mPixelStack = new byte[MaxStackSize + 1];
}
// Initialize GIF data stream decoder.
data_size = read();
clear = 1 << data_size;
end_of_information = clear + 1;
available = clear + 2;
old_code = NullCode;
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
for (code = 0; code < clear; code++) {
mPrefix[code] = 0;
mSuffix[code] = (byte) code;
}
// Decode GIF pixel stream.
datum = bits = count = first = top = pi = bi = 0;
for (i = 0; i < npix;) {
if (top == 0) {
if (bits < code_size) {
// Load bytes until there are enough bits for a code.
if (count == 0) {
// Read a new data block.
count = readBlock();
if (count <= 0) {
break;
}
bi = 0;
}
datum += (((int) mBlock[bi]) & 0xff) << bits;
bits += 8;
bi++;
count--;
continue;
}
// Get the next code.
code = datum & code_mask;
datum >>= code_size;
bits -= code_size;
// Interpret the code
if ((code > available) || (code == end_of_information)) {
break;
}
if (code == clear) {
// Reset decoder.
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
available = clear + 2;
old_code = NullCode;
continue;
}
if (old_code == NullCode) {
mPixelStack[top++] = mSuffix[code];
old_code = code;
first = code;
continue;
}
in_code = code;
if (code == available) {
mPixelStack[top++] = (byte) first;
code = old_code;
}
while (code > clear) {
mPixelStack[top++] = mSuffix[code];
code = mPrefix[code];
}
first = ((int) mSuffix[code]) & 0xff;
// Add a new string to the string table,
if (available >= MaxStackSize) {
break;
}
mPixelStack[top++] = (byte) first;
mPrefix[available] = (short) old_code;
mSuffix[available] = (byte) first;
available++;
if (((available & code_mask) == 0)
&& (available < MaxStackSize)) {
code_size++;
code_mask += available;
}
old_code = in_code;
}
// Pop a pixel off the pixel stack.
top--;
mPixels[pi++] = mPixelStack[top];
i++;
}
for (i = pi; i < npix; i++) {
mPixels[i] = 0; // clear missing pixels
}
}
readImage之后,读取的是图形扩展部分,更多源码详见附件文件。
解码完成后,怎么播放Gif?
private class GifDecoderListener
implements Runnable, FutureListener<GifDecoder> {
private final Path mPath;
private Future<GifDecoder> mFuture;
public GifDecoderListener(Path path) {
mPath = path;
}
@Override
public void onFutureDone(Future<GifDecoder> future) {
mFuture = future;
if (null != mFuture.get()) {
mMainHandler.sendMessage(
mMainHandler.obtainMessage(MSG_RUN_OBJECT, this));
}
}
@Override
public void run() {
updateGifDecoder(mPath, mFuture);
}
}
在GifDecoder执行完成后,会调用Listener的OnFeatureDone,进一步,Gif的播放是在一个Runnable中完成的:
private class GifRunnable implements Runnable {
private GifEntry mGifEntry;
private Path mPath;
private void free() {
if (null != mGifEntry) {
if (null != mGifEntry.gifDecoder) {
mGifEntry.gifDecoder.free();
mGifEntry.gifDecoder = null;
}
mGifEntry = null;
}
}
public GifRunnable(Path path, GifEntry gifEntry) {
mPath = path;
mGifEntry = gifEntry;
if (null == mGifEntry || null == mGifEntry.gifDecoder) {
free();
return;
}
boolean imageChanged = mGifEntry.animatedIndex != mCurrentIndex;
MediaItem item = getMediaItem(0);
Path currentPath = (item != null ? item.getPath() : null);
imageChanged |= path != currentPath;
if (imageChanged) {
free();
return;
}
mGifEntry.currentFrame = 0;
mGifEntry.totalFrameCount = mGifEntry.gifDecoder.getFrameCount();
if (mGifEntry.totalFrameCount <= 1) {
free();
return;
}
}
@Override
public void run() {
if (!mIsActive) {
free();
return;
}
if (null == mGifEntry || null == mGifEntry.gifDecoder) {
free();
return;
}
boolean imageChanged = mGifEntry.animatedIndex != mCurrentIndex;
MediaItem item = getMediaItem(0);
Path currentPath = (item != null ? item.getPath() : null);
imageChanged |= mPath != currentPath;
if (imageChanged) {
free();
return;
}
Bitmap frameBitmap = mGifEntry.gifDecoder.getFrameImage(mGifEntry.currentFrame);
if (null == frameBitmap) {
free();
return;
}
long delay = (long) mGifEntry.gifDecoder.getDelay(mGifEntry.currentFrame);
mGifEntry.currentFrame = (mGifEntry.currentFrame + 1) % mGifEntry.totalFrameCount;
ScreenNail gifFrame = new BitmapScreenNail(frameBitmap);
if (mGifEntry.entry.currentGifFrame != null) {
mGifEntry.entry.currentGifFrame.recycle();
mGifEntry.entry.currentGifFrame = null;
}
mGifEntry.entry.currentGifFrame = gifFrame;
updateTileProvider(mGifEntry.entry);
mPhotoView.notifyImageChange(0);
mMainHandler.sendMessageDelayed(
mMainHandler.obtainMessage(MSG_RUN_OBJECT, this), delay);
}
}
会根据时间间隔、GifFrame帧数,循环投递播放消息,即:
mMainHandler.sendMessageDelayed( mMainHandler.obtainMessage(MSG_RUN_OBJECT, this), delay);
直到收到退出事件,调用free释放资源。
GifDecoder.java的源码可以下载:http://download.csdn.net/download/lin20044140410/10251700