1. 前言
BMP是一種與硬件設備無關的圖像文件格式,是Windows環(huán)境中交換與圖有關的數(shù)據(jù)的一種標準,在Windows環(huán)境中運行的圖形圖像軟件都支持BMP圖像格式。BMP格式的圖片存放的就是原始的RGB數(shù)據(jù),一般沒有做壓縮,也就是圖片的畫質是最原始的,也導致BMP圖片占用的內存非常大。現(xiàn)在常用的jpg、jpeg格式都是壓縮格式,保存的時候通過算法編碼壓縮,顯示的時候再解壓成RGB數(shù)據(jù)渲染顯示。
BMP格式在嵌入式設備里用的還是較多的,BMP雖然占用內存大,優(yōu)點是顯示速度快,因為不需要解碼,在性能一般,不是很強的設備上使用BMP顯示效率較高。
為了解BMP格式,這篇文章就采用Linux開發(fā)板作為實驗平臺,在LCD屏上讀取BMP圖片,完成繪制,不需要借助任何第三方庫,全部由純C語言代碼一行一行敲出來,深入理解Linux下幀緩沖編程框架、BMP圖片的存儲結構原理。
一般BMP圖片由以下4個部分組成: 1:文件頭 2:圖像參數(shù) 3:調色板 4:位圖數(shù)據(jù)
現(xiàn)在一般采用的圖片都是RGB888,24位真彩色,就沒有調色板,只有3個部分組成。
其中文件頭存放圖片的屬性,位圖數(shù)據(jù)偏移量。圖像參數(shù)存放圖片的寬高、像素位數(shù)等信息。位圖數(shù)據(jù)就是存儲的原始RGB數(shù)據(jù),可以直接在LCD屏上顯示。
下面列出BMP圖片的結構:
位圖數(shù)據(jù)存儲規(guī)則:
(1)每行的字節(jié)數(shù)必須是4的倍數(shù),如果不是,則需要用0補齊。 (2)BMP位圖數(shù)據(jù)的存放是從下到上,從左到右的。先讀最后一行,讀完后在讀倒數(shù)第二行。
按照上面的介紹,就可以定義一個BMP解碼專用的結構體,對應文件里每個字節(jié)數(shù)據(jù),結構體成員變量必須按照上面截圖里的說明定義。整個結構體還需要進行強制1個字節(jié)對齊,不然每個編譯器對結構體的空間開辟規(guī)則有差異,會導致數(shù)據(jù)錯位。
#pragma pack(1) //強制1個字節(jié)對齊
//BMP的文件頭
struct _BMP_HEAD
{
char type[2]; //圖片的類型 "BM"
unsigned int size; //文件大小
unsigned short r1; //保留1
unsigned short r2; //保留2
unsigned int seek; //數(shù)據(jù)偏移字節(jié)(真實像素點數(shù)據(jù))
};
//BMP的參數(shù)信息
struct _BMP_INFO
{
unsigned int size; //當前結構體大小
unsigned int w; //寬度
unsigned int h; //高度
unsigned short flag; //固定為1
unsigned short bit; //像素點的位數(shù)
unsigned int r1; //壓縮方式 0
unsigned int r2; //水平分辨率
unsigned int r3; //垂直分辨率
unsigned int r4; //垂直分辨率
unsigned int r5; //引用色彩
unsigned int r6; //關鍵色彩
};
復制代碼
2. 實現(xiàn)代碼
要在LCD屏上完成BMP圖片的顯示,編寫代碼需要分幾步完成,先編寫LCD屏的基本顯示代碼,封裝畫點函數(shù),LCD屏測試沒有問題之后,再編寫B(tài)MP解碼代碼,完成圖片的渲染顯示。
2.1 封裝LCD屏畫點函數(shù)
#include
#include
#include
#include
unsigned char *fb_mem;
struct fb_var_screeninfo var;//可變參數(shù)
struct fb_fix_screeninfo fix;//固定參數(shù)
?
?
/*畫點*/
void show_pixel(int x,int y,int color)
{
unsigned long *show32 = NULL;
/* 定位到LCD屏上的位置*/
show32=(unsigned long*)(fb_mem+y*var.xres*var.bits_per_pixel/8 + x*var.bits_per_pixel/8);
*show32 =color; /*向指向的LCD地址賦數(shù)據(jù)*/
}
?
?
int main(int argc,char**argv)
{
?
int fb;
fb=open("/dev/fb0",2);
if(fb<0)
? {
? ? printf("fb0打開失敗!\n");
? ? return -1;
? }
?
? /*1. 獲取可變參數(shù)*/
? ioctl(fb,FBIOGET_VSCREENINFO,&var);
? printf("x=%d\n",var.xres);
? printf("y=%d\n",var.yres);
? printf("bit=%d\n",var.bits_per_pixel);
?
? /*2. 獲取固定參數(shù)*/
? ioctl(fb,FBIOGET_FSCREENINFO,&fix);
? printf("line_byte=%d\n",fix.line_length);
? printf("smem_len=%d\n",fix.smem_len);
?
? /*3. 映射LCD地址*/
? fb_mem=mmap(NULL,fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);
?
? int i,j;
? for(i=0;i;i++)>
2.2 顯示BMP圖片
在工程目錄下準備幾張測試的BMP圖片,程序運行時,在命令行上傳入要顯示的圖片文件地址接口。
#include
#include
#include
#include
#include
#include
#include
#include
#pragma pack(push) /* 將當前pack設置壓棧保存 */
#pragma pack(1) /* 必須在結構體定義之前使用,這是為了讓結構體中各成員按1字節(jié)對齊 */
/* 1、需要文件信息頭:14個字節(jié) */
typedef struct tagBITMAPFILEHEADER { /* bmfh */
unsigned short bfType; //保存圖片類似。 'BM'
unsigned long bfSize; //圖片的大小
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned long bfOffBits; //RGB數(shù)據(jù)偏移地址
}BITMAPFILEHEADER;
/* 位圖信息頭 */
typedef struct tagBITMAPINFOHEADER { /* bmih */
unsigned long biSize; //結構體大小
unsigned long biWidth; //寬度
unsigned long biHeight; //高度
unsigned short biPlanes;
unsigned short biBitCount; //顏色位數(shù)
unsigned long biCompression;
unsigned long biSizeImage;
unsigned long biXPelsPerMeter;
unsigned long biYPelsPerMeter;
unsigned long biClrUsed;
unsigned long biClrImportant;
} BITMAPINFOHEADER;
#pragma pack(pop) /* 恢復先前的pack設置 */
unsigned char *fbmem=NULL;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
static int iFileSize = 0;
void show_pixel(int x,int y,int color)
{
unsigned char *show8=NULL;
unsigned short *show16=NULL;
unsigned long *show32 = NULL;
int red;
int green;
int blue;
/* 定位到LCD屏上的位置 */
show8 = fbmem + y*var.xres*var.bits_per_pixel/8 + x*var.bits_per_pixel/8;
show16 = (unsigned short *)show8;
show32 = (unsigned long *)show8;
switch(var.bits_per_pixel)
{
case 8:
{
*show8 = color;
break;
}
case 16:
{
/* RGB:565 */
red = (color >> 16)&0xff;
green = (color >> 8)&0xff;
blue = color&0xff;
color = ((red>>3)<<11) | ((green>>2)<<6) |(blue>>3);
*show16 = color;
break;
}
case 32:
{
*show32 = color;
break;
}
default:break;
}
}
/*映射圖片地址*/
static unsigned char *getbmpadd(char *name)
{
unsigned char *bmpmem = NULL;
FILE* filp;
int fd;
struct stat t_stat;
/* 以r+可讀可寫方式打開name */
filp = fopen(name,"r+");
if(filp == NULL)
{
printf("can't open %s\n",name);
return NULL;
}
/* 把文件指針轉化為文件描述符 */
fd = fileno(filp);
/* 獲取文件大小 */
fstat(fd, &t_stat);
iFileSize = t_stat.st_size;
/* 映射 */
bmpmem = mmap(NULL,iFileSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd, 0);
if(bmpmem == (unsigned char *)-1)
{
printf("can't mmap..\n");
return NULL;
}
return bmpmem;
}
void bmp_destroy(unsigned char *bmpmem)
{
munmap(bmpmem,iFileSize);
}
void Convert_One_Line(unsigned char *src,unsigned char *dst,int iWidth)
{
unsigned char *show8=NULL;
unsigned short *show16=NULL;
unsigned long *show32 = NULL;
unsigned char *buf = src;
int red;
int green;
int blue;
int i;
/* 定位到LCD屏上的位置 */
show8 = dst;
show16 = (unsigned short *)show8;
show32 = (unsigned long *)show8;
for(i=0;i>3)<<11) | ((green>>2)<<6) |(blue>>3);
show16++;
break;
}
case 32:
{
*show32 = (red<<16)|(green<<8)|blue;
show32++; // 4個字節(jié)
break;
}
default:break;
}
}
}
/* 獲取顏色陣列數(shù)據(jù) */
int getbmpandshow(unsigned char *bmpmem)
{
/* 定義文件信息頭 */
BITMAPFILEHEADER *bithead;
/* 定義文件參數(shù)信息 */
BITMAPINFOHEADER *bitinfo;
unsigned char *src=NULL;
unsigned char *dst=NULL;
int iWidth;
int iHeight;
int iBpp;
int iLineWidth;
int iRealLineWidth;
int iFbLineWidth;
int i=0;
/* 獲取文件信息頭起始地址 */
bithead =(BITMAPFILEHEADER *)bmpmem;
/* 獲取位圖信息頭起始地址 */
bitinfo = (BITMAPINFOHEADER *)(bmpmem + sizeof(BITMAPFILEHEADER));
iWidth = bitinfo->biWidth;
iHeight = bitinfo->biHeight;
iBpp = bitinfo->biBitCount;
printf("iWidth = %d\n",iWidth);
printf("iHeight = %d\n",iHeight);
printf("iBpp = %d\n",iBpp);
/* 找到顏色陣列,RGB數(shù)據(jù)的起始地址 */
src = bmpmem + bithead->bfOffBits;
/* 得到圖片一行字節(jié)數(shù) */
iLineWidth = iWidth*iBpp/8;
/* 向4取整,保證一行必須是4的倍數(shù) */
iRealLineWidth = (iLineWidth+3)&~0x3; // iLineWidth % 4 =0
/* src指向圖片RGB數(shù)據(jù)最后一行的首地址*/
src += iRealLineWidth*(iHeight-1);
/* LCD屏一行的總字節(jié)數(shù) */
iFbLineWidth = var.xres * var.bits_per_pixel/8;
/*dst指向LCD的首地址*/
dst = fbmem;
for(i=0;i\n",argv[0]);
return -1;
}
lcd_init(argv[1]);
/* 4、清屏 */
memset(fbmem,0x0,fix.smem_len);
/* 4.1、顯示圖片-映射圖片地址 */
bmpmem = getbmpadd(argv[2]);
if(NULL == bmpmem)
{
printf("can't get bmp address!!\n");
return -1;
}
getbmpandshow(bmpmem); // 顯示圖片
bmp_destroy(bmpmem); //釋放映射的空間
return 0;
};i++)>;i++)>
審核編輯:湯梓紅
-
lcd
+關注
關注
34文章
4411瀏覽量
167100 -
Linux
+關注
關注
87文章
11229瀏覽量
208927 -
BMP
+關注
關注
0文章
48瀏覽量
17044
發(fā)布評論請先 登錄
相關推薦
評論