[轉]關於framebuffer的用法

From:http://www.linuxsir.org/bbs/thread241071.html


大家都知道Unix/Linux系統是由命令驅動的。那麼最基本的系統是命令行的(就是想DOS一樣的界面)。X-Window-System是Unix/Linux上的圖形系統,它是通過X-Server來控制硬件的。但有一些Linux的發行版在引導的時候就會在屏幕上出現圖形,這時的圖形是不可能由X來完成的,那是什麼機制呢?答案是FrameBuffer。
FrameBuffer不是一個圖形系統,更不是窗口系統。它比X要低級,簡單來說FrameBuffer就是一種機制的實現。這種機制是把屏幕上的每個點映射成一段線性內存空間,程序可以簡單的改變這段內存的值來改變屏幕上某一點的顏色。X的高度可移植性就是來自於這種機制,不管是在那種圖形環境下,只要有這種機制的實現就可以運行X。所以在幾乎所有的平臺上都有相應的X版本的移植。
好了,閒話少說,下面我們來看看可以利用FrameBuffer來乾點什麼。首先看看你是否有了相應的驅動:找一下在/dev/下是否有fb*這個設備文件,這是個字符類的特殊文件。

Shell代碼 
  1. ls -l /dev/fb0  (Enter)                    
  2. rw-rw----  1 root video 290 Jan 27 15:32 /dev/fb0  

如果沒有這個文件也可以找找其他的比如:/dev/fb1,/dev/fb2...如果找不到這些文件,那就得重新編譯內核了。下面假設存在這個文件/dev/fb0,這就是FrameBuffer的設備文件。
有了這個我們可以play with FrameBuffer了。(一下的操作不一定要在X下,可以在啓動了FrameBuffer的虛擬控制檯下)
Shell代碼 
  1. cat /dev/fb0 > sreensnap   
  2.   
  3. ls -l sreensnap  
  4.   
  5. -rw-r--r--  1 wsw wsw 6291456 Jan 27 21:30 sreensnap  

我們得到了一個恰好6M的文件,再做下面的操作:
Shell代碼 
  1. clear                   /*清楚屏幕的輸出*/  
  2. cat sreensnap > /dev/fb0  

是不是奇怪的事情發生了?好像是中了病毒一般?屏幕又恢復了以前的狀態?不用着急,
Shell代碼 
  1. clear  

這樣屏幕就正常了。

通過以上的操作,我想你也猜到了。文件/dev/fb0就是控制屏幕上的每一點的顏色的文件。我們可以寫程序來改變這個文件的內容,就可以方便的在屏幕上畫圖了:-)

我下面就來寫一個小程序,探測一下屏幕的屬性。

Shell代碼 
  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <fcntl.h>  
  4. #include <linux/fb.h>  
  5. #include <sys/mman.h>  
  6.   
  7. int main () {  
  8.     int fp=0;  
  9.     struct fb_var_screeninfo vinfo;  
  10.     struct fb_fix_screeninfo finfo;  
  11.     fp = open ("/dev/fb0",O_RDWR);  
  12.   
  13.     if (fp < 0){  
  14.         printf("Error : Can not open framebuffer device\n");  
  15.         exit(1);  
  16.     }  
  17.   
  18.     if (ioctl(fp,FBIOGET_FSCREENINFO,&finfo)){  
  19.         printf("Error reading fixed information\n");  
  20.         exit(2);  
  21.     }  
  22.       
  23.     if (ioctl(fp,FBIOGET_VSCREENINFO,&vinfo)){  
  24.         printf("Error reading variable information\n");  
  25.         exit(3);  
  26.     }  
  27.   
  28.     printf("The mem is :%d\n",finfo.smem_len);  
  29.     printf("The line_length is :%d\n",finfo.line_length);  
  30.     printf("The xres is :%d\n",vinfo.xres);  
  31.     printf("The yres is :%d\n",vinfo.yres);  
  32.     printf("bits_per_pixel is :%d\n",vinfo.bits_per_pixel);  
  33.     close (fp);  
  34. }  

struct fb_var_screeninfo 和 struct fb_fix_screeninfo 兩個數據結構是在/usr/include/linux/fb.h中定義的,裏面有些有趣的值:(都是無符號32位的整數)
在fb_fix_screeninfo中有
__u32 smem_len 是這個/dev/fb0的大小,也就是內存大小。
__u32 line_length 是屏幕上一行的點在內存中佔有的空間,不是一行上的點數。
在fb_var_screeninfo 中有
__u32 xres ,__u32 yres 是x和y方向的分辨率,就是兩個方向上的點數。
__u32 bits_per_pixel 是每一點佔有的內存空間。

把上面的程序編譯以後運行,在我的機器上的結果如下:
Shell代碼 
  1. The mem is :6291456  
  2. The line_length is :4096  
  3. The xres is :1024  
  4. The yres is :768  
  5. bits_per_pixel is :32  

內存長度恰好是6M,每行佔有4M的空間,分辨率是1024x768,色彩深度是32位。細心的你可能已經發現有些不對。屏幕上的點有1024x768=786432個,每個點佔有32比特。屏幕一共的佔有內存數爲32x786432=25165824 就是3145728字節,恰好是3M但是上面的程序告訴我們有6M的存儲空間。這是因爲在現代的圖形系統中大多有緩衝技術,顯存中存有兩頁屏幕數據,這是方便快速的改變屏幕內容實現動畫之類比較高的要求。關於這種緩衝技術有點複雜,我們目前先不討論。對於我們來說只有這3M內存來存放這一個屏幕的顏色數據。
好了,現在你應該對FrameBuffer有一個大概的瞭解了吧。那麼接下來你一定會想在屏幕上畫一些東西,讓我們先從畫一個點開始吧。先說說我的想法:在類Unix系統中,一切東西都是文件。我們對屏幕的讀寫就可以轉換成對/dev/fb0的讀寫。那麼就把/dev/fb0用open打開,再用lseek定位要讀寫的位置,最後調用read或者write來操作。通過這麼一大段的操作我們才完成了對一個點的讀或者寫。這種方法開銷太大了。還有一種方法,我們把/dev/fb0映射到程序進程的內存空間中來,然後得到一個指向這段存儲空間的指針,這樣就可以方便的讀寫了。但是我們要知道能映射多少和該映射多少,這能很方便的從上面一個程序得出的參數來決定。
下面是程序代碼:
Shell代碼 
  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <fcntl.h>  
  4. #include <linux/fb.h>  
  5. #include <sys/mman.h>  
  6.   
  7. int main () {  
  8.     int fp=0;  
  9.     struct fb_var_screeninfo vinfo;  
  10.     struct fb_fix_screeninfo finfo;  
  11.     long screensize=0;  
  12.     char *fbp = 0;  
  13.     int x = 0, y = 0;  
  14.     long location = 0;  
  15.     fp = open ("/dev/fb0",O_RDWR);  
  16.   
  17.     if (fp < 0){  
  18.         printf("Error : Can not open framebuffer device\n");  
  19.         exit(1);  
  20.     }  
  21.   
  22.     if (ioctl(fp,FBIOGET_FSCREENINFO,&finfo)){  
  23.         printf("Error reading fixed information\n");  
  24.         exit(2);  
  25.     }  
  26.       
  27.     if (ioctl(fp,FBIOGET_VSCREENINFO,&vinfo)){  
  28.         printf("Error reading variable information\n");  
  29.         exit(3);  
  30.     }  
  31.   
  32.         screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;  
  33.     /*這就是把fp所指的文件中從開始到screensize大小的內容給映射出來,得到一個指向這塊空間的指針*/  
  34.     fbp =(char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fp,0);  
  35.     if ((int) fbp == -1)  
  36.         {  
  37.             printf ("Error: failed to map framebuffer device to memory.\n");  
  38.             exit (4);  
  39.         }  
  40. /*這是你想畫的點的位置座標,(00)點在屏幕左上角*/  
  41.      x = 100;  
  42.      y = 100;  
  43.     location = x * (vinfo.bits_per_pixel / 8) + y  *  finfo.line_length;  
  44.   
  45.   *(fbp + location) = 100;      /* 藍色的色深 */     /*直接賦值來改變屏幕上某點的顏色*/  
  46.   *(fbp + location + 1) = 15;   /* 綠色的色深*/         
  47.   *(fbp + location + 2) = 200;  /* 紅色的色深*/         
  48.   *(fbp + location + 3) = 0;        /* 是否透明*/         
  49.   munmap (fbp, screensize); /*解除映射*/  
  50.   close (fp);               /*關閉文件*/  
  51.   return 0;  
  52.   
  53. }  

因爲這是對線性存儲空間的讀寫,所以代碼有點不清晰,不易理解。但是有了這個基本的代碼實現,我們可以很容易寫一些DrawPoint之類的函數去包裝一下低層的對線性存儲空間的讀寫。但是有了畫點的程序,再寫出畫線畫圓的函數就不是非常困難了。

這些就是我對FrameBuffer的初步研究,匆忙之間寫些東西不成文章,以後要寫些更高級一點的函數的實現。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章