离屏渲染(Off-Screen Rendering)
在离屏渲染模式下,CEF不会创建原生浏览器窗口。CEF为宿主程序提供无效的区域和像素缓存区,而宿主程序负责通知鼠标键盘以及焦点事件给CEF。离屏渲染目前不支持混合加速,所以性能上可能无法和非离屏渲染相比。离屏浏览器将收到和窗口浏览器同样的事件通知,
下面介绍如何使用离屏渲染:
实现CefRenderHandler接口。除非特别说明,所有的方法都需要覆写。
调用CefWindowInfo::SetAsOffScreen(),将CefWindowInfo传递给CefBrowserHost::CreateBrowser()之前还可以选择设置CefWindowInfo::SetTransparentPainting()。如果没有父窗口被传递给SetAsOffScreen,则有些类似上下文菜单这样的功能将不可用。
CefRenderHandler::GetViewRect方法将被调用以获得所需要的可视区域。
CefRenderHandler::OnPaint() 方法将被调用以提供无效区域(脏区域)以及更新过的像素缓存。cefclient程序里使用OpenGL绘制缓存,但你可以使用任何别的绘制技术。
可以调用CefBrowserHost::WasResized()方法改变浏览器大小。这将导致对GetViewRect()方法的调用,以获取新的浏览器大小,然后调用OnPaint()重新绘制。
调用CefBrowserHost::SendXXX()方法通知浏览器的鼠标、键盘和焦点事件。
调用CefBrowserHost::CloseBrowser()销毁浏览器。
使用命令行参数–off-screen-rendering-enabled运行cefclient,可以测试离屏渲染的效果。
离屏渲染可以Qt背景透明下的显示问题,比如做一些异形窗口。
我们从一个简单的例子来实现Qt中的Cef3的离屏渲染。上面的步骤看起来有点复杂,但我们从实际的例子入手,一步步完善,就简单了。
第一步:
在CefSettings中的windowless_rendering_enabled设置为true
settings.windowless_rendering_enabled = true;
第二步:
继承CefRenderHandler,并且实现它全部接口(也就是纯虚函数),和重新部分virtual函数(是部分,可以不用全部实现,这个看你的功能需求)
// Called to retrieve the view rectangle which is relative to screen
// coordinates. This method must always provide a non-empty rectangle.
///
/*--cef()--*/
virtual void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) = 0;
// Called when an element should be painted. Pixel values passed to this
// method are scaled relative to view coordinates based on the value of
// CefScreenInfo.device_scale_factor returned from GetScreenInfo. |type|
// indicates whether the element is the view or the popup widget. |buffer|
// contains the pixel data for the whole image. |dirtyRects| contains the set
// of rectangles in pixel coordinates that need to be repainted. |buffer| will
// be |width|*|height|*4 bytes in size and represents a BGRA image with an
// upper-left origin. This method is only called when
// CefWindowInfo::shared_texture_enabled is set to false.
///
/*--cef()--*/
virtual void OnPaint(CefRefPtr<CefBrowser> browser,
PaintElementType type,
const RectList& dirtyRects,
const void* buffer,
int width,
int height) = 0;
解释一下:
virtual void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) = 0;
这个纯虚函数就是获取浏览器的矩形范围,想象一下,如果你想将浏览器放在某个QWidget,或者子控件上,那么这里的rect就应该返回该QWidget的矩形区域。在实际中,这个rect设置为(0, 0, width, height)。
看这里的rect的引用,其实就是要对这个rect进行赋值,剩下的就交给Cef自己去做了。
第二个函数:
virtual void OnPaint(CefRefPtr<CefBrowser> browser,
PaintElementType type,
const RectList& dirtyRects,
const void* buffer,
int width,
int height) = 0;
这个就比较关键了
它就是将获取的每一帧图片的数据存放在buffer指针指向内存中,width和height是这张图片的宽和高。
这个type,根据源码其实就是
typedef enum {
PET_VIEW = 0,
PET_POPUP,
} cef_paint_element_type_t;
这个枚举,这个可以先不用去管它,后面再深入了解它就行,知道有这么个参数就行。
第三步:
将第二步中OnPaint中获取的图像数据,贴到QWidget所在的矩形位置上。
通过事件,将刚刚获取的图像,以QImage对象的方式,发送给窗口,在paintEvent中将这个QImage贴到界面上(调用QPainter的drawImage)
第四步:
在窗口的resizeEvent事件中调用
调用CefBrowserHost的WasResized()方法,这样窗口大小变化的时候,离屏渲染出来的图像大小也会变化,我们看到的就是“浏览器”随着窗口的尺寸变化而变化。
以上就是我自己摸索出来的最简单的离屏渲染的几个步骤,接下来的几篇博客,我会逐步添加一些响应事件,让离屏渲染的功能更加完善。万丈高楼平地起,我们一步步来。
我们从代码开始:
main.cpp中
#include "cefosrwidget.h"
#include "simple_app.h"
#include "simple_handler.h"
#include <QApplication>
void QCefInitSettings(CefSettings & settings)
{
//std::string cache_path = AppGetWorkingDirectory().toStdString() + "/.cache";//缓存地址
// CefString(&settings.cache_path) = CefString(cache_path);
settings.multi_threaded_message_loop = true;//多线程消息循环
settings.log_severity = LOGSEVERITY_DISABLE;//日志
settings.windowless_rendering_enabled = true;
settings.no_sandbox = false;//沙盒
}
int QCefInit(int& argc, char** argv)
{
HINSTANCE hInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
CefMainArgs mainArgs(hInstance);
CefRefPtr<SimpleApp> app(new SimpleApp); //CefApp实现,用于处理进程相关的回调。
int exit_code = CefExecuteProcess(mainArgs, app.get(), nullptr);
if (exit_code >= 0) {
return exit_code;
}
CefSettings settings;
QCefInitSettings(settings);
CefInitialize(mainArgs, settings, app, nullptr);
return -1;
}
void CefQuit()
{
CefShutdown();
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
int result = QCefInit(argc, argv);
if (result >= 0) {
return result;
}
CefOSRWidget widget;
widget.show();
a.exec();
CefQuit();
return 0;
}
simple_app.h
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
#include "include/cef_app.h"
// Implement application-level callbacks for the browser process.
class SimpleApp : public CefApp, public CefBrowserProcessHandler {
public:
SimpleApp();
// CefApp methods:
virtual CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler()
OVERRIDE {
return this;
}
// CefBrowserProcessHandler methods:
virtual void OnContextInitialized() OVERRIDE;
private:
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING(SimpleApp);
};
#endif // CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
simple_app.cc
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "simple_app.h"
#include <string>
#include "include/cef_browser.h"
#include "include/cef_command_line.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"
#include "include/wrapper/cef_helpers.h"
#include "simple_handler.h"
namespace {
// When using the Views framework this object provides the delegate
// implementation for the CefWindow that hosts the Views-based browser.
class SimpleWindowDelegate : public CefWindowDelegate {