一步步CEF(5)之窗口截图(非离屏版本)

前面有解释了离屏渲染的相关内容,效率一般。如果电脑配置了独立显卡,真屏截图比离屏快那么一点点,如果没有GPU的服务器,结果很难堪。现在开始记录操作流程,免得忘记了细节。

 第一、修改root_windows_win.c中的

void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings,bool initially_hidden) 

函数;

  DWORD dwStyle = WS_CHILD | WS_POPUP;  //真屏截图用到
  if (build_simple_browser)  
  {
	  dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN; //这是浏览器编译用到的
  }

注:使用WS_CHILD和WS_POPUP使得整个网页填充慢窗体,方便截图。

第二、修改root_windows_win.c中

void RootWindowWin::SetBounds(int x, int y, size_t width, size_t height) {
  REQUIRE_MAIN_THREAD();

  if (hwnd_) {
	  LONG st = GetWindowLong(hwnd_, GWL_STYLE);
	  if (st & WS_THICKFRAME)
	  {
		  /*   突破屏幕限制   */
		  st = st ^ WS_THICKFRAME;
		  st |= WS_POPUP;
		  SetWindowLong(hwnd_, GWL_STYLE, st);
	  }
	  win_bounds.left = x;
	  win_bounds.top = y;
	  win_bounds.right = x + width;
	  win_bounds.bottom = y + height;
	  SetWindowPos(hwnd_, NULL, x, y, static_cast<int>(width), static_cast<int>(height), SWP_NOZORDER);
  }
}

看注释就知道了,如果不修改的话,截图会受限于屏幕的尺寸。(我是翻查了别人的VB代码参考过来的)

第三、修改root_windows_win.c中

void RootWindowWin::OnPaint()

提示面页渲染更新,连续截图需要用到,具体代码给也没用什么,我这边是用共享内存设置标志的,其他的方式也行。

第四、修改root_windows_win.c中的onSize函数

void RootWindowWin::OnSize(bool minimized) {
	
	RECT rect;
	GetClientRect(hwnd_, &rect);

	int width = win_bounds.right - win_bounds.left;
	int height = win_bounds.bottom - win_bounds.top;

	if (minimized) {
		ShowWindow(hwnd_, SW_SHOWNORMAL);
		MoveWindow(hwnd_, width - 30, height - 30, width, height, FALSE);

		// Notify the browser window that it was hidden and do nothing further.
		if (browser_window_)
		{
			// browser_window_->Hide();
		}
		return;
	}
。。。。省略这段代码。。。。(client例子里面的)
}

如果没处理最小化的情况,一旦手痒点一下最小化,那恭喜你,得到一堆无用的小图。

另外还有OnCreate和onClose也要设置标志,如果不用理会也行,就读取browser->getHost()->getWindowHandle()判断一下非空就开始截图,也是可以的。

第五、修改cef_client.c的RunMain()函数:


		RootWindowConfig window_config;
		window_config.always_on_top = false;
		window_config.with_controls = false;
		window_config.with_osr = false;
		window_config.initially_hidden = false;

			rootWin->SetBounds(0, 0, ww, hh);


			HWND hwnd_ = rootWin->GetWindowHandle();
			if (hwnd_)
			{
				::MoveWindow(hwnd_, ww - 30 , hh - 30, ww, hh, FALSE);  //放在屏幕右下角;如果不怕影响可以忽略。
			}

第六、使用PrintWindow+GetDIBits截图

//为了方便理解,我抄来了保存bmp的代码,要实现内存保存也很简单的。
static void SaveHwndToBmpFile(HWND hWnd, const char *lpszPath)
	{
		UpdateWindow(hWnd);
		HDC hDC = ::GetWindowDC(hWnd);
		if (!hDC)
		{
			return;
		}

		HDC hMemDC = ::CreateCompatibleDC(hDC);
		if (!hMemDC)
		{
			return;
		}

		RECT rc;
		::GetWindowRect(hWnd, &rc);

		HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, rc.right - rc.left, rc.bottom - rc.top);
		if (!hBitmap)
		{
			return;
		}

		HBITMAP hOldBmp = (HBITMAP)::SelectObject(hMemDC, hBitmap);

		//::PrintWindow(hWnd, hMemDC, 0);
		::PrintWindow(hWnd, hMemDC, PW_CLIENTONLY);
		//::PrintWindow(hWnd, hMemDC, 2);

		BITMAP bitmap = { 0 };
		::GetObject(hBitmap, sizeof(BITMAP), &bitmap);
		BITMAPINFOHEADER bi = { 0 };
		BITMAPFILEHEADER bf = { 0 };

		CONST int nBitCount = 32;
		bi.biSize = sizeof(BITMAPINFOHEADER);
		bi.biWidth = bitmap.bmWidth;
		bi.biHeight = bitmap.bmHeight;
		bi.biPlanes = 1;
		bi.biBitCount = nBitCount;
		bi.biCompression = BI_RGB;
		DWORD dwSize = ((bitmap.bmWidth * nBitCount + 31) / 32) * 4 * bitmap.bmHeight;

		HANDLE hDib = GlobalAlloc(GHND, dwSize + sizeof(BITMAPINFOHEADER));
		LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
		*lpbi = bi;

		::GetDIBits(hMemDC, hBitmap, 0, bitmap.bmHeight, (BYTE*)lpbi + sizeof(BITMAPINFOHEADER), (BITMAPINFO*)lpbi, DIB_RGB_COLORS);


		FILE *fp = fopen(lpszPath, "wb");
		bf.bfType = 0x4d42;
		dwSize += sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
		bf.bfSize = dwSize;
		bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

		fwrite((BYTE*)&bf, sizeof(BITMAPFILEHEADER), 1, fp);

		fwrite((BYTE*)lpbi, 1, dwSize - sizeof(BITMAPFILEHEADER), fp);
		fclose(fp);

		GlobalUnlock(hDib);
		GlobalFree(hDib);

		::SelectObject(hMemDC, hOldBmp);
		::DeleteObject(hBitmap);
		::DeleteObject(hMemDC);
		::ReleaseDC(hWnd, hDC);
	}
	

大概就这些要点,还有一些细节可能有所忽略,要具体操作的时候才能发现吧。就这样了。。。

2023-12-15增补

目前CEF119.4.3截图超过屏幕会变成白板的,在本地电脑只需要MoveWindow一遍就可以拿到大图,但是在服务器上就必须一次拿一部分,最后合并大图,个人猜测跟服务器电脑的配置有关,建议服务器直接配无头模式(Off-screen)。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值