Today プラグインサンプルプログラム2では、背景を透過して、文字を書いてみました。
文字が表示できたら、今度は画像を表示してみたくなるのが人情というもの。
というわけで、ただ表示するだけでは面白くないので、α(アルファ/透明度)の情報を持ったPNG画像を、背景上にBlendして表示してみましょう。
具体的には、以下のような画像を用意し、数字を背景上に描画します。
絵じゃなくて字じゃん!というツッコミはなしでw
わかりにくいかもしれませんが、背景が透過しており、なおかつ数字の縁にはアンチエイリアスがかかっています。
白一色だと見にくいと思うので、同じサイズの黒の画像を裏に重ねて、少し右下にずらして影の効果を出しています。
まずは、読み込む画像ファイルのフルパスを作る必要があります。
WindowsMobileは、相対パスが使えないという制限もありますし、そうでなかったとしても、フルパス指定の方が、何かと安全です。
今回は、DLLと同じところに画像ファイルを置きます。
なのでまず、DLLがアタッチされた時に、自分自身のパスを保存しておきます。
現状、DLLアタッチ時に、リソースのロードをしているので、そこでDLLのパス保存もしておきましょう。
自分自身のパスを得るには、DllMainの第1引数を指定して、GetModuleFileNameをコールします。
通常、実行ファイルが自分自身のパスを得るには、第1引数にNULLを指定してGetModuleFileNameをコールしますが、DLLでそれをやると、DLLを呼び出した実行ファイルの名前が取れてきてしまいます。
(Todayプラグインだと、「\Windows\shell32.exe」)
今回は、DLL自身のパスを知りたいので、ちゃんとDLLのインスタンスハンドルを渡しましょう。
実際に画像ファイルのパスを作りたい時には、このDLLのパスをベースに、ファイル名を入れ替えればいい事になります。
本当は、makepathやsplitpathといった関数が使用できれば楽でよかったのですが、見あたらなかったので、自力で入れ替えています。
処理としては、後ろから文字を調べて、最初に「\」が出てきたところより後ろを、読みたいファイル名に置き換えています。
// パス作成 bool CApp::makePath( LPTSTR path, LPCTSTR fname ) { int len = 0; int len2 = 0; int i = 0; if( NULL == path || NULL == fname ){ return false; } if( NULL == ::lstrcpy( path, m_dllPath )){ return false; } len = ::lstrlen( path ); len2 = ::lstrlen( fname ); if( 0 >= len || 0 >= fname ){ return false; } if( MAX_PATH < len + len2 + 1 ){ return false; } for( i = 0; i < len; i++ ){ if( TEXT( '\\' ) == path[len-i-1] ){ break; } } if( i == len ){ return false; } if( NULL == ::lstrcpy( path + len - i, fname )){ return false; } return true; }
PNGファイルをロードする前に、ロードした画像を保存しておく領域が必要です。
しかも、ただ保存しておくだけではなく、そこから裏画面に転送(描画)が可能な必要があります。
さらに、今回はそこまではやりませんが、ピクセルデータをメモリ上で直接いじれると、色々と便利です。
というわけで、これらが可能なDIBSectionを作成することにします。
今回は、ロード用のDIBSectionだけではなく、今まで表画面との互換性を持った状態だった裏画面もDIBSectionにします。
将来的にやりたい(やるかもしれない)事として、ロードした画像と裏画面のピクセルを、直接メモリ上の値をいじってBlendしたいというものがあります。
この時、裏画面の形式が表画面によって変わってしまうと、Blendする際に、色々な形式の組み合わせの計算式を用意する必要が出てきてしまいます。
裏画面はそのまま表画面へと転送するので、透明度を持つ必要はなく、24bitのRGB。
画像をロードする領域は、透明度を扱える必要があるので、32bitのARGBで作成します。
もちろん、ロードする画像に透明度がないようであれば、こちらも24bitで構いません。
このように固定しておけば、RGBのそれぞれが1バイトずつのBlendが出来、画像側で1バイトのαを考慮するかどうかだけの切り分けですむため、作る処理は少なくて済みます。
ただし、今まで裏画面は表画面との互換性があるものを作成していましたが、固定で24bitのものとすることにより、裏画面と表画面の形式が異なってしまいます。
異なるとどうなるかというと、裏画面を表画面に転送(BitBlt)する際のパフォーマンスが落ちるので、他のアイテムに比べて、一瞬遅れて表示されるように見えるかもしれません。
まぁ、ゲーム等の頻繁に画面の書き換えが発生するような用途の場合には、このあたりを考慮してやり方を工夫する必要があると思いますが、必要なときに必要なところだけ書き換えればいいようなTodayアイテムであれば、あまり気にしなくてもいいでしょう。
DIBSection作成処理は、以下のような感じです。
// DIBセクション作成 bool CDIBSection::create( HWND hwnd, int width, int height, int cbit ) { HDC hdc = NULL; // Windowデバイスコンテキストハンドル destroy(); // デバイスコンテキストの作成 hdc = ::GetDC( hwnd ); if( NULL == hdc ){ goto Error; } m_hdc = CreateCompatibleDC( hdc ); if( NULL == m_hdc ){ goto Error; } // BITMAPINFOHEADER構造体の初期化 ZeroMemory( &m_bi, sizeof( m_bi )); m_bi.biSize = sizeof( m_bi ); m_bi.biWidth = width; m_bi.biHeight = height; m_bi.biPlanes = 1; m_bi.biBitCount = cbit; m_bi.biCompression = BI_RGB; m_bi.biSizeImage = 0; m_bi.biXPelsPerMeter = 0; m_bi.biYPelsPerMeter = 0; m_bi.biClrUsed = 0; m_bi.biClrImportant = 0; // DIBSectionの作成 m_hBitmap = CreateDIBSection( m_hdc, ( LPBITMAPINFO )&m_bi, DIB_RGB_COLORS, ( LPVOID* )&m_dibmem, NULL, 0 ); if( NULL == m_hBitmap ){ goto Error; } // ビットマップの関連付け m_hOldBitmap = SelectBitmap( m_hdc, m_hBitmap ); if( NULL == m_hOldBitmap ){ goto Error; } if( hdc ){ ::ReleaseDC( hwnd, hdc ); hdc = NULL; } return true; Error: destroy(); if( hdc ){ ::ReleaseDC( hwnd, hdc ); hdc = NULL; } return false; }