スポンサーリンク

CEFでオフスクリーンレンダリング

・settings.multi_threaded_message_loop = false; にする
・CefRenderHandler をオーバーライドしCefClientのインスタンスに設定
・イベントはSendMouseClickEvent等で送信

#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

#include <wx/gdicmn.h> // wxPointに必要
#include <wx/frame.h>  // wxFrameに必要


#ifdef _DEBUG
#pragma comment(lib, "D:\\cmmon\\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\\Debug\\libcef.lib")
#pragma comment(lib, "D:\\cmmon\\MD\\Debug\\libcef_dll_wrapper.lib")
#else
#pragma comment(lib, "D:\\cmmon\\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\\Release\\libcef.lib")
#pragma comment(lib, "D:\\cmmon\\MD\\Release\\libcef_dll_wrapper.lib")
#endif


#include <wx/evtloop.h>

/////////////////////////////////////
/////////////////////////////////////
/////////////////////////////////////
#include <include/cef_app.h>

#include <string>


wxBitmap FromRGBA(const unsigned char* rgba_buffer, int width, int height)
{
    // wxImage用にRGBを抜き出す
    // 既存の生配列からstd::vectorへの変換例
    std::vector<unsigned char> rgb_data(rgba_buffer, rgba_buffer + width * height * 3);

    for (int i = 0; i < width * height; ++i) {
        rgb_data[i * 3 + 0] = rgba_buffer[i * 4 + 2];
        rgb_data[i * 3 + 1] = rgba_buffer[i * 4 + 1];
        rgb_data[i * 3 + 2] = rgba_buffer[i * 4 + 0];
    }

    wxImage image(width, height, rgb_data.data(), true);

    // wxBitmapへ変換
    return wxBitmap(image);

}


class MyRenderHandler : public CefRenderHandler {
public:
    wxFrame* m_pFrame = nullptr;
    wxBitmap *m_pbitmap = nullptr;
    void setBitmap(wxBitmap *bmp) {
        m_pbitmap = bmp;
    }
    void setFrame(wxFrame* frame) {
        m_pFrame = frame;
    }

    // GetViewRect() をoverrideする
    // 必須:ビューポートのサイズを返す
    void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override {
        // ビューポートのサイズを返す
        rect = CefRect(0, 0, width_, height_);
    }

    // 必須:レンダリング結果がここに来る
    void OnPaint(CefRefPtr<CefBrowser> browser,
        PaintElementType type,
        const RectList& dirtyRects,
        const void* buffer,
        int width, int height) override {

        // ※ buffer は 32bit BGRA フォーマット
        *m_pbitmap = FromRGBA(static_cast<const unsigned char*>(buffer), width, height);

        if (m_pFrame) {
            m_pFrame->Refresh(); // ウィンドウを更新
        }

    }

    // 例: サイズを外から設定
    void SetSize(int w, int h) {
        width_ = w; height_ = h;
    }

private:
    int width_ = 800;
    int height_ = 600;

    IMPLEMENT_REFCOUNTING(MyRenderHandler);
};
      
class MyHandler : 
    public CefClient, 
    public CefLifeSpanHandler  {

    //
    CefRefPtr<CefRenderHandler> renderHandler_;

public:

    //
    MyHandler(CefRefPtr<CefRenderHandler> renderHandler)
        : renderHandler_(renderHandler) {}

    //
    CefRefPtr<CefRenderHandler> GetRenderHandler() override {
        return renderHandler_;
    }


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

    void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
        m_Browser = browser;
    }

    void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
        m_Browser = nullptr;

        // イベントから抜ける
        if (wxEventLoopBase::GetActive()) {
            wxEventLoopBase::GetActive()->Exit();
        }

    }

    void CloseAllBrowsers(bool force_close) {
        if (m_Browser) {
            m_Browser->GetHost()->CloseBrowser(force_close);
        }
    }


    CefRefPtr<CefBrowser> GetBrowser() { return m_Browser; }

    IMPLEMENT_REFCOUNTING(MyHandler);

private:
    CefRefPtr<CefBrowser> m_Browser;
};

class MyCustomEventLoop : public wxGUIEventLoop
{
public:
    MyCustomEventLoop() = default;
    virtual ~MyCustomEventLoop() = default;


protected:

    // Yieldが必要な場合
    virtual void DoYieldFor(long eventsToProcess) override
    {
        // デフォルトの実装
        wxGUIEventLoop::DoYieldFor(eventsToProcess);
    }

    // メッセージループの各イテレーション開始時に呼ばれるフック
    virtual void OnNextIteration() override
    {

        // デフォルトの実装
        wxGUIEventLoop::OnNextIteration();
    }

    //////////////////////////////////////////////
    // メッセージを処理する
    bool Dispatch() override
    {
        bool running = true;
        MSG msg;
        while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                running = false;
            else
            {
                ::TranslateMessage(&msg);
                ::DispatchMessage(&msg);
            }
        }

        CefDoMessageLoopWork();

        ::Sleep(10);

        
        return true;  // まだ継続

    }
    //////////////////////////////////////////////

};
// メインフレーム
class MyFrame : public wxFrame
{
    CefRefPtr<MyHandler> g_handler;

    wxBitmap m_cefbmp;
public:

    MyFrame()
        : wxFrame(nullptr, wxID_ANY, "CEF Offscreen Rendering")
    {
        Bind(wxEVT_CLOSE_WINDOW, &MyFrame::OnClose, this);

        Bind(wxEVT_SIZE, &MyFrame::OnSize, this);

        Bind(wxEVT_PAINT, &MyFrame::OnPaint, this);


        Bind(wxEVT_LEFT_DOWN, &MyFrame::OnLButtonDown, this);
        Bind(wxEVT_LEFT_UP, &MyFrame::OnLButtonUp, this);




        CallAfter(&MyFrame::PostCreate);
    }

private:
    void OnLButtonDown(wxMouseEvent& event) {
        CefMouseEvent mouse_event;
        mouse_event.x = event.GetX();
        mouse_event.y = event.GetY();
        mouse_event.modifiers = event.GetModifiers();

        // マウスボタンDOWN
        g_handler->GetBrowser()->GetHost()->SendMouseClickEvent(
            mouse_event,
            MBT_LEFT,
            false, // isMouseUp == false (DOWN)
            1
        );

        event.Skip();  // 必要なら他のハンドラにも渡す
    }

    void OnLButtonUp(wxMouseEvent& event) {
        CefMouseEvent mouse_event;
        mouse_event.x = event.GetX();
        mouse_event.y = event.GetY();
        mouse_event.modifiers = event.GetModifiers();

        // マウスボタンUP
        g_handler->GetBrowser()->GetHost()->SendMouseClickEvent(
            mouse_event,
            MBT_LEFT,
            true, // isMouseUp == true (UP)
            1
        );

        event.Skip();
    }


    void PostCreate() {

        CefRefPtr<MyRenderHandler> renderHandler = new MyRenderHandler();
        renderHandler->setBitmap(&m_cefbmp);
        renderHandler->setFrame(this);
        renderHandler->SetSize(400, 400); // ビューポートのサイズを設定

        g_handler = CefRefPtr<MyHandler>(new MyHandler(renderHandler) );

        CefBrowserSettings browser_settings;

        CefWindowInfo window_info;
        CefRect cefRect(
            0,
            0,
            400,
            400);

        HWND hwnd = (HWND)this->GetHandle();

        //window_info.SetAsChild(hwnd, cefRect);

        // オフスクリーンレンダリング
        window_info.SetAsWindowless(nullptr);


        CefBrowserHost::CreateBrowser(
            window_info,
            g_handler,
            "https://www.google.com",
            browser_settings,
            nullptr,
            nullptr);


        this->Layout(); // レイアウトの更新
    }

    void OnClose(wxCloseEvent& evt)
    {
        // CEFのブラウザを閉じる
        g_handler->CloseAllBrowsers(true);

        // ウィンドウを閉じない指示
        evt.Veto();
    }

    void OnSize(wxSizeEvent& evt)
    {
        if (g_handler) {
            HWND hBrowserWnd = g_handler->GetBrowser()->GetHost()->GetWindowHandle();
            // ウィンドウサイズ変更時の処理
            if (g_handler && g_handler->GetBrowser()) {
                RECT rect(0, 0, evt.GetSize().GetWidth(), evt.GetSize().GetHeight());
                SetWindowPos(hBrowserWnd, NULL,
                    0, 0,
                    rect.right - rect.left,
                    rect.bottom - rect.top,
                    SWP_NOZORDER);

                // ブラウザにサイズ変更を通知
                g_handler->GetBrowser()->GetHost()->WasResized();
            }
        }

        evt.Skip(); // イベントをスキップしてデフォルトの処理を行う
    }

    // OnPaint()
    void OnPaint(wxPaintEvent& evt)
    {
        // 描画処理をここに追加
        wxPaintDC dc(this);

        if (m_cefbmp.IsOk()) {
            // CEFのビットマップを描画
            dc.DrawBitmap(m_cefbmp, 0, 0, true);
        }

    }

};


class MyApp : public wxApp
{
public:
    bool OnInit() override
    {

        HINSTANCE hInstance = ::GetModuleHandle(NULL);
        CefMainArgs main_args(hInstance);
        int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);
        if (exit_code >= 0)
            return false;

        // CEFの設定
        CefSettings settings;
        settings.no_sandbox = true;

        // マルチスレッドメッセージループを無効にする
        // オフスクリーンレンダリングでは false必須
        settings.multi_threaded_message_loop = false;


        CefInitialize(main_args, settings, nullptr, nullptr);

        ///////////////////////////////////////////////////




        auto frame = new MyFrame();
        frame->Show();
        return true;
    }

    int OnRun() override
    {
        // wxGUIEventLoop を使って自前ループを構築
        MyCustomEventLoop* loop = new MyCustomEventLoop;
        wxEventLoopBase::SetActive(loop);   // アクティブなループとして登録
        int retval = loop->Run();           // ここでメッセージループ開始
        delete loop;                        // ループ終了後に解放

        // CEFのシャットダウン
        CefShutdown();

        return retval;
    }

    virtual int OnExit() {
        return wxApp::OnExit();
    }

};

// WinMainをマクロで定義
wxIMPLEMENT_APP(MyApp);

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: