Qt+Cef3离屏渲染(一)

离屏渲染(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,可以测试离屏渲染的效果。

这段话是来自:https://github.com/fanfeilong/cefutil/blob/master/doc/CEF%20General%20Usage-zh-cn.md#off-screen-rendering

离屏渲染可以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 {
 public:
  explicit SimpleWindowDelegate(CefRefPtr<CefBrowserView> browser_view)
      : browser_view_(browser_view) {}

  void OnWindowCreated(CefRefPtr<CefWindow> window) OVERRIDE {
    // Add the browser view and show the window.
    window->AddChildView(browser_view_);
    window->Show();

    // Give keyboard focus to the browser view.
    browser_view_->RequestFocus();
  }

  void OnWindowDestroyed(CefRefPtr<CefWindow> window) OVERRIDE {
    browser_view_ = NULL;
  }

  bool CanClose(CefRefPtr<CefWindow> window) OVERRIDE {
    // Allow the window to close if the browser says it's OK.
    CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
    if (browser)
      return browser->GetHost()->TryCloseBrowser();
    return true;
  }

  CefSize GetPreferredSize(CefRefPtr<CefView> view) OVERRIDE {
    return CefSize(800, 600);
  }

 private:
  CefRefPtr<CefBrowserView> browser_view_;

  IMPLEMENT_REFCOUNTING(SimpleWindowDelegate);
  DISALLOW_COPY_AND_ASSIGN(SimpleWindowDelegate);
};

}  // namespace

SimpleApp::SimpleApp() {}

void SimpleApp::OnContextInitialized() {
  CEF_REQUIRE_UI_THREAD();

  CefRefPtr<CefCommandLine> command_line =
      CefCommandLine::GetGlobalCommandLine();

////  const bool use_transparent_painting =
////      use_windowless_rendering_ &&
//      command_line_->HasSwitch(switches::kTransparentPaintingEnabled);

#if defined(OS_WIN) || defined(OS_LINUX)
  // Create the browser using the Views framework if "--use-views" is specified
  // via the command-line. Otherwise, create the browser using the native
  // platform framework. The Views framework is currently only supported on
  // Windows and Linux.
  const bool use_views = command_line->HasSwitch("use-views");
#else
  const bool use_views = false;
#endif
}

这些和前面的例子都一样,接下来就有点不同了:
simple_handler.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_HANDLER_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_

#include "include/cef_client.h"
#include "include/base/cef_lock.h"

#include <list>
#include <QImage>
#include <QPainter>
#include <WinUser.h>
#include <QDebug>
#include <QObject>

class SimpleHandler : public CefClient,
                      public CefLifeSpanHandler,
                      public CefLoadHandler,
                      public CefFocusHandler,
                      public CefDisplayHandler,
                      public CefRenderHandler //新加的

{
public:
  explicit SimpleHandler(bool use_views);
  ~SimpleHandler() OVERRIDE;

  void setWinHandle(HWND hwnd);
  void setReceiver(QObject *receiver);

  // Provide access to the single global instance of this object.
  static SimpleHandler* GetInstance();

  // CefClient methods:
  virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() OVERRIDE { return this; }

  virtual CefRefPtr<CefFocusHandler> GetFocusHandler() OVERRIDE { return this; }

  virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() OVERRIDE { return this; }

  virtual CefRefPtr<CefLoadHandler> GetLoadHandler() OVERRIDE { return this; }

  virtual CefRefPtr<CefRenderHandler> GetRenderHandler() OVERRIDE { return this; } //新加的

  // CefLifeSpanHandler methods:
  virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
  virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
  virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;

  // CefLoadHandler methods:
  virtual void OnLoadError(CefRefPtr<CefBrowser> browser,
                           CefRefPtr<CefFrame> frame,
                           ErrorCode errorCode,
                           const CefString& errorText,
                           const CefString& failedUrl) OVERRIDE;



  // CefFocusHandler methods
//  virtual bool OnSetFocus(CefRefPtr<CefBrowser> browser, FocusSource source) OVERRIDE;

  // Request that all existing browser windows close.
  void CloseAllBrowsers(bool force_close);

  bool IsClosing() const { return is_closing_; }

  std::list<CefRefPtr<CefBrowser> > getCefBrowerList();

  //CefRenderHandler methods
  virtual void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) OVERRIDE;

  virtual void OnPaint(CefRefPtr<CefBrowser> browser,
                       PaintElementType type,
                       const RectList& dirtyRects,
                       const void* buffer,
                       int width,
                       int height) OVERRIDE;

private:
  // True if the application is using the Views framework.
  const bool use_views_;

  // List of existing browser windows. Only accessed on the CEF UI thread.
  typedef std::list<CefRefPtr<CefBrowser>> BrowserList;
  BrowserList browser_list_;

  bool is_closing_;

  // True if mouse cursor change is disabled.
  bool m_bMouseCursorChangeDisabled;

  HWND m_hwnd;
  QObject *m_receiver;

  // Include the default reference counting implementation.
  IMPLEMENT_REFCOUNTING(SimpleHandler);
};

#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_

simple_handler.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_handler.h"

#include <sstream>
#include <string>
#include <QDebug>
#include <QDateTime>
#include <QBuffer>
#include <QPixmap>
#include <QtWin>

#include <QCoreApplication>

#include "include/base/cef_bind.h"
#include "include/cef_app.h"
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
#include "events.h"

namespace {

SimpleHandler* g_instance = NULL;

}  // namespace

SimpleHandler::SimpleHandler(bool use_views)
    : use_views_(use_views), is_closing_(false) {
  DCHECK(!g_instance);
  g_instance = this;

  m_bMouseCursorChangeDisabled = false;

  m_receiver = nullptr;
}

SimpleHandler::~SimpleHandler() {
    g_instance = nullptr;
}

void SimpleHandler::setWinHandle(HWND hwnd)
{
    m_hwnd = hwnd;
}

void SimpleHandler::setReceiver(QObject *receiver)
{
    m_receiver = receiver;
}

// static
SimpleHandler* SimpleHandler::GetInstance() {
    return g_instance;
}

void SimpleHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
  CEF_REQUIRE_UI_THREAD()

  if (m_bMouseCursorChangeDisabled)
      browser->GetHost()->SetMouseCursorChangeDisabled(true);
  else
    browser->GetHost()->SetMouseCursorChangeDisabled(false);

  // Add to the list of existing browsers.
  browser_list_.push_back(browser);
}

bool SimpleHandler::DoClose(CefRefPtr<CefBrowser> browser) {
  CEF_REQUIRE_UI_THREAD()

  // Closing the main window requires special handling. See the DoClose()
  // documentation in the CEF header for a detailed destription of this
  // process.
  if (browser_list_.size() == 1) {
    // Set a flag to indicate that the window close should be allowed.
    is_closing_ = true;
  }

  // Allow the close. For windowed browsers this will result in the OS close
  // event being sent.
  return false;
}

void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
  CEF_REQUIRE_UI_THREAD()

  // Remove from the list of existing browsers.
  BrowserList::iterator bit = browser_list_.begin();
  for (; bit != browser_list_.end(); ++bit) {
    if ((*bit)->IsSame(browser)) {
      browser_list_.erase(bit);
      break;
    }
  }

  if (browser_list_.empty()) {
    // All browser windows have closed. Quit the application message loop.
    CefQuitMessageLoop();
  }
}

void SimpleHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
                                CefRefPtr<CefFrame> frame,
                                ErrorCode errorCode,
                                const CefString& errorText,
                                const CefString& failedUrl) {
  CEF_REQUIRE_UI_THREAD()

  qDebug() << "errorCode: " << errorCode;

  // Don't display an error for downloaded files.
  if (errorCode == ERR_ABORTED)
    return;

  // Display a load error message.
  std::stringstream ss;
  ss << "<html><body bgcolor=\"white\">"
        "<h2>Failed to load URL "
     << std::string(failedUrl) << " with error " << std::string(errorText)
     << " (" << errorCode << ").</h2></body></html>";
  frame->LoadString(ss.str(), failedUrl);
}

void SimpleHandler::CloseAllBrowsers(bool force_close) {
  if (!CefCurrentlyOn(TID_UI)) {
    // Execute on the UI thread.
    CefPostTask(TID_UI, base::Bind(&SimpleHandler::CloseAllBrowsers, this,
                                   force_close));
    return;
  }

  if (browser_list_.empty())
    return;

  BrowserList::const_iterator it = browser_list_.begin();
  for (; it != browser_list_.end(); ++it)
      (*it)->GetHost()->CloseBrowser(force_close);
}

std::list<CefRefPtr<CefBrowser> > SimpleHandler::getCefBrowerList()
{
    return browser_list_;
}

void SimpleHandler::GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect)
{
    RECT clientRect;
    if (!::GetClientRect(m_hwnd, &clientRect))
        return;

    rect.x = rect.y = 0;
    rect.width = clientRect.right;
    rect.height = clientRect.bottom;

    return;
}

void SimpleHandler::OnPaint(CefRefPtr<CefBrowser> browser,
                            CefRenderHandler::PaintElementType type,
                            const CefRenderHandler::RectList &dirtyRects,
                            const void *buffer,
                            int width,
                            int height)
{
    HBITMAP bitmap= CreateBitmap(width,height,1,32,buffer);
    QImage image = QtWin::imageFromHBITMAP(bitmap);

    if (image.isNull())
    {
        qDebug() << "image is null";
        return;
    }

    //以下为测试代码,将图片写到文件中去
    //QString fileName = QString("D:/1/%1.png").arg(QDateTime::currentMSecsSinceEpoch());
    //image.save(fileName, "PNG");

    UpdateEvent *updateEvent = new UpdateEvent(image);
    QCoreApplication::postEvent(m_receiver, updateEvent);
}

自定义事件,需要了解postEvent是在异步的,可以跨线程使用,如果不清楚,可以查阅相应资料

events.h

#ifndef EVENTS_H
#define EVENTS_H

#include <QEvent>
#include <QImage>

class UpdateEvent : public QEvent
{
public:
    const static Type eventType = static_cast<Type>(QEvent::User+1);
    explicit UpdateEvent(const QImage &image);
    QImage m_image;
};

#endif // EVENTS_H

events.cpp

#include "events.h"

UpdateEvent::UpdateEvent(const QImage &image) :QEvent(eventType)
{
    m_image = image;
}

cefosrwidget.h

#ifndef CEFOSRWIDGET_H
#define CEFOSRWIDGET_H

#include "simple_handler.h"

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class CefOSRWidget; }
QT_END_NAMESPACE

class CefOSRWidget : public QWidget
{
    Q_OBJECT

public:
    CefOSRWidget(QWidget *parent = nullptr);
    ~CefOSRWidget() override;

protected:
    void resizeEvent(QResizeEvent *event) override;

    void paintEvent(QPaintEvent *event) override;

    bool event(QEvent *event) override;

    CefRefPtr<CefBrowser> getBrowser();

private:
    Ui::CefOSRWidget *ui;

    CefRefPtr<SimpleHandler> m_browserHander;

    QImage m_image;
};
#endif // CEFOSRWIDGET_H

cefosrwidget.cpp

#include "cefosrwidget.h"
#include "ui_cefosrwidget.h"

#include "events.h"

#include <QMessageBox>
#include <QPainter>
#include <QMouseEvent>
#include <QDebug>

CefOSRWidget::CefOSRWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::CefOSRWidget)
{
    ui->setupUi(this);

    //Cef离屏渲染的话,设置了WA_TranslucentBackground也没有渲染问题
//    setAttribute(Qt::WA_TranslucentBackground);
//    setWindowFlags(Qt::FramelessWindowHint);

    HWND wnd = (HWND)this->winId();
    QString indexUrl = "www.baidu.com";
    //这里可以换成本地的网,加载本地网页文件,格式为:file://绝对路径中的xx.html
    //例如"file://" + QCoreApplication::applicationDirPath() + "/index.html"

    CefWindowInfo cefWndInfo;
    cefWndInfo.SetAsWindowless(wnd);

    CefBrowserSettings cefBrowSetting;
//    cefBrowSetting.background_color = 180;
    cefBrowSetting.windowless_frame_rate = 60; //设置帧率,默认值是30

    m_browserHander = CefRefPtr<SimpleHandler>(new SimpleHandler(false));
    m_browserHander->setWinHandle(wnd);
    m_browserHander->setReceiver(this);

    //注意这里是异步创建
    bool isCreated = CefBrowserHost::CreateBrowser(cefWndInfo, m_browserHander, CefString(indexUrl.toStdWString()), cefBrowSetting, nullptr, nullptr);
    if (!isCreated)
    {
        QMessageBox::information(nullptr, "warning", "brower create failed!");
    }
}

void CefOSRWidget::resizeEvent(QResizeEvent *event)
{
    QWidget::resizeEvent(event);

    CefRefPtr<CefBrowser> pBrower = getBrowser();
    if (pBrower)
    {
        pBrower->GetHost()->WasResized();
        pBrower->GetHost()->SetMouseCursorChangeDisabled(false);
    }
}

void CefOSRWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
    QPainter painter(this);

    QRect rect = this->rect();

//    painter.setOpacity(0.5); //这里可以改变浏览器整体的透明度,比如这里设置为0.5

    painter.drawImage(QRect(rect.left(), rect.top(), rect.width(), rect.height()), m_image, m_image.rect());
}

bool CefOSRWidget::event(QEvent *event)
{
    if (event->type() == UpdateEvent::eventType)
    {
        UpdateEvent *updateEvent = dynamic_cast<UpdateEvent*>(event);
        m_image = updateEvent->m_image;
        this->update();

        return true;
    }
    return QWidget::event(event);
}

CefRefPtr<CefBrowser> CefOSRWidget::getBrowser()
{
    if (!m_browserHander.get())
    {
        return nullptr;
    }

    std::list<CefRefPtr<CefBrowser> > cefBrowerList = m_browserHander->getCefBrowerList();
    if (cefBrowerList.size() == 0)
    {
        qDebug() << "cefBrowerList.size() == 0"; //浏览器是异步创建的,OnAfterCreated是在创建成功后调用的
        return nullptr;
    }

    return cefBrowerList.front();
}

CefOSRWidget::~CefOSRWidget()
{
    delete ui;
}

在这里插入图片描述

注意点:
在pro文件中,需要添加

LIBS += Gdi32.lib

要点讲解:
(1)离屏渲染需要继承CefRenderHandler,并且实现GetViewRect和OnPaint
(2)在OnPaint中

void SimpleHandler::OnPaint(CefRefPtr<CefBrowser> browser,
                            CefRenderHandler::PaintElementType type,
                            const CefRenderHandler::RectList &dirtyRects,
                            const void *buffer,
                            int width,
                            int height)
{
    HBITMAP bitmap= CreateBitmap(width,height,1,32,buffer);
    QImage image = QtWin::imageFromHBITMAP(bitmap);

    if (image.isNull())
    {
        qDebug() << "image is null";
        return;
    }

    //以下为测试代码,将图片写到文件中去
    //QString fileName = QString("D:/1/%1.png").arg(QDateTime::currentMSecsSinceEpoch());
    //image.save(fileName, "PNG");

    UpdateEvent *updateEvent = new UpdateEvent(image);
    QCoreApplication::postEvent(m_receiver, updateEvent);
}

使用了windows平台的API,将图形数据转成了HBITMAP格式图片,需要包含头文件

#include <QtWin>

以及在pro文件中添加

LIBS += Gdi32.lib

刚开始的时候,为了测试,将获取的图片,写到本地磁盘上,然后通过就可以知道是否获取到离屏的图片

 //以下为测试代码,将图片写到文件中去
    //QString fileName = QString("D:/1/%1.png").arg(QDateTime::currentMSecsSinceEpoch());
    //image.save(fileName, "PNG");

(3)通过Qt的postEvent发送异步事件,去通知Qt窗口更新,将获取的QImage复制一份,放到自定义事件中。

UpdateEvent *updateEvent = new UpdateEvent(image);
QCoreApplication::postEvent(m_receiver, updateEvent);

为了可以指定接收者,需要添加

 void setReceiver(QObject *receiver);

将Qt窗口的指针传递进来, 将离屏渲染得到的图片一张张发送给receiver,所以这里要指定接受者

(4)与真窗口不同的CefWindowInfo 设置:

    HWND wnd = (HWND)this->winId();
    QString indexUrl = "www.baidu.com";
    //这里可以换成本地的网,加载本地网页文件,格式为:file://绝对路径中的xx.html
    //例如"file://" + QCoreApplication::applicationDirPath() + "/index.html"

    CefWindowInfo cefWndInfo;
    cefWndInfo.SetAsWindowless(wnd);

    CefBrowserSettings cefBrowSetting;
//    cefBrowSetting.background_color = 180;
    cefBrowSetting.windowless_frame_rate = 60; //设置帧率,默认值是30

(5)
重写event事件,根据自定义的更新事件,在paintEvent中进行重绘

protected:
    bool event(QEvent *event) override;
bool CefOSRWidget::event(QEvent *event)
{
    if (event->type() == UpdateEvent::eventType)
    {
        UpdateEvent *updateEvent = dynamic_cast<UpdateEvent*>(event);
        m_image = updateEvent->m_image;
        this->update();

        return true;
    }
    return QWidget::event(event);
}
void CefOSRWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
    QPainter painter(this);

    QRect rect = this->rect();

//    painter.setOpacity(0.5); //这里可以改变浏览器整体的透明度,比如这里设置为0.5

    painter.drawImage(QRect(rect.left(), rect.top(), rect.width(), rect.height()), m_image, m_image.rect());
}

离屏渲染,可以解决了Qt窗口中setAttribute(Qt::WA_TranslucentBackground);
背景透明时的显示问题。

在这里插入图片描述
试试将窗口设置为无边框,背景透明窗口。

本工程运行的是release版,代码放在百度云上

链接:https://pan.baidu.com/s/1XNstavzVVkTL2yq0Ee3iVg
提取码:hr13

下面是去掉了cef运行需要的资源文件(如果你看过我之前的教程,就可以下载下面的,体积更小)
链接:https://pan.baidu.com/s/1q-BxygVWCwuEWLfdpqzwKg
提取码:zfdj

接下来需要解决的问题:
(1)鼠标的点击事件
(2)滚轮事件
(3)鼠标hoved到链接上,鼠标形状的变化
(4)键盘输入
(5)输入框的右键弹出层

(这些问题会在后续说明如何处理。。。)

评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值