圖片播放器(七):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,移植後
(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;
}
}
}