GIFDecoder源码分析

Posted on 2017-03-13 09:03 xl_phoenix 阅读(...) 评论(...) 编辑 收藏

源码见:ddxxll2008/gifdecoder_java

run()

public void run(){
        if(in != null){
            readStream();
        }else if(gifData != null){
            readByte();
        }
    }
    
    private int readByte(){
        in = new ByteArrayInputStream(gifData);
        gifData = null;
        return readStream();
    }

GifDecoder的入口是run函数,里面包含了readStream()和readByte()两个方法,但是readByte()里也返回了一个readStream(),所以从readStream()方法进行分析。

readStream

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;
    }
    

readStream()里先进行一些参数的初始化,然后读取Gif头文件。

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 readLSD() {
        // logical screen size
        width = readShort();
        height = readShort();
        // packed fields
        int packed = read();
        gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
        // 2-4 : color resolution
        // 5 : gct sort flag
        gctSize = 2 << (packed & 7); // 6-8 : gct size
        bgIndex = read(); // background color index
        pixelAspect = read(); // pixel aspect ratio
    }
    
    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 checks
            int 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;
    }

readHeader()里主要是判断是否是Gif文件,如果是的话,则在readLSD()里获得Gif的一些基本信息。readLSD()读取的信息包括长宽,颜色和颜色索引等。之后通过readColorTable(int ncolors)读取颜色表单。之后就是通过readContents()来获取Gif的内容了。

readContents()

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) block[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:
                    status = STATUS_FORMAT_ERROR;
            }
        }
    }

在readContents()里,只要没有发生错误,就会一直读取下去,直到读取完成。这里面的readImage()方法是用来获取图片信息的。通过decodeImageData()方法来获取各个像素点的数据,之后新建一个bitmap,通过setPixels()来设置像素点的信息。然后根据此bitmap生成一个GifFrame的对象,并通过action.parseOk(true, frameCount)返回解析成功以及解析成功的帧数。全部解析完成后,帧数为-1。

private void readImage() {
        ix = readShort(); // (sub)image position & size
        iy = readShort();
        iw = readShort();
        ih = readShort();
        int packed = read();
        lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
        interlace = (packed & 0x40) != 0; // 2 - interlace flag
        // 3 - sort flag
        // 4-5 - reserved
        lctSize = 2 << (packed & 7); // 6-8 - local color table size
        if (lctFlag) {
            lct = readColorTable(lctSize); // read table
            act = lct; // make local table active
        } else {
            act = gct; // make global table active
            if (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 data
        skip();
        if (err()) {
            return;
        }
        frameCount++;
        // create new image to receive frame data
        image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
        // createImage(width, height);
        setPixels(); // transfer pixel data to image
        if (gifFrame == null) {
            gifFrame = new GifFrame(image, delay);
            currentFrame = gifFrame;
        } else {
            GifFrame f = gifFrame;
            while(f.nextFrame != null){
                f = f.nextFrame;
            }
            f.nextFrame = new GifFrame(image, delay);
        }
        // frames.addElement(new GifFrame(image, delay)); // add image to frame
        // list
        if (transparency) {
            act[transIndex] = save;
        }
        resetFrame();
        action.parseOk(true, frameCount);
    }

readContents()里还有读取其他信息的函数,比如readGraphicControlExt(),readBlock()和readNetscapeExt()。这些都是获取一些信息用的函数。

使用方法

GIFDecoder里有很多方法,可以获取Gif的信息。先构造一个GifDecoder,然后调用gifDecoder.run()方法,就可以得到一个完整的GIFDecoder对象,之后便能通过GIFDecoder里的各种方法去获得Gif图片的信息了。

//解析gif图片
gifDecoder = new GifDecoder(fileInputStream, new GifAction() {
    @Override
    public void parseOk(boolean parseStatus, int frameIndex) {
        Logger.d(parseStatus + "  " + frameIndex);
    }
});
gifDecoder.run();

posts - 79, comments - 2, trackbacks - 0, articles - 0

Copyright © xl_phoenix