CEFにHTMLテキストを直接与える場合、Data URIでテキストをURL化し、LoadURLに指定する。
// HTML文書追加に必要 // #include <include/cef_frame.h> // CefFrame // #include <include/cef_parser.h> // CefURIEncode void SetHTML(CefRefPtr<CefBrowser> browser) { /* CEFでHTMLコンテンツを表示する方法の例 LoadURLを使うので、URI経由でしかロードできない。 そこで、リソースをURIで表す技術のData URIを使い、 HTML文書をURIにエンコードしてロードする。 */ // 表示したいHTML文書 const std::u8string html_content = u8R"(<html> <head><meta charset="UTF-8"></head> <body style="background-color:#CCCCCC;"><p>こんにちは☺</p></body> </html>)"; ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// CefRefPtr<CefFrame> frame = browser->GetMainFrame(); // 1. HTMLコンテンツを CefString に変換 CefString html_cef_string = CefString( (const char*)html_content.c_str() ); // 2. Data URI の作成 std::string data_uri_str = "data:text/html;charset=utf-8,"; // 3. HTMLコンテンツをエンコードして Data URI に追加 CefString encoded_html = CefURIEncode(html_cef_string, false); data_uri_str += encoded_html.ToString(); // 3. Data URI をロード frame->LoadURL(CefString(data_uri_str)); }


#include <windows.h> #include <optional> #include <iostream> #include <include/cef_app.h> #ifdef _DEBUG #pragma comment(lib, R"(D:\libraries\CEF\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\Debug\libcef.lib)") #pragma comment(lib, R"(D:\libraries\CEF\MD\Debug\libcef_dll_wrapper.lib)") #else #pragma comment(lib, R"(D:\libraries\CEF\cef_binary_138.0.15+gd0f1f64+chromium-138.0.7204.50_windows64\Release\libcef.lib)") #pragma comment(lib, R"(D:\libraries\CEF\MD\Release\libcef_dll_wrapper.lib)") #endif #include "MyCEFControl.hpp" #include <Windows.h> #include <windowsx.h> #include <imm.h> #pragma comment(lib, "imm32.lib") 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); } } std::optional<LRESULT> WndProc_IME_Work(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); void SendKeyEventToBrowser(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { MyHandler* handler = nullptr; LPCREATESTRUCT pcs; PAINTSTRUCT ps; HDC hdc; CefRefPtr<CefBrowser> browser; handler = reinterpret_cast<MyHandler*>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); if(handler) browser = handler->GetBrowser(); SendKeyEventToBrowser(hwnd, msg, wp, lp);// キーボード入力のメッセージ処理 auto imeret = WndProc_IME_Work(hwnd, msg, wp, lp);// IME関係のメッセージ処理 if (imeret.has_value()) { return imeret.value(); } 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: pcs = reinterpret_cast<LPCREATESTRUCT>(lp); SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pcs->lpCreateParams)); 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: if (handler && browser) browser->GetHost()->SetFocus(true); 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); } /////////////////////////////////////////////// /////////////////////////////////////////////// /////////////////////////////////////////////// void HandleImeComposition(HWND hWnd, CefRefPtr<CefBrowser> browser, UINT msg, WPARAM wParam, LPARAM lParam);
std::optional<LRESULT> WndProc_IME_Work(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hWnd, GWLP_USERDATA); CefRefPtr<CefBrowser> browser = nullptr; if (handler) { browser = handler->GetBrowser(); } switch (msg) { case WM_SETFOCUS: if (browser) { browser->GetHost()->SetFocus(true); } break; case WM_KILLFOCUS: if (browser) { browser->GetHost()->SetFocus(false); } break; case WM_IME_STARTCOMPOSITION: return 0; case WM_IME_COMPOSITION: { HIMC hIMC = ImmGetContext(hWnd); if (hIMC) { POINT caret{}; CefRefPtr<CefRenderHandler> baseRenderHandler = handler->GetRenderHandler(); MyRenderHandler* renderHandler = static_cast<MyRenderHandler*>(baseRenderHandler.get()); // MyRenderHandler からキャレット位置を取得 if (renderHandler && renderHandler->GetImeCaretPos(caret)) { CANDIDATEFORM cf{}; cf.dwIndex = 0; cf.dwStyle = CFS_CANDIDATEPOS; cf.ptCurrentPos = caret; ImmSetCandidateWindow(hIMC, &cf); } } } if (browser) HandleImeComposition(hWnd, browser, msg, wParam, lParam); return 0; case WM_IME_ENDCOMPOSITION: if (browser) { browser->GetHost()->ImeFinishComposingText(true); } if (handler) { CefRefPtr<CefRenderHandler> baseRenderHandler = handler->GetRenderHandler(); MyRenderHandler* renderHandler = static_cast<MyRenderHandler*>(baseRenderHandler.get()); renderHandler->SetImeCaretInvalid(); } return 0; } return std::nullopt; }
void HandleImeComposition(HWND hWnd, CefRefPtr<CefBrowser> browser, UINT msg, WPARAM wParam, LPARAM lParam) { HIMC hIMC = ImmGetContext(hWnd); if (!hIMC) return; // 変換中文字列の取得 (GCS_COMPSTR) if (lParam & GCS_COMPSTR) { LONG size = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, nullptr, 0); std::wstring comp; comp.resize(size / sizeof(wchar_t)); ImmGetCompositionStringW(hIMC, GCS_COMPSTR, &comp[0], size); // キャレット位置 (GCS_CURSORPOS) LONG cursor = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, nullptr, 0); // 属性(下線の種類などに使える) // LONG attrSize = ImmGetCompositionStringW(hIMC, GCS_COMPATTR, nullptr, 0); std::vector<CefCompositionUnderline> underlines; // 簡略のため全部同じ属性にしているが、 // 実際は GCS_COMPATTR の値から変換中部分/確定部分などを判定して設定する CefCompositionUnderline ul; ul.range = CefRange(0, static_cast<int>(comp.size())); ul.color = 0; // デフォルト色 ul.background_color = 0; // デフォルト背景 ul.thick = false; underlines.push_back(ul); // selection_range, replacement_range はここでは簡略化 CefRange selection(cursor, cursor); browser->GetHost()->ImeSetComposition( comp, underlines, CefRange::InvalidRange(), // replacement_range selection // selection_range ); } // 確定文字列の取得 (GCS_RESULTSTR) if (lParam & GCS_RESULTSTR) { LONG size = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, nullptr, 0); std::wstring result; result.resize(size / sizeof(wchar_t)); ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, &result[0], size); // resultをDebugString OutputDebugStringW(result.c_str()); browser->GetHost()->ImeCommitText( result, CefRange::InvalidRange(), 0 // relative_cursor_pos ); browser->GetHost()->ImeFinishComposingText(false); } ImmReleaseContext(hWnd, hIMC); }
/////////////////////////////////////////////// /////////////////////////////////////////////// ///////////////////////////////////////////////
void SendKeyEventToBrowser(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { MyHandler* handler = reinterpret_cast<MyHandler*>(GetWindowLongPtr(hWnd, GWLP_USERDATA)); if (!handler) return; CefRefPtr<CefBrowser> browser = handler->GetBrowser(); if (!browser) return; CefKeyEvent ev; switch (msg) { case WM_KEYDOWN: case WM_SYSKEYDOWN: ev.type = KEYEVENT_RAWKEYDOWN; break; case WM_KEYUP: case WM_SYSKEYUP: ev.type = KEYEVENT_KEYUP; break; case WM_CHAR: case WM_SYSCHAR: ev.type = KEYEVENT_CHAR; break; default: return; // 対象外 } ev.windows_key_code = static_cast<int>(wParam); ev.native_key_code = static_cast<int>(lParam); ev.is_system_key = (msg == WM_SYSKEYDOWN || msg == WM_SYSKEYUP || msg == WM_SYSCHAR); // 必要なら修飾キーも付与 ev.modifiers = 0; if (GetKeyState(VK_SHIFT) & 0x8000) ev.modifiers |= EVENTFLAG_SHIFT_DOWN; if (GetKeyState(VK_CONTROL) & 0x8000) ev.modifiers |= EVENTFLAG_CONTROL_DOWN; if (GetKeyState(VK_MENU) & 0x8000) ev.modifiers |= EVENTFLAG_ALT_DOWN; browser->GetHost()->SendKeyEvent(ev); }
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, g_handler.get() ); if (hwnd == NULL) return -1; ///////////////////////////////////////////////////////////////////////// //SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)(g_handler.get())); ///////////////////////////////////////////////////////////////////////// MyRenderSetting(hwnd, g_handler); ///////////////////////////////////////////////////////////////////////// // メッセージループ CefRunMessageLoop(); // CEFのシャットダウン CefShutdown(); return 0; }
#pragma once #include <include/cef_app.h> #include <optional> 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; HWND m_main_hwnd = nullptr; // IME用にメインウィンドウのハンドルを保持 // 追加: 最後に通知されたキャレット矩形(ブラウザの View 座標系) std::optional<CefRect> m_ime_caret_rect; // 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); // メインウィンドウのハンドルを設定 void SetMainHwnd(HWND hwnd); // IMEのキャレット位置が変化したときに呼ばれる void OnImeCompositionRangeChanged( CefRefPtr<CefBrowser> browser, const CefRange& selected_range, const CefRenderHandler::RectList& character_bounds) override; bool GetImeCaretPos(POINT& pt) const; void SetImeCaretInvalid(); private: IMPLEMENT_REFCOUNTING(MyRenderHandler); }; class MyHandler : public CefClient, public CefLifeSpanHandler, public CefRenderHandler { 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(); void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override { return renderHandler_->GetViewRect(browser, rect); } void OnPaint(CefRefPtr<CefBrowser> browser, PaintElementType type, const RectList& dirtyRects, const void* buffer, int width, int height) override { return renderHandler_->OnPaint(browser, type, dirtyRects, buffer, width, height); } IMPLEMENT_REFCOUNTING(MyHandler); private: CefRefPtr<CefBrowser> m_Browser; }; CefRefPtr<MyHandler> MyInitCEF(HINSTANCE hInstance); void MyRenderSetting(HWND window, CefRefPtr<MyHandler> ghandler);
#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; } void MyRenderHandler::OnImeCompositionRangeChanged( CefRefPtr<CefBrowser> browser, const CefRange& selected_range, const CefRenderHandler::RectList& character_bounds) { // character_bounds[0] がキャレット位置に対応する矩形 if (character_bounds.empty()) return; /////////////////////////////////////////////////////////// #if 0 int idx = 0; // 選択範囲の末尾をキャレットとみなす if (selected_range.to > 0 && selected_range.to <= static_cast<int>(character_bounds.size())) { idx = selected_range.to - 1; } else { idx = static_cast<int>(character_bounds.size()) - 1; } m_ime_caret_rect = character_bounds[idx]; #else // 常に最初の矩形をキャレット位置とみなす m_ime_caret_rect = character_bounds[0]; #endif } bool MyRenderHandler::GetImeCaretPos(POINT& pt) const { if (!m_ime_caret_rect) return false; pt.x = m_ime_caret_rect->x; pt.y = m_ime_caret_rect->y; return true; } void MyRenderHandler::SetImeCaretInvalid() { m_ime_caret_rect = std::nullopt; } void MyRenderHandler::SetMainHwnd(HWND hwnd) { m_main_hwnd = hwnd; } //============================================================= 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); // ビューポートのサイズを設定 renderHandler->SetMainHwnd(window); // IME用にメインウィンドウハンドルを設定 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); }
CEFをオフスクリーンレンダリングしているときに、キー入出力を行う。各種キーイベントを取得してCEFに与えることでキーボード対応できる。日本語入力も出来るがキャレットがテキストボックスの位置に出ないことに注意。
///////////////////////////////////////////////////////////////////////// // キー修飾フラグ変換 int GetCefKeyboardModifiers(WPARAM wparam, LPARAM lparam) { int modifiers = 0; // 押下状態 if (GetKeyState(VK_SHIFT) & 0x8000) modifiers |= EVENTFLAG_SHIFT_DOWN; if (GetKeyState(VK_CONTROL) & 0x8000) modifiers |= EVENTFLAG_CONTROL_DOWN; if (GetKeyState(VK_MENU) & 0x8000) modifiers |= EVENTFLAG_ALT_DOWN; // Lock 状態 if (GetKeyState(VK_CAPITAL) & 1) modifiers |= EVENTFLAG_CAPS_LOCK_ON; if (GetKeyState(VK_NUMLOCK) & 1) modifiers |= EVENTFLAG_NUM_LOCK_ON; // 左右キー switch (wparam) { case VK_LSHIFT: case VK_LCONTROL: case VK_LMENU: modifiers |= EVENTFLAG_IS_LEFT; break; case VK_RSHIFT: case VK_RCONTROL: case VK_RMENU: modifiers |= EVENTFLAG_IS_RIGHT; break; default: break; } // 拡張ビット (bit 24) const bool extended = (lparam & 0x01000000) != 0; // テンキー判定 if ((wparam >= VK_NUMPAD0 && wparam <= VK_NUMPAD9) || wparam == VK_DECIMAL || wparam == VK_ADD || wparam == VK_SUBTRACT || wparam == VK_MULTIPLY || wparam == VK_DIVIDE) { modifiers |= EVENTFLAG_IS_KEY_PAD; } else if (wparam == VK_RETURN) { // Enter: Numpad Enter なら extended が true(Winで Numpad Enter は拡張付き) if (extended) modifiers |= EVENTFLAG_IS_KEY_PAD; } return modifiers; }
// キー入力 void OnKeyDown(HWND hwnd, UINT msg, int wp,int lp) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); CefKeyEvent ev{}; ev.type = KEYEVENT_RAWKEYDOWN; ev.windows_key_code = (int)wp; ev.native_key_code = (int)lp; ev.is_system_key = (msg == WM_SYSKEYDOWN); ev.modifiers = GetCefKeyboardModifiers(wp, lp); /* Ctrl/Shift/Alt を反映 */; handler->GetBrowser()->GetHost()->SendKeyEvent(ev); } void OnKeyUp(HWND hwnd, UINT msg, int wp, int lp) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); CefKeyEvent ev{}; ev.type = KEYEVENT_KEYUP; ev.windows_key_code = (int)wp; ev.native_key_code = (int)lp; ev.is_system_key = (msg == WM_SYSKEYUP); ev.modifiers = GetCefKeyboardModifiers(wp, lp); handler->GetBrowser()->GetHost()->SendKeyEvent(ev); } void OnChar(HWND hwnd, UINT msg, int wp, int lp) { MyHandler* handler = (MyHandler*)GetWindowLongPtr(hwnd, GWLP_USERDATA); CefKeyEvent ev; ev.type = KEYEVENT_CHAR; ev.character = static_cast<wchar_t>(wp); // 入力文字 ev.unmodified_character = static_cast<wchar_t>(wp); // 修飾なし ev.windows_key_code = static_cast<int>(wp); // 互換目的 ev.modifiers = GetCefKeyboardModifiers(wp, lp); handler->GetBrowser()->GetHost()->SendKeyEvent(ev); }
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: 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; case WM_KEYDOWN: case WM_SYSKEYDOWN: OnKeyDown(hwnd,msg,wp,lp); // キーダウン return 0; case WM_KEYUP: case WM_SYSKEYUP: OnKeyUp(hwnd, msg, wp, lp); // キーアップ return 0; case WM_CHAR: OnChar(hwnd, msg, wp, lp); // 文字入力 return 0; } return DefWindowProc(hwnd, msg, wp, lp); }
#pragma once #include <vtkeigen/eigen/Dense> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/depth_first_search.hpp> #include <vtkGraphToPolyData.h> #include <vtkAssembly.h> #include <vtkProperty.h> #include <vtkGlyph3DMapper.h> #include <vtkSphereSource.h> #include <vtkActor.h> #include <vtkNew.h> #include <vtkPolyDataMapper.h>
// ------------------------------------------------------------------ // ノード情報 // ------------------------------------------------------------------ struct Node { Eigen::Vector3d coord; // 3次元座標 };
// ------------------------------------------------------------------ // グラフの型定義(無向グラフ) // ------------------------------------------------------------------ using boostGraph = boost::adjacency_list< boost::vecS, // OutEdgeList boost::vecS, // VertexList boost::undirectedS, // 無向グラフ Node, // 頂点プロパティ boost::no_property // 辺プロパティ(必要なら追加可能) >;
// ------------------------------------------------------------------ // 2次元等間隔グリッドグラフを作成する関数 // ------------------------------------------------------------------ //! @brief Nx × Ny の 2次元グリッドグラフを作成 //! @param Nx X方向のノード数 //! @param Ny Y方向のノード数 //! @param spacing 隣接ノード間の距離(デフォルト 1.0) //! @return 完成したグラフ boostGraph CreateGridGraph(int Nx, int Ny, double spacing = 1.0) { if (Nx <= 0 || Ny <= 0) { throw std::invalid_argument("Nx and Ny must be positive"); } boostGraph mygraph; // 1. 頂点を作成しつつ座標を格納 std::vector<boostGraph::vertex_descriptor> vertices; vertices.reserve(static_cast<std::size_t>(Nx * Ny)); for (int ix = 0; ix < Nx; ++ix) { for (int iy = 0; iy < Ny; ++iy) { size_t v = boost::add_vertex(mygraph); mygraph[v].coord = Eigen::Vector3d( ix * spacing, iy * spacing, 0.0 // 2次元なので z = 0 ); vertices.push_back(v); } } // 2. インデックス → 頂点記述子の変換 auto indexToID = [&](int ix, int iy) -> boostGraph::vertex_descriptor { return vertices[static_cast<std::size_t>(iy + Ny * ix)]; }; // 3. 4方向(上下左右)の辺を張る for (int ix = 0; ix < Nx; ++ix) { for (int iy = 0; iy < Ny; ++iy) { boostGraph::vertex_descriptor v = indexToID(ix, iy); // 右 (+x) if (ix + 1 < Nx) { boost::add_edge(v, indexToID(ix + 1, iy), mygraph); } // 上 (+y) if (iy + 1 < Ny) { boost::add_edge(v, indexToID(ix, iy + 1), mygraph); } } } return mygraph; }
vtkSmartPointer<vtkAssembly> CreateVTKActor(const boostGraph& mygraph) { // ============================================================== // 1. vtkPoints と vtkCellArray を手動で作る // ============================================================== vtkNew<vtkPoints> points; vtkNew<vtkCellArray> lines; // 辺(ライン) vtkNew<vtkCellArray> verts; // 頂点(Glyph用) // 頂点インデックス → VTKのpointId のマップは不要(順番に追加するだけ) points->SetNumberOfPoints(boost::num_vertices(mygraph)); // まず全頂点の座標を登録 boost::graph_traits<boostGraph>::vertex_iterator vi, vi_end; vtkIdType pointId = 0; for (boost::tie(vi, vi_end) = boost::vertices(mygraph); vi != vi_end; ++vi, ++pointId) { const Eigen::Vector3d& pos = mygraph[*vi].coord; points->SetPoint(pointId, pos.x(), pos.y(), pos.z()); // Glyph用に頂点セルも作っておく verts->InsertNextCell(1, &pointId); } // 次に全辺をラインとして登録 boost::graph_traits<boostGraph>::edge_iterator ei, ei_end; for (boost::tie(ei, ei_end) = boost::edges(mygraph); ei != ei_end; ++ei) { auto u = boost::source(*ei, mygraph); auto v = boost::target(*ei, mygraph); vtkIdType ids[2] = { static_cast<vtkIdType>(boost::get(boost::vertex_index, mygraph, u)), static_cast<vtkIdType>(boost::get(boost::vertex_index, mygraph, v)) }; lines->InsertNextCell(2, ids); } // ============================================================== // 2. PolyData 作成 // ============================================================== vtkNew<vtkPolyData> polyData; polyData->SetPoints(points); polyData->SetLines(lines); polyData->SetVerts(verts); // Glyph3DMapper がこれを使う // ============================================================== // 3. ノード(球)のActor // ============================================================== vtkNew<vtkSphereSource> sphere; sphere->SetRadius(0.12); sphere->SetPhiResolution(18); sphere->SetThetaResolution(18); vtkNew<vtkGlyph3DMapper> glyphMapper; glyphMapper->SetInputData(polyData); glyphMapper->SetSourceConnection(sphere->GetOutputPort()); glyphMapper->ScalingOff(); // 全部同じ大きさ glyphMapper->Update(); vtkNew<vtkActor> nodeActor; nodeActor->SetMapper(glyphMapper); nodeActor->GetProperty()->SetColor(0.1, 0.7, 1.0); // 青系 nodeActor->GetProperty()->SetSpecular(0.6); nodeActor->GetProperty()->SetSpecularPower(30); // ============================================================== // 4. 辺(ライン)のActor // ============================================================== vtkNew<vtkPolyDataMapper> lineMapper; lineMapper->SetInputData(polyData); vtkNew<vtkActor> lineActor; lineActor->SetMapper(lineMapper); lineActor->GetProperty()->SetColor(0.65, 0.65, 0.65); lineActor->GetProperty()->SetLineWidth(2.5); // ============================================================== // 5. まとめて返す(Assembly) // ============================================================== vtkNew<vtkAssembly> assembly; assembly->AddPart(nodeActor); assembly->AddPart(lineActor); return assembly.GetPointer(); }

PowerPointではVBAマクロを使えるが、記録機能がないため、全部自分で書かないといけない。
'======================================= ' オブジェクトの位置とサイズを設定する関数(エントリポイント) '======================================= Sub SetObjectSizePosition() ' ピクセルを cm に変換するための値 Dim pt As Double pt = 28.3464567 ' 設定 ' 現状維持したい項目は Null に設定 SetSelectedObjectSize 9.86 * pt, Null SetSelectedObjectPosition 9.86 * pt , 6.91 * pt End Sub '======================================= ' オブジェクトのサイズを設定する関数 '======================================= Sub SetSelectedObjectSize(ByVal w As Variant, ByVal h As Variant) Dim shp As Shape If ActiveWindow.Selection.Type = ppSelectionShapes Then Set shp = ActiveWindow.Selection.ShapeRange(1) If IsNull(w) = False Then shp.Width = CDbl(w) End If If IsNull(h) = False Then shp.Height = CDbl(h) End If Else MsgBox "オブジェクトが選択されていません。" End If End Sub '======================================= ' オブジェクトの位置を設定する関数 '======================================= Sub SetSelectedObjectPosition(ByVal x As Variant, ByVal y As Variant) Dim shp As Shape If ActiveWindow.Selection.Type = ppSelectionShapes Then Set shp = ActiveWindow.Selection.ShapeRange(1) If IsNull(x) = False Then shp.Left = CDbl(x) End If If IsNull(y) = False Then shp.Top = CDbl(y) End If Else MsgBox "オブジェクトが選択されていません。" End If End Sub

ビルド後、DLLをReleaseディレクトリにコピーしたい。わけあって環境変数は変えたくない。
以下のような copy_release.batを作成して、.vcxproj ファイルと同じ場所に置く。これは後で$(ProjectDir)copy_release.batとして指定するため。
コピー先のTARGET_DIRは第一引数で指定し、VC++プロジェクト側からは $(TargetDir) で指定する。
@echo on setlocal set "TARGET_DIR=%~1" copy "D:\libraries\VTK-9.5.1\shared-mfcunicode-release\bin\*.dll" "%TARGET_DIR%" copy "D:\libraries\wxWidgets-3.3.1\bin\vc_x64_dll\*.dll" "%TARGET_DIR%"
構成プロパティ → ビルド イベント → ビルド後のイベントで、$(ProjectDir)copy_release.batを$(TargetDir)引数で呼び出す

テンプレートとしてエクスポートする場合、バッチファイルをプロジェクトに登録しておく必要がある。

ImageMagickで変換して作成した.pngを読み込んだときGimpのレイヤーグループが作れなかった。
どうやらインデクスカラーの画像を扱うときはレイヤーグループを使えないらしい。

問題はなぜインデクスカラーになっているのかという点で、どうやらimagemagickはpngに変換するとき、色数を見て効率の良さそうな方を自動で選ぶらしい。
RGBカラーを指定するには、以下のように -define を使用する。
RGBAにしたい場合は以下。
magick convert sample16.ppm -alpha on -define png:color-type=6 testRGBA.png
C++のstd::unordered_mapと同じように使えるtsl::robin_map。
std::unordered_mapと比較してみた。VC++2022でInsertとFindの時間を計ってみたが、何度かやると順位が入れ替わったりするのであまり大きな差はなさそう。
https://github.com/Tessil/robin-map
#include <iostream> #include <vector> #include <string> #include <algorithm> #include <random> #include <chrono> #include <tsl/robin_map.h> /* using KeyT = int; using ValueT = std::string; KeyT MakeKey(int i) { return i; } ValueT MakeValue(int i) { return "Value_" + std::to_string(i); } */ using KeyT = std::string; using ValueT = int; KeyT MakeKey(int i) { return "Value_" + std::to_string(i); } ValueT MakeValue(int i) { return i; }
std::vector< std::pair<KeyT, ValueT> > CreateData(int count) { std::vector< std::pair<KeyT, ValueT> > data; data.reserve(count); for (int i = 0; i < count; ++i) { data.emplace_back(MakeKey(i), MakeValue(i)); } return data; }
template<typename HashMap> void InsertTest(HashMap& mymap,const std::vector< std::pair<KeyT, ValueT> >& data) { for (size_t i = 0; i < data.size(); ++i) { mymap.insert({ data[i].first,data[i].second }); } }
template<typename HashMap> int FindTest(const HashMap& mymap, const std::vector< std::pair<KeyT, ValueT> >& data) { int results_count = 0; for (size_t i = 0; i < data.size(); ++i) { auto itr = mymap.find(data[i].first); if (itr != mymap.end()) { ++results_count; } } return results_count; }
struct test_times { std::chrono::microseconds insert_time; std::chrono::microseconds find_time; };
template<typename HashMap> test_times test( const std::vector< std::pair<KeyT, ValueT> > inserts, const std::vector< std::pair<KeyT, ValueT> > finds, HashMap& mymap) { test_times result; std::chrono::steady_clock::time_point start,end; /////////////////////////////////////////////////////////////// { // データを保存する時間の計測 start = std::chrono::high_resolution_clock::now(); mymap.reserve(inserts.size()); InsertTest(mymap, inserts); end = std::chrono::high_resolution_clock::now(); result.insert_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start); } /////////////////////////////////////////////////////////////// { // データを検索する時間の計測 start = std::chrono::high_resolution_clock::now(); volatile int ret = FindTest(mymap, finds); end = std::chrono::high_resolution_clock::now(); result.find_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start); } /////////////////////////////////////////////////////////////// return result; }
int main() { tsl::robin_map<KeyT, ValueT> mymap_robin; std::unordered_map<KeyT, ValueT> mymap_std; auto data = CreateData(100000); auto inserts = data; auto finds = data; int count = 100; int count_robin_insert = 0; int count_robin_find = 0; int count_std_insert = 0; int count_std_fine = 0; for (int i = 0; i < count; i++) { // データの並びを変える std::shuffle(inserts.begin(), inserts.end(), std::mt19937{ std::random_device{}() }); std::shuffle(finds.begin(), finds.end(), std::mt19937{ std::random_device{}() }); mymap_robin.clear(); mymap_robin.rehash(0); mymap_std.clear(); mymap_std.rehash(0); test_times robin = test(inserts, finds, mymap_robin); test_times stdmp = test(inserts, finds, mymap_std); count_robin_find += robin.find_time.count(); count_robin_insert += robin.insert_time.count(); count_std_insert += stdmp.insert_time.count(); count_std_fine += stdmp.find_time.count(); } std::cout << "robin insert time (ms): " << count_robin_insert / count << std::endl; std::cout << " std insert time (ms): " << count_std_insert / count << std::endl; std::cout << "------------------------" << std::endl; std::cout << "robin find time (ms): " << count_robin_find / count << std::endl; std::cout << " std find time (ms): " << count_std_fine / count << std::endl; return 0; }
KeyT == std::string , ValueT==int の場合
KeyT==int , ValueT == std::string の場合
構造体の配列から特定の要素だけをまとめた配列を作成する。
#include <vector> #include <string> struct test { int a; float b; std::string c; };
template <typename StructT, typename MemberT> std::vector<MemberT> column(const std::vector<StructT>& src, MemberT StructT::* member) { std::vector<MemberT> result; result.reserve(src.size()); for (const auto& v : src) { result.push_back(v.*member); } return result; }
int main() { std::vector<test> vec = { {1, 1.1f, "one"}, {2, 2.2f, "two"}, {3, 3.3f, "three"} }; std::vector<float> b_values = column(vec, &test::b); for (float b : b_values) { printf("%f\n", b); } return 0; }
メンバへのポインタを初めて使った。
about:config → browser.urlbar.autoFill をfalseに設定する。
