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