スポンサーリンク

Win32APIでCEFのオフスクリーンレンダリング コード整頓

Win32APIからCEFを使用するコードを書きたかったので前に書いたものを整頓した。

実行には.exeファイルと同じ場所

cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\Resources\

の内容(localesフォルダなど)をコピーする。

#include "MyCEFControl.hpp"

#include <Windows.h>
#include <windowsx.h>
 
void OnPaint(HWND hwnd, HDC hdc) {

    MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if (handler) {
        CefRefPtr<CefRenderHandler> baseRenderHandler = handler->GetRenderHandler();
        MyRenderHandler* renderHandler = static_cast<MyRenderHandler*>(baseRenderHandler.get());
        RGBAImage img = renderHandler->m_browserImage;
        if (img.buffer.size() > 0) {
            // ビットマップ情報の設定
            BITMAPINFO bmi;
            ZeroMemory(&bmi, sizeof(BITMAPINFO));
            bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
            bmi.bmiHeader.biWidth = img.width;
            bmi.bmiHeader.biHeight = -img.height; // 上下反転
            bmi.bmiHeader.biPlanes = 1;
            bmi.bmiHeader.biBitCount = 24; // RGB24ビット
            bmi.bmiHeader.biCompression = BI_RGB;
            // 画像データを描画
            SetDIBitsToDevice(
                hdc,
                0, 0,                  // 描画位置
                img.width, img.height, // 描画サイズ
                0, 0,                  // ソースの開始位置
                0, img.height,         // ソースの開始ラインとライン数
                img.buffer.data(),     // ピクセルデータ
                &bmi,
                DIB_RGB_COLORS         // カラーテーブルの使用方法
            );
        }
    }
}

void OnMouseMove(HWND hwnd, int x, int y) {
    MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if (handler) {
        CefMouseEvent mouse_event;
        mouse_event.x = x;
        mouse_event.y = y;
        mouse_event.modifiers = 0;
        handler->GetBrowser()->GetHost()->SendMouseMoveEvent(mouse_event, false);
        InvalidateRect(hwnd, NULL, FALSE);
    }
}

void OnLButtonDown(HWND hwnd, int x, int y) {
    MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if (handler) {
        CefMouseEvent mouse_event;
        mouse_event.x = x;
        mouse_event.y = y;
        mouse_event.modifiers = 0;
        handler->GetBrowser()->GetHost()->SendMouseClickEvent(mouse_event, cef_mouse_button_type_t::MBT_LEFT, false, 1);
        InvalidateRect(hwnd, NULL, FALSE);
    }
}

void OnLButtonUp(HWND hwnd, int x, int y) {
    MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if (handler) {
        CefMouseEvent mouse_event;
        mouse_event.x = x;
        mouse_event.y = y;
        mouse_event.modifiers = 0;
        handler->GetBrowser()->GetHost()->SendMouseClickEvent(mouse_event, cef_mouse_button_type_t::MBT_LEFT, true, 1);
        InvalidateRect(hwnd, NULL, FALSE);
    }
}

void OnClose(HWND hwnd) {
    MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    if (handler) {
        handler->CloseAllBrowsers(true);
    }
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    MyHandler* handler = nullptr;
    LPCREATESTRUCT pcs;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (msg) {
    case WM_CLOSE:

        OnClose(hwnd);
        DestroyWindow(hwnd);

        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        OnPaint(hwnd, hdc);
        EndPaint(hwnd, &ps);
        return 0;

    case WM_CREATE:

        return 0;
    case WM_DESTROY:
        //CefQuitMessageLoop();
        PostQuitMessage(0);
        return 0;

    case WM_MOUSEMOVE:
        OnMouseMove(hwnd, GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
        return 0;
    case WM_LBUTTONDOWN:
        OnLButtonDown(hwnd, GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
        return 0;
    case WM_LBUTTONUP:
        OnLButtonUp(hwnd, GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
        return 0;
    }

    return DefWindowProc(hwnd, msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow) {


    HWND hwnd;
    MSG msg;
    WNDCLASS winc;

    winc.style = CS_HREDRAW | CS_VREDRAW;
    winc.lpfnWndProc = WndProc;
    winc.cbClsExtra = winc.cbWndExtra = 0;
    winc.hInstance = hInstance;
    winc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    winc.hCursor = LoadCursor(NULL, IDC_ARROW);
    winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    winc.lpszMenuName = NULL;
    winc.lpszClassName = TEXT("SZL-WND");

    if (!RegisterClass(&winc)) return -1;

    /////////////////////////////////////////////////////////////////////////
    CefRefPtr<MyHandler> g_handler = MyInitCEF(hInstance);
    if (!g_handler) {
        return -1;
    }
    /////////////////////////////////////////////////////////////////////////
    hwnd = CreateWindow(
        TEXT("SZL-WND"), TEXT("CEF test"),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        CW_USEDEFAULT, CW_USEDEFAULT,
        500, 500,
        NULL, NULL, hInstance, nullptr
    );

    if (hwnd == NULL) return -1;
    /////////////////////////////////////////////////////////////////////////
    SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)(g_handler.get()));
    /////////////////////////////////////////////////////////////////////////
    MyRenderSetting(hwnd,g_handler);
    /////////////////////////////////////////////////////////////////////////

    // メッセージループ
    CefRunMessageLoop();

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


    return 0;
}

MyCEFControl.hpp

#pragma once

#include <include/cef_app.h>


struct RGBAImage {
    int width;
    int height;
    std::vector<unsigned char> buffer; // RGBAの生データ
};

RGBAImage FromRGBA(const unsigned char* rgba_buffer, int width, int height);

class MyRenderHandler : public CefRenderHandler {
public:
    HWND m_pCanvasWindow = nullptr;
    RGBAImage m_browserImage;

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

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

    // サイズを外から設定
    void SetSize(int w, int h);

private:

    IMPLEMENT_REFCOUNTING(MyRenderHandler);
};

class MyHandler :
    public CefClient,
    public CefLifeSpanHandler {

    CefRefPtr<CefRenderHandler> renderHandler_;

public:

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

    CefRefPtr<CefRenderHandler> GetRenderHandler() override;

    CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override;

    void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;

    void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;

    void CloseAllBrowsers(bool force_close);

    CefRefPtr<CefBrowser> GetBrowser();



    IMPLEMENT_REFCOUNTING(MyHandler);

private:
    CefRefPtr<CefBrowser> m_Browser;
};



CefRefPtr<MyHandler> MyInitCEF(HINSTANCE hInstance);

void MyRenderSetting(HWND window, CefRefPtr<MyHandler> ghandler);

MyCEFControl.cpp

#include "MyCEFControl.hpp"

void MyRenderHandler::GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect){
    // ビューポートのサイズを返す
    rect = CefRect(0, 0, m_browserImage.width, m_browserImage.height);
}

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

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

    InvalidateRect(m_pCanvasWindow, NULL, FALSE);

}

// サイズを外から設定
void MyRenderHandler::SetSize(int w, int h) {
    m_browserImage.buffer.resize(w * h * 4);
    m_browserImage.width = w;
    m_browserImage.height = h;
}

//=============================================================

CefRefPtr<CefRenderHandler> MyHandler::GetRenderHandler() {
    return renderHandler_;
}

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

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

void MyHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
    m_Browser = nullptr;
    CefQuitMessageLoop();

}

void MyHandler::CloseAllBrowsers(bool force_close) {
    if (m_Browser) {
        m_Browser->GetHost()->CloseBrowser(force_close);
    }
}
CefRefPtr<CefBrowser> MyHandler::GetBrowser() {
    return m_Browser; 
}

//=============================================================
RGBAImage 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 + 0];
        rgb_data[i * 3 + 1] = rgba_buffer[i * 4 + 1];
        rgb_data[i * 3 + 2] = rgba_buffer[i * 4 + 2];
    }

    RGBAImage img;
    img.width = width;
    img.height = height;
    img.buffer = std::move(rgb_data);

    return img;

}

CefRefPtr<MyHandler> MyInitCEF(HINSTANCE hInstance) {

    CefMainArgs main_args(hInstance);
    int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);
    if (exit_code >= 0)
        return nullptr;

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

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


    CefInitialize(main_args, settings, nullptr, nullptr);

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

    return g_handler;
}

void MyRenderSetting(HWND window, CefRefPtr<MyHandler> ghandler) {

    // MyRenderHandlerを取得
    CefRefPtr<CefRenderHandler> baseRenderHandler = ghandler->GetRenderHandler();
    CefRefPtr<MyRenderHandler> renderHandler = static_cast<MyRenderHandler*>(baseRenderHandler.get());

    renderHandler->m_pCanvasWindow = window;
    renderHandler->SetSize(400, 400); // ビューポートのサイズを設定


    CefBrowserSettings browser_settings;

    CefWindowInfo window_info;
    CefRect cefRect(
        0,
        0,
        800,
        600);


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


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

}

コメントを残す

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

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


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