平台: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);
}
