點9圖 NinePatch chunk解析

本文連接:https://blog.csdn.net/u013365670/article/details/25415393
   最近在工做中須要解析點9圖的頭信息(chunk)的格式,讀取拉伸座標片斷、padding信息,在網上找了一下沒有相關信息,下面詳解一下解析過程。html

點9圖科普
    點9圖的定義見官方文檔:http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch
    簡單來講就是左邊的點表明垂直拉伸的區域,上邊的點表明水平拉伸的區域;右邊的點表明文字等的垂直可可顯示區域,下邊的點表明文字等的水平可顯示區域。因此,左上重合的區域就是拉伸區域;右下重合的區域就是顯示區域。android

    Android的編譯工具aapt會把標記了拉伸區域的點9圖處理成一個普通的png,並把相關的信息放入png頭部的meta-data區域,即chunk。這個過程詳見frameworks/base/tools/aapt/Images.cpp的static status_t do_9patch(const char* imageName, image_info* image)git

PNG文件結構
    官方文檔參見右邊的連接:The Metadata in PNG files,摘抄部分以下github

    A PNG always starts with an 8-byte signature: 137 80 78 71 13 10 26 10 (decimal values). The remainder of the file consists a series of chunks beginning with an IHDR chunk and ending with an IEND chunk.數據結構

    好了,瞭解了PNG的文件結構咱們就能夠取出chunk了,只須要按照文檔的說明,按順序取出Chunk data對應的信息便可。特別須要注意的是,讀取字節流的時候須要採起大端方式。下面上代碼ide

    /**
     * PNG Chunk struct
     * <a href="http://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_PNG_files">The Metadata in PNG files</a>
     * 
     *   +--------+---------+
     *   | Length | 4 bytes |
     *   +--------+---------+
     *   | Chunk  | 4 bytes |
     *   |  type  |         |
     *   +--------+---------+
     *   | Chunk  | Length  |
     *   |  data  |  bytes  |
     *   +--------+---------+
     *   | CRC    | 4 bytes |
     *   +--------+---------+
     *   
     * @param pngName
     * @return chunk
     * @throws IOException
     */
    private byte[] loadNinePatchChunk(InputStream is) throws IOException {
        IntReader reader = new IntReader(is, true);
        // check PNG signature
        // A PNG always starts with an 8-byte signature: 137 80 78 71 13 10 26 10 (decimal values).
        if (reader.readInt() != 0x89504e47 || reader.readInt() != 0x0D0A1A0A) {
            return null;
        }
 
        while (true) {
            int length = reader.readInt();
            int type = reader.readInt();
            // check for nine patch chunk type (npTc)
            if (type != 0x6E705463) {
                reader.skip(length + 4/*crc*/);
                continue;
            }
            return reader.readByteArray(length);
        }
    }
解析Chunk
    點9圖的Chunk是按照struct Res_png_9patch的結構組織的,詳見frameworks/base/include/androidfw/ResourceTypes.h。
struct Res_png_9patch
{
    Res_png_9patch() : wasDeserialized(false), xDivs(NULL),
                       yDivs(NULL), colors(NULL) { }
 
    int8_t wasDeserialized;
    int8_t numXDivs;
    int8_t numYDivs;
    int8_t numColors;
 
    // These tell where the next section of a patch starts.
    // For example, the first patch includes the pixels from
    // 0 to xDivs[0]-1 and the second patch includes the pixels
    // from xDivs[0] to xDivs[1]-1.
    // Note: allocation/free of these pointers is left to the caller.
    int32_t* xDivs;
    int32_t* yDivs;
 
    int32_t paddingLeft, paddingRight;
    int32_t paddingTop, paddingBottom;
 
    enum {
        // The 9 patch segment is not a solid color.
        NO_COLOR = 0x00000001,
 
        // The 9 patch segment is completely transparent.
        TRANSPARENT_COLOR = 0x00000000
    };   
    // Note: allocation/free of this pointer is left to the caller.
    uint32_t* colors;
 
    // Convert data from device representation to PNG file representation.
    void deviceToFile();
    // Convert data from PNG file representation to device representation.
    void fileToDevice();
    // Serialize/Marshall the patch data into a newly malloc-ed block
    void* serialize();
    // Serialize/Marshall the patch data
    void serialize(void* outData);
    // Deserialize/Unmarshall the patch data
    static Res_png_9patch* deserialize(const void* data);
    // Compute the size of the serialized data structure
    size_t serializedSize();
};
  瞭解了struct Res_png_9patch,咱們只需根據struct Res_png_9patch的結構解析上面步驟獲得的chunk data便可。代碼以下:
    public static NinePatchChunk deserialize(byte[] data) {
        ByteBuffer byteBuffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
 
        byte wasSerialized = byteBuffer.get();
        //        if (wasSerialized == 0) return null;
 
        NinePatchChunk chunk = new NinePatchChunk();
        chunk.mDivX = new int[byteBuffer.get()];
        chunk.mDivY = new int[byteBuffer.get()];
        chunk.mColor = new int[byteBuffer.get()];
 
        checkDivCount(chunk.mDivX.length);
        checkDivCount(chunk.mDivY.length);
 
        // skip 8 bytes
        byteBuffer.getInt();
        byteBuffer.getInt();
 
        chunk.mPaddings.left = byteBuffer.getInt();
        chunk.mPaddings.right = byteBuffer.getInt();
        chunk.mPaddings.top = byteBuffer.getInt();
        chunk.mPaddings.bottom = byteBuffer.getInt();
 
        // skip 4 bytes
        byteBuffer.getInt();
 
        readIntArray(chunk.mDivX, byteBuffer);
        readIntArray(chunk.mDivY, byteBuffer);
        readIntArray(chunk.mColor, byteBuffer);
 
        return chunk;
    }
    NinePatchChunk.mDivX和NinePatchChunk.mDivY就是點9圖的拉伸區域標識信息,NinePatchChunk.mPaddings是點9圖的pading信息,這樣點9圖的Chunk信息就解析完畢了。
總結
    所有代碼見https://gist.github.com/luxiaoyu/085135ff88d7c57a18c5
    調研這種SDK文檔上沒有詳細信息的數據結構,須要從Android源碼入手,耐心查看相關代碼和註釋,grep和find是好幫手。工具


————————————————
版權聲明:本文爲CSDN博主「魯曉宇-Baidu」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/u013365670/article/details/25415393ui

相關文章
相關標籤/搜索