- PR -

DIBからSystem.Drawing.Bitmapを作成

投稿者投稿内容
Mecky
会議室デビュー日: 2005/03/07
投稿数: 9
投稿日時: 2005-03-07 21:31
環境[Windows XP]
言語[VC++.NET]
分類[.NET]


初めて書き込みます。
以前より、この掲示板を参考にさせていただいています。

現在、アンマネージなサードパーティのDLL(VC6で作成)をラップする
VC++.NETのDLLを作成しています。

このアンマネージなDLLのメソッドのひとつに、そのDLLが作成した
ビットマップ(DIB形式)のハンドラ(hDIB)を返却する関数があり、
このハンドラから、 「System.Drawing.Bitmap」オブジェクトを
作成したいと考えています。

以下のように書いてみましたが、
返却されるBitmapオブジェクトは、まったく異なるもの(ほとんどが黒いまま)になってしまいます。

-----ソースここから
/*
DLLから返却されたhDIBを引数にとり、DIBをBITMAPに変換する
*/
Bitmap* WrapDll::Hoge::CopyStructToBmp(HANDLE pDIB)
{
// 変数の宣言
LPVOID pMem; //
CARD_IMAGE BMI; // BITMAPINFOのbmiColorの要素数を変えた構造体
CARD_IMAGE* pBMI; // BITMAPINFOのポインタ
int width; // 幅
int height; // 高さ
Bitmap* bmp; // ビットマップ(マネージ)
BitmapData* bmpdata; // ビットマップのデータ部分(マネージ)
IntPtr pbd; // ビットマップのデータ部分の先頭(マネージ)
System::Drawing::Rectangle rect; // 矩形の構造体(マネージ)
int i; // カウンタ

try {
// 格納用の構造体のポインタを取得
pBMI = &BMI;
// HANDLEからメモリポインタを取得
pMem = (BITMAPINFO*)GlobalLock(pDIB);

// メモリを判定
if ( pMem != NULL ) {
// ハンドラから読み込む
MoveMemory(pBMI, pMem, sizeof(BMI));
// 幅と高さを取得
width = (int)pBMI->bmiHeader.biWidth;
height = (int)pBMI->bmiHeader.biHeight;

// マネージなビットマップオブジェクトを作成
bmp = new Bitmap(width, height, PixelFormat::Format4bppIndexed);
// 画像の四隅を表す構造体に値をセット(構造体のメソッドによりセット)
rect.set_X(0);
rect.set_Y(0);
rect.set_Width(width);
rect.set_Height(height);
// 読み書きできるモードで全域をロックし、BitmapDataオブジェクトを作成
bmpdata = bmp->LockBits(
rect,
ImageLockMode::ReadWrite,
PixelFormat::Format4bppIndexed
);

// ビットマップデータの先頭アドレスを取得
pbd = bmpdata->Scan0;

// BITOMAPINFO構造体のデータ部分を読み込んでBitmapオブジェクトに書き込む
for ( i = 0; i < sizeof(pBMI->bmiColors); i++ ) {
Marshal::WriteByte(pbd, i, pBDT + i);
}

// アンロック
bmp->UnlockBits(bmpdata);
}
// アンロック
GlobalUnlock(pDIB);

// 戻り値の返却
return bmp;

}
catch (Exception* e) {
// 丸投げ
throw e;
}
};
-----ソースここまで

bmiHeaderの各要素が取得できているので、渡されたハンドラから、
データは渡ってきていると思うのですが...

アドバイスよろしくお願いします。

※ちなみにこのアンマネージなDLLは、VB6.0から呼んだ場合、StretchDIBitsで
DIB画像を表示する仕様になっています。



[ メッセージ編集済み 編集者: Mecky 編集日時 2005-03-07 21:33 ]

[ メッセージ編集済み 編集者: Mecky 編集日時 2005-03-07 21:34 ]
にしざき
ぬし
会議室デビュー日: 2003/06/30
投稿数: 304
投稿日時: 2005-03-08 08:58
試していませんが、.NET 247によると、
・System.Drawing.Bitmap を作る
・LockBits
・CopyMemory
と書いてありますね。
あと、
new Bitmap(int, int, int, PixelFormat, IntPtr)
でもいけると書いてあります。
Mecky
会議室デビュー日: 2005/03/07
投稿数: 9
投稿日時: 2005-03-08 09:34
にしざき様、ありがとうございます。
いただいた情報の方を見てみます。

>new Bitmap(int, int, int, PixelFormat, IntPtr)
は、3番目の引数の「stride」の求め方がはっきりせず、
一度断念してます...
にしざき
ぬし
会議室デビュー日: 2003/06/30
投稿数: 304
投稿日時: 2005-03-08 09:54
> 3番目の引数の「stride」の求め方がはっきりせず
確か、昔の記憶では
((biWidth * biPlanes * biBitCount + 31) / 32) * 4
とかじゃなかったっけ?
細かいところは覚えていませんが。

4バイト境界に合わせるっぽいので修正。

[ メッセージ編集済み 編集者: にしざき 編集日時 2005-03-08 09:59 ]
にしざき
ぬし
会議室デビュー日: 2003/06/30
投稿数: 304
投稿日時: 2005-03-08 10:02
もしくは
biSizeImage / abs(biHeight)
でもいいのかもしれない。
Mecky
会議室デビュー日: 2005/03/07
投稿数: 9
投稿日時: 2005-03-08 10:41
にしざき様、何度もご返答ありがとうございます。

私の方も「Stride」の計算方法をしらべてみましたが、MSDNによると、

>DIB のサイズは、stride * height として計算される。
>stride は、幅 * ピクセルごとのビット/8 を最も近い
>DWORD アラインメントに切り上げた値、height は、biHeight の絶対値である。

なんだそうです。
#あまりBitmapの仕様が理解できていないのかも...

ちょっとこれから、別の作業に入ることになったので、
今までいただいた情報を元に夕方にでも、もう一度トライしてみます。


にしざき
ぬし
会議室デビュー日: 2003/06/30
投稿数: 304
投稿日時: 2005-03-08 11:02
>DIB のサイズは、stride * height として計算される。
これから逆算したのが
biSizeImage / abs(biHeight)
で、

>stride は、幅 * ピクセルごとのビット/8 を最も近い
>DWORD アラインメントに切り上げた値、height は、biHeight の絶対値である。
これから計算したのが
((biWidth * biPlanes * biBitCount + 31) / 32) * 4
(いまさら biPlanes != 1 のデータってあるの?)
です。
どちらも同じ値になるはずなんですけど、ならなければ式が間違っています。

あ、プレーンって行ごとじゃなかったっけ?だったら式の biPlanes がいらないんだけど…

[ メッセージ編集済み 編集者: にしざき 編集日時 2005-03-08 11:04 ]
Mecky
会議室デビュー日: 2005/03/07
投稿数: 9
投稿日時: 2005-03-08 16:47
どうも、お世話になります。

Strideの計算方法をご教授いただいたおかげで、コンストラクタ
new Bitmap(int, int, int, PixelFormat, IntPtr)
を利用することで画像の取得はできたようです。
#内容を検証するため、画像をファイルに保存しようとしてますが、
 ちょっとハマり気味...

で、DIBのハンドラから取得したBITMAPINFOHEADERの内容を確認してみたところ、
biSizeImage / abs(biHeight)
の方で、それっぽい値が取得できたので、とりあえずその値を使用してます。

ファイル内容の検証ができたら、また報告します。

スキルアップ/キャリアアップ(JOB@IT)