自己的实现

 unsigned int component(png_const_bytep row, png_uint_32 x, unsigned int c, unsigned int bit_depth, unsigned int channels) {
png_uint_32 bit_offset_hi = bit_depth * ((x >> ) * channels);
png_uint_32 bit_offset_lo = bit_depth * ((x & 0x3f) * channels + c); row = (png_const_bytep)(((PNG_CONST png_byte(*)[])row) + bit_offset_hi);
row += bit_offset_lo >> ;
bit_offset_lo &= 0x07; switch (bit_depth) {
case :
return (row[] >> ( - bit_offset_lo)) & 0x01;
case :
return (row[] >> ( - bit_offset_lo)) & 0x03;
case :
return (row[] >> ( - bit_offset_lo)) & 0x0f;
case :
return row[];
case :
return (row[] << ) + row[];
default:
fprintf(stderr, "pixel_component: invalid bit depth %u\n", bit_depth);
return row[];
}
} void readPixel(png_const_bytep row, png_uint_32 x, png_uint_32 bit_depth, png_uint_32 color_type, BitmapPixel *pPixel) {
unsigned int channels = PNG_COLOR_TYPE_RGB_ALPHA ? : ;
pPixel->r = component(row, x, , bit_depth, channels);
pPixel->g = component(row, x, , bit_depth, channels);
pPixel->b = component(row, x, , bit_depth, channels);
pPixel->a = color_type == PNG_COLOR_TYPE_RGB_ALPHA ? component(row, x, , bit_depth, channels) : 0xFF;
} BitmapData* PNG_ARGB8888(FILE *pngFile) {
BitmapData *pData;
png_structp png_ptr;
png_infop info_ptr;
png_bytepp pRow = NULL;
png_uint_32 width, height, ystart, xstart, ystep, xstep, px, ppx, py, count;
png_int_32 bit_depth, color_type, interlace_method, passes, pass; if (!pngFile) {
return NULL;
}
if (!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) {
LETV_LOGE("ERROR PNG_ARGB8888: out of memory allocating png_struct!\n");
return NULL;
}
if (!(info_ptr = png_create_info_struct(png_ptr))) {
LETV_LOGE("ERROR PNG_ARGB8888: out of memory allocating png_info!\n");
png_destroy_read_struct(&png_ptr, NULL, NULL);
return NULL;
}
if (setjmp(png_jmpbuf(png_ptr))) {
LETV_LOGE("ERROR PNG_ARGB8888: setjmp png_ptr failed!\n");
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return NULL;
}
png_init_io(png_ptr, pngFile);
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, );
color_type = png_get_color_type(png_ptr, info_ptr);
if (!(color_type & PNG_COLOR_TYPE_RGB)) {
LETV_LOGE("ERROR PNG_ARGB8888: PNG_COLOR_TYPE unsupported!\n");
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return NULL;
}
interlace_method = png_get_interlace_type(png_ptr, info_ptr);
switch (interlace_method) {
case PNG_INTERLACE_NONE:
passes = ;
break;
case PNG_INTERLACE_ADAM7:
passes = PNG_INTERLACE_ADAM7_PASSES;
break;
default:
LETV_LOGE("ERROR PNG_ARGB8888: png unknown interlace!\n");
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return NULL;
} pData = (BitmapData*)LETV_MEM_MALLOC(sizeof(BitmapData));
if (!pData) {
LETV_LOGE("ERROR PNG_ARGB8888: allocate memory for bitmap data failed!\n");
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return NULL;
} width = png_get_image_width(png_ptr, info_ptr);
height = png_get_image_height(png_ptr, info_ptr);
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
pRow = png_get_rows(png_ptr, info_ptr);
for (pass = ; pass < passes; ++pass) {
if (interlace_method == PNG_INTERLACE_ADAM7) {
if (PNG_PASS_COLS(width, pass) == )
continue; xstart = PNG_PASS_START_COL(pass);
ystart = PNG_PASS_START_ROW(pass);
xstep = PNG_PASS_COL_OFFSET(pass);
ystep = PNG_PASS_ROW_OFFSET(pass);
} else {
ystart = xstart = ;
ystep = xstep = ;
} pData->infoHeader.width = width / xstep;
pData->infoHeader.height = height / ystep;
pData->fileHeader.size = sizeof(BitmapPixel) * pData->infoHeader.width * pData->infoHeader.height;
pData->pPixels = (BitmapPixel*)LETV_MEM_MALLOC(pData->fileHeader.size);
if (!pData->pPixels) {
LETV_LOGE("ERROR PNG_ARGB8888: allocate memory for bitmap pixels failed!\n");
break;
} count = ;
for (py = ystart; py < ystart + height; py += ystep) {
for (px = xstart, ppx = ; px < xstart + width; px += xstep, ppx++) {
readPixel(*(pRow + py), ppx, bit_depth, color_type, pData->pPixels + count);
count++;
}
}
break;
}
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return pData;
}

转自:https://www.0xaa55.com/forum.php?mod=viewthread&tid=425&extra=page%3D1&page=1

我要讲的三个大部分分别是:
1、libpng是什么,能做什么?
2、怎样让自己的程序可以使用libpng库?
3、怎样借助libpng读写PNG文件

1、libpng是什么?
libpng是一款C语言编写的比较底层的读写PNG文件的跨平台的库。借助它,你可以轻松读写PNG文件的每一行像素。
因为PNG文件是经过压缩而且格式复杂的图形文件(有的PNG文件甚至像GIF文件一样带动画效果)
而且PNG可以是带透明通道的真彩色图像、不带透明通道的真彩色图像、索引颜色、灰度颜色等各种格式,如果大家都自己写程序分析PNG文件就会显得很麻烦、很累。因此,通过使用libpng你就能直接使用现成的函数、程序来读写PNG文件了。
注:libpng的官网说,“PNG”这个词的发音是“拼”(ping),而不是“批恩鸡”(pee en gee,也就是直接读字母),也不是其它的单词发音(什么pinj、pig之类的发音)。
但是如果你不是以英语为母语的话(咱都是中国人呢~~)你就不必蛋疼地在意这个发音的问题,直接见人就说“批恩鸡”就行啦。(当我没说)

2、怎样让自己的程序可以使用libpng库?
有很多种方法。
方法1:上网下载libpng的DLL、LIB文件以及头文件,然后在自己的程序里,包含png.h,链接libpng.lib,就可以了。但是这样的话你的程序需要libpng.dll才能运行,而libpng使用了zlib所以可能你还需要zlib.dll才能运行。因此你还需要下载zlib的头文件、lib、DLL。
方法2:直接下载libpng的源码和zlib的源码,然后把.c文件和.h文件都加入到自己的工程里面。这招最好使因为这样便于调试。只是你的程序会很大因为你的程序直接集成了libpng和zlib。
方法3:下载libpng的源码和zlib的源码,自己将其编译为DLL或LIB,然后包含png.h,链接LIB文件,就能使用。不过你如果没编译好也可能会出问题。

3、怎样借助libpng读写PNG文件
首先来讲如何写入PNG文件。
第一步:初始化libpng库。
当你需要读一个PNG文件或者写一个PNG文件的时候,你需要先定义两个结构体指针:

 png_structp png_ptr=NULL;//libpng的结构体
png_infop info_ptr=NULL;//libpng的信息
 

你可以把上面的结构体指针定义为全局变量使用。
每这两个结构体对应一个PNG文件。因此当你要同时操作多个PNG文件的时候,你就需要定义多个png_structp和png_infop来处理这些PNG文件了。
因为是要写文件,所以要这样初始化:

 int iRetVal;
png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
if(!png_ptr)
goto 错误处理;
info_ptr=png_create_info_struct(png_ptr);
if(!info_ptr)
{
png_destroy_write_struct(&png_ptr,NULL);
goto 错误处理;
}
iRetVal=setjmp(png_jmpbuf(png_ptr));//安装错误处理跳转点
//当libpng内部出现错误的时候,libpng会调用longjmp直接跳转到这里运行。
if(iRetVal)//setjmp的返回值就是libpng跳转后提供的错误代码(貌似总是1,但是还是请大家看libpng的官方文档)
{
fprintf(stderr,"错误码:%d\n",iRetVal);
goto 错误处理;
}
 

只要最后png_ptr和info_ptr都不是NULL就行了。否则就算是出错了。
这里可以看到libpng使用了setjmp来做错误处理。有关setjmp的信息请点这里进去看。
这两个结构体有对应的释放函数:png_destroy_write_struct
结束对一个PNG的访问之后,你只需像这样调用这个函数:

 png_destroy_write_struct(&png_ptr,&info_ptr);
 

就可以了。
接下来打开文件准备写文件。还是大家熟悉的C语言文件流。

 FILE*fp=fopen("C:\\TEST.PNG","wb");
if(!fp)
goto 错误处理;
 

打开了文件以后,你要让libpng和这个文件流绑定起来,因此你需要调用png_init_io来完成绑定。

 png_init_io(png_ptr,fp);
 

接下来就是关键的部分了:设置PNG文件的属性、写入PNG文件头、写入PNG文件。

 //设置PNG文件头
png_set_IHDR(png_ptr,info_ptr,
图像宽度,图像高度,//尺寸
,//颜色深度,也就是每个颜色成分占用位数(8表示8位红8位绿8位蓝,如果有透明通道则还会有8位不透明度)
PNG_COLOR_TYPE_RGB,//颜色类型,PNG_COLOR_TYPE_RGB表示24位真彩深色,PNG_COLOR_TYPE_RGBA表示32位带透明通道真彩色
PNG_INTERLACE_NONE,//不交错。PNG_INTERLACE_ADAM7表示这个PNG文件是交错格式。交错格式的PNG文件在网络传输的时候能以最快速度显示出图像的大致样子。
PNG_COMPRESSION_TYPE_BASE,//压缩方式
PNG_FILTER_TYPE_BASE);//这个不知道,总之填写PNG_FILTER_TYPE_BASE即可。
png_set_packing(png_ptr);//设置打包信息
png_write_info(png_ptr,info_ptr);//写入文件头
 

执行完这些语句以后,你会发现libpng已经通过文件流指针fp写入了PNG的文件头。
接下来要做的就是写入PNG的图像信息。其实就是把颜色保存到PNG。
不像恶心的BMP居然有“底到上型”和“顶到下型”之分,PNG只有“顶到下型”,因此你不需要考虑行序。
写图的方法之一是调用png_write_image(png_ptr,行指针数组的指针);这个你不需要考虑交错文件的写入的遍数。
而如果你需要手动写入每一行的数据,你需要调用png_write_row(png_ptr,一行像素的指针);来进行逐行的像素值写入。
如果你设置了交错格式的PNG,你需要多写入几遍图形数据,你需要调用png_set_interlace_handling(png_ptr);来得知你需要写入的遍数。如果你没有设置交错格式的PNG,你只需要写入一遍。

以下文本写给小白。高手请略过。
这里需要详细说明图像是怎么写入的。首先说明一下什么是图像。一个图像,是由一个一个的正方形的小像素点组成的。
每个像素点都有自己的颜色值,用三个字节来表示红色、绿色、蓝色的分量。所有的颜色都是用红绿蓝三基色混合搭配调出来的颜色。
然后对于libpng,图像是一行一行写入到文件的。这里所说的“行”和“列”指的是排列起来的像素点。比如一个图像的宽度是1024像素,高度是768像素,那么这个图像就有768行,每行有1024个像素点。
假设你设置的颜色深度是8,颜色类型是PNG_COLOR_TYPE_RGB,那么你的每个像素点都是由三个字节组成的,这三个字节分别是红色、绿色和蓝色的分量。
而如果颜色类型是PNG_COLOR_TYPE_RGBA,那么你的每个像素点都是由四个字节组成的,这四个字节分别是红色、绿色和蓝色的分量和不透明度。这样的图像才支持透明颜色的显示。
调用png_write_row的方法很简单,就是把一行的像素点的颜色设置好,然后调用它:png_write_row(png_ptr,这行像素的第一个像素在内存中的位置);就可以写入一行。
而调用png_write_image你需要把每一行的像素颜色都设置好,然后建立一个指针数组,这个指针数组的每一个指针都指向每一行的像素。明白吧?

写入好像素以后,调用png_write_end(png_ptr,info_ptr);把文件的结尾写入。
调用png_destroy_write_struct(&png_ptr,&info_ptr);结束对这个PNG文件的访问。
最后fclose(fp);关闭文件。这个时候你会发现,你已经成功地产生了一个PNG文件!而且可以用PS打开了。

读取PNG文件也是类似的步骤,首先你需要初始化libpng库。
你需要先定义两个结构体指针:

 png_structp png_ptr=NULL;//libpng的结构体
png_infop info_ptr=NULL;//libpng的信息

你可以把上面的结构体指针定义为全局变量使用。

每这两个结构体对应一个PNG文件。因此当你要同时操作多个PNG文件的时候,你就需要定义多个png_structp和png_infop来处理这些PNG文件了。
因为是要读文件,所以要这样初始化:

 int iRetVal;
png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
if(!png_ptr)
goto 错误处理;
info_ptr=png_create_info_struct(png_ptr);
if(!info_ptr)
{
png_destroy_read_struct(&png_ptr,NULL,NULL);
goto 错误处理;
}
iRetVal=setjmp(png_jmpbuf(png_ptr));//安装错误处理跳转点
//当libpng内部出现错误的时候,libpng会调用longjmp直接跳转到这里运行。
if(iRetVal)//setjmp的返回值就是libpng跳转后提供的错误代码(貌似总是1,但是还是请大家看libpng的官方文档)
{
fprintf(stderr,"错误码:%d\n",iRetVal);
goto 错误处理;
}
 

只要最后png_ptr和info_ptr都不是NULL就行了。否则就算是出错了。
这两个结构体有对应的释放函数:png_destroy_read_struct
结束对一个PNG的访问之后,你只需像这样调用这个函数:

 png_destroy_read_struct(&png_ptr,&info_ptr,NULL);
 

就可以了。
接下来打开文件准备读文件。还是大家熟悉的C语言文件流。

 FILE*fp=fopen("C:\\TEST.PNG","rb");
if(!fp)
goto 错误处理;

打开了文件以后,你要让libpng和这个文件流绑定起来,因此你需要调用png_init_io来完成绑定。绑定之后,你还需要获取PNG的文件头信息。因此你需要调用png_read_info(png_ptr, info_ptr);

 png_init_io(png_ptr,fp);
png_read_info(png_ptr, info_ptr);
 

读取了文件头,你就能获取文件头的信息。比如文件尺寸、位深度等。代码如下:

 png_get_IHDR(png_ptr,info_ptr,&width,&height,&bit_depth,&color_type,NULL,NULL,NULL);
 

有些PNG文件是有背景色的,因此你需要处理这些背景色信息。我们可以用png_get_valid来判断这个PNG是否有背景色信息。png_get_valid(png_ptr,info_ptr,PNG_INFO_bKGD)返回0表示没有背景色信息,返回非零表示有背景色信息。然后我们调用png_get_bKGD来读取背景色。

 png_color_16p pBackground;
png_get_bKGD(png_ptr,info_ptr,&pBackground);

大家可以看看png_color_16p的原型:

 typedef struct png_color_16_struct
{
png_byte index;
png_uint_16 red;
png_uint_16 green;
png_uint_16 blue;
png_uint_16 gray;
} png_color_16;
 

如果这个PNG是调色板颜色的位图,那么index表示背景色的调色板颜色序号。
red、green、blue表示背景色的颜色值。如果png_get_IHDR返回的位深度(bit_depth)是16,那么red、green、blue就是16位的颜色值,范围0~65535。(瞬间觉得PNG高大上啊!16+16+16=48,这个比真彩色还要真彩色!屌!)
而如果png_get_IHDR返回的位深度(bit_depth)是8,那么red、green、blue其实都是8位的颜色值,范围0~255,也就是24位真彩色。
接下来就是关键的步骤了,读取颜色数据。
因为有些PNG是灰度色,有些PNG是索引颜色,有些PNG是48位色,总之各种奇葩。为了便于读取,我们应该先规范一下格式。

 if(colortype==PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png_ptr);//要求转换索引颜色到RGB
if(colortype==PNG_COLOR_TYPE_GRAY && bit_depth<)
png_set_expand_gray_1_2_4_to_8(png_ptr);//要求位深度强制8bit
if(bit_depth==)
png_set_strip_16(png_ptr);//要求位深度强制8bit
if(png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png_ptr);
if(colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);//灰度必须转换成RGB
 

经过这些设定以后,我们读取的PNG就一律是R8:G8:B8:A8的4字节格式了(红绿蓝透明均为8位,每像素4字节)
然后准备读取PNG。首先分配一个足够大的内存来存储颜色数据,然后分配一个内存来存储颜色数据每行的指针。
因为颜色已经被规范为32位了所以我们可以直接把每个像素当做一个COLORREF变量。

 ppLinePtrs=(COLORREF**)malloc(g_dwHeight*sizeof(COLORREF*));//列指针
if(!ppLinePtrs)
goto Error;
i=g_dwHeight;
y=;
while(i--)//逆行序读取,因为位图是底到上型
ppLinePtrs[y++]=(COLORREF*)&g_pBits[i*g_dwWidth];
 

这个时候就是万事俱备的时候,只需要调用png_read_image(png_ptr,(png_bytepp)ppLinePtrs);就能完成读取。
读取完以后,调用png_read_end(png_ptr,info_ptr);结束读取,调用png_destroy_read_struct(&png_ptr,&info_ptr,NULL);销毁结构体,然后fclose(fp);就算一切都搞定了。

接下来我会放上两份源码,一份把BMP转换成PNG(支持把两个BMP合体转换成带透明通道的PNG)
而另一份是结合了分层窗体的技术,把PNG当做分层窗体的界面来显示的源码。
效果不错。发张图晒晒。
<ignore_js_op>

libpng使用的更多相关文章

  1. pngcrush caught libpng error: Not a PNG file..

    今日真机测试遇到这样的奇葩问题: While reading XXX/XXX.png pngcrush caught libpng error: Not a PNG file.. 替换几次图片都没解决 ...

  2. iOS真机运行 Xcode报错(libpng error: CgBI: unhandled critical chunk)问题已解决;

    Cocos2d-x加载图片资源出现libpng error: CgBI: unhandled critical chunk Xcode7.3 设置Remove Text Metadata From P ...

  3. libpng安装与配置(Win7+VS2010)

    一.下载 libpng:http://libmng.com/pub/png/libpng.html zlib:http://www.zlib.net/ IDE:VS2010 二.编译 将下载的两个zi ...

  4. Visual Studio 2015 下 编译 libpng

    libpng https://github.com/glennrp/libpng zlib https://github.com/madler/zlib/releases https://github ...

  5. Win7 64位 VS2015环境编译Libpng

    第3次编译Libpng依然想不起任何东西,为了不浪费第4次的时间... http://libmng.com/pub/png/libpng.html http://www.zlib.net/ 解压两个压 ...

  6. 【Xamarin报错】libpng warning : iCCP: Not recognizing known sRGB profile that has been edited

    报错: Xamarin Android 编译时发生以下错误: libpng warning : iCCP: Not recognizing known sRGB profile that has be ...

  7. VC++编译libpng

    目录 第1章简介    1 第2章 Visual C++6.0    2 2.1 打开项目    2 2.2 编译宏    3 2.2.1 小结    5 第3章 Visual C++2010     ...

  8. Windows下zlib库和libPng库的编译和使用

    关于zlib库和libpng是干嘛的,我就不说了,度娘和谷歌都能告诉你.这里主要记录下windows下如何利用vs2010编译和使用这两个库. 一.zlib库的编译 首先要下载这个库,这个谷歌和百度也 ...

  9. While reading xxx.png pngcrush caught libpng error: Not a PNG file..

    While reading /XXX/XXX/XXX/img1.png pngcrush caught libpng error: Not a PNG filCould not find file: ...

随机推荐

  1. 【工业串口和网络软件通讯平台(SuperIO)教程】八.SuperIO通讯机制与设备驱动对接的说明

    SuperIO相关资料下载:http://pan.baidu.com/s/1pJ7lZWf 1.1    通讯机制说明 通讯的总体机制采用呼叫应答方式,就是上位机软件主动发送请求数据命令,下位机终端接 ...

  2. entityframework学习笔记--001

    最近想重新好好学习一下entityframework,于是在院子里找到了一篇不错的博客.下面把学习的过程记录下来,方便以后复习. 学习过程参考大神的博客:http://www.cnblogs.com/ ...

  3. 推荐15款创建漂亮幻灯片的 jQuery 插件

    对于设计师,开发者,摄影师或任何创造性的个人和企业,他们自己的网站是展示他们的技能和服务的最佳场所.你可能打算设计一个新的个人作品网站,不管你是从头开始或使用模板,都会需要使用 jQuery 幻灯片插 ...

  4. 使用eclipse作为python开发工具安装旧版pydev

    在Eclipse中: Help->Install New Software add之后输入的链接地址 https://dl.bintray.com/fabioz/pydev/old/ 如果使用从 ...

  5. JavaScript 变量声明提前

    <JavaScript权威指南>中指出:JavaScript变量在声明之前已经可用,JavaScript的这个特性被非正式的称为声明提前(hoisting),即JavaScript函数中声 ...

  6. ArcSDE10.2.2使用SQL操作ST_Geometry时报ORA-28579

    给esri中国的客服打电话被告知,是一直存在这个bug,arcgis10.2对应的oracle数据库版本要用11.2.0.3及以上的: 1.数据库升级可以用打补丁的当方式. 2.直接重装,我这里是直接 ...

  7. Synchronization Service Manager

    You can use this UI Shell to check the User Profile log for the SharePoint.   It's stored in this pa ...

  8. Git remote 修改源

    Git remote 修改源 git commit -m "Change repo." # 先把所有为保存的修改打包为一个commit git remote remove orig ...

  9. Sublime Text 解决中文乱码

    sublime text 是一款非常优秀的跨平台文本及源代码编辑器,本人非常喜欢,但是不支持GB2312和GBK编码在某些时候比较麻烦.可以通过向sublime text 中添加编码类型转换包(比如& ...

  10. OSX下 pip更新及安装python库

    直接执行安装命令 $ pip install builtwith 提示pip当前版本为7.1.2,要使用"pip install --upgrade pip"升级到8.1.2 $  ...