使用 libpng 配合 GDI 完成对 png 图片的解析与显示

第一步: 使用 libpng 完成对 png 图像的解析

在上一篇 《VC6 下 libpng 库的编译与初步使用  》 中我们已经完成了对 libpng 库的编译与配置, 今天就来用它来实现对 png 图片进行解析并且将解析到的图片数据通过 Windows GDI 显示到窗口中。

libpng 的详细介绍请点这里
libpng 的下载地址请点这里

推荐阅读:

利用libpng中的函数读写PNG文件

使用libpng读取PNG图片像素数据

教你如何使用libpng显示PNG图片

在这之前, 我们先做个假设:

 1. 所使用的图片确实为 png 格式

做假设目的是为了减少演示代码的复杂度, 因为代码中没有对传入的图片是否为png进行检测, 进行过多的错误处理这不符合演示代码的书写习惯。

开始进行图片得读取:

long ReadPngData( const char *szPath, int *pnWidth, int *pnHeight, unsigned char **cbData )
    {
        FILE *fp = NULL;
        long file_size = 0, pos = 0, mPos = 0;
        int color_type = 0, x = 0, y = 0, block_size = 0;

png_infop info_ptr;
        png_structp png_ptr;
        png_bytep *row_point = NULL;

fp = fopen( szPath, "rb" );
        if( !fp )    return FILE_ERROR;            //文件打开错误则返回 FILE_ERROR

png_ptr  = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);        //创建png读取结构
        info_ptr = png_create_info_struct(png_ptr);        //png 文件信息结构
        png_init_io(png_ptr, fp);                //初始化文件 I\O
        png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);                //读取png文件
       
        *pnWidth  = png_get_image_width( png_ptr, info_ptr );        //获得图片宽度
        *pnHeight = png_get_image_height( png_ptr, info_ptr );        //获得图片高度
        color_type = png_get_color_type( png_ptr, info_ptr );        //获得图片颜色类型
        file_size = (*pnWidth) * (*pnHeight) * 4;                    //计算需要存储RGB(A)数据所需的内存大小
        *cbData = (unsigned char *)malloc(file_size);            //申请所需的内存, 并将传入的 cbData 指针指向申请的这块内容

row_point = png_get_rows( png_ptr, info_ptr );            //读取RGB(A)数据

block_size = color_type == 6 ? 4 : 3;                    //根据是否具有a通道判断每次所要读取的数据大小, 具有Alpha通道的每次读4字节, 否则读3字节

//将读取到的RGB(A)数据按规定格式读到申请的内存中
        for( x = 0; x < *pnHeight; x++ )
            for( y = 0; y < *pnWidth*block_size; y+=block_size )
            {
                (*cbData)[pos++] = row_point[x][y + 2];        //B
                (*cbData)[pos++] = row_point[x][y + 1];        //G
                (*cbData)[pos++] = row_point[x][y + 0];        //R
                (*cbData)[pos++] = row_point[x][y + 3];        //Alpha
            }

png_destroy_read_struct(&png_ptr, &info_ptr, 0);
        fclose( fp );

return file_size;
    }

上面我们定义了一个 ReadPngData 的函数, 看一下这个函数的参数:

long ReadPngData(
            const char *szFileName,        //png文件路径
            int *pnWidth,                //指针类型, 用于传出图片宽度
            int *pnHeight,                //指针类型, 用于传出图片高度
            unsigned char **cbData        //二级指针, 用于指向所申请的用于存储RGB(A)的内存地址
        )

在代码中, 还根据获得的色彩类型对每次需要读取的字节数做了简单的判断

block_size = color_type == 6 ? 4 : 3; //根据是否具有a通道判断每次所要读取的数据大小, 具有Alpha通道的每次读4字节, 否则读3字节

该语句使用了问号表达式代替了 if..else 进行判断, 减少了代码的行数。

接下来就是使用两个 for 循环完成对RGB(A)数据的读取工作, 在这里, 你也可以通过不同的 RGB(A) 之间的运算, 将图像呈现出不同的显示效果。

现在, cbData指针指向的内存中就是我们所需的 RGB(A) 数据了, 有了这份数据, 就可以完成显示。该函数已被封装为 .h 头文件, 使用可以直接调用, 完成的封装代码如下: gdipng.h

- gdipng.h

#pragma once

//////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include "png.h"

//////////////////////////////////////////////////////////////////////////

#define        FILE_ERROR        -1

//////////////////////////////////////////////////////////////////////////

long ReadPngData( const char *szFileName, int *pnWidth, int *pnHeight, unsigned char **cbData );

//////////////////////////////////////////////////////////////////////////

long ReadPngData( const char *szPath, int *pnWidth, int *pnHeight, unsigned char **cbData )
{
    FILE *fp = NULL;
    long file_size = 0, pos = 0, mPos = 0;
    int color_type = 0, x = 0, y = 0, block_size = 0;

png_infop info_ptr;
    png_structp png_ptr;
    png_bytep *row_point = NULL;

fp = fopen( szPath, "rb" );
    if( !fp )    return FILE_ERROR;            //文件打开错误则返回 FILE_ERROR

png_ptr  = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);        //创建png读取结构
    info_ptr = png_create_info_struct(png_ptr);        //png 文件信息结构
    png_init_io(png_ptr, fp);                //初始化文件 I\O
    png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);                //读取png文件
   
    *pnWidth  = png_get_image_width( png_ptr, info_ptr );        //获得图片宽度
    *pnHeight = png_get_image_height( png_ptr, info_ptr );        //获得图片高度
    color_type = png_get_color_type( png_ptr, info_ptr );        //获得图片色彩深度
    file_size = (*pnWidth) * (*pnHeight) * 4;                    //计算需要存储RGB(A)数据所需的内存大小
    *cbData = (unsigned char *)malloc(file_size);            //申请所需的内容, 并将 *cbData 指向申请的这块内容

row_point = png_get_rows( png_ptr, info_ptr );            //读取RGB(A)数据

block_size = color_type == 6 ? 4 : 3;                    //根据是否具有a通道判断每次所要读取的数据大小, 具有Alpha通道的每次读4位, 否则读3位

//将读取到的RGB(A)数据按规定格式读到申请的内存中
    for( x = 0; x < *pnHeight; x++ )
        for( y = 0; y < *pnWidth*block_size; y+=block_size )
        {
            (*cbData)[pos++] = row_point[x][y + 2];        //B
            (*cbData)[pos++] = row_point[x][y + 1];        //G
            (*cbData)[pos++] = row_point[x][y + 0];        //R
            (*cbData)[pos++] = row_point[x][y + 3];        //alpha
        }

png_destroy_read_struct(&png_ptr, &info_ptr, 0);
    fclose( fp );

return file_size;
}

//////////////////////////////////////////////////////////////////////////

接下来请看第2页精彩内容

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/c09e917bfd6ffdd3c585a2d5acc97d66.html