Frame Buffer


Frame Buffer
2010年11月18日
  大家都知道Unix/Linux系统是由命令驱动的。X-Window-System是Unix/Linux上的图形系统,它是通过X-Server来控制硬件的。但有一些Linux的发行版在引导的时候就会在屏幕上出现图形,这时的图形是不可能由X来完成的,那是什么机制呢?答案是FrameBuffer。
  帧缓冲(framebuffer)是 Linux 为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer 设备驱动来完成的,所以可知其具有良好的移植性。
  帧缓冲设备对应的设备文件为/dev/fb*,如果系统有多个显示卡,Linux 下还可支持多个帧缓冲设备,最多可达32 个,分别为/dev/fb0 到/dev/fb31,当前缺省的帧缓冲设备通常指向/dev/fb0。当然在嵌入式系统中支持一个显示设备就够了。帧缓冲设备为标准字符设备,主设备号为29,次设备号则从0到31。分别对应/dev/fb0-/dev/fb31。在MID上,设备信息是/dev/graphics/fb0,在PC机上有显卡的原因是找不到fb的。   
  通过/dev/fb,应用程序的操作主要有这几种: 
  1.读/写(read/write)/dev/fb:相当于读/写屏幕缓冲区。在MID上,采用的方式是cat /dev/graphics/fb0 tmp和cat tmp > /dev/graphics/fb0可以实现屏幕内存存储和重现,但是本人试了没有效果(原因不知)。
  2.映射(map)操作:由于 Linux 工作在保护模式,每个应用程序都有自己的虚拟地址空间,在应用程序中是不能直接访问物理缓冲区地址的。为此,Linux 在文件操作 file_operations 结构中提供了 mmap 函数,可将文件的内容映射到用户空间。对于帧缓冲设备,则可通过映射操作,可将屏幕缓冲区的物理地址映射到用户空间的一段虚拟地址中,之后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图了。
  mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。munmap执行相反的操作,删除特定地址区域的对象映射。成功执行时,mmap()返回被映射区的指针,munmap()返回0。
  基于文件的映射,在mmap和munmap执行过程的任何时刻,被映射文件的st_atime可能被更新。如果st_atime字段在前述的情况下没有得到更新,用PROT_WRITE 和 MAP_SHARED标志建立起来的文件映射,其st_ctime 和 st_mtime在对映射区写入之后,在msync()通过MS_SYNC 和 MS_ASYNC两个标志调用之前会被更新。
  3.I/O控制:对于帧缓冲设备,对设备文件的 ioctl操作可读取/设置显示设备及屏幕的参数,如分辨率,显示颜色数,屏幕大小等等。ioctl 的操作是由底层的驱动程序来完成的。
  典型程序段如下: 
  #include 
  #include 
  #include 
  #include 
  #include 
  int main () {
  int fp=0;
  struct fb_var_screeninfo vinfo;
  struct fb_fix_screeninfo finfo;
  fp = open ("/dev/graphics/fb0",O_RDWR);
  if (fp 内存大小。__u32 line_length 是屏幕上一行的点在内存中占有的空间,不是一行上的点数。在fb_var_screeninfo 中有__u32 xres ,__u32 yres 是x和y方向的分辨率,就是两个方向上的点数。__u32 bits_per_pixel 是每一点占有的内存空间。
  如上:内存长度是1536000B,一行占的内存是1600B,分辨率是800*480,每像素占2B。你会发现1536000/(800*480*2)=2,分配的内存空间是单帧画面需要的内存空间的2倍?这是因为在现代的图形系统中大多有缓冲技术,显存中存有两页屏幕数据,这是方便快速的交换屏幕内容。
  参考地址:http://blog.ednchina.com/exbob/37028/category.aspx
  参考地址:http://blog.csdn.net/scwinter/archive/2010/01/08/5 148967.aspx 如对Android原生(Natvie)C开发还任何疑问,请参阅http://emck.avaw.com/?p=205
  虽然现在能通过交叉环境编译程序,并push到Android上执行,但那只是console台程序,是不是有些单调呢?下面就要看如何通过Linux的 framebuffer 技术在Android上画图形,关于Linux的framebuffer技术,这里就不再详细讲解了,请大家google一下。
  操作framebuffer的主要步骤如下:
  1、打开一个可用的FrameBuffer设备;
  2、通过mmap调用把显卡的物理内存空间映射到用户空间;
  3、更改内存空间里的像素数据并显示;
  4、退出时关闭framebuffer设备。
  下面的这个例子简单地用framebuffer画了一个渐变的进度条,代码 framebuf.c 如下:
  #include 
  #include 
  #include 
  #include 
  #include 
  inline static unsigned short int make16color(unsigned char r, unsigned char g, unsigned char b)
  {
  return (
  (((r >> 3) & 31) > 2) & 63) > 3) & 31)        );
  }
  int main() {
  int fbfd = 0;
  struct fb_var_screeninfo vinfo;
  struct fb_fix_screeninfo finfo;
  long int screensize = 0;
  char *fbp = 0;
  int x = 0, y = 0;
  int guage_height = 20, step = 10;
  long int location = 0;
  // Open the file for reading and writing
  fbfd = open("/dev/graphics/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("sizeof(unsigned short) = %d\n", sizeof(unsigned short));
  printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel );
  printf("xoffset:%d, yoffset:%d, line_length: %d\n", vinfo.xoffset, vinfo.yoffset, finfo.line_length );
  // 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");
  //set to black color first
  memset(fbp, 0, screensize);
  //draw rectangle
  y = (vinfo.yres   guage_height) / 2   2;       // Where we are going to put the pixel
  for (x = step   2; x 内存,然后就可以操作这块内存空间来显示你想画的图形了。
  最后别忘了关闭设备:
  munmap(fbp, screensize);
  close(fbfd);
  效果图如下: 
  
  Android framebuffer截图   Linux的帧缓冲(Frame Buffer)之二:显示图形和图像 
  现在你应该对FrameBuffer有一个大概的了解了吧。那么接下来你一定会想在屏幕上画一些东西,让我们先从画一个方块开始吧。先说说我的想法:在类Unix系统中,一切东西都是文件。我们对屏幕的读写就可以转换成对帧缓冲设备的读写。那么就把帧缓冲设备用open打开,再用lseek定位要读写的位置,最后调用read或者write来操作。通过这么一大段的操作我们才完成了对一个点的读或者写。
  这种方法开销太大了。还有一种方法,我们把/dev/fb0映射到程序进程的内存空间中来,然后得到一个指向这段存储空间的指针,这样就可以方便的读写了。但是我们要知道能映射多少和该映射多少,这能很方便的从上面一个程序得出的参数来决定。
  #include 
  #include 
  #include 
  #include 
  #include 
  int main () {
  int fp=0;
  struct fb_var_screeninfo vinfo;
  struct fb_fix_screeninfo finfo;
  long screensize=0;
  char *fbp = 0;
  int x = 0, y = 0;
  long location = 0;
  fp = open ("/dev/graphics/fb0",O_RDWR);
  if (fp 代码有点不清晰,不易理解。但是有了这个基本的代码实现,我们可以很容易写一些DrawPoint之类的函数去包装一下低层的对线性存储空间的读写。有了画点的程序,再写出画线画圆的函数就不是非常困难了。
  本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/scwinter/archive/2010/01/08/5 148967.aspx

猜你喜欢

转载自mibk67mibk.iteye.com/blog/1362412