android 开发 解码gif图片,获取每帧bitmap

环境:android 4.3  (注意对于android4.4版本解码出来不正确,除了第一帧正确外,其余的都是显示不同的地方)  通用版本见: android 开发对gif解码(适配android 4.2、4.3、4.4版本)

使用方法:

void showGif2()
    {
        gifDecoder = new GifImageDecoder();
        try {
            gifDecoder.read(this.getResources().openRawResource(R.drawable.b17));  //这是Gif图片资源
            int size =gifDecoder.getFrameCount();
            for(int i=0;i<size;i++)
            {
                
                ImageView iv_image = new ImageView(CustomActivity.this);
                iv_image.setPadding(5, 5, 5, 5);
                LayoutParams lparams = new LayoutParams(100,100);
                iv_image.setLayoutParams(lparams);
                iv_image.setImageBitmap(gifDecoder.getFrame(i));
                ll_decodeimages.addView(iv_image);
//                gifFrame.nextFrame();
            }
        } catch (NotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


见效果:

全部代码:

package com.xlm.testgif;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
import android.view.Menu;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;

public class CustomActivity extends Activity {

    LinearLayout ll_decodeimages;
//    GifFrame gifFrame = null;
    GifImageDecoder gifDecoder;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom);
        ll_decodeimages = (LinearLayout) findViewById(R.id.ll_decodeimages);
        showGif2();
    }
//    void showGif()
//    {
//        // 解析gif动画
//        gifFrame = GifFrame.createGifImage(fileConnect(this.getResources().openRawResource(R.drawable.b10)));
//        int size = gifFrame.size();
//        for(int i=0;i<size;i++)
//        {
//            
//            ImageView iv_image = new ImageView(CustomActivity.this);
//            iv_image.setPadding(5, 5, 5, 5);
//            LayoutParams lparams = new LayoutParams(100,100);
//            iv_image.setLayoutParams(lparams);
//            iv_image.setImageBitmap(gifFrame.getImage());
//            ll_decodeimages.addView(iv_image);
//            gifFrame.nextFrame();
//        }
//    }
    void showGif2()
    {
        gifDecoder = new GifImageDecoder();
        try {
            gifDecoder.read(this.getResources().openRawResource(R.drawable.b17));
            int size =gifDecoder.getFrameCount();
            for(int i=0;i<size;i++)
            {
                
                ImageView iv_image = new ImageView(CustomActivity.this);
                iv_image.setPadding(5, 5, 5, 5);
                LayoutParams lparams = new LayoutParams(100,100);
                iv_image.setLayoutParams(lparams);
                iv_image.setImageBitmap(gifDecoder.getFrame(i));
                ll_decodeimages.addView(iv_image);
//                gifFrame.nextFrame();
            }
        } catch (NotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
//    Handler handler = new Handler()
//    {
//
//        @Override
//        public void handleMessage(Message msg) {
//            switch(msg.what)
//            {
//            case 1:
//                Bitmap bmp = (Bitmap) msg.obj;
//                if(bmp!=null)
//                {
//                    ImageView iv_image = new ImageView(CustomActivity.this);
//                    iv_image.setImageBitmap(bmp);
//                    ll_decodeimages.addView(iv_image);
//                }
//                break;
//            }
//        }
//        
//    };
    /**
     * 独立线程解码gif图片
     * @author huqiang
     *
     */
//    class DecodeGif implements Runnable 
//    {
//        Context mContext ;
//        Handler mHandler;
//        public DecodeGif(Context context,Handler handler)
//        {
//            this.mContext = context;
//            this.mHandler = handler;
//        }
//        @Override
//        public void run() {
//            //开始解析gif
//            // 解析gif动画
////            gifFrame = GifFrame.createGifImage(fileConnect(this.getResources().openRawResource(R.drawable.test)));
//        }
//        public void start()
//        {
//            new Thread(this).start();
//        }
//    }
    // 读取文件
        public byte[] fileConnect(InputStream is)
        {
            try
            {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                int ch = 0;
                while ((ch = is.read()) != -1)
                {
                    baos.write(ch);
                }
                byte[] b = baos.toByteArray();
                baos.close();
                baos = null;
                is.close();
                is = null;
                return b;
            } catch (Exception e)
            {
                return null;
            }
        }

}
package com.xlm.testgif;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

public class GifImageDecoder {
    private static final String TAG = GifImageDecoder.class.getSimpleName();
    private final GifImageDecoder self = this;

    // File read status: No errors.
    public static final int STATUS_OK = 0;
    // File read status: Error decoding file (may be partially decoded)
    public static final int STATUS_FORMAT_ERROR = 1;
    // File read status: Unable to open source.
    public static final int STATUS_OPEN_ERROR = 2;
    // Trailer
    private static final byte TRR_CODE = (byte) 0x3B;
    // Image Block
    private static final byte IMG_CODE = (byte) 0x2C;
    // Extension
    private static final byte EXT_CODE = (byte) 0x21;
    // Graphic Control Extension
    private static final byte GC_EXT = (byte) 0xF9;
    // Application Extension
    private static final byte APP_EXT = (byte) 0xFF;
    // Comment Extension
    private static final byte CMT_EXT = (byte) 0xFE;
    // Plain Text Extension
    private static final byte TXT_EXT = (byte) 0x01;

    private static final int MIN_DELAY = 100;
    private static final int MIN_DELAY_ENFORCE_THRESHOLD = 20;

    protected int mStatus;
    protected int mWidth; // full mCurrentImage mWidth
    protected int mHeight; // full mCurrentImage mHeight
    protected Bitmap mCurrentImage; // current frame
    protected Bitmap mLastImage; // previous frame
    protected int mDispose = 0; // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
    protected int mLastDispose = 0;
    protected int mDelay = 0; // mDelay in milliseconds
    protected ArrayList<GifFrame> mGifFrames; // mGifFrames read from current file
    protected int mFrameCount;

    private int mOffset = 0;

    private GifHeader mGifHeader;
    private GraphicControlExtension mGcExt;
    private ImageBlock mImageBlock;

    private static class GifFrame {
        public GifFrame(Bitmap im, int del) {
            image = im;
            delay = del;
        }

        public Bitmap image;
        public int delay;
    }

    /**
     * Gets display duration for specified frame.
     *
     * @param n int index of frame
     * @return delay in milliseconds
     */
    public int getDelay(int n) {
        mDelay = -1;
        if ((n >= 0) && (n < mFrameCount)) {
            mDelay = mGifFrames.get(n).delay;
            if (mDelay < MIN_DELAY_ENFORCE_THRESHOLD) {
                mDelay = MIN_DELAY;
            }
        }
        return mDelay;
    }
    /**
     * Gets the number of GifFrames read from file.
     *
     * @return frame count
     */
    public int getFrameCount() {
        return mFrameCount;
    }

    /**
     * Gets the first (or only) image read.
     *
     * @return BufferedBitmap containing first frame, or null if none.
     */
    public Bitmap getBitmap() {
        return getFrame(0);
    }

    /**
     * Gets the image contents of frame n.
     *
     * @return BufferedBitmap representation of frame, or null if n is invalid.
     */
    public Bitmap getFrame(int n) {
        if (mFrameCount <= 0)
            return null;
        n = n % mFrameCount;
        return (mGifFrames.get(n)).image;
    }

    /**
     * Reads GIF image from stream
     *
     * @param is containing GIF file.
     * @return read status code (0 = no errors)
     */

    public int read(InputStream is) throws IOException {
        init();
        if (is != null) {
            byte[] buffer = Utils.streamToBytes(is);
            mGifHeader = new GifHeader(buffer, mOffset);
            mOffset += mGifHeader.size;
            mWidth = mGifHeader.getWidth();
            mHeight = mGifHeader.getHeight();
            if (!mGifHeader.getSignature().equals("GIF")) {
                return STATUS_FORMAT_ERROR;
            }
            while (buffer[mOffset] != TRR_CODE) {
                if (buffer[mOffset] == IMG_CODE) {
                    // ImageBlock
                    mImageBlock = new ImageBlock(buffer, mOffset);
                    mOffset += mImageBlock.size;

                    mFrameCount++;
                    // create new image to receive frame data
                    mCurrentImage = extractImage();
                    if (mLastDispose > 0) {
                        if (mLastDispose == 3) {
                            // use image before last
                            int n = mFrameCount - 2;
                            if (n > 0) {
                                mLastImage = getFrame(n - 1);
                            } else {
                                mLastImage = null;
                            }
                        }
                    }
                    mGifFrames.add(new GifFrame(mCurrentImage, mDelay)); // add image to frame
                    resetFrame();
                } else if (buffer[mOffset] == EXT_CODE) {
                    if (buffer[mOffset + 1] == GC_EXT) {
                        //GraphicControlExtension
                        mGcExt = new GraphicControlExtension(buffer, mOffset);
                        mOffset += mGcExt.size;
                        mDispose = mGcExt.getDisposalMothod(); // disposal method
                        if (mDispose == 0) {
                            mDispose = 1; // elect to keep old image if discretionary
                        }
                        mDelay = mGcExt.getDelayTime() * 10; // delay in milliseconds
                    } else if (buffer[mOffset + 1] == APP_EXT) {
                        //ApplicationExtension
                        ApplicationExtension appExt = new ApplicationExtension(buffer, mOffset);
                        mOffset += appExt.size;
                    } else if (buffer[mOffset + 1] == CMT_EXT) {
                        //CommentExtension
                        CommentExtension cmtExt = new CommentExtension(buffer, mOffset);
                        mOffset += cmtExt.size;
                    } else if (buffer[mOffset + 1] == TXT_EXT) {
                        //PlainTextExtension
                        PlainTextExtension txtExt = new PlainTextExtension(buffer, mOffset);
                        mOffset += txtExt.size;
                    } else {
                        throw new IOException();
                    }
                } else {
                    throw new IOException();
                }
            }
        } else {
            mStatus = STATUS_OPEN_ERROR;
        }
        return mStatus;
    }

    /**
     * Initializes or re-initializes reader
     */
    protected void init() {
        mStatus = STATUS_OK;
        mFrameCount = 0;
        mGifFrames = new ArrayList<GifFrame>();
    }

    /**
     * Resets frame state for reading next image.
     */
    protected void resetFrame() {
        mLastDispose = mDispose;
        mLastImage = mCurrentImage;
        mDispose = 0;
        mDelay = 0;
    }

    /**
     * Extract new image
     *
     * @return image
     */
    @SuppressLint("NewApi") private Bitmap extractImage() {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            outputStream.write(mGifHeader.bytes);
            if (mGcExt != null) {
                if ((mWidth != mImageBlock.getImageWidth() || mHeight != mImageBlock.getImageHeight()) &&
                        mGcExt.getTransparentColorFlag() == 0) {
                    mGcExt.setTransparentColorFlagTrue();
                }
                outputStream.write(mGcExt.bytes);
            }
            outputStream.write(mImageBlock.bytes);
            outputStream.write((byte) 0x3B);
            outputStream.flush();

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inMutable = true;
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            Bitmap newBitmap = BitmapFactory.decodeStream(new BufferedInputStream(new ByteArrayInputStream(outputStream.toByteArray())));
            if (newBitmap != null) {
                if (mLastImage == null) {
                    return newBitmap;
                } else {
                    Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
                    Canvas canvas = new Canvas(bitmap);
                    canvas.drawBitmap(mLastImage, 0, 0, null);
                    canvas.drawBitmap(newBitmap, 0, 0, null);
                    return bitmap;
                }
            } else {
                if (mLastImage != null) {
                    return mLastImage;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return null;
    }

    private class GifHeader {
        public byte[] bytes;
        public int size;

        public GifHeader(byte[] bytes, int offset) {
            boolean globalColorTableFlag = (bytes[offset + 0x0A] & 0x80) != 0x00;
            int globalColorTableSize = (bytes[offset + 0x0A] & 0x07);

            // get size
            size = 0x0D;
            if (globalColorTableFlag) {
                size += Math.pow(2, (globalColorTableSize + 1)) * 3;
            }

            this.bytes = new byte[size];
            System.arraycopy(bytes, offset, this.bytes, 0, size);
        }

        public String getSignature() {
            return new String(bytes, 0, 3);
        }

        public String getVersion() {
            return new String(bytes, 3, 3);
        }

        public int getWidth() {
            return (bytes[6] & 0xFF) + ((bytes[7] & 0xFF) << 8);
        }

        public int getHeight() {
            return (bytes[8] & 0xFF) + ((bytes[9] & 0xFF) << 8);
        }

        public int getGlobalColorTableFlag() {
            return (bytes[10] & 0x80) >> 7;
        }

        public int getColorResolution() {
            return (bytes[10] & 0x70) >> 4;
        }

        public int getSortFlag() {
            return (bytes[10] & 0x08) >> 3;
        }

        public int getSizeOfGlobalColorTable() {
            return (bytes[10] & 0x07);
        }

        public int getBackgroundColorIndex() {
            return bytes[11] & 0xFF;
        }

        public int getPixelAspectRatio() {
            return bytes[12];
        }

        public int[] getGlobalColorTable() {
            if (getGlobalColorTableFlag() == 0) {
                return new int[0];
            }
            int[] colors = new int[(int) Math.pow(2, getSizeOfGlobalColorTable() + 1)];
            for (int i = 0; i < colors.length; i++) {
                colors[i] = ((bytes[13 + (i * 3)] & 0xFF) << 16) + ((bytes[13 + (i * 3) + 1] & 0xFF) << 8) + (bytes[13 + (i * 3) + 2] & 0xFF);
            }
            return colors;
        }
    }

    private class ImageBlock {
        public byte[] bytes;
        public int size;

        public ImageBlock(byte[] bytes, int offset) {
            int blockSize;
            boolean localColorTableFlag = (bytes[offset + 0x09] & 0x80) != 0x00;
            int localColorTableSize = (bytes[offset + 0x09] & 0x07);

            //get size
            size = 0x0A;
            if (localColorTableFlag) {
                size += Math.pow(2, (localColorTableSize + 1)) * 3;
            }
            size += 1; //LZW Minimum Code Size

            //ImageData
            blockSize = bytes[offset + size] & 0xFF;
            size += 1;
            while (blockSize != 0x00) {
                size += blockSize;
                blockSize = bytes[offset + size] & 0xFF;
                size += 1;
            }

            this.bytes = new byte[size];
            System.arraycopy(bytes, offset, this.bytes, 0, size);
        }

        public int getImageSeparator() {
            return bytes[0] & 0xFF;
        }

        public int ImageLeftPosition() {
            return (bytes[1] & 0xFF) + ((bytes[2] & 0xFF) << 8);
        }

        public int getImageTopPosition() {
            return (bytes[3] & 0xFF) + ((bytes[4] & 0xFF) << 8);
        }

        public int getImageWidth() {
            return (bytes[5] & 0xFF) + ((bytes[6] & 0xFF) << 8);
        }

        public int getImageHeight() {
            return (bytes[7] & 0xFF) + ((bytes[8] & 0xFF) << 8);
        }

        public int getLocalColorTableFlag() {
            return (bytes[9] & 0x80) >> 7;
        }

        public int getInterlaceFlag() {
            return (bytes[9] & 0x40) >> 6;
        }

        public int getSortFlag() {
            return (bytes[9] & 0x20) >> 5;
        }

        public int getReserved() {
            return (bytes[9] & 0x18) >> 2;
        }

        public int getSizeOfLocalColorTable() {
            return bytes[9] & 0x03;
        }

        public int[] getLocalColorTable() {
            if (getLocalColorTableFlag() == 0) {
                return new int[0];
            }
            int[] colors = new int[(int) Math.pow(2, getSizeOfLocalColorTable() + 1)];
            for (int i = 0; i < colors.length; i++) {
                colors[i] = ((bytes[10 + (i * 3)] & 0xFF) << 16) + ((bytes[10 + (i * 3) + 1] & 0xFF) << 8) + (bytes[10 + (i * 3) + 2] & 0xFF);
            }
            return colors;
        }

        public int getLZWMinimumCodeSize() {
            if (getLocalColorTableFlag() == 0) {
                return bytes[10] & 0xFF;
            } else {
                return bytes[10 + (int) Math.pow(2, getSizeOfLocalColorTable() + 1) * 3] & 0xFF;
            }
        }
    }

    private class ApplicationExtension {
        public byte[] bytes;
        public int size;

        public ApplicationExtension(byte[] bytes, int offset) {
            int blockSize;
            // get size
            size = 0x0E;

            blockSize = bytes[offset + size] & 0xFF;
            size += 1;
            while (blockSize != 0x00) {
                size += blockSize;
                blockSize = bytes[offset + size] & 0xFF;
                size += 1;
            }

            this.bytes = new byte[size];
            System.arraycopy(bytes, offset, this.bytes, 0, size);
        }

        public int getExtensionIntroducer() {
            return bytes[0] & 0xFF;
        }

        public int getExtensionLabel() {
            return bytes[1] & 0xFF;
        }

        public int getBlockSize1() {
            return bytes[2] & 0xFF;
        }

        public String getApplicationIdentifier() {
            return new String(bytes, 3, 8);
        }

        public String getApplicationAuthenticationCode() {
            return new String(bytes, 11, 3);
        }
    }

    private class GraphicControlExtension {
        public byte[] bytes;
        public int size;

        public GraphicControlExtension(byte[] bytes, int offset) {
            size = 8;
            this.bytes = new byte[size];
            System.arraycopy(bytes, offset, this.bytes, 0, size);
        }

        public int getExtensionIntroducer() {
            return bytes[0] & 0xFF;
        }

        public int getGraphicControlLabel() {
            return bytes[1] & 0xFF;
        }

        public int getBlockSize() {
            return bytes[2] & 0xFF;
        }

        public int getReserved() {
            return (bytes[3] & 0xE0) >> 5;
        }

        public int getDisposalMothod() {
            return (bytes[3] & 0x1C) >> 2;
        }

        public int getUserInputFlag() {
            return (bytes[3] & 0x02) >> 1;
        }

        public int getTransparentColorFlag() {
            return (bytes[3] & 0x01);
        }

        public int getDelayTime() {
            return (bytes[4] & 0xFF) + ((bytes[5] & 0xFF) << 8);
        }

        public int getTransparentColorIndex() {
            return bytes[6];
        }

        public void setTransparentColorFlagTrue() {
            int value = getReserved() | getDisposalMothod() | getUserInputFlag() | 0x01;
            bytes[3] = (byte) Integer.parseInt(Utils.toHex(value, 2), 16);
        }
    }

    private class CommentExtension {
        public byte[] bytes;
        public int size;

        public CommentExtension(byte[] bytes, int offset) {
            int blockSize;
            // get size
            size = 0x02;

            blockSize = bytes[offset + size] & 0xFF;
            size += 1;
            while (blockSize != 0x00) {
                size += blockSize;
                blockSize = bytes[offset + size] & 0xFF;
                size += 1;
            }

            this.bytes = new byte[size];
            System.arraycopy(bytes, offset, this.bytes, 0, size);
        }
    }

    private class PlainTextExtension {
        public byte[] bytes;
        public int size;

        public PlainTextExtension(byte[] bytes, int offset) {
            int blockSize;
            // get size
            size = 0x0F;

            blockSize = bytes[offset + size] & 0xFF;
            size += 1;
            while (blockSize != 0x00) {
                size += blockSize;
                blockSize = bytes[offset + size] & 0xFF;
                size += 1;
            }

            this.bytes = new byte[size];
            System.arraycopy(bytes, offset, this.bytes, 0, size);
        }
    }
}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class Utils {
    public static String toHex(int value, int length) {
        String hex = Integer.toHexString(value);
        hex = hex.toUpperCase();

        if (hex.length() < length) {
            while (hex.length() < length)
                hex = "0" + hex;
        } else if (hex.length() > length) {
            hex = hex.substring(hex.length() - length);
        }
        return hex;
    }

    public static byte[] streamToBytes(InputStream stream) throws IOException,
            OutOfMemoryError {
        byte[] buff = new byte[1024];
        int read;
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        while ((read = stream.read(buff)) != -1) {
            bao.write(buff, 0, read);
        }
        try {
            stream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bao.toByteArray();
    }
}
原文地址:https://www.cnblogs.com/feijian/p/4495281.html