您的位置:首页 > 运维架构 > Linux

Linux framebuffer显示bmp图片

2017-09-22 12:45 477 查看
framebuffer简介 

    帧缓冲(framebuffer)是Linux为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。framebuffer是LCD对应的一中HAL(硬件抽象层),提供抽象的,统一的接口操作,用户不必关心硬件层是怎么实施的。这些都是由Framebuffer设备驱动来完成的。 

    帧缓冲设备对应的设备文件为/dev/fb*,如果系统有多个显示卡,Linux下还可支持多个帧缓冲设备,最多可达32个,分别为/dev/fb0到 /dev/fb31,而/dev/fb则为当前缺省的帧缓冲设备,通常指向/dev/fb0,在嵌入式系统中支持一个显示设备就够了。帧缓冲设备为标准字符设备,主设备号为29,次设备号则从0到31。分别对应/dev/fb0-/dev/fb31。

通过/dev/fb,应用程序的操作主要有这几种: 

1. 读/写(read/write)/dev/fb:相当于读/写屏幕缓冲区。 

2. 映射(map)操作:由于Linux工作在保护模式,每个应用程序都有自己的虚拟地址空间,在应用程序中是不能直接访问物理缓冲区地址的。而帧缓冲设备可以通过mmap()映射操作将屏幕缓冲区的物理地址映射到用户空间的一段虚拟地址上,然后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图了。 

3. I/O控制:对于帧缓冲设备,对设备文件的ioctl操作可读取/设置显示设备及屏幕的参数,如分辨率,屏幕大小等相关参数。ioctl的操作是由底层的驱动程序来完成的。

在应用程序中,操作/dev/fb的一般步骤如下: 

1. 打开/dev/fb设备文件。 

2. 用ioctl操作取得当前显示屏幕的参数,根据屏幕参数可计算屏幕缓冲区的大小。 

3. 将屏幕缓冲区映射到用户空间。 

4. 映射后即可直接读写屏幕缓冲区,进行绘图和图片显示。

framebuffer相关数据结构介绍 

1. fb_info结构体:帧缓冲设备中最重要的数据结构体,包括了帧缓冲设备属性和操作的完整性属性。

2. fb_ops结构体:fb_info结构体的成员变量,fb_ops为指向底层操作的函数的指针。

3.fb_var_screen和fb_fix_screen结构体:fb_var_screen记录用户可以修改的显示控制器参数,fb_fix_screen记录用户不能修改的显示控制器参数。

以下代码使用framebuffer显示一张图片:

[cpp]
view plain
copy

print?

#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 short blue;  
    unsigned short green;  
    unsigned short red;  
    unsigned short 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, vinfo.green, vinfo.blue );  
  
    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);  
    }  
  
    printf("sizeof file header=%d\n", sizeof(BITMAPFILEHEADER));  
  
    printf("into show_bmp function\n");  
  
    //显示图像  
    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;  
    char tmp[1024*10];  
  
    fp = fopen( "./niu.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;  
        unsigned short int tmp;  
        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)=pix.reserved;  
  
        line_x++;  
        if (line_x == InfoHead.ciWidth )  
        {  
            line_x = 0;  
            line_y++;  
            if(line_y == InfoHead.ciHeight)  
                break;  
        }  
    }  
    fclose( fp );  
    return( 0 );  
}  

#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 short blue;
unsigned short green;
unsigned short red;
unsigned short 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, vinfo.green, vinfo.blue );

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);
}

printf("sizeof file header=%d\n", sizeof(BITMAPFILEHEADER));

printf("into show_bmp function\n");

//显示图像
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;
char tmp[1024*10];

fp = fopen( "./niu.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;
unsigned short int tmp;
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)=pix.reserved;

line_x++;
if (line_x == InfoHead.ciWidth )
{
line_x = 0;
line_y++;
if(line_y == InfoHead.ciHeight)
break;
}
}
fclose( fp );
return( 0 );
}


注意:上面的程序只在framebuffer上显示图片,却没有删除刷新屏幕,可以使用下面的命令恢复屏幕

保存屏幕信息:dd if=/dev/fb0 of=fbfile  或: cp /dev/fb0 fbfile

恢复屏幕信息:dd if=fbfile of=/dev/fb0  或: cat fbfile > /dev/fb0
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: