PNG图像读写及显示_第1页
PNG图像读写及显示_第2页
PNG图像读写及显示_第3页
PNG图像读写及显示_第4页
PNG图像读写及显示_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

利用C++类实现PNG图像读写及显示(一)

摘要运用libpng库函数,设计了一个可读写PNG格式图像的C++类。同时,在VC++6.0开发平台下,设计出一个基于多文档结构的图像浏览器,实现PNG格式图像的读写及显示。关键词PNG,图像格式,C++,类一、前言PNG是一种可携式网络图像格式(PortableNetworkGraphicFormat,PNG),其名称来源于非官方的“PNG'sNotGIF”,是一种位图文件(Bitmapfile)存储格式,读成“ping”[1]。在20世纪90年代,GIF已经普遍使用,然而在1995年新年前后,Unisys和CompuServe两家公司突然宣布编程使用GIF均需要向其支付版税,理由是他们拥有LZW压缩算法(GIF格式采用该算法)的版权。付费问题,迫使众多开发人员设计和开发新的用以代替GIF的图像格式。PNG出现的初衷就是为了替代GIF文件格式,避免支付版税。因此,设计PNG时,保留了GIF的众多特性,如(1)使用彩色查找表(也称调色板)可支持256种颜色的彩色图像;(2)流式读/写性能(Streamability);(3)逐次逼近显示(Progressivedisplay);(4)透明性(Transparency);(5)使用无损压缩等。同时,也增加了一些GIF文件格式所不具备的特性,如(1)每个像素为48位的真彩色图像;(2)每个像素为16位的灰度图像;(3)可为灰度图和真彩色图添加α通道;(4)添加图像的γ信息;(5)使用循环冗余码(Cyclicredundancycode,CRC)检测损害的文件;(6)加快图像显示的逐次逼近显示方式;(7)标准的读/写工具包等。目前,PNG已成为国际互网络联盟(WorldWideWebConsortium,W3C)推荐的标准[2],是互联网中常用的图像格式之一,常见的绘图软件和浏览器均支持PNG图像浏览(其中IE4.0以上版本均支持PNG)。本文利用提供的libpng库[3],设计一个可读写PNG图像的C++类,同时利用VC++6.0开发平台,通过PNG图像浏览器的实例设计,说明PNG类的使用方法。二、PNG文件结构PNG图像由一个8字节的PNG文件署名(PNGfilesignature)域和按照特定结构组织的3个以上的数据块(Chunk)组成。PNG文件署名域是用来识别该文件是不是PNG文件,如果用十进制数表示,该域的值依此是137,80,78,71,13,10,26,10,(对应的十六进制数为89,50,4e,47,0d,0a,1a,0a)。PNG数据块由表1所示的4个域构成[2]。根据类型的不同,PNG数据块又可分为两种:一种是关键数据块(Criticalchunk),这是标准的数据块,另一种叫做辅助数据块(Ancillarychunks),是可选的数据块。其中,关键数据块又定义了4个标准数据块,分别为(1)文件头数据块IHDR(Headerchunk);(2)调色板数据块PLTE(Palettechunk);(3)图像数据块IDAT(Imagedatachunk);(4)图像结束数据IEND(Imagetrailerchunk)。由于篇幅限制,这里只介绍与编程设计最为密切的文件头数据块结构,具体见表2,其他数据块格式可参考文献[2]。表1PNG数据块的结构名称字节数说明Length(长度)4指定数据块中数据域的长度,其长度不超过(231-1)字节ChunkTypeCode(数据块类型码)4数据块类型码由ASCII字母(A-Z和a-z)组成ChunkData(数据块数据)可变长度存储按照ChunkTypeCode指定的数据CRC(循环冗余检测)4存储用来检测是否有错误的循环冗余码

表2文件头数据块的结构名称字节数说明Width(宽度)4图像宽度(单位:像素,0为无效值)Height(长度)4图像高度(单位:像素,0为无效值)Bitdepth(图像深度)1索引彩色图像:1,2,4或8

灰度图像:1,2,4,8或16

真彩色图像:8或16Colortype(颜色类型)10:灰度图像,

2:真彩色图像

3:索引彩色图像

4:带α通道数据的灰度图像6:带α通道数据的真彩色图像Compressionmethod(压缩方法)1显示所用压缩方法,国际标准中只定义了一种方法(method0)Filtermethod(滤波器方法)1滤波器方法Interlacemethod(隔行扫描方法)10:非隔行扫描;1:

Adam7(由Adam

M.

Costello开发的7遍隔行扫描方法三、PNG类设计这一小节我们设计一个名为MyPNG的类,主要功能包括(1)将一幅PNG图像文件读入内存,对应函数为PngLoadImage(constchar*pstrFileName);(2)将内存中的图像数据保存为PNG图像,对应函数为PngSaveImage(constchar*pstrFileName);(3)显示PNG图像,对应函数为Draw(CDC*pDC,intnX=0,intnY=0,intnWidth=-1,intnHeight=-1)。通常,Window应用程序中的其他格式的图像显示可通过显示其对应位图实现。由于PNG图像数据的存放格式与位图格式不同,因此为了显示PNG图像,需要将PNG图像数据转换成对应的位图格式数据,类中的成员函数PngToBitmap()就是实现这个功能。此外,显示位图像素数据时,需要知道位图文件信息,因此要将PNG图像的信息(如宽度、高度等)转换成相应位图文件信息,实现该功能的类成员函数为FillBitmapInfo()。于是,可得到MyPNG类的头文件定义:#ifndef_PNG_INC_#define_PNG_INC_#include"png.h"//声明libpng库函数、png相关的结构体等信息的头文件classMyPNG{public: MyPNG(); ~MyPNG(); boolPngToBitmap();//将Png数据区转换到bitmap数据区 boolFillBitmapInfo(); BOOLDraw(CDC*pDC,intnX=0,intnY=0,intnWidth=-1,intnHeight=-1);//显示位图 BOOLPngLoadImage(constchar*pstrFileName);//载入PNG BOOLPngSaveImage(constchar*pstrFileName);//将pPixelBuffer的数据保存PNGpublic: png_structppng_ptr;//libpng定义的结构体指针,存放用于读/写png图像的信息 png_infopinfo_ptr;//libpng定义的结构体指针,存放png文件信息png_byte*pbImage;//存放次序是R-G-B//PNG图像数据,该数据存放规则与位图相反(第一个像素在左下角,自下而上,从左到右),即第一个像素在左上角(自上而下,从左到右存放)intcxImgSize,cyImgSize;//图像宽度、高度intcImgChannels;//颜色通道//图像深度,索引彩色图像:1,2,4或8;灰度图像:1,2,4,8或16;真彩色图像:8或16intiBitDepth;intiColorType;//颜色类型,0--灰度图像,2--真彩色图像,3--索引彩色图像,4--带α通道数据的灰度图像 //6--带α通道数据的真彩色图像png_colorbkgColor;//背景颜色BITMAPINFOm_bmi;//位图文件信息 BYTE*pPixelBuffer;//存放PNG的位图数据缓存区WORDwImgRowBytes;//每行的字节数};#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)deletepPixelBuffer;}利用C++类实现PNG图像读写及显示(二)成员函数PngLoadImage从路径pstrFileName读入PNG图像,并将PNG图像数据转换成位图数据,设置位图信息。如果执行函数成功,返回TRUE,失败则返回FALSE。函数中使用了一个例外处理的宏定义,Try{}Catch(){};其详细定义在另外一个头文件cexcept.h(libpng例程visupng有该文件定义),这里不再列举。BOOLMyPNG::PngLoadImage(constchar*pstrFileName){FILE*pfFile;png_bytepbSig[8];doubledGamma;inti;png_color_16*pBackground;png_uint_32ulChannels,ulRowBytes;staticpng_byte**ppbRowPointers=NULL;if(!pstrFileName){pbImage=NULL;returnFALSE;}//文件名指针为空则返回if(!(pfFile=fopen(pstrFileName,"rb"))){pbImage=NULL;returnFALSE;}//打开PNG文件fread(pbSig,1,8,pfFile);//读取8位PNG文件署名if(!png_check_sig(pbSig,8)){pbImage=NULL;returnFALSE;}//检查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;returnFALSE;}//创建失败,返回info_ptr=png_create_info_struct(png_ptr);//创建结构体//创建失败,释放已创建信息,返回falseif(!info_ptr){png_destroy_read_struct(&png_ptr,NULL,NULL);pbImage=NULL;returnFALSE;}Try{#if!defined(PNG_NO_STDIO)png_init_io(png_ptr,pfFile);//初始化png结构体#elsepng_set_read_fn(png_ptr,(png_voidp)pfFile,png_read_data);#endifpng_set_sig_bytes(png_ptr,8);//设置PNG文件署名png_read_info(png_ptr,info_ptr);//读取PNG信息png_get_IHDR(png_ptr,info_ptr,(unsignedlong*)&cxImgSize,(unsignedlong*)&cyImgSize,&iBitDepth,&iColorType,NULL,NULL,NULL);//获取宽度、高度、颜色深度、颜色类型等信息//将图像数据扩展成每个像素由3个8比特组成if(iBitDepth==16)png_set_strip_16(png_ptr);if(iColorType==PNG_COLOR_TYPE_PALETTE)png_set_expand(png_ptr);if(iBitDepth<8)png_set_expand(png_ptr);if(png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS))png_set_expand(png_ptr);if(iColorType==PNG_COLOR_TYPE_GRAY||iColorType==PNG_COLOR_TYPE_GRAY_ALPHA)png_set_gray_to_rgb(png_ptr);if(png_get_bKGD(png_ptr,info_ptr,&pBackground))//设置背景透明颜色,{png_set_background(png_ptr,pBackground,PNG_BACKGROUND_GAMMA_FILE,1,1.0);bkgColor.red=(byte)pBackground->red;bkgColor.green=(byte)pBackground->green;bkgColor.blue=(byte)pBackground->blue;}else{bkgColor.red=0;bkgColor.green=0;bkgColor.blue=0;}//判断是否需要gamma转换if(png_get_gAMA(png_ptr,info_ptr,&dGamma))png_set_gamma(png_ptr,(double)2.2,dGamma);png_read_update_info(png_ptr,info_ptr);//变换后,需要更新info_ptrpng_get_IHDR(png_ptr,info_ptr,(unsignedlong*)&cxImgSize,(unsignedlong*)&cyImgSize,&iBitDepth,&iColorType,NULL,NULL,NULL);//获取宽度、高度、颜色类型、颜色深度等信息ulRowBytes=png_get_rowbytes(png_ptr,info_ptr);//行字节数ulChannels=png_get_channels(png_ptr,info_ptr);//通道信息cImgChannels=ulChannels; if(pbImage!=NULL){free(pbImage);pbImage=NULL; }//空间已经存在,则先释放空间if((pbImage=(png_byte*)malloc(ulRowBytes*cyImgSize*sizeof(png_byte)))==NULL){png_error(png_ptr,"LoadPNG:Outofmemory");}if((ppbRowPointers=(png_bytepp)malloc(cyImgSize*sizeof(png_bytep)))==NULL)//分配内存空间{png_error(png_ptr,"LoadPNG:Outofmemory");}for(i=0;i<cyImgSize;i++)ppbRowPointers[i]=pbImage+i*ulRowBytes;//设置PNG图像的每行地址png_read_image(png_ptr,ppbRowPointers);//一次读入图像信息png_read_end(png_ptr,NULL);//读取辅助块(additionalchunks)信息free(ppbRowPointers);//释放内存空间ppbRowPointers=NULL;}Catch(msg){png_destroy_read_struct(&png_ptr,&info_ptr,NULL); pbImage=NULL;if(ppbRowPointers)free(ppbRowPointers);fclose(pfFile);returnFALSE;}fclose(pfFile);FillBitmapInfo(); //填充Bitmap信息boolret=PngToBitmap();//将Png图像数据转换成位图数据returnret;}PngSaveImage实现将内存图像数据保存为文件名为pstrFileName的PNG图像,如果执行成功,函数返回TRUE,失败则返回FALSE。BOOLMyPNG::PngSaveImage(constchar*pstrFileName){constintciBitDepth=8,ciChannels=3;FILE*pfFile;png_uint_32ulRowBytes;staticpng_byte**ppbRowPointers=NULL;inti,iWidth,iHeight;iWidth=cxImgSize;iHeight=cyImgSize;if(!pstrFileName)returnFALSE;//文件指针为空则返回if(!(pfFile=fopen(pstrFileName,"wb")))returnFALSE;//创建文件//创建标准的PNG结构信息png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,(png_error_ptr)png_cexcept_error,(png_error_ptr)NULL);if(!png_ptr){fclose(pfFile);returnFALSE;}info_ptr=png_create_info_struct(png_ptr);if(!info_ptr){fclose(pfFile);png_destroy_write_struct(&png_ptr,(png_infopp)NULL);returnFALSE;}Try{#if!defined(PNG_NO_STDIO)png_init_io(png_ptr,pfFile);//初始化png结构体信息#elsepng_set_write_fn(png_ptr,(png_voidp)pfFile,png_write_data,png_flush);#endifpng_set_IHDR(png_ptr,info_ptr,iWidth,iHeight,ciBitDepth,PNG_COLOR_TYPE_RGB,PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_BASE,PNG_FILTER_TYPE_BASE);//写入每像素3比特的PNG图像png_write_info(png_ptr,info_ptr);//写入文件头信息png_set_bgr(png_ptr);//将BGR次序转换为RGB次序ulRowBytes=iWidth*ciChannels;//每行的字节数if((ppbRowPointers=(png_bytepp)malloc(iHeight*sizeof(png_bytep)))==NULL)Throw"MyPNG:Outofmemory";//分配内存失败;//设置每行的指针地址for(i=0;i<iHeight;i++)ppbRowPointers[i]=pPixelBuffer+(iHeight-1-i)*(((ulRowBytes+3)>>2)<<2);png_write_image(png_ptr,ppbRowPointers);//一次写入整幅图像png_write_end(png_ptr,info_ptr);//写辅助块信息free(ppbRowPointers);//释放空间ppbRowPointers=NULL;png_destroy_write_struct(&png_ptr,(png_infopp)NULL);//写操作结束,释放空间}Catch(msg){png_destroy_write_struct(&png_ptr,(png_infopp)NULL);if(ppbRowPointers)free(ppbRowPointers);fclose(pfFile);returnFALSE;}fclose(pfFile);returnTRUE;}利用C++类实现PNG图像读写及显示(三)函数Draw主要用来显示PNG图像对应的位图图像,通过指定宽度和高度,可实现图像的缩放显示。该函数主要为VC++应用程序编写。BOOLMyPNG::Draw(CDC*pDC,intnX,intnY,intnWidth,intnHeight){if(nWidth==-1) nWidth=cxImgSize;if(nHeight==-1) nHeight=cyImgSize; StretchDIBits(pDC->m_hDC,nX,nY,nWidth,nHeight,0,0,cxImgSize,cyImgSize,pPixelBuffer,&m_bmi,BI_RGB,SRCCOPY);returntrue;}成员函数FillBitmapInfo的功能是根据PNG图像大小设置位图信息,为显示图像做准备。boolMyPNG::FillBitmapInfo()//给变量m_bmi(位图信息头)赋值{ m_bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); m_bmi.bmiHeader.biHeight=cyImgSize; m_bmi.bmiHeader.biWidth=cxImgSize; m_bmi.bmiHeader.biPlanes=1; m_bmi.bmiHeader.biBitCount=24; m_bmi.bmiHeader.biCompression=0; returntrue;}成员函数PngToBitmap主要实现将PNG图像数据转换成位图图像数据。boolMyPNG::PngToBitmap(){BYTE*src,*dst;BYTEr,g,b,a;constintcDIChannels=3;WORDwDIRowBytes;intxImg,yImg;if(pPixelBuffer!=NULL){deletepPixelBuffer;pPixelBuffer=NULL; }//空间已分配则先释放wImgRowBytes=cImgChannels*cxImgSize;//每行的字节数wDIRowBytes=(WORD)((cDIChannels*cxImgSize+3L)>>2)<<2;pPixelBuffer=newBYTE[wDIRowBytes*cyImgSize];//分配内存空间if(pPixelBuffer==NULL){ AfxMessageBox("PNGtoBitmap:Outofmemory.");returnfalse;}//分配失败则返回for(yImg=0;yImg<cyImgSize;yImg++)//拷贝图像数据 { src=pbImage+yImg*wImgRowBytes; dst=pPixelBuffer+(cyImgSize-yImg-1)*wDIRowBytes;//+cxImgPos*cDIChannels; for(xImg=0;xImg<cxImgSize;xImg++) {r=*src++;g=*src++;b=*src++; *dst++=b;*dst++=g;*dst++=r;//位图的次序为BGR,PNG的次序为RGB if(cImgChannels==4){a=*src++;}//通道信息跳过 } } returntrue;}利用C++类实现PNG图像读写及显示(四)四、实例设计在设计实例前,先准备好相关的库。PNG文件的读写库为libpng,由于PNG用到LZ77派生压缩算法,因此,编译读写库时,libpng需要连接zlib库[4],这两个库的源代码可在文献[3]和文献[4]中获得。下面开始讲解如何应用VC++6.0设计PNG图像浏览器。第一步:建立一个名为PngImage的多文档框架应用程序,其View类选择继承于CScrollView。第二步:在应用程序CPngImageApp的初始化函数InitInstance()中,加入以下代码cmdInfo.m_nShellCommand=CCommandLineInfo::FileNothing;该代码位于ParseCommandLine(cmdInfo)后,目的是使程序启动时,不打开新的空白文档。同时将字符串IDR_PNGIMATYPE修改为“\nPNG\nPNG\nPNG(*.png)\n.png\nPNG.Document\nPNGDocument”,目的是打开文件时,默认的文件对话框只显示后缀名为PNG文件。第三步:从libpng库和zlib库中,选择以下文件,将他们添加到PngImage项目中。pngconf.h,png.h,libpng.lib,cexcept.h,zlib.lib,zlib.h,zconf.h同时,将第三节设计的MyPNG类也添加到项目中。第四步:为文档类CPngImageDoc添加公有成员变量MyPNGm_png,并改写OnOpenDocument函数,具体如下:BOOLCPngImageDoc::OnOpenDocument(LPCTSTRlpszPathName){if(!CDocument::OnOpenDocument(lpszPathName))returnFALSE;BOOLret=m_png.PngLoadImage((constchar*)lpszPathName); returnret; }重写文档类的虚函数OnSaveDocument,具体如下:BOOLCPngImageDoc::OnSaveDocument(LPCTSTRlpszPathName){BOOLret=m_png.PngSaveImage((constchar*)lpszPathName);if(ret==TRUE) AfxMessageBox("保存成功!");elseAfxMessageBox("保存失败!"); returnret;}同时,为菜单IDR_PNGIMATYPE中的“另存为”添加消息映射,具体代码如下:voidCPngImageDoc::OnFileSaveAs(){ BOOLret; staticcharBASED_CODEszSaveFilter[]="PNG(*.png)|*.png||"; //过滤文件 //创建文件保

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论