记录下几种常见截屏方式。
1.windows下截屏
#include <Windows.h>
typedef struct {
HDC hBmpFileDC;
HDC hDesktopDC;
HWND hDesktopWnd;
void* pBits;
HBITMAP hBmpFileBitmap;
}win_picture;
win_picture image;
void get_screen_size(int* width, int* height)
{
*width = GetSystemMetrics(SM_CXSCREEN);
*height = GetSystemMetrics(SM_CYSCREEN);
}
int init_capture(int* width, int* height, int *image_len)
{
BITMAPINFO bi;
BITMAPFILEHEADER bitHead;
*width = GetSystemMetrics(SM_CXSCREEN);
*height = GetSystemMetrics(SM_CYSCREEN);
image.hDesktopWnd = GetDesktopWindow();
image.hDesktopDC = GetDC(image.hDesktopWnd);
ZeroMemory(&bi, sizeof(bi));
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biHeight = -(*height);
bi.bmiHeader.biWidth = *width;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 24;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biSizeImage = 3 * (*height) * (*width);
image.hBmpFileDC = CreateCompatibleDC(image.hDesktopDC);
image.hBmpFileBitmap = CreateDIBSection(image.hDesktopDC,
&bi, DIB_RGB_COLORS, &image.pBits, NULL, 0);
SelectObject(image.hBmpFileDC, image.hBmpFileBitmap);
ZeroMemory(&bitHead, sizeof(bitHead));
bitHead.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bitHead.bfSize = bi.bmiHeader.biSizeImage + bitHead.bfOffBits;
bitHead.bfType = 'MB';
*image_len = (*width) * (*height) * 3;
return 0;
}
int capture(unsigned char **dst_buf, int width, int height)
{
CURSORINFO cursorInfo = { 0 };
cursorInfo.cbSize = sizeof(cursorInfo);
//Retrieves information about the global cursor
GetCursorInfo(&cursorInfo);
ICONINFO IconInfo = { 0 };
//Retrieves information about the specified icon or cursor.
GetIconInfo(cursorInfo.hCursor, &IconInfo);
BitBlt(image.hBmpFileDC,
0, 0, width, height,
image.hDesktopDC, 0, 0, SRCCOPY);
DeleteObject(IconInfo.hbmColor);
DeleteObject(IconInfo.hbmMask);
//Draws an icon or cursor into the specified device context.
DrawIcon(image.hBmpFileDC,
cursorInfo.ptScreenPos.x - IconInfo.xHotspot,
cursorInfo.ptScreenPos.y - IconInfo.yHotspot,
cursorInfo.hCursor);
*dst_buf = (unsigned char*)image.pBits;
return 0;
}
void free_picture()
{
if (image.hBmpFileBitmap != NULL || image.hBmpFileDC != NULL)
{
DeleteDC(image.hBmpFileDC);
DeleteObject(image.hBmpFileBitmap);
ReleaseDC(image.hDesktopWnd, image.hDesktopDC);
}
}
2.linux使用双缓冲FrameBuffer截屏,可以先用cat /dev/fb0 > output,验证是否有图像。
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
int init_image_data(int *fd, long *screensize, int *width, int *height)
{
static struct fb_var_screeninfo vi;
static struct fb_fix_screeninfo fi;
int x = 0, y = 0;
*fd = open("/dev/fb0", O_RDWR);
if(ioctl(*fd, FBIOGET_VSCREENINFO, &vi) < 0)
{
printf("ioctl FBIOGET_VSCREENINFO failed\n");
return 0;
}
if(ioctl(*fd, FBIOPUT_VSCREENINFO, &vi) < 0)
{
printf("ioctl FBIOPUT_VSCREENINFO failed\n");
return 0;
}
if(ioctl(*fd, FBIOGET_FSCREENINFO, &fi) < 0)
{
printf("ioctl FBIOGET_FSCREENINFO failed\n");
return 0;
}
*screensize = vi.xres * vi.yres * vi.bits_per_pixel / 8;
printf("width = %d\n", vi.xres);
printf("height = %d\n", vi.yres);
*width = vi.xres;
*height = vi.yres;
return 1;
}
void set_image_data(long screensize, int fd, char *buf)
{
char *bits = mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
memcpy(bits, buf, screensize);
munmap(bits, screensize);
}
void get_image_data(long screensize, int fd, char *buf)
{
char *bits = mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
memcpy(buf, bits, screensize);
munmap(bits, screensize);
}
void free_image(int fd)
{
close(fd);
}
3.linux使用xcb截屏,需要xcb库。
#include <stdlib.h>
#include <fcntl.h>
#include <xcb/xcb.h>
#include <xcb/xfixes.h>
#include <xcb/xproto.h>
#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
#define BLEND(target, source, alpha) \
(target) + ((source) * (255 - (alpha)) + 255 / 2) / 255
typedef struct xcb_info
{
xcb_connection_t *con;
xcb_drawable_t drawable;
xcb_get_image_reply_t *img;
}xcb_info;
xcb_info xcb_info_i;
static xcb_screen_t *get_screen(const xcb_setup_t *setup, int screen_num)
{
xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup);
xcb_screen_t *screen = NULL;
for(;it.rem > 0; xcb_screen_next(&it))
{
if(!screen_num)
{
screen = it.data;
break;
}
screen_num--;
}
return screen;
}
//check draw mouse event
static int check_xfixes(xcb_connection_t *con)
{
xcb_xfixes_query_version_cookie_t cookie;
xcb_xfixes_query_version_reply_t *reply;
cookie = xcb_xfixes_query_version(con, XCB_XFIXES_MAJOR_VERSION,
XCB_XFIXES_MINOR_VERSION);
reply = xcb_xfixes_query_version_reply(con, cookie, NULL);
if(reply)
{
free(reply);
return 1;
}
return 0;
}
static void xcbgrab_draw_mouse(xcb_connection_t *con, unsigned char *data_buff,
int pic_x, int pic_y, int pic_width, int pic_height)
{
unsigned int *cursor;
int length;
xcb_xfixes_get_cursor_image_cookie_t cc;
xcb_xfixes_get_cursor_image_reply_t *ci;
int cx, cy, x, y, w, h, c_off, i_off;
cc = xcb_xfixes_get_cursor_image(con);
ci = xcb_xfixes_get_cursor_image_reply(con, cc, NULL);
if(!ci)
return;
cursor = xcb_xfixes_get_cursor_image_cursor_image(ci);
length = xcb_xfixes_get_cursor_image_cursor_image_length(ci);
if(!cursor)
return;
cx = ci->x - ci->xhot;
cy = ci->y - ci->yhot;
x = FFMAX(cx, pic_x);
y = FFMAX(cy, pic_y);
w = FFMIN(cx + ci->width, pic_x + pic_width) - x;
h = FFMIN(cy + ci->height, pic_y + pic_height) - y;
c_off = x - cx;
i_off = x - pic_x;
cursor += (y - cy) * ci->width;
data_buff += (y - pic_y) * pic_width * 4; //4 represents the size of a pixel
for(y = 0; y < h; y++)
{
cursor += c_off;
data_buff += i_off * 4;
for(x = 0; x < w; x++, cursor++, data_buff += 4)
{
int r, g, b, a;
r = *cursor & 0xff;
g = (*cursor >> 8) & 0xff;
b = (*cursor >> 16) & 0xff;
a = (*cursor >> 24) & 0xff;
if(!a)
continue;
if(a == 255)
{
data_buff[0] = r;
data_buff[1] = g;
data_buff[2] = b;
} else
{
data_buff[0] = BLEND(r, data_buff[0], a);
data_buff[1] = BLEND(g, data_buff[1], a);
data_buff[2] = BLEND(b, data_buff[2], a);
}
}
cursor += ci->width - w - c_off;
data_buff += (pic_width - w - i_off) * 4;
}
free(ci);
// return length;
}
void init_capture(int *width, int *height, int *stride)
{
DEBUG("entry init_capture");
xcb_screen_t *screen;
const xcb_setup_t *setup;
const xcb_format_t *fmt;
int screen_num;
int bpp = 0;
int length;
xcb_info_i.con = xcb_connect(":0.0", &screen_num);
setup = xcb_get_setup(xcb_info_i.con);
screen = get_screen(setup, screen_num);
fmt = xcb_setup_pixmap_formats(setup);
length = xcb_setup_pixmap_formats_length(setup);
while (length--)
{
if (screen->root_depth == fmt->depth)
{
bpp = fmt->bits_per_pixel;
break;
}
fmt++;
}
*width = screen->width_in_pixels;
*height = screen->height_in_pixels;
*stride = (*width) * (bpp >> 3);
xcb_info_i.drawable = screen->root;
}
void capture(unsigned char **buf, int *size_buf, int width, int height)
{
xcb_generic_error_t *err = NULL;
xcb_get_image_cookie_t iq;
int ret_mouse;
if (xcb_info_i.img)
{
free(xcb_info_i.img);
xcb_info_i.img = NULL;
}
iq = xcb_get_image(
xcb_info_i.con, XCB_IMAGE_FORMAT_Z_PIXMAP,
xcb_info_i.drawable, 0, 0,
width, height, ~0);
xcb_info_i.img = xcb_get_image_reply(xcb_info_i.con, iq, &err);
if (err)
{
DEBUG("get image failed\n");
return;
}
*buf = xcb_get_image_data(xcb_info_i.img);
*size_buf = xcb_get_image_data_length(xcb_info_i.img);
ret_mouse = check_xfixes(xcb_info_i.con);
if (!ret_mouse)
{
DEBUG("connot draw the mouse\n");
return;
}
xcbgrab_draw_mouse(xcb_info_i.con, *buf,
0, 0, width, height);
}
void free_picture()
{
if (xcb_info_i.img)
{
free(xcb_info_i.img);
xcb_info_i.img = NULL;
}
if(xcb_info_i.con)
{
xcb_disconnect(xcb_info_i.con);
xcb_info_i.con = NULL;
}
}
参考ffmpeg中。