1. 程式人生 > >圖片播放器(七):PNG圖片的顯示函式

圖片播放器(七):PNG圖片的顯示函式

1.思路分析

(1)png更像是jpg而不像是bmp
(2)png和jpg都是壓縮格式的圖片,都是二進位制檔案,不同之處是壓縮和解壓縮的演算法不同。
(3)通過libjpeg來編解碼jpg圖片,那麼同樣有一個libpng用來編解碼png圖片。
(4)工作思路和順序:找到並移植並部署libpng,然後查readme和其他文件示例程式碼等來使用libpng提供的API來對png圖片進行解碼,並將解碼出來的資料丟到framebuffer中去顯示。


2.libpng移植

(1)下載原始碼包:
(2)解壓、配置、修改Makefile、編譯、部署
./configure --host=arm-linux --enable-shared --enable-static --prefix=/open_source/PNG/png_1.6.7/libdecode
(3)配置出錯,報錯資訊:configure: error: zlib not installed
分析問題是因為libpng依賴於zlib庫,所以要先移植zlib庫才可以。
(4)轉而去移植zlib,移植後

再過來配置,還是報錯,原因是因為沒有匯出相關環境變數,所以libpng在配置的時候找不到剛才移植的zlib庫的庫檔案和標頭檔案。
(5)解決方案就是使用epport臨時性的匯出,
# export LDFLAGS="-L/open_source/ZLIB/zlib-1.2.8/libdecode/lib"
# export CFLAGS="-I/open_source/ZLIB/zlib-1.2.8/libdecode/include"
# export CPPFLAGS="-I/open_source/ZLIB/zlib-1.2.8/libdecode/include"
(6)匯出後再次配置./configure --host=arm-linux --enable-shared --enable-static --prefix=/open_source/PNG/png_1.6.7/libdecode
(7)make && make install

(8)得到Makefile

3.zlib移植

(1)下載:http://www.zlib.net/,並解壓
(2)配置:export CC=arm-linux-gcc      
        ./configure -shared --prefix=/open_source/ZLIB/zlib-1.2.8/libdecode
(3)make

(4)make install
(5)make install後/opt/libdecode目錄下的lib和include目錄下就有了zlib的靜態庫動態庫和標頭檔案了

(6)回到libpng移植


4.解碼顯示png圖片

1.參考原始碼包自帶的資料
(1)readme
(2)libpng-manual.txt
(3)example.c 和 pngtest.c

 

重要結構體:

/* png 頭資訊 */
struct png_head_data
{
	unsigned short w;  /* 圖片寬 */
	unsigned short h;  /* 圖片高 */
	unsigned int   bpp;  /* 圖片的BPP資訊 */
	unsigned int   channels;  /* 獲取通道數 */
	unsigned int   color_type;
};

全域性變數和巨集定義:

因為png可以檢測檔案頭8個位元組中的任意幾個位元組,所以可以自己設定。

#define SHOW_MAX_W  1920
#define SHOW_MAX_H  1080
#define PNG_BYTES_TO_CHECK 4   /* 要檢查檔案的前多少個位元組 */

static unsigned char png_picture[SHOW_MAX_W * SHOW_MAX_H * 3] = {0};  /* 當前播放器正在顯示的的圖片的有效資料陣列 (最大值)*/

 

png 顯示的核心函式:

/* png 顯示的核心函式 */
void fb_show_png(struct show_picture_set *set, unsigned int *pfb)
{
	struct png_head_data *st_pi = malloc(sizeof(struct png_head_data));
	const char *p_path = set->path;


	fb_file_to_png(p_path, st_pi);  /* 從bmp檔案中讀頭資訊 和有效資訊 */

	switch (set->rotate){
	case ROTATE_FRONT:
		fb_png_front_show(set, st_pi, pfb);  /* bmp 圖片正方向顯示 */
		break;
	case ROTATE_180:
		fb_png_180_show(set, st_pi, pfb);  /* bmp 圖片旋轉180方向顯示 */
		break;
	case ROTATE_MIRROR_180:
		fb_png_mirror_180_show(set, st_pi, pfb);
		break;
	case ROTATE_MIRROR:
		fb_png_mirror_show(set, st_pi, pfb);
		break;
	}

	free(st_pi);
}

判斷是否為 png 檔案:

/* 判斷是否為 png 檔案*/
char fb_is_png(const     char *path)
{
	unsigned char *buf = malloc(sizeof(PNG_BYTES_TO_CHECK));
	FILE *fp = NULL;

	if ((fp = fopen(path, "rb")) == NULL){
		DERROR("NO [%s] file\n", path);
		return 'O';
	}

	if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK){//讀取4位元組
		DERROR("read [%s] file error\n", path);
		fclose(fp);
		return 'R';
	}

	/*判斷前 PNG_BYTES_TO_CHECK 個位元組是否是 (89 50 4E 47 0D 0A 1A 0A 16進位制) */
	if(png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK)){ //判斷前4位元組
		DERROR("It's not a BMP file\n");
		fclose(fp);
		return 'N';
	}else{
		fclose(fp);
		return 'Y';
	}
}

 

從png檔案中讀頭資訊:

/* 從png檔案中讀頭資訊 */
int fb_file_to_png(const        char *path, void *st_pi)
{
	FILE *fp = NULL;
	png_structp png_ptr;
	png_infop info_ptr;
	struct png_head_data *head_data = st_pi;
	unsigned char **row_pointers = NULL;
	
	if ((fp = fopen(path, "rb")) == NULL){
		DERROR("open %s file fail\n", path);
		return -1;
	}
		   
	/* Allocate/initialize the memory for image information.  REQUIRED. */
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);	
	if (png_ptr == NULL){
		fclose(fp);
		return -1;
	}
		
	
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL){
		fclose(fp);
		png_destroy_read_struct(&png_ptr, NULL, NULL);
		return -1;
	}
		
	if (setjmp(png_jmpbuf(png_ptr))){
		/* Free all of the memory associated with the png_ptr and info_ptr */
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		fclose(fp);
		/* If we get here, we had a problem reading the file */
		return -1;
	}

	/* 第3步: 將要解碼的png圖片的檔案指標和png解碼器繫結起來 */
	png_init_io(png_ptr, fp);

	/* 第4步: 讀取png圖片資訊 */
	png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA, 0);


	/* 第5步: 相關圖片資訊打印出來看一看 */
	head_data->w = info_ptr->width;
	head_data->h = info_ptr->height;
	head_data->bpp = info_ptr->pixel_depth;
	head_data->channels = info_ptr->channels; /*獲取通道數*/
	head_data->color_type = info_ptr->color_type;

	DEBUG("width = %u, height = %u, bpp = %u\n", info_ptr->width, info_ptr->height, info_ptr->pixel_depth);

	/* 第6步: 讀取真正的影象資訊 */
	row_pointers = png_get_rows(png_ptr,info_ptr);

	fb_png_read_file(path, st_pi, row_pointers); /* 從檔案中讀有效資料到 now_picture 中 */


	/* 第7步: 收尾處理 */
	png_destroy_read_struct(&png_ptr, &info_ptr, 0);
	// close file
	fclose(fp);

	return 0;

}

從檔案中讀有效資料到 png_picture 中:

/*  從檔案中讀有效資料到 png_picture 中*/
int fb_png_read_file(const         char *path, struct png_head_data *head_data, unsigned char **row_pointers)
{
	int temp, pos = 0;
	int i, j;

	/* 第7步: 影象資料移動到我們自己的buf中 */
	/* 只處理RGB24位真彩色圖片 和 RGB24+ 透明度圖片 */
	if(head_data->channels == 4 || head_data->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
	{/*如果是RGB+alpha通道,或者RGB+其它位元組*/ 
		temp = (4 * head_data->w);   /* 每行有4 * out->width個位元組 */
		for(i = 0; i < head_data->h; i++)
		{
			for(j = 0; j < temp; j += 4)
			{/* 一個位元組一個位元組的賦值 */
				png_picture[pos++] = row_pointers[i][j]; // red
				png_picture[pos++] = row_pointers[i][j+1]; // green
				png_picture[pos++] = row_pointers[i][j+2];  // blue
				png_picture[pos++] = row_pointers[i][j+3]; // alpha
			}
		}
	}
	else if(head_data->channels == 3 || head_data->color_type == PNG_COLOR_TYPE_RGB)
	{/* 如果是RGB通道 */
		temp = (3 * head_data->w);/* 每行有3 * out->width個位元組 */
		for(i = 0; i < head_data->h; i++)
		{
			for(j = 0; j < temp; j += 3)
			{/* 一個位元組一個位元組的賦值 */
				png_picture[pos++] = row_pointers[i][j]; // red
				png_picture[pos++] = row_pointers[i][j+1]; // green
				png_picture[pos++] = row_pointers[i][j+2];  // blue
			}
		}
	}
	else{
		DERROR("This [%s] file is not supported", path);
		return -1;
	}

	return 0;
}

圖片顯示函式:


/* png 圖片正方向顯示 */
void fb_png_front_show(struct show_picture_set *set, struct png_head_data *head_data, unsigned int *pfb)
{
	int x, y;
	unsigned int cont1 = 0, cont2 = 0;
	unsigned int *p_data = pfb;

	for(y = 0; y < head_data->h; y++)		
	{
		if((y + set->h) >= HEIGHT)
			break;
		for(x = 0; x < head_data->w; x++)
		{
			if((x + set->w) >= WIDTH){
				cont1 += 3;
				continue;
			}
			
			cont2 = (y + set->h) * WIDTH + x + set->w;

			*(p_data + cont2) = ((png_picture[cont1 + 0] << 16) | (png_picture[cont1 + 1] << 8) | (png_picture[cont1 + 2] << 0));
			cont1 += 3;
		}
	}
}


/* png 圖片旋轉180方向顯示 (不是當前圖片旋轉 180,是絕對而不是相對)*/
void fb_png_180_show(struct show_picture_set *set, struct png_head_data *head_data, unsigned int *pfb)
{
	int x, y;
	unsigned int cont2 = 0, cont1 = ((head_data->h * head_data->w * 3) - 3);
	unsigned int *p_data = pfb;

	for(y = 0; y < head_data->h; y++)		
	{
		if((y + set->h) >= HEIGHT)
			break;
		for(x = 0; x < head_data->w; x++)
		{
			if((x + set->w) >= WIDTH){
				cont1 -= 3;
				continue;
			}
			
			cont2 = (y + set->h) * WIDTH + x + set->w;

			*(p_data + cont2) = ((png_picture[cont1 + 0] << 16) | (png_picture[cont1 + 1] << 8) | (png_picture[cont1 + 2] << 0));
			cont1 -= 3;
		}
	}
}


/* png 圖片 ( 映象翻轉 + 旋轉180方向 )顯示 (不是當前圖片旋轉 180,是絕對而不是相對)*/
void fb_png_mirror_180_show(struct show_picture_set *set, struct png_head_data *head_data, unsigned int *pfb)
{
	int x, y;
	unsigned int cont2 = 0, cont1 = ((head_data->h * head_data->w * 3) - 3);
	unsigned int *p_data = pfb;

	for(y = 0; y < head_data->h; y++)		
	{
		if((y + set->h) >= HEIGHT)
			break;
		for(x = head_data->w -1 ; x >= 0; x--)
		{
			if((x + set->w) >= WIDTH){
				cont1 -= 3;
				continue;
			}
			
			cont2 = (y + set->h) * WIDTH + x + set->w;

			*(p_data + cont2) = ((png_picture[cont1 + 0] << 16) | (png_picture[cont1 + 1] << 8) | (png_picture[cont1 + 2] << 0));
			cont1 -= 3;
		}
	}
}

/* png 圖片 ( 映象翻轉 )顯示 (不是當前圖片翻轉,是絕對而不是相對)*/
void fb_png_mirror_show(struct show_picture_set *set, struct png_head_data *head_data, unsigned int *pfb)
{
	int x, y;
	unsigned int cont2 = 0, cont1 = 0;
	unsigned int *p_data = pfb;

	for(y = 0; y < head_data->h; y++)		
	{
		if((y + set->h) >= HEIGHT)
			break;
		for(x = head_data->w -1 ; x >= 0; x--)
		{
			if((x + set->w) >= WIDTH){
				cont1 += 3;
				continue;
			}
			
			cont2 = (y + set->h) * WIDTH + x + set->w;

			*(p_data + cont2) = ((png_picture[cont1 + 0] << 16) | (png_picture[cont1 + 1] << 8) | (png_picture[cont1 + 2] << 0));
			cont1 += 3;
		}
	}
}