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映射时需将安装虚拟尺寸进行映射,否则,截屏可能失败。

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

/********************************************************************
	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;

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

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

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

你可能感兴趣的:(c,android,struct,header,null,语言)