表2 文件头数据块的结构
名称 |
字节数 |
说明 |
Width (宽度) |
4 |
图像宽度(单位:像素,0为无效值) |
Height (长度) |
4 |
图像高度(单位:像素,0为无效值) |
Bit depth (图像深度) |
1 |
索引彩色图像:1,2,4或8 灰度图像:1,2,4,8或16 真彩色图像:8或16 |
Color type (颜色类型) |
1 |
0:灰度图像, 2:真彩色图像 3:索引彩色图像 4:带α通道数据的灰度图像
6:带α通道数据的真彩色图像 |
Compression method (压缩方法) |
1 |
显示所用压缩方法,国际标准中只定义了一种方法(method 0) |
Filter method (滤波器方法) |
1 |
滤波器方法 |
Interlace method (隔行扫描方法) |
1 |
0:非隔行扫描;1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法 |
三、PNG类设计
这一小节我们设计一个名为MyPNG的类,主要功能包括(1)将一幅PNG图像文件读入内存,对应函数为PngLoadImage (const char *pstrFileName);(2)将内存中的图像数据保存为PNG图像,对应函数为PngSaveImage (const char *pstrFileName);(3)显示PNG图像,对应函数为Draw( CDC *pDC, int nX = 0, int nY = 0, int nWidth = -1, int nHeight = -1 )。通常,Window应用程序中的其他格式的图像显示可通过显示其对应位图实现。由于PNG图像数据的存放格式与位图格式不同,因此为了显示PNG图像,需要将PNG图像数据转换成对应的位图格式数据,类中的成员函数PngToBitmap()就是实现这个功能。此外,显示位图像素数据时,需要知道位图文件信息,因此要将PNG图像的信息(如宽度、高度等)转换成相应位图文件信息,实现该功能的类成员函数为FillBitmapInfo()。于是,可得到MyPNG类的头文件定义:
#ifndef _PNG_INC_
#define _PNG_INC_
#include "png.h" //声明libpng库函数、png相关的结构体等信息的头文件
class MyPNG
{
public:
MyPNG();
~MyPNG();
bool PngToBitmap(); //将Png数据区转换到bitmap数据区
bool FillBitmapInfo();
BOOL Draw( CDC *pDC, int nX = 0, int nY = 0, int nWidth = -1, int nHeight = -1 ); //显示位图
BOOL PngLoadImage (const char *pstrFileName); //载入PNG
BOOL PngSaveImage (const char *pstrFileName); //将pPixelBuffer的数据保存PNG
public:
png_structp png_ptr; //libpng定义的结构体指针,存放用于读/写png图像的信息
png_infop info_ptr; // libpng定义的结构体指针,存放png文件信息
png_byte *pbImage; //存放次序是R-G-B
//PNG图像数据,该数据存放规则与位图相反(第一个像素在左下角,自下而上,从左到右),即第一个像素在左上角(自上而下,从左到右存放)
int cxImgSize, cyImgSize; //图像宽度、高度
int cImgChannels; //颜色通道
//图像深度,索引彩色图像:1,2,4或8;灰度图像:1,2,4,8或16;真彩色图像:8或16
int iBitDepth;
int iColorType; //颜色类型,0 --灰度图像,2--真彩色图像,3--索引彩色图像,4--带α通道数据的灰度图像
//6--带α通道数据的真彩色图像
png_color bkgColor; //背景颜色
BITMAPINFO m_bmi; //位图文件信息
BYTE *pPixelBuffer; //存放PNG的位图数据缓存区
WORD wImgRowBytes; //每行的字节数
};
#endif
下面是类成员函数的详细定义。类的构造函数用来初始化成员变量信息,析构函数的作用是释放内存空间。
MyPNG::MyPNG()
{ png_ptr=NULL; info_ptr=NULL; pbImage=NULL; pPixelBuffer=NULL;
bkgColor.red=127; bkgColor.green=127; bkgColor.blue=127; cxImgSize=0; cyImgSize=0; wImgRowBytes=0;
}
MyPNG::~MyPNG()
{ if (pbImage!=NULL) free(pbImage); if (png_ptr!=NULL) free(png_ptr);
if (info_ptr!=NULL) free(info_ptr); if (pPixelBuffer!=NULL) delete pPixelBuffer;
}
成员函数PngLoadImage从路径pstrFileName读入PNG图像,并将PNG图像数据转换成位图数据,设置位图信息。如果执行函数成功,返回TRUE,失败则返回FALSE。函数中使用了一个例外处理的宏定义,Try{} Catch(){};其详细定义在另外一个头文件cexcept.h (libpng例程visupng有该文件定义),这里不再列举。
BOOL MyPNG::PngLoadImage (const char *pstrFileName)
{ FILE *pfFile; png_byte pbSig[8];
double dGamma; int i;
png_color_16 *pBackground; png_uint_32 ulChannels, ulRowBytes;
static png_byte **ppbRowPointers = NULL;
if (!pstrFileName) {pbImage=NULL; return FALSE; } //文件名指针为空则返回
if (!(pfFile = fopen(pstrFileName, "rb"))) { pbImage=NULL; return FALSE; } //打开PNG文件
fread(pbSig, 1, 8, pfFile); //读取8位PNG文件署名
if (!png_check_sig(pbSig, 8)) { pbImage=NULL; return FALSE; } //检查PNG文件署名
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
(png_error_ptr)png_cexcept_error, (png_error_ptr)NULL); //创建结构体信息
if (!png_ptr) {pbImage=NULL; return FALSE;} //创建失败,返回
info_ptr = png_create_info_struct(png_ptr); //创建结构体
//创建失败,释放已创建信息,返回false
|