2010年06月17日

vbpicture

使い方は
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ポインタ使うとこですが、まあそれは好みということで。
posted by Nick at 13:12| Comment(0) | TrackBack(0) | プログラミング

テスト

まあとりあえずはテスト。
posted by Nick at 13:07| Comment(0) | TrackBack(0) | 雑記