使い方は
vbpicture vp( ファイル名 );
で初期化するだけで、
BitBlt( GetDC( 0 ), 0, 0, vp.W, vp.H, vp.hDC, 0, 0, SRCCOPY );
とかでコピーできます。さらに
long* pd = vp.GetpData();
で、32ビットDIBピクセルデータのアドレスが取得できまして、
for( int y = 0; y < vp.H; y ++ ) {
for( int x = 0; x < vp.W; x ++ ) {
pd[ x + y * vp.W ] = x * 255 / vp.W;
}
}
とかしてBitBltしちゃうと、変更したピクセルが反映されて出力されます。
いやほんと便利。32ビットDIB限定なのでめんどくさいワードアラインメントだとか
15ビットと16ビットの誤差修正だとかないし
応用すればアルファチャンネルも使えちゃいます。
ソースはこれ。まず宣言部
#include "windows.h"
#include "olectl.h"
class vbpicture {
public:
// 読み取り専用メンバ
// コンストラクタでだけは書き込んでるけどね
const HDC hDC;
const int W;
const int H;
// 書き込み可能ポインタ(32ビット)
long* GetpData();
vbpicture( WCHAR *fnm );
~vbpicture();
private:
long *pdata;
HBITMAP hbmp;
BITMAP bm;
};
次にソース
long* vbpicture::GetpData() { return pdata; }
vbpicture::vbpicture( WCHAR* fnm ) : W(0), H(0), hDC(0) {
// clear
hbmp = 0;
memset( &bm, 0, sizeof( bm ) );
pdata = 0;
// OLEのLoadPictureで読み込み
VARIANT v;
int slen = wcslen( fnm );
v.bstrVal = SysAllocStringLen( 0, slen );
memcpy( v.bstrVal, fnm, slen * 2 );
v.vt = VT_BSTR;
IDispatch *id;
IPicture *ip;
HRESULT hr = OleLoadPictureFile( v, &id );
SysFreeString( v.bstrVal );
if( hr != S_OK ) return;
hr = id->QueryInterface( IID_IPicture, (void**)&ip );
id->Release();
if( hr != S_OK ) return;
// 読み取り専用 W, H に値をセット
OLE_XSIZE_HIMETRIC ow;
OLE_YSIZE_HIMETRIC oh;
ip->get_Width( &ow );
ip->get_Height( &oh );
HDC hdcdt = GetDC( 0 );
(int)W = (int)( GetDeviceCaps( hdcdt, LOGPIXELSX )
/ 2540.0 * ow );
(int)H = (int)( GetDeviceCaps( hdcdt, LOGPIXELSY )
/ 2540.0 * oh );
// CreateCompatibleBitmap/DCで自前DCを確保
// 読み取り専用 hDC に値をセット
(HDC)hDC = CreateCompatibleDC( hdcdt );
BITMAPINFO bmi; memset( &bmi, 0, sizeof( bmi ) );
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biHeight = H;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biWidth = W;
bmi.bmiHeader.biSize = sizeof( bmi.bmiHeader );
hbmp = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS, (void**)&pdata, 0, 0 );
HGDIOBJ hg = SelectObject( hDC, hbmp );
GetObject( hbmp, sizeof( bm ), &bm );
// 自前DCに描画
ip->Render( hDC, 0, 0, W, H, 0, oh, ow, -oh, 0 );
// 掃除
ip->Release();
ReleaseDC( 0, hdcdt );
}
vbpicture::~vbpicture() {
if( hbmp ) DeleteObject( hbmp );
if( hDC ) DeleteDC( hDC );
}
読み取り専用変数ってのが無いような気がしたので、
無理やり実装してみました。(C#だとReadOnlyとかあるんですが)
constをキャストして無理やり書き込んでますが、
これをするのはコンストラクタだけ、という自分ルールを作りました。
まあ使う側がやっちゃったら自己責任だよね、ということで。
あとはOLEのLoadPictureを使ってるのがポイント。
しかしOLEってVBで使うにはほとんど気にしなくていいんですが、
VCで使うとめっちゃ面倒ですよね。便利なんだか不便利なんだか分かりません。
MSって一見簡単そうなものを超複雑に作るのが得意技ですね。
最後にlongポインタ使ってるのもポイントかな?
32ビット限定なんで32ビット変数で返してます。
普通はcharポインタ使うとこですが、まあそれは好みということで。