开源gif

来源:互联网 发布:网络连接正常后请重试 编辑:程序博客网 时间:2024/04/29 16:17

类似ImageVeiw:


/** * GifView<br> * 本类可以显示一个gif动画,其使用方法和android的其它view(如imageview)一样。<br> * */public class GifView extends ImageView implements GifAction{        /**         * gif解码器         */        private GifDecoder gifDecoder = null;        /**         * 当前要画的帧的图         */        private Bitmap currentImage = null;                private Context mContext;                private boolean isRun = true;                private boolean pause = false;        private DrawThread drawThread = null;                        private View backView = null;                private GifImageType animationType = GifImageType.SYNC_DECODER;        /**         * 解码过程中,Gif动画显示的方式<br>         * 如果图片较大,那么解码过程会比较长,这个解码过程中,gif如何显示         *         */        public enum GifImageType{                /**                 * 在解码过程中,不显示图片,直到解码全部成功后,再显示                 */                WAIT_FINISH (0),                /**                 * 和解码过程同步,解码进行到哪里,图片显示到哪里                 */                SYNC_DECODER (1),                /**                 * 在解码过程中,只显示第一帧图片                 */                COVER (2);                                GifImageType(int i){                        nativeInt = i;                }                final int nativeInt;        }                        public GifView(Context context) {        super(context);        this.mContext = context;        //gifDecoder = new GifDecoder(this);        setScaleType(ImageView.ScaleType.FIT_XY);    }        public GifView(Context context, AttributeSet attrs) {        this(context, attrs,0);            }          public GifView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        this.mContext = context;       // TypedArray a = context.obtainStyledAttributes(attrs,R.array.);        //gifDecoder = new GifDecoder(this);        setScaleType(ImageView.ScaleType.FIT_XY);    }        /**     * 设置图片,开始解码     * @param is 要设置的图片     */    private void setGifDecoderImage(InputStream is){        if(gifDecoder == null){            gifDecoder = new GifDecoder(this);        }        gifDecoder.setGifImage(is);        gifDecoder.start();                    }        /**     * 把本Gif动画设置为另外view的背景     * @param v 要使用gif作为背景的view     */    public void setAsBackground(View v){        backView = v;    }        protected Parcelable onSaveInstanceState() {        super.onSaveInstanceState();        if(gifDecoder != null)                gifDecoder.free();                        return null;        }        /**     * 以资源形式设置gif图片     * @param resId gif图片的资源ID     */    public void setGifImage(int resId)    {        Resources r = getResources();        InputStream is = r.openRawResource(resId);        setGifDecoderImage(is);    }        public void destroy(){        if(gifDecoder != null)            gifDecoder.free();    }        /**     * 只显示第一帧图片<br>     * 调用本方法后,gif不会显示动画,只会显示gif的第一帧图     */    public void showCover(){        if(gifDecoder == null)                return;        pause = true;        currentImage = gifDecoder.getImage();        invalidate();    }        /**     * 继续显示动画<br>     * 本方法在调用showCover后,会让动画继续显示,如果没有调用showCover方法,则没有任何效果     */    public void showAnimation(){        if(pause){                pause = false;        }    }        /**     * 设置gif在解码过程中的显示方式<br>     * <strong>本方法只能在setGifImage方法之前设置,否则设置无效</strong>     * @param type 显示方式     */    public void setGifImageType(GifImageType type){        if(gifDecoder == null)                animationType = type;    }        /**     * @hide     */    public void parseOk(boolean parseStatus,int frameIndex){        if(parseStatus){                if(gifDecoder != null){                        switch(animationType){                        case WAIT_FINISH:                                if(frameIndex == -1){                                        if(gifDecoder.getFrameCount() > 1){     //当帧数大于1时,启动动画线程                                        DrawThread dt = new DrawThread();                                dt.start();                                }else{                                        reDraw();                                }                                }                                break;                        case COVER:                                if(frameIndex == 1){                                        currentImage = gifDecoder.getImage();                                        reDraw();                                }else if(frameIndex == -1){                                        if(gifDecoder.getFrameCount() > 1){                                                if(drawThread == null){                                                        drawThread = new DrawThread();                                                        drawThread.start();                                                }                                        }else{                                                reDraw();                                        }                                }                                break;                        case SYNC_DECODER:                                if(frameIndex == 1){                                        currentImage = gifDecoder.getImage();                                        reDraw();                                }else if(frameIndex == -1){                                        reDraw();                                }else{                                        if(drawThread == null){                                                drawThread = new DrawThread();                                                drawThread.start();                                        }                                }                                break;                        }                 }else{                        Log.e("gif","parse error");                }                        }    }        private void reDraw(){        if(redrawHandler != null){                        Message msg = redrawHandler.obtainMessage();                        redrawHandler.sendMessage(msg);        }            }        private void drawImage(){        setImageBitmap(currentImage);        invalidate();    }         private Handler redrawHandler = new Handler(){        public void handleMessage(Message msg) {            try{                    if(backView != null){                    backView.setBackgroundDrawable(new BitmapDrawable(currentImage));                }else{                    drawImage();                }            }catch(Exception ex){                Log.e("GifView", ex.toString());            }        }    };        /**     * 动画线程     */    private class DrawThread extends Thread{            public void run(){                if(gifDecoder == null){                        return;                }                while(isRun){                    if(gifDecoder.getFrameCount() == 1){                        //如果单帧,不进行动画                        GifFrame f = gifDecoder.next();                        currentImage = f.image;                    gifDecoder.free();                    reDraw();                                                break;                    }                if (pause == false) {                    GifFrame frame = gifDecoder.next();                    if (frame == null) {                        SystemClock.sleep(50);                        continue;                    }                    if (frame.image != null)                        currentImage = frame.image;                    else if (frame.imageName != null) {                        currentImage = BitmapFactory.decodeFile(frame.imageName);                    }                    long sp = frame.delay;                    if (redrawHandler != null) {                        reDraw();                        SystemClock.sleep(sp);                    } else {                        break;                    }                } else {                    SystemClock.sleep(50);                        }                }        }    }    }


解码线程:


public class GifDecoder extends Thread{/** * 状态:正在解码中 */public static final int STATUS_PARSING = 0;/** * 状态:图片格式错误 */public static final int STATUS_FORMAT_ERROR = 1;/** * 状态:打开失败 */public static final int STATUS_OPEN_ERROR = 2;/** * 状态:解码成功 */public static final int STATUS_FINISH = -1;private InputStream in;private int status;public int width; // full image widthpublic int height; // full image heightprivate boolean gctFlag; // global color table usedprivate int gctSize; // size of global color tableprivate int loopCount = 1; // iterations; 0 = repeat foreverprivate int[] gct; // global color tableprivate int[] lct; // local color tableprivate int[] act; // active color tableprivate int bgIndex; // background color indexprivate int bgColor; // background colorprivate int lastBgColor; // previous bg colorprivate int pixelAspect; // pixel aspect ratioprivate boolean lctFlag; // local color table flagprivate boolean interlace; // interlace flagprivate int lctSize; // local color table sizeprivate int ix, iy, iw, ih; // current image rectangleprivate int lrx, lry, lrw, lrh;private Bitmap image; // current frameprivate Bitmap lastImage; // previous frameprivate GifFrame currentFrame = null;private boolean isShow = false;private byte[] block = new byte[256]; // current data blockprivate int blockSize = 0; // block size// last graphic control extension infoprivate int dispose = 0;// 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prevprivate int lastDispose = 0;private boolean transparency = false; // use transparent colorprivate int delay = 0; // delay in millisecondsprivate int transIndex; // transparent color indexprivate static final int MaxStackSize = 4096;// max decoder pixel stack size// LZW decoder working arraysprivate short[] prefix;private byte[] suffix;private byte[] pixelStack;private byte[] pixels;private GifFrame gifFrame; // frames read from current fileprivate int frameCount;private GifAction action = null;public GifDecoder(GifAction action){this.action = action;}public void setGifImage(InputStream is){in = is;}/** * 设置是否要缓存图片<br> * 当设置要缓存图片时,解码出来的图片会直接写到文件中,而不保留在内存中,缓存时如果有Sd卡,会直接缓存到sd卡中, * 如果没有则会缓存到当前apk下的文件目录中<br> * <b>如果创建缓存目录失败,会自动切换为不缓存</b><br> * 默认不缓存 *  * @param cache *            是否要缓存,true要缓存 * @param context */// public void setCacheImage(boolean cache,Context context){// cacheImage = cache;// try{// if(cacheImage){// boolean f = true;//// if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){// imagePath =// android.os.Environment.getExternalStorageDirectory().getPath() +// File.separator + "gifView_tmp_dir" + File.separator + getDir();// if(!createDir(imagePath)){// f = true;// }else{// f = false;// }// }else{// f = true;// }//// if(f){// imagePath = context.getFilesDir().getAbsolutePath() + File.separator +// getDir();// if(!createDir(imagePath))// cacheImage = false;// }// }// }catch(Exception ex){// cacheImage = false;// }// }// private void delDir(String folderPath,boolean dirDel) {// try {// delAllFile(folderPath);// if(dirDel){// String filePath = folderPath;// filePath = filePath.toString();// java.io.File myFilePath = new java.io.File(filePath);// myFilePath.delete();// }// } catch (Exception e) {//// }// }// private boolean delAllFile(String path) {// boolean bea = false;// File file = new File(path);// if (!file.exists()) {// return bea;// }// if (!file.isDirectory()) {// return bea;// }// String[] tempList = file.list();// File temp = null;// for (int i = 0; i < tempList.length; i++) {// if (path.endsWith(File.separator)) {// temp = new File(path + tempList[i]);// } else {// temp = new File(path + File.separator + tempList[i]);// }// if (temp.isFile()) {// temp.delete();// }else if (temp.isDirectory()) {// delAllFile(path + File.separator + tempList[i]);// delDir(path + File.separator + tempList[i],true);// bea = true;// }// }// return bea;// }// private synchronized String getDir(){// return String.valueOf(System.currentTimeMillis());// }// private boolean createDir(String path) {// boolean ret = false;// try {// File file = new File(path);// if (!file.exists()) {// ret = file.mkdirs();//// }else// ret = true;// } catch (Exception e) {//// ret = false;// }// return ret;// }// private void saveImage(Bitmap image,String name){// try{// File f = new File(imagePath + File.separator + name + ".png");// FileOutputStream fos = new FileOutputStream(imagePath + File.separator +// getDir() + ".png");// image.compress(Bitmap.CompressFormat.PNG, 100, fos);// }catch(Exception ex){//// }// }public void run(){if (in != null){readStream();}}/** * 释放资源 */public void free(){GifFrame fg = gifFrame;while (fg != null){if (fg.image != null && !fg.image.isRecycled())fg.image.recycle();fg.image = null;fg = null;gifFrame = gifFrame.nextFrame;fg = gifFrame;}if (in != null){try{in.close();}catch (Exception ex){}in = null;}status = 0;currentFrame = null;}/** * 当前状态 *  * @return */public int getStatus(){return status;}/** * 解码是否成功,成功返回true *  * @return 成功返回true,否则返回false */public boolean parseOk(){return status == STATUS_FINISH;}/** * 取某帧的延时时间 *  * @param n *            第几帧 * @return 延时时间,毫秒 */public int getDelay(int n){delay = -1;if ((n >= 0) && (n < frameCount)){// delay = ((GifFrame) frames.elementAt(n)).delay;GifFrame f = getFrame(n);if (f != null)delay = f.delay;}return delay;}/** * 取所有帧的延时时间 *  * @return */public int[] getDelays(){GifFrame f = gifFrame;int[] d = new int[frameCount];int i = 0;while (f != null && i < frameCount){d[i] = f.delay;f = f.nextFrame;i++;}return d;}/** * 取总帧 数 *  * @return 图片的总帧数 */public int getFrameCount(){return frameCount;}/** * 取第一帧图片 *  * @return */public Bitmap getImage(){return getFrameImage(0);}public int getLoopCount(){return loopCount;}private void setPixels(){int[] dest = new int[width * height];// fill in starting image contents based on last image's dispose codeif (lastDispose > 0){if (lastDispose == 3){// use image before lastint n = frameCount - 2;if (n > 0){lastImage = getFrameImage(n - 1);}else{lastImage = null;}}if (lastImage != null){lastImage.getPixels(dest, 0, width, 0, 0, width, height);// copy pixelsif (lastDispose == 2){// fill last image rect area with background colorint c = 0;if (!transparency){c = lastBgColor;}for (int i = 0; i < lrh; i++){int n1 = (lry + i) * width + lrx;int n2 = n1 + lrw;for (int k = n1; k < n2; k++){dest[k] = c;}}}}}// copy each source line to the appropriate place in the destinationint pass = 1;int inc = 8;int iline = 0;for (int i = 0; i < ih; i++){int line = i;if (interlace){if (iline >= ih){pass++;switch (pass){case 2:iline = 4;break;case 3:iline = 2;inc = 4;break;case 4:iline = 1;inc = 2;}}line = iline;iline += inc;}line += iy;if (line < height){int k = line * width;int dx = k + ix; // start of line in destint dlim = dx + iw; // end of dest lineif ((k + width) < dlim){dlim = k + width; // past dest edge}int sx = i * iw; // start of line in sourcewhile (dx < dlim){// map color and insert in destinationint index = ((int) pixels[sx++]) & 0xff;int c = act[index];if (c != 0){dest[dx] = c;}dx++;}}}image = Bitmap.createBitmap(dest, width, height, Config.ARGB_4444);}/** * 取第几帧的图片 *  * @param n *            帧数 * @return 可画的图片,如果没有此帧或者出错,返回null */public Bitmap getFrameImage(int n){GifFrame frame = getFrame(n);if (frame == null)return null;elsereturn frame.image;}/** * 取当前帧图片 *  * @hide * @return 当前帧可画的图片 */public GifFrame getCurrentFrame(){return currentFrame;}/** * 取第几帧,每帧包含了可画的图片和延时时间 *  * @hide * @param n *            帧数 * @return */public GifFrame getFrame(int n){GifFrame frame = gifFrame;int i = 0;while (frame != null){if (i == n){return frame;}else{frame = frame.nextFrame;}i++;}return null;}/** * 重置,进行本操作后,会直接到第一帧 *  * @hide */public void reset(){currentFrame = gifFrame;}/** * 下一帧,进行本操作后,通过getCurrentFrame得到的是下一帧 *  * @hide * @return 返回下一帧 */public GifFrame next(){if (isShow == false){isShow = true;return gifFrame;}else{if (currentFrame == null)return null;if (status == STATUS_PARSING){if (currentFrame.nextFrame != null)currentFrame = currentFrame.nextFrame;// currentFrame = gifFrame;}else{currentFrame = currentFrame.nextFrame;if (currentFrame == null){currentFrame = gifFrame;}}return currentFrame;}}private int readStream(){init();if (in != null){readHeader();if (!err()){readContents();if (frameCount < 0){status = STATUS_FORMAT_ERROR;action.parseOk(false, -1);}else{status = STATUS_FINISH;action.parseOk(true, -1);}}try{in.close();}catch (Exception e){e.printStackTrace();}}else{status = STATUS_OPEN_ERROR;action.parseOk(false, -1);}return status;}private void decodeImageData(){int NullCode = -1;int npix = iw * ih;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 ((pixels == null) || (pixels.length < npix)){pixels = new byte[npix]; // allocate new pixel array}if (prefix == null){prefix = new short[MaxStackSize];}if (suffix == null){suffix = new byte[MaxStackSize];}if (pixelStack == null){pixelStack = 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++){prefix[code] = 0;suffix[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) block[bi]) & 0xff) << bits;bits += 8;bi++;count--;continue;}// Get the next code.code = datum & code_mask;datum >>= code_size;bits -= code_size;// Interpret the codeif ((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){pixelStack[top++] = suffix[code];old_code = code;first = code;continue;}in_code = code;if (code == available){pixelStack[top++] = (byte) first;code = old_code;}while (code > clear){pixelStack[top++] = suffix[code];code = prefix[code];}first = ((int) suffix[code]) & 0xff;// Add a new string to the string table,if (available >= MaxStackSize){break;}pixelStack[top++] = (byte) first;prefix[available] = (short) old_code;suffix[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--;pixels[pi++] = pixelStack[top];i++;}for (i = pi; i < npix; i++){pixels[i] = 0; // clear missing pixels}}private boolean err(){return status != STATUS_PARSING;}private void init(){status = STATUS_PARSING;frameCount = 0;gifFrame = null;gct = null;lct = null;}private int read(){int curByte = 0;try{curByte = in.read();}catch (Exception e){status = STATUS_FORMAT_ERROR;}return curByte;}private int readBlock(){blockSize = read();int n = 0;if (blockSize > 0){try{int count = 0;while (n < blockSize){count = in.read(block, n, blockSize - n);if (count == -1){break;}n += count;}}catch (Exception e){e.printStackTrace();}if (n < blockSize){status = STATUS_FORMAT_ERROR;}}return n;}private int[] readColorTable(int ncolors){int nbytes = 3 * ncolors;int[] tab = null;byte[] c = new byte[nbytes];int n = 0;try{n = in.read(c);}catch (Exception e){e.printStackTrace();}if (n < nbytes){status = STATUS_FORMAT_ERROR;}else{tab = new int[256]; // max size to avoid bounds checksint i = 0;int j = 0;while (i < ncolors){int r = ((int) c[j++]) & 0xff;int g = ((int) c[j++]) & 0xff;int b = ((int) c[j++]) & 0xff;tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;}}return tab;}private void readContents(){// read GIF file content blocksboolean done = false;while (!(done || err())){int code = read();switch (code){case 0x2C: // image separatorreadImage();break;case 0x21: // extensioncode = read();switch (code){case 0xf9: // graphics control extensionreadGraphicControlExt();break;case 0xff: // application extensionreadBlock();String app = "";for (int i = 0; i < 11; i++){app += (char) block[i];}if (app.equals("NETSCAPE2.0")){readNetscapeExt();}else{skip(); // don't care}break;default: // uninteresting extensionskip();}break;case 0x3b: // terminatordone = true;break;case 0x00: // bad byte, but keep going and see what happensbreak;default:status = STATUS_FORMAT_ERROR;}}}private void readGraphicControlExt(){read(); // block sizeint packed = read(); // packed fieldsdispose = (packed & 0x1c) >> 2; // disposal methodif (dispose == 0){dispose = 1; // elect to keep old image if discretionary}transparency = (packed & 1) != 0;delay = readShort() * 10; // delay in millisecondstransIndex = read(); // transparent color indexread(); // block terminator}private void readHeader(){String id = "";for (int i = 0; i < 6; i++){id += (char) read();}if (!id.startsWith("GIF")){status = STATUS_FORMAT_ERROR;return;}readLSD();if (gctFlag && !err()){gct = readColorTable(gctSize);bgColor = gct[bgIndex];}}private void readImage(){ix = readShort(); // (sub)image position & sizeiy = readShort();iw = readShort();ih = readShort();int packed = read();lctFlag = (packed & 0x80) != 0; // 1 - local color table flaginterlace = (packed & 0x40) != 0; // 2 - interlace flag// 3 - sort flag// 4-5 - reservedlctSize = 2 << (packed & 7); // 6-8 - local color table sizeif (lctFlag){lct = readColorTable(lctSize); // read tableact = lct; // make local table active}else{act = gct; // make global table activeif (bgIndex == transIndex){bgColor = 0;}}int save = 0;if (transparency){save = act[transIndex];act[transIndex] = 0; // set transparent color if specified}if (act == null){status = STATUS_FORMAT_ERROR; // no color table defined}if (err()){return;}decodeImageData(); // decode pixel dataskip();if (err()){return;}frameCount++;// create new image to receive frame dataimage = Bitmap.createBitmap(width, height, Config.ARGB_4444);// createImage(width, height);setPixels(); // transfer pixel data to imageif (gifFrame == null){// if(cacheImage){// String name = getDir();// gifFrame = new GifFrame(imagePath + File.separator + name +// ".png",delay);// saveImage(image,name);// }else{gifFrame = new GifFrame(image, delay);// }currentFrame = gifFrame;}else{GifFrame f = gifFrame;while (f.nextFrame != null){f = f.nextFrame;}// if(cacheImage){// String name = getDir();// f.nextFrame = new GifFrame(imagePath + File.separator + name +// ".png",delay);// saveImage(image,name);// }else{f.nextFrame = new GifFrame(image, delay);// }}// frames.addElement(new GifFrame(image, delay)); // add image to frame// listif (transparency){act[transIndex] = save;}resetFrame();action.parseOk(true, frameCount);}private void readLSD(){// logical screen sizewidth = readShort();height = readShort();// packed fieldsint packed = read();gctFlag = (packed & 0x80) != 0; // 1 : global color table flag// 2-4 : color resolution// 5 : gct sort flaggctSize = 2 << (packed & 7); // 6-8 : gct sizebgIndex = read(); // background color indexpixelAspect = read(); // pixel aspect ratio}private void readNetscapeExt(){do{readBlock();if (block[0] == 1){// loop count sub-blockint b1 = ((int) block[1]) & 0xff;int b2 = ((int) block[2]) & 0xff;loopCount = (b2 << 8) | b1;}} while ((blockSize > 0) && !err());}private int readShort(){// read 16-bit value, LSB firstreturn read() | (read() << 8);}private void resetFrame(){lastDispose = dispose;lrx = ix;lry = iy;lrw = iw;lrh = ih;lastImage = image;lastBgColor = bgColor;dispose = 0;transparency = false;delay = 0;lct = null;}/** * Skips variable length blocks up to and including next zero length block. */private void skip(){do{readBlock();} while ((blockSize > 0) && !err());}}

GifFrame类:


public class GifFrame {    /**     * 构造函数     * @param im 图片     * @param del 延时     */    public GifFrame(Bitmap im, int del) {            image = im;            delay = del;    }        public GifFrame(String name,int del){        imageName = name;        delay = del;    }        /**     * 图片     */    public Bitmap image;    /**     * 延时     */    public int delay;    /**     * 当图片存成文件时的文件名     */    public String imageName = null;        /**     * 下一帧     */    public GifFrame nextFrame = null;}

GifAction类:


public interface GifAction {/** * @param parseStatus 解码是否成功,成功会为true * @param frameIndex  当前解码的第几帧,当全部解码成功后,这里为-1 */public void parseOk(boolean parseStatus,int frameIndex);}