| 
 
| ## 2.3 PNG图像处理 
 ### 2.3.1 PNG文件格式和libpng编译
 
                 跟JPEG文件格式一样,PNG也是一种使用了算法压缩后的图像格式,与JPEG不同,PNG使用从LZ77派生的无损数据压缩算法。对于PNG文件格式,也有相应的开源工具libpng。
 
 libpng库可从官网上下载最新的源代码:
 
 http://www.libpng.org/pub/png/libpng.html
 
 在使用libpng之前,我们先要交叉编译libpng的库文件和头文件并存到开发板的文件系统中。以下是libpng的编译过程:
 
 1. 解压并进入文件目录
 
 ```c
 tar xzf libpng-1.6.37.tar.gz
 cd libpng-1.6.37/
 ```
 
 2. 交叉编译
 
 ```c
 ./configure --prefix=/work/projects/libpng-1.6.37/tmp/ --host=arm-linux
 make
 make install
 ```
 
 3. 将编译出来的头文件和库文件拷贝到交叉编译器的相应目录下
 
 ```c
 cd /work/projects/libpng-1.6.37/tmp/include
 cp * /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include
 cd /work/projects/libpng-1.6.37/tmp/lib
 cp *so* -d /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib
 ```
 
 4. 将编译出来的头文件和库文件拷贝到开发板文件系统的相应目录下
 
 ```c
 cd /work/projects/libpng-1.6.37/tmp/lib
 cp *.so* /work/nfs_root/fs_mini_mdev_new/lib/ -d
 ```
 
 ### 2.3.2 libpng接口函数的解析和使用
 
 libpng的使用方法可以参考解压包中的使用说明libpng-manual.txt和例程example.c。libjpeg的使用步骤简单总结如下:
 
 1. 分配和初始化两个与libpng相关的结构体png_ptr,info_ptr
 
 A. png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 
         参数2,3,4分别是用户自定义的错误处理函数,若无,则填NULL。
 
            B. info_ptr = png_create_info_struct(png_ptr);
 
 2. 设置错误返回点
 
 setjmp(png_jmpbuf(png_ptr));
 
 当出现错误时,libpng将会自动调用返回到这个点。在这个点我们可以进行一些清理工作。如果在调用png_create_read_struct时没有设置自定义的错误处理函数,这一步是必须要做的。
 
 3. 指定源文件
 
 png_init_io(png_ptr, fp);
 
 参数1是步骤1中分配的png_ptr结构体,参数2是需要解析的PNG文件的文件句柄。
 
 4. 获取PNG图像的信息
 
 A. 解析图片数据信息
 
 png_read_png(png_ptr, info_ptr, png_transforms, png_voidp_NULL);
 
 该函数会把所有的图片数据解码到info_ptr数据结构中。至于转化为什么格式,由参数png_transforms决定,它是一个整型参数,可以使用libpng库中定义的宏进行传参。这个参数相关的宏有很多,具体的可以参考库中的相关文件的解析。
 
 B.查询图像信息
 
 此外,我们还可以通过png_get_image_width,png_get_image_height,png_get_color_type等函数获得png图像的宽度,高度,颜色类型等信息,更多的图像信息获取函数可以在文件pngget.c中找到。
 
 5. 将info_ptr中的图像数据读取出来
 
 有两种读取PNG图像信息的方法:
 
 A. 一次性把所有的数据读入内存
 
 png_read_image(png_ptr, row_pointers);
 
 参数1是步骤1中分配的png_ptr,参数2是存放图片数据的指针。
 
 B. 也可以逐行读取
 
 row_pointers = png_get_rows(png_ptr, info_ptr);
 
 参数1和参数2分别是步骤1中分配的png_ptr, info_ptr,返回值是每行数据的首地址。
 
 参数1是步骤1中分配的png_ptr,参数2是存放图片数据的指针。
 
 6. 销毁内存
 
 png_destroy_read_struct(&png_ptr, &info_ptr, 0);
 
 ### 2.3.3 使用libpng把png文件转为rgb格式,在LCD上显示
 
 ```c
 代码清单2.3
 1.        /**********************************************************************
 2.         * 函数名称: IsnotPng
 3.         * 功能描述:判断是否为PNG文件
 4.         * 输入参数: ppFp - 文件句柄指针
 5.                            strFileName - 文件名
 6.         * 返 回 值:0 - 是PNG格式 其他-不是PNG格式
 7.         ***********************************************************************/
 8.        int IsnotPng(FILE **ppFp, const char *strFileName)
 9.        {
 10.            char strCheckHeader[8];
 11.            *ppFp= fopen(strFileName, "rb");
 12.            if (*ppFp== NULL) {
 13.                return -1;
 14.            }
 15.            /* 读取PNG文件前8个字节,使用库函数png_sig_cmp即可判断是否为PNG格式 */
 16.            if (fread(strCheckHeader, 1, 8, *ppFp) != 8)
 17.                return -1;
 18.            return png_sig_cmp(strCheckHeader, 0, 8);
 19.
 20.        }
 21.
 22.        /**********************************************************************
 23.         * 函数名称: DecodePng2Rgb
 24.         * 功能描述:把PNG文件解析为RGB888格式
 25.         * 输入参数: ptData - 内含文件信息
 26.         *                             strFileName - 文件名
 27.         * 输出参数:PT_PictureData->pucRgbData - 内含rgb数据
 28.         * 返 回 值:0 - 成功 其他-失败
 29.         ***********************************************************************/
 30.        static int DecodePng2Rgb(const char *strFileName, PT_PictureData ptData)
 31.        {
 32.            int i, j;
 33.            int iPos = 0;
 34.            png_bytepp pucPngData;
 35.            /* 0.判断该文件是否为PNG格式 */
 36.            if (IsnotPng(&ptData->ptFp, strFileName)) {
 37.                printf("file is not png ...\n");
 38.                return -1;
 39.            }
 40.
 41.            /* 1.分配和初始化两个与libpng相关的结构体png_ptr,info_ptr */
 42.            ptData->ptPngStrPoint  = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 43.            ptData->ptPngInfoPoint= png_create_info_struct(ptData->ptPngStrPoint);
 44.
 45.            /* 2.设置错误的返回点 */
 46.            setjmp(png_jmpbuf(ptData->ptPngStrPoint));
 47.            rewind(ptData->ptFp); //等价fseek(fp, 0, SEEK_SET);
 48.
 49.            /* 3.指定源文件 */
 50.            png_init_io(ptData->ptPngStrPoint, ptData->ptFp);
 51.
 52.            /* 4.获取PNG图像数据信息和通道数,宽度,高度等
 53.              * 使用PNG_TRANSFORM_EXPAND宏做参数的作用是根据通道数的不同,
 54.              * 将PNG图像转换为BGR888或ABGR8888格式*/
 55.            png_read_png(ptData->ptPngStrPoint, ptData->ptPngInfoPoint, PNG_TRANSFORM_EXPAND, 0);
 56.            ptData->iChannels    = png_get_channels(ptData->ptPngStrPoint, ptData->ptPngInfoPoint);
 57.            ptData->iWidth    = png_get_image_width(ptData->ptPngStrPoint, ptData->ptPngInfoPoint);
 58.            ptData->iHeight  = png_get_image_height(ptData->ptPngStrPoint, ptData->ptPngInfoPoint);
 59.
 60.
 61.            /* 5.将info_ptr中的图像数据读取出来 */
 62.            pucPngData = png_get_rows(ptData->ptPngStrPoint, ptData->ptPngInfoPoint); //也可以分别每一行获取png_get_rowbytes();
 63.            if (ptData->iChannels == 4) { //判断是24位还是32位
 64.                ptData->iRawSize= ptData->iWidth * ptData->iHeight*4; //申请内存先计算空间
 65.                ptData->pucRawData= (unsigned char*)malloc(ptData->iRawSize);
 66.                if (NULL == ptData->pucRawData) {
 67.                    printf("malloc rgba faile ...\n");
 68.                    png_destroy_read_struct(&ptData->ptPngStrPoint, &ptData->ptPngInfoPoint, 0);
 69.                    fclose(ptData->ptFp);
 70.                    return -1;
 71.                }
 72.                /* 从pucPngData里读出实际的RGBA数据出来
 73.                 * 源数据为ABGR格式*/
 74.                for (i = 0; i < ptData->iHeight; i++)
 75.                    for (j = 0; j < ptData->iWidth * 4; j += 4) {
 76.                            ptData->pucRawData[iPos++] = pucPngData[i][j + 3];
 77.                            ptData->pucRawData[iPos++] = pucPngData[i][j + 2];
 78.                            ptData->pucRawData[iPos++] = pucPngData[i][j + 1];
 79.                            ptData->pucRawData[iPos++] = pucPngData[i][j + 0];
 80.                        }
 81.
 82.                /* 将得到的RGBA转换为RGB888格式 */
 83.                if(RgbaToRgb(ptData)!=0)
 84.                    return -1;
 85.
 86.            }
 87.            else if (ptData->iChannels == 3 ) { //判断颜色深度是24位还是32位
 88.                ptData->iRgbSize= ptData->iWidth * ptData->iHeight*3; //申请内存先计算空间
 89.                ptData->pucRgbData = (unsigned char*)malloc(ptData->iRgbSize);
 90.                if (NULL == ptData->pucRgbData) {
 91.                    printf("malloc rgba faile ...\n");
 92.                    png_destroy_read_struct(&ptData->ptPngStrPoint, &ptData->ptPngInfoPoint, 0);
 93.                    fclose(ptData->ptFp);
 94.                    return -1;
 95.                }
 96.                /* 从pucPngData里读出实际的RGB数据
 97.                  * 源数据为BGR格式*/
 98.                for (i = 0; i < ptData->iHeight; i ++) {
 99.                    for (j = 0; j < ptData->iWidth*3; j += 3) {
 100.                        ptData->pucRgbData[iPos++] = pucPngData[i][j+2];
 101.                        ptData->pucRgbData[iPos++] = pucPngData[i][j+1];
 102.                        ptData->pucRgbData[iPos++] = pucPngData[i][j+0];
 103.                    }
 104.                }
 105.                ptData->iBpp = 24;//转化之后的格式为RGB888格式
 106.            }
 107.            else return -1;
 108.
 109.
 110.            /* 6:销毁内存 */
 111.            png_destroy_read_struct(&ptData->ptPngStrPoint, &ptData->ptPngInfoPoint, 0);
 112.            fclose(ptData->ptFp);
 113.
 114.
 115.            return 0;
 116.        }
 ```
 
 
 
 | 
 |