在NDK中使用libpng读取pixel数据

来源:互联网 发布:北京公交线路优化 编辑:程序博客网 时间:2024/05/18 10:09

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

原文链接
原创粉丝点击