• libpng使用


    自己的实现

      1 unsigned int component(png_const_bytep row, png_uint_32 x, unsigned int c, unsigned int bit_depth, unsigned int channels) {
      2     png_uint_32 bit_offset_hi = bit_depth * ((x >> 6) * channels);
      3     png_uint_32 bit_offset_lo = bit_depth * ((x & 0x3f) * channels + c);
      4 
      5     row = (png_const_bytep)(((PNG_CONST png_byte(*)[8])row) + bit_offset_hi);
      6     row += bit_offset_lo >> 3;
      7     bit_offset_lo &= 0x07;
      8 
      9     switch (bit_depth) {
     10     case 1:
     11         return (row[0] >> (7 - bit_offset_lo)) & 0x01;
     12     case 2:
     13         return (row[0] >> (6 - bit_offset_lo)) & 0x03;
     14     case 4:
     15         return (row[0] >> (4 - bit_offset_lo)) & 0x0f;
     16     case 8:
     17         return row[0];
     18     case 16:
     19         return (row[0] << 8) + row[1];
     20     default:
     21         fprintf(stderr, "pixel_component: invalid bit depth %u
    ", bit_depth);
     22         return row[0];
     23     }
     24 }
     25 
     26 void readPixel(png_const_bytep row, png_uint_32 x, png_uint_32 bit_depth, png_uint_32 color_type, BitmapPixel *pPixel) {
     27     unsigned int channels = PNG_COLOR_TYPE_RGB_ALPHA ? 4 : 3;
     28     pPixel->r = component(row, x, 0, bit_depth, channels);
     29     pPixel->g = component(row, x, 1, bit_depth, channels);
     30     pPixel->b = component(row, x, 2, bit_depth, channels);
     31     pPixel->a = color_type == PNG_COLOR_TYPE_RGB_ALPHA ? component(row, x, 3, bit_depth, channels) : 0xFF;
     32 }
     33 
     34 BitmapData* PNG_ARGB8888(FILE *pngFile) {
     35     BitmapData *pData;
     36     png_structp png_ptr;
     37     png_infop info_ptr;
     38     png_bytepp pRow = NULL;
     39     png_uint_32 width, height, ystart, xstart, ystep, xstep, px, ppx, py, count;
     40     png_int_32 bit_depth, color_type, interlace_method, passes, pass;
     41 
     42     if (!pngFile) {
     43         return NULL;
     44     }
     45     if (!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) {
     46         LETV_LOGE("ERROR PNG_ARGB8888: out of memory allocating png_struct!
    ");
     47         return NULL;
     48     }
     49     if (!(info_ptr = png_create_info_struct(png_ptr))) {
     50         LETV_LOGE("ERROR PNG_ARGB8888: out of memory allocating png_info!
    ");
     51         png_destroy_read_struct(&png_ptr, NULL, NULL);
     52         return NULL;
     53     }
     54     if (setjmp(png_jmpbuf(png_ptr))) {
     55         LETV_LOGE("ERROR PNG_ARGB8888: setjmp png_ptr failed!
    ");
     56         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
     57         return NULL;
     58     }
     59     png_init_io(png_ptr, pngFile);
     60     png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
     61     color_type = png_get_color_type(png_ptr, info_ptr);
     62     if (!(color_type & PNG_COLOR_TYPE_RGB)) {
     63         LETV_LOGE("ERROR PNG_ARGB8888: PNG_COLOR_TYPE unsupported!
    ");
     64         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
     65         return NULL;
     66     }
     67     interlace_method = png_get_interlace_type(png_ptr, info_ptr);
     68     switch (interlace_method) {
     69     case PNG_INTERLACE_NONE:
     70         passes = 1;
     71         break;
     72     case PNG_INTERLACE_ADAM7:
     73         passes = PNG_INTERLACE_ADAM7_PASSES;
     74         break;
     75     default:
     76         LETV_LOGE("ERROR PNG_ARGB8888: png unknown interlace!
    ");
     77         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
     78         return NULL;
     79     }
     80 
     81     pData = (BitmapData*)LETV_MEM_MALLOC(sizeof(BitmapData));
     82     if (!pData) {
     83         LETV_LOGE("ERROR PNG_ARGB8888: allocate memory for bitmap data failed!
    ");
     84         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
     85         return NULL;
     86     }
     87 
     88     width = png_get_image_width(png_ptr, info_ptr);
     89     height = png_get_image_height(png_ptr, info_ptr);
     90     bit_depth = png_get_bit_depth(png_ptr, info_ptr);
     91     pRow = png_get_rows(png_ptr, info_ptr);
     92     for (pass = 0; pass < passes; ++pass) {
     93         if (interlace_method == PNG_INTERLACE_ADAM7) {
     94             if (PNG_PASS_COLS(width, pass) == 0)
     95                 continue;
     96 
     97             xstart = PNG_PASS_START_COL(pass);
     98             ystart = PNG_PASS_START_ROW(pass);
     99             xstep = PNG_PASS_COL_OFFSET(pass);
    100             ystep = PNG_PASS_ROW_OFFSET(pass);
    101         } else {
    102             ystart = xstart = 0;
    103             ystep = xstep = 1;
    104         }
    105 
    106         pData->infoHeader.width = width / xstep;
    107         pData->infoHeader.height = height / ystep;
    108         pData->fileHeader.size = sizeof(BitmapPixel) * pData->infoHeader.width * pData->infoHeader.height;
    109         pData->pPixels = (BitmapPixel*)LETV_MEM_MALLOC(pData->fileHeader.size);
    110         if (!pData->pPixels) {
    111             LETV_LOGE("ERROR PNG_ARGB8888: allocate memory for bitmap pixels failed!
    ");
    112             break;
    113         }
    114 
    115         count = 0;
    116         for (py = ystart; py < ystart + height; py += ystep) {
    117             for (px = xstart, ppx = 0; px < xstart + width; px += xstep, ppx++) {
    118                 readPixel(*(pRow + py), ppx, bit_depth, color_type, pData->pPixels + count);
    119                 count++;
    120             }
    121         }
    122         break;
    123     }
    124     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    125     return pData;
    126 }

    转自: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文件的时候,你需要先定义两个结构体指针:

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

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

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

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

    1 png_destroy_write_struct(&png_ptr,&info_ptr);
     

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

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

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

    1 png_init_io(png_ptr,fp);
     

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

     1 //设置PNG文件头
     2 png_set_IHDR(png_ptr,info_ptr,
     3     图像宽度,图像高度,//尺寸
     4     8,//颜色深度,也就是每个颜色成分占用位数(8表示8位红8位绿8位蓝,如果有透明通道则还会有8位不透明度)
     5     PNG_COLOR_TYPE_RGB,//颜色类型,PNG_COLOR_TYPE_RGB表示24位真彩深色,PNG_COLOR_TYPE_RGBA表示32位带透明通道真彩色
     6     PNG_INTERLACE_NONE,//不交错。PNG_INTERLACE_ADAM7表示这个PNG文件是交错格式。交错格式的PNG文件在网络传输的时候能以最快速度显示出图像的大致样子。
     7     PNG_COMPRESSION_TYPE_BASE,//压缩方式
     8     PNG_FILTER_TYPE_BASE);//这个不知道,总之填写PNG_FILTER_TYPE_BASE即可。
     9 png_set_packing(png_ptr);//设置打包信息
    10 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库。
    你需要先定义两个结构体指针:

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

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

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

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

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

    1 png_destroy_read_struct(&png_ptr,&info_ptr,NULL);
     

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

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

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

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

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

    1 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来读取背景色。

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

    大家可以看看png_color_16p的原型:

    1 typedef struct png_color_16_struct
    2 {
    3     png_byte index;
    4     png_uint_16 red;
    5     png_uint_16 green;
    6     png_uint_16 blue;
    7     png_uint_16 gray;
    8 } 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位色,总之各种奇葩。为了便于读取,我们应该先规范一下格式。

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

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

    1 ppLinePtrs=(COLORREF**)malloc(g_dwHeight*sizeof(COLORREF*));//列指针
    2 if(!ppLinePtrs)
    3     goto Error;
    4 i=g_dwHeight;
    5 y=0;
    6 while(i--)//逆行序读取,因为位图是底到上型
    7     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> 

  • 相关阅读:
    HAProxy的基础配置详解
    Nginx七层负载均衡的几种调度算法
    基于PXE网络启动的Linux系统自动化安装
    centos源码编译安装新版本内核
    Linux计划任务管理
    Linux多网卡绑定(bond)及网络组(team)
    Linux逻辑卷管理(LVM)
    pandas基础操作
    subprocess
    python常用库(转)
  • 原文地址:https://www.cnblogs.com/zl1991/p/6207584.html
Copyright © 2020-2023  润新知