平台:tiny4412SDK 1161 + HD700
kernel:linux 3.5
bmp:24位深
前言:
前邊設置好了HD700的驅動,能夠正常顯示像素,現在學習bmp圖片格式並顯示一張bmp圖片。
1、bmp圖片格式:
BMP是英文Bitmap(位圖)的簡寫,它是Windows操作系統中的標准圖像文件格式,能夠被多種Windows應用程序所支持。隨着Windows操作系統的流行與豐富的Windows應用程序的開發,BMP位圖格式理所當然地被廣泛應用。
常用的圖片格式有:JPEG、GIF、PSD、PNG、SWF、SVG……
2、bmp圖片的優缺點:
優點:包含的圖像信息較豐富,幾乎不進行壓縮
缺點:占用磁盤空間過大
3、bmp圖片的應用范圍:
網絡上應用較少,目前BMP在單機上比較流行
4、bmp圖片數據結構:
bmp圖片文件數據流中前54個字節是bmp格式包含的數據頭信息,其余為顏色數據。
數據頭信息如下數據結構:
//14byte文件頭 typedef struct { char cfType[2]; //文件類型,"BM"(0x4D42) long cfSize; //文件大小(字節) long cfReserved; //保留,值為0 long cfoffBits; //數據區相對於文件頭的偏移量(字節) }__attribute__((packed)) BITMAPFILEHEADER; //__attribute__((packed))的作用是告訴編譯器取消結構在編譯過程中的優化對齊 //40byte信息頭 typedef struct { char ciSize[4]; //BITMAPFILEHEADER所占的字節數 long ciWidth; //寬度 long ciHeight; //高度 char ciPlanes[2]; //目標設備的位平面數,值為1 int ciBitCount; //每個像素的位數 char ciCompress[4]; //壓縮說明 char ciSizeImage[4]; //用字節表示的圖像大小,該數據必須是4的倍數 char ciXPelsPerMeter[4];//目標設備的水平像素數/米 char ciYPelsPerMeter[4];//目標設備的垂直像素數/米 char ciClrUsed[4]; //位圖使用調色板的顏色數 char ciClrImportant[4]; //指定重要的顏色數,當該域的值等於顏色數時(或者等於0時),表示所有顏色都一樣重要 }__attribute__((packed)) BITMAPINFOHEADER;
編程過程當中需要多加關注的成員是文件類型、數據區偏移量、寬度、高度、像素位深。
5、編程思路:
1)、打開linux系統下顯示設備。
2)、獲取設備的大小信息,得到屏幕寬高,並計算屏幕大小。
3)、根據屏幕大小映射顯存區域
4)、顯示圖片
a、打開圖片文件
b、獲取數據頭
c、判斷文件類型
d、循環讀取顏色數據
e、把數據往顯存當中寫入
f、關閉圖片文件
5)、解除映射
6)、關閉顯示設備
6、注意事項:
1、從bmp圖片文件當中讀取像素信息時,需要根據位深度讀取不同大小的數據信息,例如例子當中使用的是24位深度,每次讀取一個像素數據是3個字節。
2、bmp圖片的像素數據存儲的排版是從下到上、從左到右的。
7、待優化:
1、圖片顯示需要根據位深度不同而讀取顏色數據時做適應性變化。
2、添加顯示初始位置接口
3、由於映射的顯存是一段連續的地址,需要對圖片行、列顯示做限定處理,確保超出屏幕大小不會顯示混亂。
8、代碼實現:
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <linux/fb.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <arpa/inet.h> //14byte文件頭 typedef struct { char cfType[2];//文件類型,"BM"(0x4D42) long cfSize;//文件大小(字節) long cfReserved;//保留,值為0 long cfoffBits;//數據區相對於文件頭的偏移量(字節) }__attribute__((packed)) BITMAPFILEHEADER; //__attribute__((packed))的作用是告訴編譯器取消結構在編譯過程中的優化對齊 //40byte信息頭 typedef struct { char ciSize[4];//BITMAPFILEHEADER所占的字節數 long ciWidth;//寬度 long ciHeight;//高度 char ciPlanes[2];//目標設備的位平面數,值為1 int ciBitCount;//每個像素的位數 char ciCompress[4];//壓縮說明 char ciSizeImage[4];//用字節表示的圖像大小,該數據必須是4的倍數 char ciXPelsPerMeter[4];//目標設備的水平像素數/米 char ciYPelsPerMeter[4];//目標設備的垂直像素數/米 char ciClrUsed[4]; //位圖使用調色板的顏色數 char ciClrImportant[4]; //指定重要的顏色數,當該域的值等於顏色數時(或者等於0時),表示所有顏色都一樣重要 }__attribute__((packed)) BITMAPINFOHEADER; typedef struct { unsigned char blue; unsigned char green; unsigned char red; // unsigned char reserved; }__attribute__((packed)) PIXEL;//顏色模式RGB BITMAPFILEHEADER FileHead; BITMAPINFOHEADER InfoHead; static char *fbp = 0; static int xres = 0; static int yres = 0; static int bits_per_pixel = 0; int show_bmp(); int main ( int argc, char *argv[] ) { int fbfd = 0; struct fb_var_screeninfo vinfo; struct fb_fix_screeninfo finfo; long int screensize = 0; struct fb_bitfield red; struct fb_bitfield green; struct fb_bitfield blue; //打開顯示設備 fbfd = open("/dev/fb0", O_RDWR); if (!fbfd) { printf("Error: cannot open framebuffer device.\n"); exit(1); } if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) { printf("Error:reading fixed information.\n"); exit(2); } if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) { printf("Error: reading variable information.\n"); exit(3); } printf("R:%d,G:%d,B:%d \n", vinfo.red.offset, vinfo.green.offset, vinfo.blue.offset ); printf("R:%d,G:%d,B:%d \n", vinfo.red.length, vinfo.green.length, vinfo.blue.length ); printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel ); xres = vinfo.xres; yres = vinfo.yres; bits_per_pixel = vinfo.bits_per_pixel; //計算屏幕的總大小(字節) screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8; printf("screensize=%d byte\n",screensize); //對象映射 fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0); if ((int)fbp == -1) { printf("Error: failed to map framebuffer device to memory.\n"); exit(4); } //顯示圖像 show_bmp(); //刪除對象映射 munmap(fbp, screensize); close(fbfd); return 0; } int show_bmp() { FILE *fp; int rc; int line_x, line_y; long int location = 0, BytesPerLine = 0; fp = fopen( "timg.bmp", "rb" ); if (fp == NULL) { return(-1); } rc = fread( &FileHead, sizeof(BITMAPFILEHEADER),1, fp ); if ( rc != 1) { printf("read header error!\n"); fclose(fp); return(-2); } //檢測是否是bmp圖像 if (memcmp(FileHead.cfType, "BM", 2) != 0) { printf("it's not a BMP file\n"); fclose(fp); return(-3); } rc = fread((char *)&InfoHead, sizeof(BITMAPINFOHEADER),1, fp); if (rc != 1) { printf("read infoheader error!\n"); fclose(fp); return(-4); } //跳轉的數據區 fseek(fp, FileHead.cfoffBits, SEEK_SET); //每行字節數 BytesPerLine = (InfoHead.ciWidth * InfoHead.ciBitCount + 31) / 32 * 4; line_x = line_y = 0; //向framebuffer中寫BMP圖片 while(!feof(fp)) { PIXEL pix; rc = fread( (char *)&pix, 1, sizeof(PIXEL), fp); if (rc != sizeof(PIXEL)) break; location = line_x * bits_per_pixel / 8 + (InfoHead.ciHeight - line_y - 1) * xres * bits_per_pixel / 8; //顯示每一個像素 *(fbp + location + 0)=pix.blue; *(fbp + location + 1)=pix.green; *(fbp + location + 2)=pix.red; *(fbp + location + 3)=0; line_x++; if (line_x == InfoHead.ciWidth) { line_x = 0; line_y++; if(line_y == InfoHead.ciHeight) break; } } fclose(fp); return(0); }