工程源码: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映射时需将安装虚拟尺寸进行映射,否则,截屏可能失败。
针对显存设备的主要代码:
/******************************************************************** created: 2012/02/07 filename: myfb.c author: purpose: *********************************************************************/ #ifndef WIN32 //------------------------------------------------------------------- #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <linux/fb.h> #include <linux/kd.h> struct FB { unsigned short *bits; unsigned size; int fd; struct fb_fix_screeninfo fi; struct fb_var_screeninfo vi; }; int fb_bpp(struct FB *fb) { if (fb) { return fb->vi.bits_per_pixel; } return 0; } int fb_width(struct FB *fb) { if (fb) { return fb->vi.xres; } return 0; } int fb_height(struct FB *fb) { if (fb) { return fb->vi.yres; } return 0; } int fb_size(struct FB *fb) { if (fb) { unsigned bytespp = fb->vi.bits_per_pixel / 8; return (fb->vi.xres * fb->vi.yres * bytespp); } return 0; } int fb_virtual_size(struct FB *fb) { if (fb) { unsigned bytespp = fb->vi.bits_per_pixel / 8; return (fb->vi.xres_virtual * fb->vi.yres_virtual * bytespp); } return 0; } void * fb_bits(struct FB *fb) { unsigned short * bits = NULL; if (fb) { int offset, bytespp; bytespp = fb->vi.bits_per_pixel / 8; /* HACK: for several of our 3d cores a specific alignment * is required so the start of the fb may not be an integer number of lines * from the base. As a result we are storing the additional offset in * xoffset. This is not the correct usage for xoffset, it should be added * to each line, not just once at the beginning */ offset = fb->vi.xoffset * bytespp; offset += fb->vi.xres * fb->vi.yoffset * bytespp; bits = fb->bits + offset / sizeof(*fb->bits); } return bits; } void fb_update(struct FB *fb) { if (fb) { fb->vi.yoffset = 1; ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); fb->vi.yoffset = 0; ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); } } static int fb_open(struct FB *fb) { if (NULL == fb) { return -1; } fb->fd = open("/dev/graphics/fb0", O_RDONLY); if (fb->fd < 0) { printf("open(\"/dev/graphics/fb0\") failed!\n"); return -1; } if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0) { printf("FBIOGET_FSCREENINFO failed!\n"); goto fail; } if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0) { printf("FBIOGET_VSCREENINFO failed!\n"); goto fail; } fb->bits = mmap(0, fb_virtual_size(fb), PROT_READ, MAP_SHARED, fb->fd, 0); if (fb->bits == MAP_FAILED) { printf("mmap() failed!\n"); goto fail; } return 0; fail: close(fb->fd); return -1; } static void fb_close(struct FB *fb) { if (fb) { munmap(fb->bits, fb_virtual_size(fb)); close(fb->fd); } } static struct FB g_fb; struct FB * fb_create(void) { memset(&g_fb, 0, sizeof(struct FB)); if (fb_open(&g_fb)) { return NULL; } return &g_fb; } void fb_destory(struct FB *fb) { fb_close(fb); } //------------------------------------------------------------------- #endif//#ifndef WIN32
2.生成图片
这里生成的图片是bmp格式的,可以根据设备像素的位数自动生成16位(rgb565)、24位(rbg888)两种图片。
主要工作是要正确填充bmp的文件头信息,24位(rbg888)较简单。
16位(rgb565)复杂一点,biCompression成员的值必须是BI_BITFIELDS,原来调色板的前三个位置被三个DWORD变量占据,称为红、绿、蓝掩码,在565格式下,它们则分别为:0xF800、0x07E0、0x001F。
另外,需要主要的是windows要求文件大小必须是4的倍数,文件大小需要做下面的处理才能正确显示。
head->bfSize = head->bfOffBits + size; head->bfSize = (head->bfSize + 3) & ~3; size = head->bfSize - head->bfOffBits;
/******************************************************************** created: 2012/02/07 filename: savebmp.c author: purpose: *********************************************************************/ #include <stdlib.h> #include <stdio.h> #include <memory.h> //------------------------------------------------------------------- /* 位图文件的组成 结构名称 符 号 位图文件头 (bitmap-file header) BITMAPFILEHEADER bmfh 位图信息头 (bitmap-information header) BITMAPINFOHEADER bmih 彩色表 (color table) RGBQUAD aColors[] 图象数据阵列字节 BYTE aBitmapBits[] */ typedef struct bmp_header { short twobyte ;//两个字节,用来保证下面成员紧凑排列,这两个字符不能写到文件中 //14B char bfType[2] ;//!文件的类型,该值必需是0x4D42,也就是字符'BM' unsigned int bfSize ;//!说明文件的大小,用字节为单位 unsigned int bfReserved1;//保留,必须设置为0 unsigned int bfOffBits ;//!说明从文件头开始到实际的图象数据之间的字节的偏移量,这里为14B+sizeof(BMPINFO) }BMPHEADER; typedef struct bmp_info { //40B unsigned int biSize ;//!BMPINFO结构所需要的字数 int biWidth ;//!图象的宽度,以象素为单位 int biHeight ;//!图象的宽度,以象素为单位,如果该值是正数,说明图像是倒向的,如果该值是负数,则是正向的 unsigned short biPlanes ;//!目标设备说明位面数,其值将总是被设为1 unsigned short biBitCount ;//!比特数/象素,其值为1、4、8、16、24、或32 unsigned int biCompression ;//说明图象数据压缩的类型 #define BI_RGB 0L //没有压缩 #define BI_RLE8 1L //每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引); #define BI_RLE4 2L //每个象素4比特的RLE压缩编码,压缩格式由2字节组成 #define BI_BITFIELDS 3L //每个象素的比特由指定的掩码决定。 unsigned int biSizeImage ;//图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0 int biXPelsPerMeter ;//水平分辨率,用象素/米表示 int biYPelsPerMeter ;//垂直分辨率,用象素/米表示 unsigned int biClrUsed ;//位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项) unsigned int biClrImportant ;//对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。 }BMPINFO; typedef struct tagRGBQUAD { unsigned char rgbBlue; unsigned char rgbGreen; unsigned char rgbRed; unsigned char rgbReserved; } RGBQUAD; typedef struct tagBITMAPINFO { BMPINFO bmiHeader; //RGBQUAD bmiColors[1]; unsigned int rgb[3]; } BITMAPINFO; static int get_rgb888_header(int w, int h, BMPHEADER * head, BMPINFO * info) { int size = 0; if (head && info) { size = w * h * 3; memset(head, 0, sizeof(* head)); memset(info, 0, sizeof(* info)); head->bfType[0] = 'B'; head->bfType[1] = 'M'; head->bfOffBits = 14 + sizeof(* info); head->bfSize = head->bfOffBits + size; head->bfSize = (head->bfSize + 3) & ~3;//windows要求文件大小必须是4的倍数 size = head->bfSize - head->bfOffBits; info->biSize = sizeof(BMPINFO); info->biWidth = w; info->biHeight = -h; info->biPlanes = 1; info->biBitCount = 24; info->biCompression = BI_RGB; info->biSizeImage = size; printf("rgb888:%dbit,%d*%d,%d\n", info->biBitCount, w, h, head->bfSize); } return size; } static int get_rgb565_header(int w, int h, BMPHEADER * head, BITMAPINFO * info) { int size = 0; if (head && info) { size = w * h * 2; memset(head, 0, sizeof(* head)); memset(info, 0, sizeof(* info)); head->bfType[0] = 'B'; head->bfType[1] = 'M'; head->bfOffBits = 14 + sizeof(* info); head->bfSize = head->bfOffBits + size; head->bfSize = (head->bfSize + 3) & ~3; size = head->bfSize - head->bfOffBits; info->bmiHeader.biSize = sizeof(info->bmiHeader); info->bmiHeader.biWidth = w; info->bmiHeader.biHeight = -h; info->bmiHeader.biPlanes = 1; info->bmiHeader.biBitCount = 16; info->bmiHeader.biCompression = BI_BITFIELDS; info->bmiHeader.biSizeImage = size; info->rgb[0] = 0xF800; info->rgb[1] = 0x07E0; info->rgb[2] = 0x001F; printf("rgb565:%dbit,%d*%d,%d\n", info->bmiHeader.biBitCount, w, h, head->bfSize); } return size; } static int save_bmp_rgb565(FILE * hfile, int w, int h, void * pdata) { int success = 0; int size = 0; BMPHEADER head; BITMAPINFO info; size = get_rgb565_header(w, h, &head, &info); if (size > 0) { fwrite(head.bfType, 1, 14, hfile); fwrite(&info, 1, sizeof(info), hfile); fwrite(pdata, 1, size, hfile); success = 1; } return success; } static int save_bmp_rgb888(FILE * hfile, int w, int h, void * pdata) { int success = 0; int size = 0; BMPHEADER head; BMPINFO info; size = get_rgb888_header(w, h, &head, &info); if (size > 0) { fwrite(head.bfType, 1, 14, hfile); fwrite(&info, 1, sizeof(info), hfile); fwrite(pdata, 1, size, hfile); success = 1; } return success; } int save_bmp(const char * path, int w, int h, void * pdata, int bpp) { int success = 0; FILE * hfile = NULL; do { if (path == NULL || w <= 0 || h <= 0 || pdata == NULL) { printf("if (path == NULL || w <= 0 || h <= 0 || pdata == NULL)\n"); break; } remove(path); hfile = fopen(path, "wb"); if (hfile == NULL) { printf("open(%s) failed!\n", path); break; } switch (bpp) { case 16: success = save_bmp_rgb565(hfile, w, h, pdata); break; case 24: success = save_bmp_rgb888(hfile, w, h, pdata); break; default: printf("error: not support format!\n"); success = 0; break; } } while (0); if (hfile != NULL) fclose(hfile); return success; } //-------------------------------------------------------------------
3.运行方式:
cygwin下用ndk编译成可执行文件,再将生成的可执行文件push到模拟器的/data/local路径,
修改文件为可执行的,直接运行便可以截屏,图片保存到/mnt/sdcard/s.bmp。
Administrator@xxx /cygdrive/e $ cd myprj/screenshot/jni/ Administrator@xxx /cygdrive/e/myprj/screenshot/jni $ ndk-build Compile thumb : savebmp <= savebmp.c Compile thumb : savebmp <= screenshot.c StaticLibrary : libsavebmp.a Executable : save Install : save => libs/armeabi/save Administrator@xxx /cygdrive/e/myprj/screenshot/jni $ adb shell error: device offline Administrator@xxx /cygdrive/e/myprj/screenshot/jni $ adb shell # Administrator@xxx /cygdrive/e/myprj/screenshot/jni $ adb push ../libs/armeabi/save /data/local 83 KB/s (10636 bytes in 0.125s) Administrator@xxx /cygdrive/e/myprj/screenshot/jni $ adb shell # cd /data/local cd /data/local # chmod 777 save chmod 777 save # ./save ./save rgb565:16bit,800*480,768068 # ls -l /mnt/sdcard ls -l /mnt/sdcard d---rwxr-x system sdcard_rw 2012-02-19 12:39 LOST.DIR d---rwxr-x system sdcard_rw 2012-02-20 13:22 DCIM ----rwxr-x system sdcard_rw 768068 2012-02-20 13:22 s.bmp #
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,里面列举了五种格式,不过有些设备上可能不是下面的任何一种。
case 1: /* RGBA_8888 */ fbinfo.bpp = 32; fbinfo.size = w * h * 4; fbinfo.width = w; fbinfo.height = h; fbinfo.red_offset = 0; fbinfo.red_length = 8; fbinfo.green_offset = 8; fbinfo.green_length = 8; fbinfo.blue_offset = 16; fbinfo.blue_length = 8; fbinfo.alpha_offset = 24; fbinfo.alpha_length = 8; break; case 2: /* RGBX_8888 */ fbinfo.bpp = 32; fbinfo.size = w * h * 4; fbinfo.width = w; fbinfo.height = h; fbinfo.red_offset = 0; fbinfo.red_length = 8; fbinfo.green_offset = 8; fbinfo.green_length = 8; fbinfo.blue_offset = 16; fbinfo.blue_length = 8; fbinfo.alpha_offset = 24; fbinfo.alpha_length = 0; break; case 3: /* RGB_888 */ fbinfo.bpp = 24; fbinfo.size = w * h * 3; fbinfo.width = w; fbinfo.height = h; fbinfo.red_offset = 0; fbinfo.red_length = 8; fbinfo.green_offset = 8; fbinfo.green_length = 8; fbinfo.blue_offset = 16; fbinfo.blue_length = 8; fbinfo.alpha_offset = 24; fbinfo.alpha_length = 0; break; case 4: /* RGB_565 */ fbinfo.bpp = 16; fbinfo.size = w * h * 2; fbinfo.width = w; fbinfo.height = h; fbinfo.red_offset = 11; fbinfo.red_length = 5; fbinfo.green_offset = 5; fbinfo.green_length = 6; fbinfo.blue_offset = 0; fbinfo.blue_length = 5; fbinfo.alpha_offset = 0; fbinfo.alpha_length = 0; break; case 5: /* BGRA_8888 */ fbinfo.bpp = 32; fbinfo.size = w * h * 4; fbinfo.width = w; fbinfo.height = h; fbinfo.red_offset = 16; fbinfo.red_length = 8; fbinfo.green_offset = 8; fbinfo.green_length = 8; fbinfo.blue_offset = 0; fbinfo.blue_length = 8; fbinfo.alpha_offset = 24; fbinfo.alpha_length = 8; break;
ics-src\frameworks\base\cmds\screencap\screencap.cpp
ics-src\frameworks\base\cmds\screenshot\screenshot.c