在NDK中使用libpng读取pixel数据

libpng的详细使用方法在于它的官方文档libpng-manual.txt,下载文件夹下含有。使用openGL生成纹理的时候需要图片的像素数据。使用libpng可以帮助我们解析PNG标准格式的结构,获得pixel数据。

在NDK中读取assets文件夹内容的方法在头文件#include <android/asset_manager.h>中定义。直接看代码: 

/**
 * Read png pixel data from file, caller must be free it
 */
static void* readPng(const char* filePath, Texture* texture) {
 
    void* pixelData = NULL;
    AAsset* asset   = NULL;
    do
    {
        off_t size;
        asset = getAAsset(filePath, &size);
 
        if(!asset) {
            break;
        }
 
        unsigned char head[8];
        AAsset_read(asset, head, 8);
        if(png_sig_cmp(head, 0, 8)) {
            LOGE("File %s, is not PNG", filePath);
            break;
        }
 
       /* Create and initialize the png_struct with the desired error handler
        * functions.  If you want to use the default stderr and longjump method,
        * you can supply NULL for the last three parameters.  We also supply the
        * the compiler header file version, so that we know if the application
        * was compiled with a compatible version of the library.  REQUIRED
        */
        png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
        if (!pngPtr) {
            LOGE("Unable to create PNG structure: %s", filePath);
            break;
        }
 
        // Allocate/initialize the memory for image information.  REQUIRED
        png_infop infoPtr = png_create_info_struct(pngPtr);
 
        if (!infoPtr) {
            png_destroy_read_struct(&pngPtr, NULL, NULL);
            LOGE("Unable to create PNG info : %s", filePath);
            break;
        }
 
        png_infop endInfo = png_create_info_struct(pngPtr);
        if (!endInfo) {
            png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
            LOGE("Unable to create PNG end info : %s", filePath);
            break;
        }
 
        // Set error handling if you are using the setjmp/longjmp method (this is
        // the normal method of doing things with libpng).  REQUIRED unless you
        // set up your own error handlers in the png_create_read_struct() earlier.
        if (setjmp(png_jmpbuf(pngPtr))) {
          // Free all of the memory associated with the png_ptr and info_ptr
          png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo);
          LOGE("Error during setjmp : %s", filePath);
          break;
        }
 
        /* If you are using replacement read functions, instead of calling
         * png_init_io() here you would call:
         * where user_io_ptr is a structure you want available to the callbacks
         */
        png_set_read_fn(pngPtr, (void*)asset, readPngData);
 
        // If we have already read some of the signature
        png_set_sig_bytes(pngPtr, 8);
 
       /* The call to png_read_info() gives us all of the information from the
        * PNG file before the first IDAT (image data chunk).  REQUIRED
        */
        png_read_info(pngPtr, infoPtr);
 
        int bitDepth;
        int colorType;
        int interlaceype;
 
        png_uint_32 width;
        png_uint_32 height;
 
        png_get_IHDR(pngPtr, infoPtr,
                &width,
                &height,
                &bitDepth,&colorType, &interlaceype, NULL, NULL);
 
        texture->width  = (float) width;
        texture->height = (float) height;
 
        LOGD("PNG width = %f, height = %f", texture->width, texture->height);
 
        // Update the png info struct.
        png_read_update_info(pngPtr, infoPtr);
 
        // Allocate the memory to hold the image using the fields of info_ptr
 
        unsigned int rowBytes = png_get_rowbytes(pngPtr, infoPtr);
        LOGD("Row size: %d bytes", rowBytes);
 
        // Allocate the pixel data as a big block, to be given to openGL
        pixelData = png_malloc(pngPtr, rowBytes * height);
        if(!pixelData) {
            png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo);
            LOGE("Unable to allocate PNG pixel data while loading %s", filePath);
            break;
        }
 
        /* Turn on interlace handling.  REQUIRED if you are not using
         * png_read_image().  To see how to handle interlacing passes,
         * see the png_read_row() method below:
         */
        int numberPasses = png_set_interlace_handling(pngPtr);
        LOGD("interlacing passes = %d", numberPasses);
        for (int pass = 0; pass < numberPasses; pass++) {
            for (int row = 0; row < height; row++) {
               png_read_row(pngPtr, ((unsigned char*)pixelData + (row * rowBytes)), NULL);
            }
        }
 
        // Read rest of file, and get additional chunks in info_ptr - REQUIRED
        png_read_end(pngPtr, infoPtr);
 
        // At this point you have read the entire image
 
        // Clean up after the read, and free any memory allocated - REQUIRE
        png_destroy_read_struct(&pngPtr, &infoPtr, &endInfo);
    } while(0);
 
    AAsset_close(asset);
 
    return pixelData;
}
这里我参考了libpng官方教程,使用最简单和最直接的方法获得PNG的像素数据。libpng的API可以对PNG进行复杂的操作和设置,我一概没有使用,直接定位到pixel数据,读取到内存。

这里需要解释两个地方。第一,libpng使用了FILE对象的文件操作体系。当然在NDK中无法直接使用,我们需要使用assets来代替。libpng提供了设置读取数据的回调函数。

/* If you are using replacement read functions, instead of calling
 * png_init_io() here you would call:
 * where user_io_ptr is a structure you want available to the callbacks
 */
png_set_read_fn(pngPtr, (void*)asset, readPngData);
第二个参数带入了asset指针,第三个参数设置了回调读取函数。实现如下:

/**
 * Callback for libpng read data
 */
static void readPngData(png_structp pngPtr, png_bytep data, png_size_t length) {
    AAsset* asset = (AAsset*)png_get_io_ptr(pngPtr);
    AAsset_read(asset, data, length);
}
 最后一点, libpng读取pixel数据的时候,有2中方法。一种是一次性读取数据到一个指针数组,一个指针持有一行数据。但是openGL需要的是一维数组。所以,通常做法会构建一个一维数组,以后构建一个指针数组,以后把一维数组折叠到指针数组。意思就是,指针数组的每个指针,指向一维数组特定的位置,表示一行数据。

我这里使用了png_read_row方法,每次读取一行,直接就放到一维数组里面去了。省去了构建指针数组的空间,也更简洁直观。

获得assets的方法是NDK提供的函数:

static AAsset* getAAsset(const char* filePath, off_t* size) {
    AAsset* asset = AAssetManager_open(ADirector()->assetManager, filePath, AASSET_MODE_UNKNOWN);
 
    do {
        if(!asset) {
            LOGE("read error, file path = %s", filePath);
            break;
        }
 
        *size = AAsset_getLength(asset);
        LOGD("file path = %s, file size = %d", filePath, *size);
 
 
    } while(0);
 
    return asset;
}

原文链接
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值