AllenYao 发表于 2006-11-2 23:11:57

初学Framebuffer

昨天开始学习Framebuffer,今天改了改别人的代码,编写了画点,画横竖线函数,感觉有了一点点入门!把代码贴在这里,一是作为自己的学习记录,二是希望能帮助有需要的朋友。^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>

char *fbp = 0;
int xres = 0;
int yres = 0;
int bits_per_pixel = 0;

void draw_point(int x, int y, int color);
void draw_hline(int x, int y, int width, int color);
void draw_vline(int x, int y, int heigth, int color);

int main()
{
    int fbfd = 0;
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    long int screensize = 0;

    // Open the file for reading and writing
    fbfd = open("/dev/fb0", O_RDWR);
    if (!fbfd) {
      printf("Error: cannot open framebuffer device.\n");
      exit(1);
    }
    printf("The framebuffer device was opened successfully.\n");

    // Get fixed screen information
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
      printf("Error reading fixed information.\n");
      exit(2);
    }

    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
      printf("Error reading variable information.\n");
      exit(3);
    }

    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;

    // Figure out the size of the screen in bytes
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

    // Map the device to memory
    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("The framebuffer device was mapped to memory successfully.\n");

        draw_point(100, 100, 2);
        draw_hline(150, 150, 150, 2);
        draw_vline(160, 160, 150, 2);

    munmap(fbp, screensize);
    close(fbfd);
    return 0;
}

/*画点函数*/
void draw_point(int x, int y, int color)
{
        long int location = 0;
       
        location = x + y * xres * bits_per_pixel / 8;
       
        *(fbp + location) = color;
       
        return;
}

/*画横线函数 draw horizontal line*/
void draw_hline(int x, int y, int width, int color)
{
        int i;
       
        for (i=0; i<width; i++)
        {
                draw_point(x+i, y, color);
        }
        return;
}

/*画竖线函数 draw vertical line*/
void draw_vline(int x, int y, int heigth, int color)
{
        int i;
       
        for (i=0; i<heigth; i++)
        {
                draw_point(x, y+i, color);
        }
        return;
}

我发现这个程序在运行时必须在控制台1运行,而且最好是root用户(其它用户在读取内容时可能会有权限问题)。

asmcos 发表于 2006-11-3 09:47:14

鼓励!

AllenYao 发表于 2006-11-3 10:22:01

多谢版主鼓励^0^

我对版主的敬仰之情,犹如滔滔江水……其实,自己对EGUI很感兴趣,还下载了EGUI的代码,但是,功力太低,好多看不懂。我在图形这方面基础太差,要先入入门,再来研究EGUI。

asmcos 发表于 2006-11-3 12:10:07

呵呵,看来是代码没有写好。
如果有问题,你就在这里提问。我会回答的。

AllenYao 发表于 2006-11-3 17:06:00

多谢版主!能画点,画线之后,下一步我想试着显示BMP位图。不知道这个学习方向是不是对的?我现在在看BMP位图格式,主要是在网上搜索了一些说明和部分代码。目前还没有完全搞懂。主要是RGB和调色板,特别是调色板,不知道怎么理解它。今天试了试,编程读取BMP文件头上的信息,多少了解了一些,接下来要显示那么像素就有点摸不着边了,因为调色板还没有理解。我会继续查找资料的,直到理解为止!

asmcos 发表于 2006-11-6 09:24:28

egui 有24位色的BMP显示的样例。
24位色不需要调色板。

AllenYao 发表于 2006-11-6 16:38:16

我是在VMware Workstation 5.5.2中安装的Arch Linux。而虚拟机打不开真彩色。vga=788可以,但是vga=789,启动时就报错了。

我也奇怪,为什么用虚拟机安装FC或Hiweed,图形看上起很好,不像只有16位色……

asmcos 发表于 2006-11-7 09:46:02

分辨率也有关系。

AllenYao 发表于 2006-11-7 17:07:18

16位色属于高彩吧?24位、32位色属于真彩吧?高彩与真彩显示效果差异大吗?也需要调色板吗?

请问版主,你是直接使用Linux做开发的吗?有没有使用虚拟机来开发啊?我看EGUI项目的文档,在FC4中也是使用的“vga=0x316 ”这是1024X768的15位色(32K),好像也不是真彩吧?

AllenYao 发表于 2006-11-8 22:11:43

弄了一晚上,终于搞出来了下面这个小程序,可以显示BMP图像了。

/*
   showbmp.c
   allenyao 2006/11/3
*/
#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>

typedef struct
{
char cfType[2];         /* 文件类型, 必须为 "BM" (0x4D42) */
char cfSize[4];         /* 文件的大小(字节) */
char cfReserved[4];   /* 保留, 必须为 0 */
char cfoffBits[4];      /* 位图阵列相对于文件头的偏移量(字节) */
} BITMAPFILEHEADER;       /* 文件头结构 */

typedef struct
{
char ciSize[4];         /* size of BITMAPINFOHEADER */
char ciWidth[4];      /* 位图宽度(像素) */
char ciHeight[4];       /* 位图高度(像素) */
char ciPlanes[2];       /* 目标设备的位平面数, 必须置为1 */
char ciBitCount[2];   /* 每个像素的位数, 1,4,8或24 */
char ciCompress[4];   /* 位图阵列的压缩方法,0=不压缩 */
char ciSizeImage[4];    /* 图像大小(字节) */
char ciXPelsPerMeter[4];/* 目标设备水平每米像素个数 */
char ciYPelsPerMeter[4];/* 目标设备垂直每米像素个数 */
char ciClrUsed[4];      /* 位图实际使用的颜色表的颜色数 */
char ciClrImportant[4]; /* 重要颜色索引的个数 */
} BITMAPINFOHEADER;       /* 位图信息头结构 */

typedef struct
{
char rgbBlue;
char rgbGreen;
char rgbRed;
char rgbReserved;
} RGBQUAD;

BITMAPFILEHEADER FileHead;
BITMAPINFOHEADER InfoHead;
RGBQUAD rgbquad;

char *fbp = 0;
int xres = 0;
int yres = 0;
int bits_per_pixel = 0;

intshow_bmp( char *bmpfile );
long chartolong( char * string, int length );
/******************************************************************************
*
******************************************************************************/
int main( int argc, char *argv[] )
{
    int fbfd = 0;
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    long int screensize = 0;

    // Open the file for reading and writing
    fbfd = open("/dev/fb0", O_RDWR);
    if (!fbfd)
    {
      printf("Error: cannot open framebuffer device.\n");
      exit(1);
    }

    // Get fixed screen information
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo))
    {
      printf("Error reading fixed information.\n");
      exit(2);
    }

    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo))
    {
      printf("Error reading variable information.\n");
      exit(3);
    }

    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;

    // Figure out the size of the screen in bytes
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

    // Map the device to memory
    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( argv[1] );

    munmap(fbp, screensize);
    close(fbfd);
    return 0;
}

/******************************************************************************
*
******************************************************************************/
int show_bmp( char *bmpfile )
{
        FILE *fp;
        int rc;
        int ciBitCount, ciWidth, ciHeight;
        int line_x, line_y;
        long int location = 0, BytesPerLine = 0;
        char tmp[1024*10];
   
        /* 打开位图文件 */
        fp = fopen( bmpfile, "rb" );
        if (fp == NULL)
        {
                return( -1 );
        }
       
        /* 读取位图文件头 */
        rc = fread( &FileHead, 1, sizeof(BITMAPFILEHEADER), fp );
        if ( rc != sizeof( BITMAPFILEHEADER ) )
        {
                fclose( fp );
                return( -2 );
        }
       
        /* 判断位图的类型 */
        if (memcmp(FileHead.cfType, "BM", 2) != 0)
        {
                fclose( fp );
                return( -3 );
        }

        /* 读取位图信息头 */
        rc = fread( (char *)&InfoHead, 1, sizeof(BITMAPINFOHEADER), fp );
        if ( rc != sizeof(BITMAPINFOHEADER) )
        {
                fclose( fp );
                return( -4 );
        }
       
        ciWidth    = (int) chartolong( InfoHead.ciWidth,    4 );
        ciHeight   = (int) chartolong( InfoHead.ciHeight,   4 );
        ciBitCount = (int) chartolong( InfoHead.ciBitCount, 4 );
       
        line_x = line_y = 0;
        /*
        while( !feof( fp ) )
        {
                rc = fread( (char *)&rgbquad, 1, sizeof(RGBQUAD), fp );
                if ( rc != sizeof(RGBQUAD) )
                {
                        break;
                }

                location = line_x * bits_per_pixel / 8 + (ciHeight - line_y - 1) * xres * bits_per_pixel / 8;

                *(fbp + location) = rgbquad.rgbBlue;
                *(fbp + location + 1) = rgbquad.rgbGreen;
                *(fbp + location + 2) = rgbquad.rgbRed;
                *(fbp + location + 3) = rgbquad.rgbReserved;
               
                line_x++;
                if ( line_x == ( ciWidth - 1 ) )
                {
                        line_x = 0;
                        line_y++;
                }
        }
        */
        /*下面这句是从书上抄来的,我对它还有疑问?*/
        BytesPerLine = (ciWidth * ciBitCount + 31) / 32 * 4;
       
        while( !feof( fp ) )
        {
                rc = fread( tmp, 1, BytesPerLine, fp );
                if ( rc != BytesPerLine )
                {
                        break;
                }
               
                location = (ciHeight - line_y - 1) * xres * bits_per_pixel / 8;
                memcpy( (fbp + location) , tmp, BytesPerLine );

                line_y++;
        }
        fclose( fp );
        return( 0 );
}

/******************************************************************************
*
******************************************************************************/
long chartolong( char * string, int length )
{
        long number;
       
        if (length <= 4)
        {
                memset( &number, 0x00, sizeof(long) );
                memcpy( &number, string, length );
        }
       
        return( number );
}

lilofreeman 发表于 2006-11-14 15:59:34

void draw_point(int x, int y, int color)
{
   long int location = 0;
   
   location = x + y * xres * bits_per_pixel / 8;
   
   *(fbp + location) = color;
   
   return;
}
这里x是否应该是x*xres*BytePerPixel?

AllenYao 发表于 2006-11-14 16:51:40

那个draw_point函数是针对800x600 256色屏幕的。老了点^0^
页: [1]
查看完整版本: 初学Framebuffer