从上一节在windows下对android进行实时快速录屏和模拟点击(一)——使用adb命令行当中,描述了适合于所有情况下的通用方法——adb。而通用方法的最大缺点就是:速度非常慢,在有些时候是远远不能满足需求的。
为了解决这个问题,本节当中描述一个更高效的方法——通过win32 API直接操控模拟器。而众所周知,windows下编程没有什么能比windows api更为高效了。
(对于通常的android开发调试当中,操作模拟器已经足够。但如果有什么需求必须要使用真机同时还要求高速度,只需要将本文中的模拟器替换为远程桌面软件——我用的是Total Control——即可)
文章未写完,先放上程序和效果图
2018.2.28:过完春节的我来填坑了2333……
窗口截屏原理
窗口截屏主要参考了这一篇博文:
重温 Win32 API ----- 截屏指定窗口并打印
基本思路是将窗口DC中的显示缓冲区数据由DDB位图转化为DIB位图,随后保存到自己的内存缓冲区当中。
与参考博文当中的方法相比,有以下几个变化:
1. 去除了无关的打印机部分
2. cScreenCap类是一个基类,主要有init()、release()、run()三个函数。run()函数会在未收到终止指令时无限循环调用回调虚函数virtual boolImgProcess( const cv::Mat & screen );
3. cScreenCap类具有RAII特性
4. DIB位图直接复制到OpenCV的Mat结构当中。由于其端序(大端BGRA)与opencv默认格式相同,可直接bit to bit 复制
头文件定义如下:
class cScreenCap
{
public:
cScreenCap( float dpi_scale );
virtual ~cScreenCap();
void init( HWND hwnd );
void release();
bool run();//会循环调用ImgProcess()
void Press( cv::Point p, int ms );
void MouseDown( cv::Point p );
void MouseUp( cv::Point p );
void Swipe( cv::Point p1, cv::Point p2, int ms );
void HitKey( int key );
virtual bool ImgProcess( const cv::Mat & screen );//图像处理回调虚函数,默认操作是在新窗口中显示图像
const float _DPI_Scale;//windows窗口缩放比例
protected:
::time_t _t;//计时
::HWND _hWnd = nullptr, _hParentWnd = nullptr;//窗口句柄
::HDC _hdcWnd = nullptr, _hdcMem = nullptr;//窗口上下文,内存上下文
HBITMAP _hbmWnd = nullptr;//DIB图像句柄
BITMAP _bmpWnd;//DIB图像
::RECT _rectClient;//客户区矩形
};
最重要的函数有两个:init()和run()。
初始化过程中,主要完成了获取客户区窗口大小(我并不需要标题栏和边框)、创建并初始化相关句柄的工作。这里特别需要注意的是,由于现在的windows为了支持高分辨率,通常都引入了DPI缩放。简而言之,就是先绘制一个大的窗口,然后缩小一定倍数显示出来。所以,实际内存中图像的宽、高是实际客户区乘以DPI缩放值。如果还按照客户区矩形的大小去获取图像,只能得到实际窗口的一部分。
void cScreenCap::init( HWND hwnd )
{
release();
//获取窗口句柄
_hWnd = hwnd;
//获取窗口大小
::GetClientRect( _hWnd, &