android上用C语言读取fb0实现截屏,并保存为rgb565的bmp .

工程源码:http://download.csdn.net/detail/victoryckl/4074654


一个增强版本源码:通过对fb0的操作,实现截屏,和送图片到fb0,支持rgb565 rgb888 xrgb8888格式的bmp和fb0


      android上用C语言读取fb0实现截屏,保存为bmp图片,

支持16位(rgb565)、24位(rbg888)两种格式数据,并在android2.2和4.0模拟器上验证通过。

截屏实现主要有两个方面的工作,读取屏幕数据和生成图片。

1.读取屏幕数据

只读方式打开显存设备 /dev/graphics/fb0,再通过mmap用共享方式(MAP_SHARED)映射到一片内存中,其他地方就可以直接以内存的方式读取屏幕数据了。

要注意的是,fb0的虚拟显示设备尺寸,大于实际屏幕显示设备的尺寸,mmap映射时需将安装虚拟尺寸进行映射,否则,截屏可能失败。

针对显存设备的主要代码:

[java] view plain copy print ?
  1. /******************************************************************** 
  2.     created:    2012/02/07 
  3.     filename:   myfb.c 
  4.     author:      
  5.      
  6.     purpose:     
  7. *********************************************************************/  
  8. #ifndef WIN32  
  9. //-------------------------------------------------------------------   
  10.   
  11. #include <stdio.h>  
  12. #include <stdlib.h>  
  13. #include <unistd.h>  
  14. #include <fcntl.h>  
  15. #include <sys/mman.h>  
  16. #include <sys/stat.h>  
  17. #include <sys/types.h>  
  18.   
  19. #include <linux/fb.h>  
  20. #include <linux/kd.h>  
  21.   
  22. struct FB {  
  23.     unsigned short *bits;  
  24.     unsigned size;  
  25.     int fd;  
  26.     struct fb_fix_screeninfo fi;  
  27.     struct fb_var_screeninfo vi;  
  28. };  
  29.   
  30. int fb_bpp(struct FB *fb)  
  31. {  
  32.     if (fb) {  
  33.         return fb->vi.bits_per_pixel;  
  34.     }  
  35.     return 0;  
  36. }  
  37.   
  38. int fb_width(struct FB *fb)  
  39. {  
  40.     if (fb) {  
  41.         return fb->vi.xres;  
  42.     }  
  43.     return 0;  
  44. }  
  45.   
  46. int fb_height(struct FB *fb)  
  47. {  
  48.     if (fb) {  
  49.         return fb->vi.yres;  
  50.     }  
  51.     return 0;  
  52. }  
  53.   
  54. int fb_size(struct FB *fb)  
  55. {  
  56.     if (fb) {  
  57.         unsigned bytespp = fb->vi.bits_per_pixel / 8;  
  58.         return (fb->vi.xres * fb->vi.yres * bytespp);  
  59.     }  
  60.     return 0;  
  61. }  
  62.   
  63. int fb_virtual_size(struct FB *fb)  
  64. {  
  65.     if (fb) {  
  66.         unsigned bytespp = fb->vi.bits_per_pixel / 8;  
  67.         return (fb->vi.xres_virtual * fb->vi.yres_virtual * bytespp);  
  68.     }  
  69.     return 0;  
  70. }  
  71.   
  72. void * fb_bits(struct FB *fb)  
  73. {  
  74.     unsigned short * bits = NULL;  
  75.     if (fb) {  
  76.         int offset, bytespp;  
  77.         bytespp = fb->vi.bits_per_pixel / 8;  
  78.   
  79.         /* HACK: for several of our 3d cores a specific alignment 
  80.         * is required so the start of the fb may not be an integer number of lines 
  81.         * from the base.  As a result we are storing the additional offset in 
  82.         * xoffset. This is not the correct usage for xoffset, it should be added 
  83.         * to each line, not just once at the beginning */  
  84.         offset = fb->vi.xoffset * bytespp;  
  85.         offset += fb->vi.xres * fb->vi.yoffset * bytespp;  
  86.         bits = fb->bits + offset / sizeof(*fb->bits);  
  87.     }  
  88.     return bits;  
  89. }  
  90.   
  91. void fb_update(struct FB *fb)  
  92. {  
  93.     if (fb) {  
  94.         fb->vi.yoffset = 1;  
  95.         ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);  
  96.         fb->vi.yoffset = 0;  
  97.         ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);  
  98.     }  
  99. }  
  100.   
  101. static int fb_open(struct FB *fb)  
  102. {  
  103.     if (NULL == fb) {  
  104.         return -1;  
  105.     }  
  106.       
  107.     fb->fd = open("/dev/graphics/fb0", O_RDONLY);  
  108.     if (fb->fd < 0) {  
  109.         printf("open(\"/dev/graphics/fb0\") failed!\n");  
  110.         return -1;  
  111.     }  
  112.   
  113.     if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0) {  
  114.         printf("FBIOGET_FSCREENINFO failed!\n");  
  115.         goto fail;  
  116.     }  
  117.     if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0) {  
  118.         printf("FBIOGET_VSCREENINFO failed!\n");  
  119.         goto fail;  
  120.     }  
  121.   
  122.     fb->bits = mmap(0, fb_virtual_size(fb), PROT_READ, MAP_SHARED, fb->fd, 0);  
  123.     if (fb->bits == MAP_FAILED) {  
  124.         printf("mmap() failed!\n");  
  125.         goto fail;  
  126.     }  
  127.   
  128.     return 0;  
  129.   
  130. fail:  
  131.     close(fb->fd);  
  132.     return -1;  
  133. }  
  134.   
  135. static void fb_close(struct FB *fb)  
  136. {  
  137.     if (fb) {  
  138.         munmap(fb->bits, fb_virtual_size(fb));  
  139.         close(fb->fd);  
  140.     }  
  141. }  
  142.   
  143. static struct FB g_fb;  
  144. struct FB * fb_create(void)  
  145. {  
  146.     memset(&g_fb, 0, sizeof(struct FB));  
  147.     if (fb_open(&g_fb)) {  
  148.         return NULL;  
  149.     }  
  150.     return &g_fb;  
  151. }  
  152.   
  153. void fb_destory(struct FB *fb)  
  154. {  
  155.     fb_close(fb);  
  156. }  
  157.   
  158. //-------------------------------------------------------------------   
  159. #endif//#ifndef WIN32  


2.生成图片

这里生成的图片是bmp格式的,可以根据设备像素的位数自动生成16位(rgb565)、24位(rbg888)两种图片。

主要工作是要正确填充bmp的文件头信息,24位(rbg888)较简单。

16位(rgb565)复杂一点,biCompression成员的值必须是BI_BITFIELDS,原来调色板的前三个位置被三个DWORD变量占据,称为红、绿、蓝掩码,在565格式下,它们则分别为:0xF800、0x07E0、0x001F。

另外,需要主要的是windows要求文件大小必须是4的倍数,文件大小需要做下面的处理才能正确显示。

[java] view plain copy print ?
  1. head->bfSize = head->bfOffBits + size;  
  2. head->bfSize = (head->bfSize + 3) & ~3;  
  3. size = head->bfSize - head->bfOffBits;  

生成图片的主要代码:

[java] view plain copy print ?
  1. /******************************************************************** 
  2.     created:    2012/02/07 
  3.     filename:   savebmp.c 
  4.     author:      
  5.      
  6.     purpose:     
  7. *********************************************************************/  
  8.   
  9. #include <stdlib.h>  
  10. #include <stdio.h>  
  11. #include <memory.h>  
  12.   
  13.   
  14. //-------------------------------------------------------------------   
  15. /* 
  16.   位图文件的组成 
  17.   结构名称 符 号 
  18.   位图文件头 (bitmap-file header) BITMAPFILEHEADER bmfh 
  19.   位图信息头 (bitmap-information header) BITMAPINFOHEADER bmih 
  20.   彩色表 (color table) RGBQUAD aColors[] 
  21.   图象数据阵列字节 BYTE aBitmapBits[] 
  22. */  
  23. typedef struct bmp_header   
  24. {  
  25.     short twobyte           ;//两个字节,用来保证下面成员紧凑排列,这两个字符不能写到文件中   
  26.     //14B   
  27.     char bfType[2]          ;//!文件的类型,该值必需是0x4D42,也就是字符'BM'   
  28.     unsigned int bfSize     ;//!说明文件的大小,用字节为单位   
  29.     unsigned int bfReserved1;//保留,必须设置为0   
  30.     unsigned int bfOffBits  ;//!说明从文件头开始到实际的图象数据之间的字节的偏移量,这里为14B+sizeof(BMPINFO)   
  31. }BMPHEADER;  
  32.   
  33. typedef struct bmp_info  
  34. {  
  35.     //40B   
  36.     unsigned int biSize         ;//!BMPINFO结构所需要的字数   
  37.     int biWidth                 ;//!图象的宽度,以象素为单位   
  38.     int biHeight                ;//!图象的宽度,以象素为单位,如果该值是正数,说明图像是倒向的,如果该值是负数,则是正向的   
  39.     unsigned short biPlanes     ;//!目标设备说明位面数,其值将总是被设为1   
  40.     unsigned short biBitCount   ;//!比特数/象素,其值为1、4、8、16、24、或32   
  41.     unsigned int biCompression  ;//说明图象数据压缩的类型   
  42. #define BI_RGB        0L    //没有压缩   
  43. #define BI_RLE8       1L    //每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引);   
  44. #define BI_RLE4       2L    //每个象素4比特的RLE压缩编码,压缩格式由2字节组成   
  45. #define BI_BITFIELDS  3L    //每个象素的比特由指定的掩码决定。   
  46.     unsigned int biSizeImage    ;//图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0   
  47.     int biXPelsPerMeter         ;//水平分辨率,用象素/米表示   
  48.     int biYPelsPerMeter         ;//垂直分辨率,用象素/米表示   
  49.     unsigned int biClrUsed      ;//位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)   
  50.     unsigned int biClrImportant ;//对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。   
  51. }BMPINFO;  
  52.   
  53. typedef struct tagRGBQUAD {  
  54.     unsigned char rgbBlue;  
  55.     unsigned char rgbGreen;  
  56.     unsigned char rgbRed;  
  57.     unsigned char rgbReserved;  
  58. } RGBQUAD;  
  59.   
  60. typedef struct tagBITMAPINFO {  
  61.     BMPINFO    bmiHeader;  
  62.     //RGBQUAD    bmiColors[1];   
  63.     unsigned int rgb[3];  
  64. } BITMAPINFO;  
  65.   
  66. static int get_rgb888_header(int w, int h, BMPHEADER * head, BMPINFO * info)  
  67. {  
  68.     int size = 0;  
  69.     if (head && info) {  
  70.         size = w * h * 3;  
  71.         memset(head, 0, sizeof(* head));  
  72.         memset(info, 0, sizeof(* info));  
  73.         head->bfType[0] = 'B';  
  74.         head->bfType[1] = 'M';  
  75.         head->bfOffBits = 14 + sizeof(* info);  
  76.         head->bfSize = head->bfOffBits + size;  
  77.         head->bfSize = (head->bfSize + 3) & ~3;//windows要求文件大小必须是4的倍数   
  78.         size = head->bfSize - head->bfOffBits;  
  79.           
  80.         info->biSize = sizeof(BMPINFO);  
  81.         info->biWidth = w;  
  82.         info->biHeight = -h;  
  83.         info->biPlanes = 1;  
  84.         info->biBitCount = 24;  
  85.         info->biCompression = BI_RGB;  
  86.         info->biSizeImage = size;  
  87.   
  88.         printf("rgb888:%dbit,%d*%d,%d\n", info->biBitCount, w, h, head->bfSize);  
  89.     }  
  90.     return size;  
  91. }  
  92.   
  93. static int get_rgb565_header(int w, int h, BMPHEADER * head, BITMAPINFO * info)  
  94. {  
  95.     int size = 0;  
  96.     if (head && info) {  
  97.         size = w * h * 2;  
  98.         memset(head, 0, sizeof(* head));  
  99.         memset(info, 0, sizeof(* info));  
  100.         head->bfType[0] = 'B';  
  101.         head->bfType[1] = 'M';  
  102.         head->bfOffBits = 14 + sizeof(* info);  
  103.         head->bfSize = head->bfOffBits + size;  
  104.         head->bfSize = (head->bfSize + 3) & ~3;  
  105.         size = head->bfSize - head->bfOffBits;  
  106.           
  107.         info->bmiHeader.biSize = sizeof(info->bmiHeader);  
  108.         info->bmiHeader.biWidth = w;  
  109.         info->bmiHeader.biHeight = -h;  
  110.         info->bmiHeader.biPlanes = 1;  
  111.         info->bmiHeader.biBitCount = 16;  
  112.         info->bmiHeader.biCompression = BI_BITFIELDS;  
  113.         info->bmiHeader.biSizeImage = size;  
  114.   
  115.         info->rgb[0] = 0xF800;  
  116.         info->rgb[1] = 0x07E0;  
  117.         info->rgb[2] = 0x001F;  
  118.   
  119.         printf("rgb565:%dbit,%d*%d,%d\n", info->bmiHeader.biBitCount, w, h, head->bfSize);  
  120.     }  
  121.     return size;  
  122. }  
  123.   
  124. static int save_bmp_rgb565(FILE * hfile, int w, int h, void * pdata)  
  125. {  
  126.     int success = 0;  
  127.     int size = 0;  
  128.     BMPHEADER head;  
  129.     BITMAPINFO info;  
  130.       
  131.     size = get_rgb565_header(w, h, &head, &info);  
  132.     if (size > 0) {  
  133.         fwrite(head.bfType, 114, hfile);  
  134.         fwrite(&info, 1, sizeof(info), hfile);  
  135.         fwrite(pdata, 1, size, hfile);  
  136.         success = 1;  
  137.     }  
  138.   
  139.     return success;  
  140. }  
  141.   
  142. static int save_bmp_rgb888(FILE * hfile, int w, int h, void * pdata)  
  143. {  
  144.     int success = 0;  
  145.     int size = 0;  
  146.     BMPHEADER head;  
  147.     BMPINFO info;  
  148.       
  149.     size = get_rgb888_header(w, h, &head, &info);  
  150.     if (size > 0) {  
  151.         fwrite(head.bfType, 114, hfile);  
  152.         fwrite(&info, 1, sizeof(info), hfile);  
  153.         fwrite(pdata, 1, size, hfile);  
  154.         success = 1;  
  155.     }  
  156.       
  157.     return success;  
  158. }  
  159.   
  160. int save_bmp(const char * path, int w, int h, void * pdata, int bpp)  
  161. {  
  162.     int success = 0;  
  163.     FILE * hfile = NULL;  
  164.   
  165.     do   
  166.     {  
  167.         if (path == NULL || w <= 0 || h <= 0 || pdata == NULL) {  
  168.             printf("if (path == NULL || w <= 0 || h <= 0 || pdata == NULL)\n");  
  169.             break;  
  170.         }  
  171.   
  172.         remove(path);  
  173.         hfile = fopen(path, "wb");  
  174.         if (hfile == NULL) {  
  175.             printf("open(%s) failed!\n", path);  
  176.             break;  
  177.         }  
  178.   
  179.         switch (bpp)  
  180.         {  
  181.         case 16:  
  182.             success = save_bmp_rgb565(hfile, w, h, pdata);  
  183.             break;  
  184.         case 24:  
  185.             success = save_bmp_rgb888(hfile, w, h, pdata);  
  186.             break;  
  187.         default:  
  188.             printf("error: not support format!\n");  
  189.             success = 0;  
  190.             break;  
  191.         }  
  192.     } while (0);  
  193.   
  194.     if (hfile != NULL)  
  195.         fclose(hfile);  
  196.       
  197.     return success;  
  198. }  
  199.   
  200. //-------------------------------------------------------------------  


3.运行方式:

cygwin下用ndk编译成可执行文件,再将生成的可执行文件push到模拟器的/data/local路径,

修改文件为可执行的,直接运行便可以截屏,图片保存到/mnt/sdcard/s.bmp。

[java] view plain copy print ?
  1. Administrator@xxx /cygdrive/e  
  2. $ cd myprj/screenshot/jni/  
  3.   
  4. Administrator@xxx /cygdrive/e/myprj/screenshot/jni  
  5. $ ndk-build  
  6. Compile thumb  : savebmp <= savebmp.c  
  7. Compile thumb  : savebmp <= screenshot.c  
  8. StaticLibrary  : libsavebmp.a  
  9. Executable     : save  
  10. Install        : save => libs/armeabi/save  
  11.   
  12. Administrator@xxx /cygdrive/e/myprj/screenshot/jni  
  13. $ adb shell  
  14. error: device offline  
  15.   
  16. Administrator@xxx /cygdrive/e/myprj/screenshot/jni  
  17. $ adb shell  
  18. #  
  19. Administrator@xxx /cygdrive/e/myprj/screenshot/jni  
  20. $ adb push ../libs/armeabi/save /data/local  
  21. 83 KB/s (10636 bytes in 0.125s)  
  22.   
  23. Administrator@xxx /cygdrive/e/myprj/screenshot/jni  
  24. $ adb shell  
  25. # cd /data/local  
  26. cd /data/local  
  27. # chmod 777 save  
  28. chmod 777 save  
  29. # ./save  
  30. ./save  
  31. rgb565:16bit,800*480,768068  
  32. # ls -l /mnt/sdcard  
  33. ls -l /mnt/sdcard  
  34. d---rwxr-x system   sdcard_rw          2012-02-19 12:39 LOST.DIR  
  35. d---rwxr-x system   sdcard_rw          2012-02-20 13:22 DCIM  
  36. ----rwxr-x system   sdcard_rw   768068 2012-02-20 13:22 s.bmp  
  37. #  


PS:工程源码中的VC工程主要用来调试生成图片的功能。


=====================================================================================================

      目前,在增强版本中添加了32位的支持,但是没有32位的设备进行测试,已经有几位网友测试过,32位截图会出现颜色偏差和位置偏移,目前我这里还没有办法调试,所以32位截图的问题我还没有解决。

      有位网友在小米手机上测试过也是有色差和错位问题,但后来直接在java中用Runtime.exec("/system/bin/screencap -p path")来截屏,还是png格式的。不是每个设备上都有screencap。

     关于fb0中像素的格式可以参考

android4.0p-ics-src/system/core/adb# vi framebuffer_service.c,里面列举了五种格式,不过有些设备上可能不是下面的任何一种。

[cpp] view plain copy print ?
  1. case 1: /* RGBA_8888 */  
  2.     fbinfo.bpp = 32;  
  3.     fbinfo.size = w * h * 4;  
  4.     fbinfo.width = w;  
  5.     fbinfo.height = h;  
  6.     fbinfo.red_offset = 0;  
  7.     fbinfo.red_length = 8;  
  8.     fbinfo.green_offset = 8;  
  9.     fbinfo.green_length = 8;  
  10.     fbinfo.blue_offset = 16;  
  11.     fbinfo.blue_length = 8;  
  12.     fbinfo.alpha_offset = 24;  
  13.     fbinfo.alpha_length = 8;  
  14.     break;  
  15. case 2: /* RGBX_8888 */  
  16.     fbinfo.bpp = 32;  
  17.     fbinfo.size = w * h * 4;  
  18.     fbinfo.width = w;  
  19.     fbinfo.height = h;  
  20.     fbinfo.red_offset = 0;  
  21.     fbinfo.red_length = 8;  
  22.     fbinfo.green_offset = 8;  
  23.     fbinfo.green_length = 8;  
  24.     fbinfo.blue_offset = 16;  
  25.     fbinfo.blue_length = 8;  
  26.     fbinfo.alpha_offset = 24;  
  27.     fbinfo.alpha_length = 0;  
  28.     break;  
  29. case 3: /* RGB_888 */  
  30.     fbinfo.bpp = 24;  
  31.     fbinfo.size = w * h * 3;  
  32.     fbinfo.width = w;  
  33.     fbinfo.height = h;  
  34.     fbinfo.red_offset = 0;  
  35.     fbinfo.red_length = 8;  
  36.     fbinfo.green_offset = 8;  
  37.     fbinfo.green_length = 8;  
  38.     fbinfo.blue_offset = 16;  
  39.     fbinfo.blue_length = 8;  
  40.     fbinfo.alpha_offset = 24;  
  41.     fbinfo.alpha_length = 0;  
  42.     break;  
  43. case 4: /* RGB_565 */  
  44.     fbinfo.bpp = 16;  
  45.     fbinfo.size = w * h * 2;  
  46.     fbinfo.width = w;  
  47.     fbinfo.height = h;  
  48.     fbinfo.red_offset = 11;  
  49.     fbinfo.red_length = 5;  
  50.     fbinfo.green_offset = 5;  
  51.     fbinfo.green_length = 6;  
  52.     fbinfo.blue_offset = 0;  
  53.     fbinfo.blue_length = 5;  
  54.     fbinfo.alpha_offset = 0;  
  55.     fbinfo.alpha_length = 0;  
  56.     break;  
  57. case 5: /* BGRA_8888 */  
  58.     fbinfo.bpp = 32;  
  59.     fbinfo.size = w * h * 4;  
  60.     fbinfo.width = w;  
  61.     fbinfo.height = h;  
  62.     fbinfo.red_offset = 16;  
  63.     fbinfo.red_length = 8;  
  64.     fbinfo.green_offset = 8;  
  65.     fbinfo.green_length = 8;  
  66.     fbinfo.blue_offset = 0;  
  67.     fbinfo.blue_length = 8;  
  68.     fbinfo.alpha_offset = 24;  
  69.     fbinfo.alpha_length = 8;  
  70.    break;  

     另外,关于截屏在android4.0的源码用有两个程序可以参考,这两个程序都可以截屏,一个是c++实现的,另一个是c的。

ics-src\frameworks\base\cmds\screencap\screencap.cpp

ics-src\frameworks\base\cmds\screenshot\screenshot.c

你可能感兴趣的:(android上用C语言读取fb0实现截屏,并保存为rgb565的bmp .)