特定のファイルなどがカレントディレクトリにないといけない場合、cmakeでファイル群をコピーできる。
#include <windows.h> #include <cstdio> #if defined(_WIN32) #define DLL_EXPORT extern "C" __declspec(dllexport) #else #define DLL_EXPORT extern "C" #endif
DLL_EXPORT double squared(double* a) { return *a * *a; }
DLL_EXPORT void squared(double* a) { *a = *a * *a; }
DLL_EXPORT float add_values(int a, float b) { return a + b; }
DLL_EXPORT void get_version(char* const out_buf) { const char* text = "Version 1.0.0"; int len = strlen(text); if (out_buf != NULL) { strcpy(out_buf,text); } }
// 定数を返す DLL_EXPORT int get_constant_value() { return 42; }
// メッセージを標準出力に表示 DLL_EXPORT void print_message() { printf("Hello World\n"); }
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
import ctypes from ctypes import c_int, c_float,c_double, c_size_t, c_char_p, POINTER, create_string_buffer dll = ctypes.CDLL(r".\sample.dll") # # void squared(double* a) # dll.squared.argtypes = (ctypes.POINTER(c_double),) # 引数の型指定 dll.squared.restype = None # 戻り値の型指定 v = c_double(3.0) # Cの double 型の変数作成 dll.squared(ctypes.byref(v)) # 関数呼び出し , ポインタ渡し ret = v.value # 結果取得 print("squared result:", ret) # 結果表示 # # float add_values(int a, float b) # dll.add_values.argtypes = (c_int, c_float) # 引数の型指定 dll.add_values.restype = c_float # 戻り値の型指定 ret = dll.add_values(10, c_float(2.5)) print("add_values:", ret) # # void get_version(char* out_buf) # dll.get_version.argtypes = (ctypes.c_char_p,) # 引数の型指定 dll.get_version.restype = None # 戻り値の型指定 buf = create_string_buffer(64) # 文字列を受け取るためのバッファ作成 dll.get_version(buf) # 関数呼び出し , ポインタ渡し ret = buf.value.decode("utf-8") # 結果取得 print("version:", ret) # # int get_constant_value() # dll.get_constant_value.argtypes = () # 引数なし dll.get_constant_value.restype = c_int # 戻り値の型指定 ret = dll.get_constant_value() print("constant:", ret) # # void print_message() # dll.print_message.argtypes = () # 引数なし dll.print_message.restype = None # 戻り値なし dll.print_message() # 関数呼び出し
例えば以下のような CMakeLists.txtを用意し、
cmake_minimum_required(VERSION 3.16)
# プロジェクト名と使用言語
project(MyApp LANGUAGES CXX)
# C++標準(必要に応じて変更)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 実行ファイルを作成(出力名=MyApp、ソース=main.cpp)
add_executable(MyApp main.cpp)
# /MD
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL$<$<CONFIG:Debug>:Debug>")
以下でVC++のソリューションとプロジェクトを作成する。
と、プロジェクトの出力先が絶対パスになる。これの何が問題かというとディレクトリごとコピーしてプロジェクト複製したときに出力先が変わらない。

別に手で直せばいい。量もたいしたことは無いので、
とすれば良い。もし自動でやるなら、vcxprojファイルに書かれているので、xmlを編集する要領で書ける。
#include <iostream> #include <fstream> #include "pugixml.hpp" void fix_outdir(const char* path) { pugi::xml_document doc; if (!doc.load_file(path)) return; // 全 OutDir を対象に書き換え for (auto outdir : doc.select_nodes("//OutDir"))// //OutDir とすると、出現する全てのOutDirという意味になる { pugi::xml_node n = outdir.node(); n.text() = "$(SolutionDir)$(Platform)\\$(Configuration)\\"; } doc.save_file(path); } int main() { std::string vcxproj_path = R"(C:\myusertemp\test\build\MyApp.vcxproj)"; fix_outdir(vcxproj_path.c_str()); }
VTK 9.5.1を使おうとしたところ、今までのwxWidgets+VTKのコードで動かなくなっていた。
なんとかWindows+wxWidgetsでVTK9.5.1での表示ができるようになった。

#include <wx/wxprec.h> #include <wx/glcanvas.h> #include <wx/frame.h> #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <GL/gl.h> #pragma comment(lib, "opengl32.lib") #include <iostream> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkOutputWindow.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkActor.h> #include <vtkPolyDataMapper.h> #include <vtkCylinderSource.h> #include <vtkInteractorStyleTrackballCamera.h> #include <vtkWin32OpenGLRenderWindow.h> #include <vtkWin32RenderWindowInteractor.h> #include <vtkConeSource.h> VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle); vtkSmartPointer<vtkActor> CreateCone() { vtkSmartPointer<vtkConeSource> coneSource; coneSource = vtkSmartPointer<vtkConeSource>::New(); vtkSmartPointer<vtkPolyDataMapper> mapper; mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(coneSource->GetOutputPort()); vtkSmartPointer<vtkActor> actor; actor = vtkSmartPointer<vtkActor>::New(); actor->SetMapper(mapper); return actor; } class MyGLCanvas : public wxGLCanvas { public: MyGLCanvas(wxFrame* parent,int* attribList = NULL); private: wxGLContext* m_context; // OpenGL context ////////////////////////////////////////// vtkNew<vtkWin32OpenGLRenderWindow> _renderWindow; vtkNew<vtkWin32RenderWindowInteractor> _interactor; vtkNew<vtkRenderer> _renderer; ////////////////////////////////////////// bool vtk_inited = false; ////////////////////////////////////////// // OpenGLコンテキストの作成をしてVTKに貸与する関数 void InitVTKFromCurrentContext(); ////////////////////////////////////////// // マウスイベントを処理 int last_x_ = 0, last_y_ = 0; void OnLeftDown(wxMouseEvent& e); void OnLeftUp(wxMouseEvent& e); void OnRightDown(wxMouseEvent& e); void OnRightUp(wxMouseEvent& e); void OnMiddleDown(wxMouseEvent& e); void OnMiddleUp(wxMouseEvent& e); void OnMotion(wxMouseEvent& e); void OnMouseWheel(wxMouseEvent& e); ////////////////////////////////////////// void OnPaint(wxPaintEvent& evt); }; MyGLCanvas::MyGLCanvas(wxFrame* parent, int* attribList) : wxGLCanvas(parent, wxID_ANY, attribList) { Bind(wxEVT_PAINT, &MyGLCanvas::OnPaint, this); m_context = new wxGLContext(this); SetBackgroundStyle(wxBG_STYLE_CUSTOM); Bind(wxEVT_LEFT_DOWN, &MyGLCanvas::OnLeftDown, this); Bind(wxEVT_LEFT_UP, &MyGLCanvas::OnLeftUp, this); Bind(wxEVT_RIGHT_DOWN, &MyGLCanvas::OnRightDown, this); Bind(wxEVT_RIGHT_UP, &MyGLCanvas::OnRightUp, this); Bind(wxEVT_MIDDLE_DOWN, &MyGLCanvas::OnMiddleDown, this); Bind(wxEVT_MIDDLE_UP, &MyGLCanvas::OnMiddleUp, this); Bind(wxEVT_MOTION, &MyGLCanvas::OnMotion, this); Bind(wxEVT_MOUSEWHEEL, &MyGLCanvas::OnMouseWheel, this); } void MyGLCanvas::OnLeftDown(wxMouseEvent& e) { SetFocus(); CaptureMouse(); last_x_ = e.GetX(); last_y_ = e.GetY(); _interactor->SetEventInformationFlipY(last_x_, last_y_, e.ControlDown(), e.ShiftDown()); _interactor->InvokeEvent(vtkCommand::LeftButtonPressEvent); } void MyGLCanvas::OnLeftUp(wxMouseEvent& e) { last_x_ = e.GetX(); last_y_ = e.GetY(); ReleaseMouse(); _interactor->SetEventInformationFlipY(last_x_, last_y_, e.ControlDown(), e.ShiftDown()); _interactor->InvokeEvent(vtkCommand::LeftButtonReleaseEvent); } void MyGLCanvas::OnRightDown(wxMouseEvent& e) { last_x_ = e.GetX(); last_y_ = e.GetY(); CaptureMouse(); _interactor->SetEventInformationFlipY(last_x_, last_y_, e.ControlDown(), e.ShiftDown()); _interactor->InvokeEvent(vtkCommand::RightButtonPressEvent); } void MyGLCanvas::OnRightUp(wxMouseEvent& e) { last_x_ = e.GetX(); last_y_ = e.GetY(); ReleaseMouse(); _interactor->SetEventInformationFlipY(last_x_, last_y_, e.ControlDown(), e.ShiftDown()); _interactor->InvokeEvent(vtkCommand::RightButtonReleaseEvent); } void MyGLCanvas::OnMiddleDown(wxMouseEvent& e) { last_x_ = e.GetX(); last_y_ = e.GetY(); CaptureMouse(); _interactor->SetEventInformationFlipY(last_x_, last_y_, e.ControlDown(), e.ShiftDown()); _interactor->InvokeEvent(vtkCommand::MiddleButtonPressEvent); } void MyGLCanvas::OnMiddleUp(wxMouseEvent& e) { last_x_ = e.GetX(); last_y_ = e.GetY(); ReleaseMouse(); _interactor->SetEventInformationFlipY(last_x_, last_y_, e.ControlDown(), e.ShiftDown()); _interactor->InvokeEvent(vtkCommand::MiddleButtonReleaseEvent); } void MyGLCanvas::OnMotion(wxMouseEvent& e) { last_x_ = e.GetX(); last_y_ = e.GetY(); _interactor->SetEventInformationFlipY(last_x_, last_y_, e.ControlDown(), e.ShiftDown()); _interactor->InvokeEvent(vtkCommand::MouseMoveEvent); } void MyGLCanvas::OnMouseWheel(wxMouseEvent& e) { last_x_ = e.GetX(); last_y_ = e.GetY(); _interactor->SetEventInformationFlipY(last_x_, last_y_, e.ControlDown(), e.ShiftDown()); if (e.GetWheelRotation() > 0) _interactor->InvokeEvent(vtkCommand::MouseWheelForwardEvent); else _interactor->InvokeEvent(vtkCommand::MouseWheelBackwardEvent); } void MyGLCanvas::InitVTKFromCurrentContext() { // wxのGLを現在化 wxPaintDC dc(this); SetCurrent(*m_context); // Win32ハンドル取得 HWND hwnd = this->GetHandle(); HDC hdc = dc.GetHDC(); HGLRC hrc = m_context->GetGLRC(); // wxWidgetsが作ったコンテキストをVTKへ貸与 _renderWindow->SetWindowId(hwnd); _renderWindow->SetDeviceContext(hdc); _renderWindow->SetContextId(hrc); _renderWindow->InitializeFromCurrentContext(); // レンダラの設定 _renderWindow->AddRenderer(_renderer); _interactor->SetRenderWindow(_renderWindow); _interactor->Initialize(); _interactor->Enable(); // マウスイベントを設定. auto style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New(); style->SetDefaultRenderer(_renderer); _interactor->SetInteractorStyle(style); // レンダリング _renderer->SetBackground(1.1, 0.2, 0.3); _renderer->AddActor(CreateCone()); vtk_inited = true; } void MyGLCanvas::OnPaint(wxPaintEvent& evt) { if (!IsShown()) return; if (!vtk_inited) InitVTKFromCurrentContext(); SetCurrent(*m_context); _renderWindow->Render(); } class MyFrame : public wxFrame { public: MyFrame(const wxString& title, int xpos, int ypos, int width, int height); ~MyFrame(); void OnSize(wxSizeEvent& event); MyGLCanvas* m_canvas = NULL; }; MyFrame::MyFrame(const wxString& title, int xpos, int ypos, int width, int height) : wxFrame((wxFrame*)NULL, -1, title, wxPoint(xpos, ypos), wxSize(width, height)) { int args[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0 }; m_canvas = new MyGLCanvas(this, args); Bind(wxEVT_SIZE, &MyFrame::OnSize, this); } void MyFrame::OnSize(wxSizeEvent& event) { m_canvas->SetSize(event.GetSize()); } MyFrame::~MyFrame() { delete m_canvas; } class MyApp : public wxApp { public: bool OnInit(); }; bool MyApp::OnInit() { // VTKのデバッグ出力ウィンドウを表示しない vtkOutputWindow::SetGlobalWarningDisplay(0); MyFrame* frame = new MyFrame(wxT("wxWidgets OpenGL"), 50, 50, 400, 400); frame->Show(); return true; } IMPLEMENT_APP(MyApp)
OnSizeを以下のように記述。
void MyGLCanvas::OnSize(wxSizeEvent& e) { if (!vtk_inited || !IsShownOnScreen()) return; int w = 0, h = 0; GetClientSize(&w, &h); // HiDPI 対応(MSW の例) int pw = w, ph = h; #ifdef __WXMSW__ double scale = GetContentScaleFactor(); pw = static_cast<int>(std::lround(w * scale)); ph = static_cast<int>(std::lround(h * scale)); #endif // 次のペイントで描画 Refresh(false); e.Skip(); // 既定処理も通す }
#include <vtkInteractorStyleTrackballCamera.h> // 移動・視線方向回転・ズームのみできるインタラクタスタイル class InteractorStylePanRollZoom final : public vtkInteractorStyleTrackballCamera { public: static InteractorStylePanRollZoom* New(); vtkTypeMacro(InteractorStylePanRollZoom, vtkInteractorStyleTrackballCamera); // 左ドラッグ開始 void OnLeftButtonDown() override { // 分割ビューなどを想定し、マウス位置からレンダラーを特定 // マウス位置にあるレンダラーをCurrentRendererにセット this->FindPokedRenderer( this->Interactor->GetEventPosition()[0], this->Interactor->GetEventPosition()[1] ); if (!this->CurrentRenderer) { return; } // vtkInteractorStyleTrackballCameraの機能を使って 視線方向基準のRollを開始 this->StartSpin(); } // 左ドラッグ終了 void OnLeftButtonUp() override { this->EndSpin(); if (this->Interactor) { this->ReleaseFocus(); } } // 不要なキーボード操作を無効化 void OnChar() override { // 何もしない 既定のキーバインドを殺す } }; vtkStandardNewMacro(InteractorStylePanRollZoom);
auto renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor); ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); auto style = vtkSmartPointer<InteractorStylePanRollZoom>::New(); interactor->SetInteractorStyle(style); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); interactor->Start(); //イベントループへ入る

vtkPolyDataMapper2D,vtkTextureActor2Dを使うとカメラの影響を受けなくなるのでスクリーンを画面に貼り付けるような用途に使える。
あと座標指定がピクセルになる。
// テクスチャオブジェクト作成 /////////////// vtkNew<vtkTexture> texture; // 画像データ作成 /////////////////////////// std::vector<unsigned char> pixels = MakeCheckerRGBA(256, 256, 32); // 画像データをvtkImageDataに変換 /////////// vtkSmartPointer<vtkImageData> imgdata = MakeImageDataFromBuffer(pixels.data(), 256, 256, 4); // テクスチャに画像データをセット /////////// SetTextureImage(texture, imgdata); // ポリゴン作成 ///////////////////////////// auto poly_quad = MakeScreenAlignedQuad(200, 200); // ポリゴンサイズをピクセル指定で作成 // 2Dマッパ & アクタ vtkNew<vtkPolyDataMapper2D> mapper2d; mapper2d->SetInputData(poly_quad); vtkNew<vtkTexturedActor2D> actor2d; actor2d->SetMapper(mapper2d); actor2d->SetPosition(0, 0); // 画面上の配置(左下オフセット) actor2d->SetTexture(texture); renderer->AddActor(actor2d);

actor->SetBackfacePropertyを設定する。
//#include <vtkParametricMobius.h> //#include <vtkParametricFunctionSource.h> // メビウス vtkNew<vtkParametricMobius> mobius; mobius->SetRadius(5.0); const double halfWidth = 2.0; mobius->SetMinimumV(-halfWidth); mobius->SetMaximumV(+halfWidth); vtkNew<vtkParametricFunctionSource> parametricSource; parametricSource->SetParametricFunction(mobius); parametricSource->SetUResolution(100); parametricSource->SetVResolution(30); parametricSource->Update(); vtkNew<vtkPolyDataMapper> mapper; mapper->SetInputConnection(parametricSource->GetOutputPort()); vtkNew<vtkActor> actor; actor->SetMapper(mapper);
// 表裏色設定 actor->GetProperty()->SetColor(1.0, 0.0, 0.0); // 表 vtkNew<vtkProperty> backProp; backProp->SetColor(0.0, 1.0, 0.0); // 裏 actor->SetBackfaceProperty(backProp);

フラグメントシェーダを置換する方法でもできる。
// カスタムシェーダ適用 actor->GetProperty()->ShadingOn(); actor->GetProperty()->SetInterpolationToPhong(); vtkShaderProperty* shaderProperty = actor->GetShaderProperty(); // フラグメントシェーダにコードを追加 shaderProperty->AddFragmentShaderReplacement( "//VTK::Light::Impl", // 挿入位置(照明計算の後) false, // 前のコードを置換 R"( vec3 faceTint2 = gl_FrontFacing ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0); fragOutput0 = vec4(faceTint2, 1.0) * fragOutput0; )", false );
#pragma once #include <vtkPolyData.h> #include <vtkSmartPointer.h> #include <vtkTexture.h> #include <vtkImageData.h>
//! @brief 指定サイズの矩形を作成 //! @note テクスチャ座標設定済み //! @param width 幅(ピクセル) //! @param height 高さ(ピクセル) //! @return 矩形ポリゴンのvtkPolyData vtkSmartPointer<vtkPolyData> MakeScreenAlignedQuad(float width, float height);
//! @brief 矩形ポリゴンのサイズを変更 //! @param quad 矩形ポリゴンのvtkPolyData //! @param newWidth 新しい幅(ピクセル) //! @param newHeight 新しい高さ(ピクセル) //! @return なし void ResizeQuad(vtkPolyData* quad, float newWidth, float newHeight);
//! @brief vtkTextureにvtkImageDataをセット //! @param texture テクスチャオブジェクト //! @param image 画像データオブジェクト //! @return なし void SetTextureImage(vtkTexture* texture, vtkImageData* image);
//! @brief 画像配列から vtkImageData を生成 //! @param pixels 画像ピクセル配列(RGBAまたはRGB) //! @param width 画像幅(ピクセル) //! @param height 画像高さ(ピクセル) //! @param numComponents ピクセルあたりの成分数(3または4) vtkSmartPointer<vtkImageData> MakeImageDataFromBuffer( const unsigned char* pixels, int width, int height, int numComponents );
#include "MyTexture.hpp" #include <vtkFloatArray.h> #include <vtkPointData.h>
vtkSmartPointer<vtkPolyData> MakeScreenAlignedQuad(float width, float height) { vtkNew<vtkPoints> pts; pts->SetNumberOfPoints(4); pts->SetPoint(0, 0.0, 0.0, 0.0); pts->SetPoint(1, width, 0.0, 0.0); pts->SetPoint(2, width, height, 0.0); pts->SetPoint(3, 0.0, height, 0.0); vtkNew<vtkCellArray> polys; vtkIdType ids[4] = { 0,1,2,3 }; polys->InsertNextCell(4, ids); vtkNew<vtkFloatArray> tcoords; tcoords->SetName("TCoords"); tcoords->SetNumberOfComponents(2); tcoords->InsertNextTuple2(0.f, 0.f); // 左下 tcoords->InsertNextTuple2(1.f, 0.f); // 右下 tcoords->InsertNextTuple2(1.f, 1.f); // 右上 tcoords->InsertNextTuple2(0.f, 1.f); // 左上 vtkNew<vtkPolyData> pd; pd->SetPoints(pts); pd->SetPolys(polys); pd->GetPointData()->SetTCoords(tcoords); return pd; }
vtkSmartPointer<vtkImageData> MakeImageDataFromBuffer( const unsigned char* pixels, int width, int height, int numComponents ) { vtkNew<vtkImageData> img; img->SetDimensions(width, height, 1); img->AllocateScalars(VTK_UNSIGNED_CHAR, numComponents); const int stride = numComponents; auto* dst = static_cast<unsigned char*>(img->GetScalarPointer()); std::memcpy(dst, pixels, static_cast<size_t>(width) * height * stride); return img; }
void ResizeQuad(vtkPolyData* quad, float newWidth, float newHeight) { auto* pts = quad->GetPoints(); pts->SetPoint(0, 0.0, 0.0, 0.0); pts->SetPoint(1, newWidth, 0.0, 0.0); pts->SetPoint(2, newWidth, newHeight, 0.0); pts->SetPoint(3, 0.0, newHeight, 0.0); pts->Modified(); quad->Modified(); }
void SetTextureImage(vtkTexture* texture, vtkImageData* image) { texture->SetInputData(image); texture->InterpolateOn(); // バイリニア補間 texture->RepeatOff(); // 画像外は伸ばさない texture->EdgeClampOn(); texture->Modified(); }
// 画像生成 std::vector<unsigned char> MakeCheckerRGBA(int w, int h, int block = 16) { std::vector<unsigned char> buf(static_cast<size_t>(w) * h * 4); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { bool on = ((x / block) + (y / block)) % 2 == 0; unsigned char c = on ? 230 : 60; size_t idx = (static_cast<size_t>(y) * w + x) * 4; buf[idx + 0] = c; buf[idx + 1] = c; buf[idx + 2] = c; buf[idx + 3] = 255; } } return buf; }
// テクスチャオブジェクト作成 /////////////// vtkNew<vtkTexture> texture; // 画像データ作成 /////////////////////////// std::vector<unsigned char> pixels = MakeCheckerRGBA(256, 256, 32); // 画像データをvtkImageDataに変換 /////////// vtkSmartPointer<vtkImageData> imgdata = MakeImageDataFromBuffer(pixels.data(), 256, 256, 4); // テクスチャに画像データをセット /////////// SetTextureImage(texture, imgdata); // ポリゴン作成 ///////////////////////////// auto poly_quad = MakeScreenAlignedQuad(100, 200); vtkNew<vtkPolyDataMapper> mapper; mapper->SetInputData(poly_quad); vtkNew<vtkActor> actor; actor->SetMapper(mapper); actor->SetTexture(texture); auto renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->AddActor(actor);

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; }
#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);
#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); }
actorなどの生成まではスレッドでできるが、Render()関数などの呼び出しはメインスレッドで行わなければいけない。
VTKのメッセージループを使う限りはシグナルを送ったりということが簡単にできないので、OnTimerで定期的にRender()をする。
#include <iostream> #include <thread> //VTK_MODULE_INITに必要 #include <vtkAutoInit.h> #include <vtkSmartPointer.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkPolyDataMapper.h> #include <vtkSphereSource.h> #include <vtkCallbackCommand.h> #include <vtkCamera.h> #include <vtkActor.h> // #include "createpoly.hpp" //必須 VTK_MODULE_INIT(vtkRenderingOpenGL2); VTK_MODULE_INIT(vtkInteractionStyle);
// 定期的に Render する void OnTimer(vtkObject* /*caller*/, unsigned long /*eventId*/, void* clientData, void* /*callData*/) { auto renderWindow = static_cast<vtkRenderWindow*>(clientData); if (renderWindow) { renderWindow->Render(); } }
// 停止フラグ std::atomic<bool> g_stop{ false };
// スレッド void work(vtkSmartPointer<vtkRenderer> renderer) { for (size_t i = 0; i < 100; i++) { // 乱数 double x = (std::rand() % 100) / 10.0 - 5.0; double y = (std::rand() % 100) / 10.0 - 5.0; double z = (std::rand() % 100) / 10.0 - 5.0; // 球体 vtkSmartPointer<vtkSphereSource> sphereSource = vtkSmartPointer<vtkSphereSource>::New(); sphereSource->SetCenter(x,y,z); sphereSource->SetRadius(5.0); sphereSource->Update(); vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New(); vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputConnection(sphereSource->GetOutputPort()); actor->SetMapper(mapper); renderer->AddActor(actor); std::this_thread::sleep_for(std::chrono::seconds(1)); if(g_stop) break; } }
int main(int /*argc*/, char** /*argv*/) { auto renderer = vtkSmartPointer<vtkRenderer>::New(); renderer->ResetCamera(); renderer->GetActiveCamera()->SetPosition(0, 0, 100); ////////////////////////////////////// auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); ////////////////////////////////////// auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New(); renderWindow->AddRenderer(renderer); renderWindow->SetInteractor(interactor); renderWindow->Render(); auto cb = vtkSmartPointer<vtkCallbackCommand>::New(); cb->SetClientData(renderWindow); cb->SetCallback(OnTimer); interactor->AddObserver(vtkCommand::TimerEvent, cb); interactor->Initialize(); interactor->CreateRepeatingTimer(10); std::thread t1(work, renderer); interactor->Start(); //イベントループへ入る g_stop = true; t1.join(); return 0; }