第一步: 使用 libpng 完成对 png 图像的解析
在上一篇 《VC6 下 libpng 库的编译与初步使用 》 中我们已经完成了对 libpng 库的编译与配置, 今天就来用它来实现对 png 图片进行解析并且将解析到的图片数据通过 Windows GDI 显示到窗口中。
libpng 的详细介绍:请点这里
libpng 的下载地址:请点这里
推荐阅读:
在这之前, 我们先做个假设:
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;
}
//////////////////////////////////////////////////////////////////////////