嵌入式Linux--使用libpng库解码png图片

解码库版本:libpng-1.6.35

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <png.h>        //解码库文件
#include <pngstruct.h>  //解码库文件
#include <pnginfo.h>    //解码库文件
#include <config.h>     //自己定义的头文件

#define PNG_BYTES_TO_CHECK 	 8  
/*
 *函数功能 :判断一个图片数是不是png图片
 *函数参数 :path是图片文件的路径名加文件名
 *返回值   :如果是png则返回0,不是或出错返回-1
 */
static int is_png(char *path) {
	FILE *fp = NULL;
	char buf[PNG_BYTES_TO_CHECK] = {0}; 

	/* Open the prospective PNG file. */   
	if ((fp = fopen(path, "rb")) == NULL)		
		return -1;	

	/* Read in some of the signature bytes */	
	if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK)	   
		return -1; 

	//比较头文件标识,是png格式则返回0,不是返回非0
	//png 文件头标识 (8 bytes)   89 50 4E 47 0D 0A 1A 0A	 
	return(png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK));
}

/*
 *函数功能:解码png图片,并将解码出来的数据存储起来
 *函数参数:pPic记录源png图片,解码出来图片宽高、位深度等
 *返回值  :解码正确返回0,错误返回-1
 */
static int png_analyze (struct pic_info *pPic)
{
	int i, j, pos=0;
	png_structp png_ptr; //定义一个png指针
	png_infop info_ptr;
	png_bytep* row_ptr;  //实际存储rgb数据的buf,二维指针
	png_byte color_type;
	FILE *fp = NULL; 

	if ((fp = fopen(pPic->pathname, "rb")) == NULL) {
		fprintf(stderr, "fopen %s error.\n", pPic->pathname);
		return -1;
	}
			
	//第1步:初始化相关结构体
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL)
	{
		fprintf(stderr, "init %s error.\n", pPic->pathname);
		fclose(fp);
		return -1;
	}
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL)
	{
		fprintf(stderr, "create %s error.\n", pPic->pathname);
		fclose(fp);
		png_destroy_read_struct(&png_ptr, NULL, NULL);
		return -1;
	}

	//第2步:设置错误返回点	
	if (setjmp(png_jmpbuf(png_ptr)))
	{
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		fclose(fp);
		return -1;
	}

	//第3步:将解码的结构体和图片文件绑定
	png_init_io(png_ptr, fp);
	
	//第4步:读文件
	/*当内存足够大可以一次性读入所有的png数据,可以使用高层函数
	该函数将会把所有的图片数据解码到info_ptr数据结构中*/	 
	png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA, NULL); 

	//第5步:提取打印文件信息
	pPic->width = info_ptr->width;
	pPic->height = info_ptr->height;
	pPic->bpp = info_ptr->pixel_depth;
	color_type = info_ptr->color_type; 	
	printf("picture resolution: %d*%d\n", pPic->width, pPic->height);
	printf("picture bpp: %d\n", pPic->bpp);
	printf("picture colortype: %d\n", color_type);
	
	// 第6步: 读取真正的图像信息
	row_ptr = png_get_rows(png_ptr, info_ptr);
	switch (color_type) {
		case PNG_COLOR_TYPE_RGB_ALPHA:
			for (i = 0; i < pPic->height; i++) {
				for (j = 0; j < pPic->width*4; j+=4) {
					pPic->pData[pos++] = row_ptr[i][j+0]; // red
					pPic->pData[pos++] = row_ptr[i][j+1]; // green
					pPic->pData[pos++] = row_ptr[i][j+2]; // blue
				}
			}
			break;
		case PNG_COLOR_TYPE_RGB:
			for (i = 0; i < pPic->height; i++) {
				for (j = 0; j < pPic->width*3; j+=3) {
					pPic->pData[pos++] = row_ptr[i][j+0]; // red
					pPic->pData[pos++] = row_ptr[i][j+1]; // green
					pPic->pData[pos++] = row_ptr[i][j+2]; // blue	
				}
			}
			break;
		default:
			break;	
	}
	
	//第7步:扫尾
	png_destroy_read_struct(&png_ptr, &info_ptr, 0);
	fclose(fp);
	return 0;	
}

int png_display (char *pathname) 
{
	int ret = -1;
	struct pic_info pic;

	//第1步:判断图片是不是jpg图片
	ret = is_png(pathname);
	if (ret != 0) {
		return -1;
	}
	
	//第2步:显示jpg图片
	pic.pathname = pathname;
	pic.pData = rgb_buf;
	ret = png_analyze(&pic);
	if (ret != 0) {
		return -1;
	}
	fb_draw_jpg(0, 0, &pic);
	return 0;
}

下面分析一下解码函数

1.png_infop 结构体在哪儿

在png.h的484行有如下内容,png_infop被声明为了png_info 的一维指针,而png_info又是png_info_def 的声明。

typedef struct png_info_def png_info;
typedef png_info * png_infop;
typedef const png_info * png_const_infop;
typedef png_info * * png_infopp;

得到了png_info_def后,接下来追根溯源,可以找到png_info_def定义在了pnginfo.h文件中:

struct png_info_def
{
   /* The following are necessary for every PNG file */
   png_uint_32 width;       /* width of image in pixels (from IHDR) */
   png_uint_32 height;      /* height of image in pixels (from IHDR) */
   png_uint_32 valid;       /* valid chunk data (see PNG_INFO_ below) */
   size_t rowbytes;         /* bytes needed to hold an untransformed row */
   png_colorp palette;      /* array of color values (valid & PNG_INFO_PLTE) */
   png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
   png_uint_16 num_trans;   /* number of transparent palette color (tRNS) */
   png_byte bit_depth;      /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
   png_byte color_type;     /* see PNG_COLOR_TYPE_ below (from IHDR) */
   /* The following three should have been named *_method not *_type */
   png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
   png_byte filter_type;    /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
   png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */

   /* The following are set by png_set_IHDR, called from the application on
    * write, but the are never actually used by the write code.
    */
   png_byte channels;       /* number of data channels per pixel (1, 2, 3, 4) */
   png_byte pixel_depth;    /* number of bits per pixel */
   png_byte spare_byte;     /* to align the data, and for future use */
..................还有很长........
}

2.png_read_png()如何传参

函数原型:png_read_png(png_ptr, info_ptr, png_transforms, NULL)
读取图片文件信息有两种方法可以使用,一种是使用高层函数(high-level)读取,png_read_png()即为高层函数;另外一种是通过一系列的低层函数(low-level)接口进行读取操作,用起来相对麻烦。使用高层函数的前提是有足够的内存去存储整个图片的信息,因为高层函数内部进行了封装,所以使用起来很方便。
参数 png_transforms:可选用的值如下,如果只填参数PNG_TRANSFORM_EXPAND,那么转换结果为四通道,包含了RGB通道和ALPHA通道;参数改为PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA后,就可以把ALPHA通道屏蔽掉,转换之后即为RGB三通道。

/* Transform masks for the high-level interface */
#define PNG_TRANSFORM_IDENTITY       0x0000    /* read and write */
#define PNG_TRANSFORM_STRIP_16       0x0001    /* read only */
#define PNG_TRANSFORM_STRIP_ALPHA    0x0002    /* read only */
#define PNG_TRANSFORM_PACKING        0x0004    /* read and write */
#define PNG_TRANSFORM_PACKSWAP       0x0008    /* read and write */
#define PNG_TRANSFORM_EXPAND         0x0010    /* read only */
#define PNG_TRANSFORM_INVERT_MONO    0x0020    /* read and write */
#define PNG_TRANSFORM_SHIFT          0x0040    /* read and write */
#define PNG_TRANSFORM_BGR            0x0080    /* read and write */
#define PNG_TRANSFORM_SWAP_ALPHA     0x0100    /* read and write */
#define PNG_TRANSFORM_SWAP_ENDIAN    0x0200    /* read and write */
#define PNG_TRANSFORM_INVERT_ALPHA   0x0400    /* read and write */
#define PNG_TRANSFORM_STRIP_FILLER   0x0800    /* write only */
/* Added to libpng-1.2.34 */
#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER
#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */
/* Added to libpng-1.4.0 */
#define PNG_TRANSFORM_GRAY_TO_RGB    0x2000     /* read only */
/* Added to libpng-1.5.4 */
#define PNG_TRANSFORM_EXPAND_16      0x4000     /* read only */

3.color_type的值有哪些

color_type的可能等于的值是由宏定义来决定的,switch判断的时候,我只判断了PNG_COLOR_TYPE_RGB_ALPHA和PNG_COLOR_TYPE_RGB两种情况,其他情况没有考虑,PNG_COLOR_TYPE_GRAY_ALPHA代表解码后的颜色类型为四通道,PNG_COLOR_TYPE_RGB代表三通道,转移数据的时候要注意这一点。

#define PNG_COLOR_TYPE_GRAY			0
#define PNG_COLOR_TYPE_RGB 			2
#define PNG_COLOR_TYPE_PALETTE 		3
#define PNG_COLOR_TYPE_GRAY_ALPHA	4
#define PNG_COLOR_TYPE_RGB_ALPHA	6

这里写图片描述

在这里插入图片描述

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
使用libpngpng文件解码为bmp数组的步骤如下: 1. 安装libpng。在Ubuntu上可以使用以下命令安装: ``` sudo apt-get install libpng-dev ``` 2. 编写C代码。下面是一个示例代码,可以将输入的png文件解码为bmp数组,并输出为另一个文件。 ```c #include <stdio.h> #include <stdlib.h> #include <png.h> int main(int argc, char *argv[]) { if (argc != 3) { printf("Usage: %s input_file.png output_file.bmp\n", argv[0]); return 1; } char *input_file = argv[1]; char *output_file = argv[2]; // 打开png文件 FILE *fp = fopen(input_file, "rb"); if (!fp) { printf("Error opening file %s\n", input_file); return 1; } // 读取png文件头信息 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { printf("Error creating read struct\n"); fclose(fp); return 1; } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { printf("Error creating info struct\n"); fclose(fp); png_destroy_read_struct(&png_ptr, NULL, NULL); return 1; } if (setjmp(png_jmpbuf(png_ptr))) { printf("Error during setjmp\n"); fclose(fp); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return 1; } png_init_io(png_ptr, fp); png_read_info(png_ptr, info_ptr); // 获取png文件信息 int width = png_get_image_width(png_ptr, info_ptr); int height = png_get_image_height(png_ptr, info_ptr); png_byte color_type = png_get_color_type(png_ptr, info_ptr); png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr); // 将png文件转换为bmp数组 if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); if (bit_depth == 16) png_set_strip_16(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); png_read_update_info(png_ptr, info_ptr); png_bytep *row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height); for (int y = 0; y < height; y++) { row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr, info_ptr)); } png_read_image(png_ptr, row_pointers); // 将bmp数组输出到文件 FILE *fout = fopen(output_file, "wb"); if (!fout) { printf("Error opening output file %s\n", output_file); fclose(fp); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); for (int y = 0; y < height; y++) free(row_pointers[y]); free(row_pointers); return 1; } fwrite("BM", 1, 2, fout); int bmp_size = width * height * 3; int bmp_offset = 54; int file_size = bmp_offset + bmp_size; fwrite(&file_size, 4, 1, fout); fwrite("\0\0\0\0", 4, 1, fout); fwrite(&bmp_offset, 4, 1, fout); int header_size = 40; int planes = 1; int bits_per_pixel = 24; int compression = 0; int image_size = bmp_size; int x_pixels_per_meter = 2835; int y_pixels_per_meter = 2835; int total_colors = 0; int important_colors = 0; fwrite(&header_size, 4, 1, fout); fwrite(&width, 4, 1, fout); fwrite(&height, 4, 1, fout); fwrite(&planes, 2, 1, fout); fwrite(&bits_per_pixel, 2, 1, fout); fwrite(&compression, 4, 1, fout); fwrite(&image_size, 4, 1, fout); fwrite(&x_pixels_per_meter, 4, 1, fout); fwrite(&y_pixels_per_meter, 4, 1, fout); fwrite(&total_colors, 4, 1, fout); fwrite(&important_colors, 4, 1, fout); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { png_byte *ptr = &row_pointers[y][x * 3]; fwrite(ptr + 2, 1, 1, fout); fwrite(ptr + 1, 1, 1, fout); fwrite(ptr, 1, 1, fout); } } fclose(fout); fclose(fp); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); for (int y = 0; y < height; y++) free(row_pointers[y]); free(row_pointers); return 0; } ``` 在终端中执行以下命令编译代码: ``` gcc png2bmp.c -o png2bmp -lpng ``` 3. 执行程序。在终端中输入以下命令,将png文件转换为bmp数组: ``` ./png2bmp input_file.png output_file.bmp ``` 其中,'input_file.png'为需要转换的png文件名,'output_file.bmp'为输出的bmp文件名。执行成功后,会在当前目录下生成一个bmp文件。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值