cef离屏渲染(osr)初识

业务需要,客户端的webview需要使用使用osr,透明绘制,才能实现一些业务,于是有了这个经历,如果你是新手,不妨看看。

环境配置

先下一个cef官方的demo,如果找不到请:点它
我下载的是这个cef离屏渲染(osr)初识-编程知识网
我的环境是win+vs2019所以我需要生成一个vs工程,
cef离屏渲染(osr)初识-编程知识网
这是我解压后的目录,一看显然可以用cmake生成vs工程
于是cef离屏渲染(osr)初识-编程知识网
如图,我是vs2019,下载是32位的,如此选择操作;于是工程就有了
cef离屏渲染(osr)初识-编程知识网
打开工程:cef离屏渲染(osr)初识-编程知识网
ceflient是一个五脏俱全的例子,所以选择它作为启动项,编译运行起来就是这个样子了:cef离屏渲染(osr)初识-编程知识网
看到是网页加载失败,因为他默认是谷歌的地址,被墙挡住了,所以就失败,换成百度的看看:修改这里cef离屏渲染(osr)初识-编程知识网
如图是在cefclient_win.cc里面。
于是成功的出来了出来了百度的首页cef离屏渲染(osr)初识-编程知识网

离屏模式

1,官方的例子肯定不是离屏模式(osr),需要操作一下;
只需要在启动时候加上 --off-screen-rendering-enabled启动参数,那么他就是离屏模式启动了或者我们在初始化cef的时候:

// Create the main message loop object.scoped_ptr<MainMessageLoop> message_loop;if (settings.multi_threaded_message_loop)message_loop.reset(new MainMessageLoopMultithreadedWin);else if (settings.external_message_pump)message_loop = MainMessageLoopExternalPump::Create();elsemessage_loop.reset(new MainMessageLoopStd);// Initialize CEF.settings.windowless_rendering_enabled = true;context->Initialize(main_args, settings, app, sandbox_info);

settings.windowless_rendering_enabled = true;设置成true,同样的效果。
2,离屏以后肉眼发现了个问题
cef离屏渲染(osr)初识-编程知识网
网页边上出现了一条线,看着很恶心,开始还一度怀疑是不是,网页绘制出来的。最后发现,是官方ceflient例子,创建的渲染窗口是一个有边框的窗口,改下这里就可以了

void OsrWindowWin::Create(HWND parent_hwnd, const RECT& rect) {CEF_REQUIRE_UI_THREAD();DCHECK(!hwnd_ && !render_handler_.get());DCHECK(parent_hwnd);DCHECK(!::IsRectEmpty(&rect));HINSTANCE hInst = ::GetModuleHandle(NULL);const cef_color_t background_color = MainContext::Get()->GetBackgroundColor();const HBRUSH background_brush = CreateSolidBrush(RGB(CefColorGetR(background_color), CefColorGetG(background_color),CefColorGetB(background_color)));RegisterOsrClass(hInst, background_brush);DWORD ex_style = 0;if (GetWindowLongPtr(parent_hwnd, GWL_EXSTYLE) & WS_EX_NOACTIVATE) {// Don't activate the browser window on creation.ex_style |= WS_EX_NOACTIVATE;}// Create the native window with a border so it's easier to visually identify// OSR windows.//他这里加了这个边框的属性WS_BORDER 把他去掉//hwnd_ = ::CreateWindowEx(//    ex_style, kWndClass, 0,//    WS_BORDER | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,//    rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,//    parent_hwnd, 0, hInst, 0);//CHECK(hwnd_);hwnd_ = ::CreateWindowEx(ex_style, kWndClass, 0,WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,parent_hwnd, 0, hInst, 0);CHECK(hwnd_);client_rect_ = rect;// Associate |this| with the window.SetUserDataPtr(hwnd_, this);#if defined(CEF_USE_ATL)accessibility_root_ = nullptr;// Create/register the drag&drop handler.drop_target_ = DropTargetWin::Create(this, hwnd_);HRESULT register_res = RegisterDragDrop(hwnd_, drop_target_);DCHECK_EQ(register_res, S_OK);
#endifime_handler_.reset(new OsrImeHandlerWin(hwnd_));// Enable Touch Events if requestedif (client::MainContext::Get()->TouchEventsEnabled())RegisterTouchWindow(hwnd_, 0);// Notify the window owner.NotifyNativeWindowCreated(hwnd_);
}

编译看下:
cef离屏渲染(osr)初识-编程知识网
就没有了

透明绘制

如何启动透明绘制呢,也是启动的时候加上这俩参数--no-proxy-server --transparent-painting-enabled就可以了;但是却发现背景变成这样的了:cef离屏渲染(osr)初识-编程知识网
这显然是不对头的,首先我怀疑是不是cef绘制给到数据就是这样的:于是找到继承这个类CefRenderHandler对象,找到改方法

oid ClientHandlerOsr::OnPaint(CefRefPtr<CefBrowser> browser,PaintElementType type,const RectList& dirtyRects,const void* buffer,int width,int height) {CEF_REQUIRE_UI_THREAD();if (!osr_delegate_)return;osr_delegate_->OnPaint(browser, type, dirtyRects, buffer, width, height);
}

这里保存图片验证下看看:最后发现,出来的图片背景不是五彩缤纷的。
那么最后只能是,渲染到窗口这个过程,出的幺蛾子,最后发现

void OsrRenderer::Render() {if (view_width_ == 0 || view_height_ == 0)return;DCHECK(initialized_);struct {float tu, tv;float x, y, z;} static vertices[] = {{0.0f, 1.0f, -1.0f, -1.0f, 0.0f},{1.0f, 1.0f, 1.0f, -1.0f, 0.0f},{1.0f, 0.0f, 1.0f, 1.0f, 0.0f},{0.0f, 0.0f, -1.0f, 1.0f, 0.0f}};glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);VERIFY_NO_ERROR;glMatrixMode(GL_MODELVIEW);VERIFY_NO_ERROR;glLoadIdentity();VERIFY_NO_ERROR;// Match GL units to screen coordinates.glViewport(0, 0, view_width_, view_height_);VERIFY_NO_ERROR;glMatrixMode(GL_PROJECTION);VERIFY_NO_ERROR;glLoadIdentity();VERIFY_NO_ERROR;// Draw the background gradient.glPushAttrib(GL_ALL_ATTRIB_BITS);VERIFY_NO_ERROR;// Don't check for errors until glEnd().glBegin(GL_QUADS);glColor4f(1.0, 0.0, 0.0, 1.0);  // redglVertex2f(-1.0, -1.0);glVertex2f(1.0, -1.0);glColor4f(0.0, 0.0, 1.0, 1.0);  // blueglVertex2f(1.0, 1.0);glVertex2f(-1.0, 1.0);glEnd();VERIFY_NO_ERROR;glPopAttrib();VERIFY_NO_ERROR;// Rotate the view based on the mouse spin.if (spin_x_ != 0) {glRotatef(-spin_x_, 1.0f, 0.0f, 0.0f);VERIFY_NO_ERROR;}if (spin_y_ != 0) {glRotatef(-spin_y_, 0.0f, 1.0f, 0.0f);VERIFY_NO_ERROR;}if (IsTransparent()) {// Alpha blending style. Texture values have premultiplied alpha.//就真的是这里搞出来的glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);VERIFY_NO_ERROR;// Enable alpha blending.glEnable(GL_BLEND);VERIFY_NO_ERROR;}// Enable 2D textures.glEnable(GL_TEXTURE_2D);VERIFY_NO_ERROR;// Draw the facets with the texture.DCHECK_NE(texture_id_, 0U);VERIFY_NO_ERROR;glBindTexture(GL_TEXTURE_2D, texture_id_);VERIFY_NO_ERROR;glInterleavedArrays(GL_T2F_V3F, 0, vertices);VERIFY_NO_ERROR;glDrawArrays(GL_QUADS, 0, 4);VERIFY_NO_ERROR;// Disable 2D textures.glDisable(GL_TEXTURE_2D);VERIFY_NO_ERROR;if (IsTransparent()) {// Disable alpha blending.glDisable(GL_BLEND);VERIFY_NO_ERROR;}// Draw a rectangle around the update region.if (settings_.show_update_rect && !update_rect_.IsEmpty()) {int left = update_rect_.x;int right = update_rect_.x + update_rect_.width;int top = update_rect_.y;int bottom = update_rect_.y + update_rect_.height;#if defined(OS_LINUX)// Shrink the box so that top & right sides are drawn.top += 1;right -= 1;
#else// Shrink the box so that left & bottom sides are drawn.left += 1;bottom -= 1;
#endifglPushAttrib(GL_ALL_ATTRIB_BITS);VERIFY_NO_ERRORglMatrixMode(GL_PROJECTION);VERIFY_NO_ERROR;glPushMatrix();VERIFY_NO_ERROR;glLoadIdentity();VERIFY_NO_ERROR;glOrtho(0, view_width_, view_height_, 0, 0, 1);VERIFY_NO_ERROR;glLineWidth(1);VERIFY_NO_ERROR;glColor3f(1.0f, 0.0f, 0.0f);VERIFY_NO_ERROR;// Don't check for errors until glEnd().glBegin(GL_LINE_STRIP);glVertex2i(left, top);glVertex2i(right, top);glVertex2i(right, bottom);glVertex2i(left, bottom);glVertex2i(left, top);glEnd();VERIFY_NO_ERROR;glPopMatrix();VERIFY_NO_ERROR;glPopAttrib();VERIFY_NO_ERROR;}
}

于是我把这里改成这样

if (IsTransparent()) {// Alpha blending style. Texture values have premultiplied alpha.//如下修改glBlendFunc(GL_ONE, GL_ZERO);VERIFY_NO_ERROR;// Enable alpha blending.glEnable(GL_BLEND);VERIFY_NO_ERROR;}

最后就变成这样了:
cef离屏渲染(osr)初识-编程知识网
为啥是黑色的,因为这个窗口时透明背景,透下到窗口背景它就是这色的。