諸事情により簡易的でよいので.exeファイルの解析をしたくなった。.exeファイルをバイナリエディタで見てみて、全く同じプロジェクトを二回以上ビルドした結果が完全一致しない。理由の一つがタイムスタンプなので、これをいじってみる。
VC++でコンソールアプリケーションを作成する。この時、余計なものが埋め込まれるとそれだけビルド時の変化が大きくなる可能性があるので、わかる範囲で不要なオプションを取り除く。
・.pdbファイルを出力しない
プロジェクトのプロパティ → リンカー → デバッグ → デバッグ情報の生成 → いいえ
・マニフェストを埋め込まない
プロジェクトのプロパティ → リンカー → マニフェスト ファイル → マニフェストの生成 → いいえ
・ASLRをしない(Address Space Layout Randomization)
ASLRはexe内のデータの位置をランダムにするクラッキング対策のオプションで、多分悪影響を与えるので切っておく。/DYNAMICBASE:NOを設定。
プロジェクトのプロパティ → リンカー → 詳細設定 → ランダム化されたベースアドレス → いいえ
まずビルドし、上書きされないように.exeファイルの名前を変える。それからもう一度ビルドする。
右クリック→プロパティ→詳細 で更新日時を確認。
実際にはここの表示を変えるわけではないのだが、バイナリ中の値を解析してこの日付とおおむね同じものが出てくれば正解だとわかるので参考にする。
WinMergeで二つの.exeをバイナリ比較する。手元のバージョンでは、CompareボタンのプルダウンにBinary比較がある。
一部違う場所が出てくるので、その右4バイトに着目する。時間を置かずにビルドすると、数秒しか違わないはずなので、違いはせいぜい下1バイトになる可能性が高い。intelのCPUはリトルエンディアンなので、バイトの並び順が逆になっている。従って変化している場所を含めて右に4バイトがタイムスタンプとなる。
上記で得られた値 0x65abe167 が実際にビルド時刻であることを確認するため、以下のプログラムを書いて確認する。
#include <ctime> #include <string> #include <iostream> #pragma warning(disable:4996) // タイムスタンプから時刻表示を作成 std::string TimestampToDate(time_t timestamp) { struct tm* tm = localtime(×tamp);// 整数値 timestamp を tm 構造体に変換 char str[50]; strftime(str, sizeof(str), "%Y/%m/%d %H:%M:%S", tm);// tmを文字列化 return std::string(str); } int main() { // タイムスタンプ(16進数指定) // バイナリに埋め込まれている値はリトルエンディアンなので // バイナリエディタ上の表示とバイト単位で逆順にして入力する time_t timestamp = 0x65abe167; std::cout << TimestampToDate(timestamp) << std::endl; return 0; }
結果
確かにファイルのプロパティ上の表示と一致する内容を得られたので、この数値がタイムスタンプであることは理解できた。今度はこれを書き換えてみる。
まず、適当な日付をタイムスタンプに変換するため、以下のプログラムを書いて走らせる。
#include <ctime> #include <string> #include <iostream> #pragma warning(disable:4996) // 時刻からタイムスタンプを作成 time_t DateToTimestamp(const std::string date) { struct tm tm = {}; int year, month, day; int hour, minute, second; sscanf(date.c_str(), "%d/%d/%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second ); tm.tm_year = year - 1900; tm.tm_mon = month - 1; tm.tm_mday = day; tm.tm_hour = hour; tm.tm_min = minute; tm.tm_sec = second; return mktime(&tm); } int main() { std::string date_str = "2022/01/01 5:25:30"; // 適当な日付 unsigned int timestamp = DateToTimestamp(date_str); printf("%x \n", timestamp); }
結果
こうして得られたタイムスタンプを、バイナリエディタで書き込む。書き込みにはStirlingを使っている。入力時、リトルエンディアンなのでバイト単位で逆順に指定する。
タイムスタンプはビルド時の色々なステップで入れられるらしいので、同じ値を見つけたらすべて書き換える。
先ほどはファイルのプロパティから確認したが、この日付はファイルのプロパティには実は表示されないので、PEヘッダを読むツールで確認する。探したところPEviewが適している。
以下からPEview version 0.9.9 ( .zip 31KB )をダウンロードする。
http://wjradburn.com/software/
wxWebView上でTinyMCEを動かし、その編集内容をC++側に取得する。
これを応用すればHTMLエディタ的なものが作れる。
<!DOCTYPE html> <html> <head> <script src="js/tinymce/tinymce.min.js"></script> <script> tinymce.init({ selector: 'textarea' }); </script> <script>
// TinyMCEから入力内容を取得する関数 function MyGetContent(){ var myContent = tinymce.get("myTinyMCEarea").getContent(); return myContent; }
</script> </head> <body> <!-- TinyMCE本体 --> <textarea id="myTinyMCEarea">my text</textarea> </body> </html>
wxWebView::RunScriptを使用すれば、読み込んでいるページのJavaScriptを実行し、結果を取得できる。これを応用し、my_test.html側にMyGetContent()関数を作成してから、RunScriptをして、取得した結果をメッセージボックスで表示する。
// https://docs.wxwidgets.org/3.0/overview_helloworld.html // プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #pragma comment(lib,"wxbase32u.lib") #pragma comment(lib,"wxbase32u_net.lib") #pragma comment(lib,"wxbase32u_xml.lib") #pragma comment(lib,"wxmsw32u_adv.lib") #pragma comment(lib,"wxmsw32u_aui.lib") #pragma comment(lib,"wxmsw32u_core.lib") #pragma comment(lib,"wxmsw32u_gl.lib") #pragma comment(lib,"wxmsw32u_html.lib") #pragma comment(lib,"wxmsw32u_media.lib") #pragma comment(lib,"wxmsw32u_propgrid.lib") #pragma comment(lib,"wxmsw32u_qa.lib") #pragma comment(lib,"wxmsw32u_ribbon.lib") #pragma comment(lib,"wxmsw32u_richtext.lib") #pragma comment(lib,"wxmsw32u_stc.lib") #pragma comment(lib,"wxmsw32u_webview.lib") #pragma comment(lib,"wxmsw32u_xrc.lib") // wxWebViewを使うために必要 #include <wx/webview.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // ウィンドウ作成 class MyFrame : public wxFrame { wxWebView* webView; public: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); // wxWebView インスタンスの作成 webView = wxWebView::New( this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxWebViewBackendEdge, 0, "" ); webView->LoadURL(R"(C:\test\data\tinymce\my_test.html)");// TinyMCEを使うHTML読み込み // ボタンを作成し、sizerに追加 wxButton* button = new wxButton(this, wxID_ANY, wxT("Run Script")); sizer->Add(webView, 1, wxEXPAND, 0); sizer->Add(button, 0, wxALIGN_CENTER | wxALL, 10); SetSizer(sizer); // ボタンのイベントをバインド button->Bind(wxEVT_BUTTON, &MyFrame::OnButtonClicked, this); } // ボタンクリックイベントのハンドラ void OnButtonClicked(wxCommandEvent& event) { if (webView) {
wxString jsCode = wxT(R"(MyGetContent();)"); wxString ret; webView->RunScript(jsCode, &ret);// my_test.html に定義したJavaScriptのfunction呼び出し wxMessageBox(ret);
} } private: }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
TinyMCEはブラウザ上で使えるHTMLのWYSIWYGエディタ。
公式ページ:
サイトの最下部付近にある GET TinyMCE FREE から、「TinyMCE Open Source Community」 内の 「Download TinyMCE Community」をクリックしてダウンロード開始。
<!DOCTYPE html> <html> <head> <script src="js/tinymce/tinymce.min.js"></script> <script> tinymce.init({ selector: 'textarea' }); </script> </head> <body> <!-- TinyMCE入力エリア --> <textarea>first text</textarea> </body> </html>
C:\test\data │ └─tinymce │ CHANGELOG.md │ my_test.html │ └─js └─tinymce ├─icons ├─langs ├─models ├─plugins ├─skins └─themes
my_test.htmlをFirefoxなどのブラウザで開くと、エディタを使用できる。
JavaScriptで、入力したテキストを取得する。
<!DOCTYPE html> <html> <head> <script src="js/tinymce/tinymce.min.js"></script> <script> tinymce.init({ selector: 'textarea' }); </script>
<script> // TinyMCEから入力内容を取得する関数 function MyGetContent(){ var myContent = tinymce.get("myTinyMCEarea").getContent(); return myContent; } // ボタンが押された時に実行する関数。 // TinyMCEからテキストを取得し、MyCheckAreaに表示する。 function MyButtonClick(){ document.getElementById("MyCheckArea").innerHTML = MyGetContent(); } </script>
</head> <body> <!-- TinyMCE本体 --> <textarea id="myTinyMCEarea">first text</textarea> <!-- 内容取得確認用ボタン --> <button id="showContent" onclick="MyButtonClick()" >Show Content</button> <!-- 取得した内容の表示領域 --> <div id="MyCheckArea"></div> </body> </html>
公式ドキュメントよりも詳しいTinyMCEの使い方(基本編)
公式ドキュメントよりも詳しいTinyMCEの使い方(基本編)
wxWebViewを使うとWebサイトを簡単に表示できる。問題はレンダリングエンジンの指定に注意が必要。
// https://docs.wxwidgets.org/3.0/overview_helloworld.html // プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #pragma comment(lib,"wxbase32u.lib") #pragma comment(lib,"wxbase32u_net.lib") #pragma comment(lib,"wxbase32u_xml.lib") #pragma comment(lib,"wxmsw32u_adv.lib") #pragma comment(lib,"wxmsw32u_aui.lib") #pragma comment(lib,"wxmsw32u_core.lib") #pragma comment(lib,"wxmsw32u_gl.lib") #pragma comment(lib,"wxmsw32u_html.lib") #pragma comment(lib,"wxmsw32u_media.lib") #pragma comment(lib,"wxmsw32u_propgrid.lib") #pragma comment(lib,"wxmsw32u_qa.lib") #pragma comment(lib,"wxmsw32u_ribbon.lib") #pragma comment(lib,"wxmsw32u_richtext.lib") #pragma comment(lib,"wxmsw32u_stc.lib") #pragma comment(lib,"wxmsw32u_webview.lib") #pragma comment(lib,"wxmsw32u_xrc.lib") // wxWebViewを使うために必要 #include <wx/webview.h> ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // ウィンドウ作成 class MyFrame : public wxFrame { public: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // wxWebView インスタンスの作成 wxWebView* webView = wxWebView::New( this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, // レンダリングエンジンの指定 // wxWebViewBackendWebKit, // wxWebViewBackendEdge, // wxWebViewBackendIE, wxWebViewBackendDefault, 0, "" ); webView->LoadURL("https://www.suzulang.com/"); } private: // イベント処理しないときはこれを入れない // wxDECLARE_EVENT_TABLE(); }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
少なくともWindowsでレイアウトが崩れる理由は、wxWebViewBackendDefaultの指定だとIEになってしまうからで(’明示的に指定する場合はwxWebViewBackendIE)、これをEdgeにしなければならない。
wxWebViewBackendEdgeを指定する。
ただし、これを指定してエラーになったり、何も表示されない場合は、wxWidgetsをCMakeする際にwxUSE_WEBVIEW_EDGE,wxUSE_WEBVIEW_EDGE_STATICを含めて再ビルドする必要がある。
なお、どうもWindowsではwxWebViewBackendWebKitは動かないらしい。とはいえEdgeは中身がChromiumなのでwxWebViewBackendEdgeで大抵のページは表示される(に違いない)。
MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // wxWebView インスタンスの作成 wxWebView* webView = wxWebView::New( this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, // レンダリングエンジンの指定 // wxWebViewBackendWebKit, wxWebViewBackendEdge, // wxWebViewBackendIE, //wxWebViewBackendDefault, 0, "" ); webView->LoadURL("https://www.suzulang.com/"); }
ファイルパスを指定して、ローカルにあるhtmlを読み込める。
webView->LoadURL("C:/test/local.html");
LoadURLの代わりにSetPageを使用すれば、メモリ上のHTMLを表示できる。
//webView->LoadURL("C:/test/local.html"); webView->SetPage( R"( <html> <head></head> <body><p style="font-size:5em;">テスト</p></body> </html> )" , "");
ANTLR4を使うと自分のプロジェクトに様々な言語のパーサーを実装できる。
ANTLR4の実行形式(javaの実行形式)を実行して、「パースしたい言語」のための、「実装したい言語」用のソースコードを生成する。
今回は、HTMLをパースするためのC++のソースコードを作成する。
こうして出力したC++のパース用コードは、ANTLR4のライブラリとリンクすることでコンパイルできるようになる。
https://www.antlr.org/download.html
URLから、Complete ANTLR 4.13.1 java binaries jarから、antlr-4.13.1-complete.jar をダウンロードする。
-Dlanguage=Cppを指定してC++のコードを出力することを指定。トークン定義ファイルHTMLLexer.g4と、HTMLParser.g4構文解析用のファイル。
実行はHTMLLexer.g4 → HTMLParser.g4 の順で行う。
以下が生成される:
・HTMLLexer.cpp
・HTMLLexer.h
・HTMLLexer.interp
・HTMLLexer.tokens
以下が生成される:
・HTMLParser.cpp
・HTMLParser.h
・HTMLParser.interp
・HTMLParser.tokens
・HTMLParserBaseListener.cpp
・HTMLParserBaseListener.h
・HTMLParserListener.cpp
・HTMLParserListener.h
以下のようなエラーが出る可能性がある(出た)。Java系のエラーなので新しいJavaをインストールして解決する。
このエラーは、「実行しようとしているツールはclass file version 55 (=Java 11)で作られているが、システムに入っているclass file version 52 (=Java 8)だ」という意味。
解決するために、今回は以下からJava 11 をダウンロードする。なおJava 11からJREがなくなったらしい。
https://www.oracle.com/jp/java/technologies/javase/jdk11-archive-downloads.html
なおダウンロードにはOracleプロファイルの作成が必要。勘弁してくれ。
生成したコードはそれだけでは動かないので、antlr4-devをCMakeする。
以下からantlr4のソースコードをダウンロードし、antlr4-dev をVC++用にCMakeにかける。
https://github.com/antlr/antlr4
antlr4-dev/runtime/Cpp/ にCMakeLists.txtがある。
CMAKE_INSTALL_PREFIXだけ好きな場所を指定し、後はそのままConfigure→Generate→Open Project。
・インクルードディレクトリに以下を追加
・追加のライブラリディレクトリに以下を追加
・C++言語標準を「ISO C++17標準」以上にする
・リンクするライブラリ
antlr4-runtime.lib
gmock.lib
gmock_main.lib
gtest.lib
gtest_main.lib
#include <iostream> #include "HTMLLexer.h" #include "HTMLParser.h" #include <antlr4-runtime.h> #pragma comment(lib,"antlr4-runtime.lib") #pragma comment(lib,"gmock.lib") #pragma comment(lib,"gmock_main.lib") #pragma comment(lib,"gtest.lib") #pragma comment(lib,"gtest_main.lib") int main(int, const char**) { // 入力 std::ifstream mysrc; mysrc.open("D:\\test.txt"); // ANTLR入力ストリームを作成 antlr4::ANTLRInputStream input(mysrc); // Lexer、トークンストリームを作成 HTMLLexer lexer(&input); antlr4::CommonTokenStream tokens(&lexer); // トークンストリームをパーサに渡す HTMLParser parser(&tokens); // HTML用のパーサなので最上位がhtmlDocument auto tree = parser.htmlDocument(); // ツリーを表示 std::cout << tree->toStringTree(&parser,true) << std::endl; return 0; }
<html> <head> <title>test</title> </head> <body> <p>test</p> </body> </html>
(htmlDocument (htmlElements (htmlElement < html > (htmlContent (htmlChardata \n) (htmlElement < head > (htmlContent (htmlElement < title > (htmlContent (htmlChardata test)) < / title >)) < / head >) (htmlChardata \n) (htmlElement < body > (htmlContent (htmlChardata \n) (htmlElement < p > (htmlContent (htmlChardata test)) < / p >) (htmlChardata \n)) < / body >) (htmlChardata \n)) < / html >)))
#include <iostream> // メモリリークを検出した位置を表示するためのマクロ #define _CRTDBG_MAP_ALLOC // メモリリークの検出 #include <crtdbg.h> // メモリリークしたメモリのnewした場所のファイル名と行数を表示する // #define _CRTDBG_MAP_ALLOC で動く機能のはずなのだがなぜか機能しないので自分で定義する #define new ::new(_NORMAL_BLOCK, __FILE__, __LINE__)
class Dummy { public: char* myleak() { char* p = new char[3] {0, 0, 0}; // メモリリークしている (19行目) return p; } void mysafety() { int* q = new int[3] {1, 1, 1}; // メモリリークしていない (24行目) printf("%d %d %d\n", q[0], q[1], q[2]); delete [] q; } Dummy() {} ~Dummy() {} };
int main() { // アプリケーション終了時に_CrtDumpMemoryLeaks()が呼ばれるように指示 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); Dummy d; std::cout << "Hello World!\n"; char* c = d.myleak(); d.mysafety(); // 出力内容と出力先 // https://learn.microsoft.com/ja-jp/cpp/c-runtime-library/reference/crtsetreportmode?view=msvc-170 // _CRT_WARN ... 警告、メッセージ、およびすぐに注意する必要のない情報。 // _CRTDBG_MODE_DEBUG ... デバッガーの出力ウィンドウにメッセージを書き込みます。 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); // メモリリークの情報を出力 // この関数は、呼び出された時点で解放されていないメモリを全て検知する。 // アプリケーション終了時でのリークを検出したいならここで書かないで // _CrtSetDbgFlagでプログラムが終了したときに自動的に呼び出されるように指示したほうがいい。 // _CrtDumpMemoryLeaks(); return 0; }
_CrtSetReportMode関数で_CRTDBG_MODE_DEBUGを指定しているので、デバッグウィンドウに以下の出力がされる。
ファイル名 , newした行番号 , newしたメモリサイズ がそれぞれ出力される。
癖をまとめてくれているサイト。
プログラマの友
http://www7b.biglobe.ne.jp/~robe/pf/pf008.html
このcrtdbg.hによるメモリリーク検出、プロジェクトの規模が大きくなってくるとかなり気を遣わなければいけないらしい。
閑古鳥
WordPressの管理画面の投稿一覧が、いつの間にかタイトルなどのカラムがとても狭くなってしまって困った。これを解決する方法を探した。functions.phpでadmin_headをadd_actionすればよいらしい。
column-cbはチェックボックス部分の幅。一応指定している。
title(タイトル),author(投稿者),categories(カテゴリ), ... をそれぞれ指定。
.postsは投稿一覧、.pagesは固定ページ一覧。
<?php /****************************************/ /* 投稿・固定ページ一覧のカラム幅指定 */ function column_title_width(){ echo '
<style>
.posts .column-cb { width: 40px; } .pages .column-cb { width: 40px;} .posts .column-title { width: 300px; } .pages .column-title { width: 300px; } .posts .column-author { width: 50px; } .pages .column-author { width: 50px; } .posts .column-categories { width: 100px; } .pages .column-categories { width: 100px; } .posts .column-tags { width: 150px; } .pages .column-tags { width: 150px; } </style>
'; } add_action('admin_head','column_title_width'); /****************************************/ ?>
VC++で、.cpp と .cuファイルが混在したプロジェクトのビルド方法。
プロジェクトを右クリックして、「ビルドの依存関係」→「ビルドのカスタマイズ」を選択。
使用できるビルド カスタマイズ ファイルの中から、 CUDA 12.1 をチェックして OK をクリックする。
.cuファイルを右クリック → プロパティ → プロパティページ を開き、「全般」から以下を設定
・ビルドから除外 → いいえ
・項目の種類 → CUDA C/C++
#include <windows.h> #include <memory> #include "mycuda.cuh" #pragma warning(disable:4996) void pnmP3_Write(const char* const fname, const int width, const int height, const unsigned char* const p); static const int N = 500;//作成画像 N × N LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { static std::unique_ptr<unsigned char[]> p_cpu; switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_CREATE: p_cpu.reset(new unsigned char[N * N * 3]); call_mycuda(N, p_cpu.get()); return 0; case WM_PAINT: { // dc設定 PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); for(int x=0;x<N;x++) for (int y = 0; y < N; y++) { size_t pos = N * y + x;//二次元座標を一次元座標に SetPixel(hdc, x, y, RGB( p_cpu[pos * 3 + 0], p_cpu[pos * 3 + 1], p_cpu[pos * 3 + 2] ) ); } EndPaint(hwnd, &ps); } } return DefWindowProc(hwnd, msg, wp, lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR 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-WINDOW"); if (!RegisterClass(&winc)) return -1; hwnd = CreateWindow( TEXT("SZL-WINDOW"), TEXT("mywindow"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, N+100, N+100, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return -1; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; } ///////////////////////////////////////////// //画像ファイル書き出し///////////////////////// //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void pnmP3_Write(const char* const fname, const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = fopen(fname, "wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, 255); size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2]); k++; } fprintf(fp, "\n"); } fclose(fp); }
#include <stdio.h> #include <cstring> #include <cuda_runtime.h> void call_mycuda(const int N, unsigned char* p_cpu); /////////////////////////////////////////////// // GPU側 ////////////////////////////////////// __global__ void gradation(unsigned char* c, const int N);
#include "mycuda.cuh" #include <device_launch_parameters.h> #pragma comment(lib,"cudart.lib") #pragma comment(lib,"cuda.lib") void call_mycuda(const int N, unsigned char* p_cpu) { //作成画像 N × N × 3 //16×16の領域に分けて計算する dim3 block(16, 16); //グリッド数 dim3 grid(N / 16 + 1, N / 16 + 1); unsigned char* p_gpu;//GPU側メモリ確保 cudaMalloc((void**)&p_gpu, N * N * 3); gradation << <grid, block >> > (p_gpu, N); //GPU→CPU側へメモリコピー cudaMemcpy(p_cpu, p_gpu, N * N * 3, cudaMemcpyDeviceToHost); cudaFree(p_gpu); } __global__ void gradation(unsigned char* c, const int N) { //アクセス法 //このスレッドが担当する画素の位置を二次元座標で求める size_t xpos = blockIdx.x * blockDim.x + threadIdx.x; size_t ypos = blockIdx.y * blockDim.y + threadIdx.y; if (xpos < N && ypos < N) { size_t pos = N * ypos + xpos;//二次元座標を一次元座標に float R = ypos / (float)N; float G = (N - ypos) / (float)N; float B = xpos / (float)N; c[pos * 3 + 0] = R * 255;//0~255にして色書き込み c[pos * 3 + 1] = G * 255; c[pos * 3 + 2] = B * 255; } }
レベルとは(誤解を恐れずに)端的にいうとマップのこと。
レベルは保存しなければ操作できないので、まず作成・保存方法を確認する。
レベルを追加するには [新規レベル] → [新規レベル] から行う。
レベルを保存するには、[ファイル] → [現行レベルを保存] から行う。名前を付けると、コンテンツブラウザに登録される。
上記方法でレベルを二つ作成して保存(My-Map-1 、 My-Map-2)したら、
1.My-Map-1 をダブルクリックして開く
2.レベルブループリントを開く
3.ノードを設定
Keyboard F に Open Level を接続し、Level に先ほど作成した My-Map-2を指定する。
これでMy-Map-1を実行し、Fを押すとMy-Map-2へ切り替わる。
VTK9.3で、PLY読み込みなどを行う際に進捗状況を%表示する方法。
vtkProgressObserverを指定するSetProgressObserverメンバ関数はvtkAlgorithmで定義されているので、vtkAlgorithmを継承した処理でないとこの方法は使えない。
1.vtkProgressObserverを継承したMyProgressObserverを定義
2.UpdateProgressをオーバーライドし、amountから進捗を取得
3.MyProgressObserverのインスタンスを作成
4.vtkPLYReaderなどのインスタンスを作成
5.SetProgressObserverにオブザーバーのポインタを与える
// 進捗管理用 #include <vtkProgressObserver.h> // PLYファイル読み込み用 #include <vtkPLYReader.h> // 読み込めているか確認用 #include <vtkPointData.h>
////////////////////////////////////////////////// // 進捗管理のためにvtkProgressObserverを継承したクラスを作成 class MyProgressObserver : public vtkProgressObserver { public: // UpdateProgressをオーバーライド virtual void UpdateProgress(double amount)override { // 進捗を表示 printf("== %d\n", int(amount*100) );// amountは進捗状況を0.0~1.0で表現 } };
void myload() { const char* filename = "C:\\test\\mill.ply"; //////////////////////////////////////////////////////////// // 進捗管理用のオブザーバを作成 vtkSmartPointer<MyProgressObserver> observer = vtkSmartPointer<MyProgressObserver>(new MyProgressObserver); //////////////////////////////////////////////////////////// // PLYファイルを読み込むオブジェクト作成 vtkSmartPointer<vtkPLYReader> plyReader = vtkSmartPointer<vtkPLYReader>::New(); plyReader->SetProgressObserver(observer);// オブザーバを設定 plyReader->SetFileName(filename); plyReader->Update(); //////////////////////////////////////////////////////////// // 読み込んだデータを取得 vtkSmartPointer<vtkPolyData> polyData = plyReader->GetOutput(); vtkSmartPointer<vtkPoints> points = polyData->GetPoints(); if (points == nullptr) { return; } for (vtkIdType i = 0; i < points->GetNumberOfPoints(); i++) { double p[3]; points->GetPoint(i, p); printf("%lf %lf %lf\n", p[0], p[1], p[2]); } }