いつもお世話になってます、Air です。
現在、自分でビットマップを作成しようとしているのですが
わからないことが数点あり書き込みさせていただきました。
まず、BITMAPINFO 構造体についてです。
メンバである、BITMAPINFOHEADER 構造体の方はさほど問題ないのですが
もう片方の RGBQUAD 構造体の作成方法がよくわかりません。
というか、これは何に使われるものなのでしょうか?
そして、BITMAPINFO 構造体が出来た後
CreateDIBitmap() か CreateDIBSection() を使いビットマップを作成すると
思うのですが、この両者の違いがわからず、どちらを使えばよいか迷っています。
どういうときにどちがを使えば効果的なのでしょうか?
皆様、大変お忙しいところ恐縮ですが
ご教示よろしくお願い致します。
・開発環境
WindowsXP VC++6.0 MFC
>まず、BITMAPINFO 構造体についてです。
>メンバである、BITMAPINFOHEADER 構造体の方はさほど問題ないのですが
>もう片方の RGBQUAD 構造体の作成方法がよくわかりません。
>というか、これは何に使われるものなのでしょうか?
ビットマップは1ピクセルに必要なビット数が、biBitCountに設定されていますが、
8bit以下の場合は、パレットと呼ばれるRGBQUADの配列が必要になります。
そこに色情報を保持しておいて、実データの中身はそのパレットのインデックス番号で、
表します。
そのパレットを操作する方法として、BITMAPINFO構造体があります。
フルカラー(24bit)の場合はパレットはないので、BITMAPINFOは必要ありません。
BITMAPINFO構造体はちょっとインチキな構造体で、Auto変数の様に静的に
確保することができません。
動的にメモリを確保して、構造体の定義にはbmiColors[1]しかないのに、
bmiColors[0],bmiColors[1]...という本来ならオーバーランするような
使い方で、後に続くRGBQUADにアクセスします。
biBitCountが8なら最大256個、4なら16個、1なら2個のRGBQUADが並んぶ様にメモリを
確保する必要があります。
BITMAPINFO* pInfo = (BITMAPINFO*) new char[ sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * ( 1 << biBitCount ) ] ;
みたいな感じです。
こんなところで解りますでしょうか。
>CreateDIBitmap() か CreateDIBSection() を使いビットマップを作成すると
>思うのですが、この両者の違いがわからず、どちらを使えばよいか迷っています。
CreateDIBitmap() はDDBです。
CreateDIBSection() はDIBです。
DIBの方がファイルに保存する時は便利ですが、表示する時はほんの少し処理が遅いです。
(DIB -DDB変換が行われる為です。今時のパソコンだとフルカラー表示が主流なので、
ほとんど差はありませんが、昔の256色しか表示できないパソコンだと、わりと時間が
かかりました。)
DDBにすると、ビットマップファイルの情報の一部がなくなってしまうので、
それをどこかにとっておかないと、保存する時にもとの物と変わってしまいます。
(biXPelsPerMeter、biYPelsPerMeterですからなくなっても困らないかもしれませんが)
まあどちらを使うかは好き好きでいいと思いますが、私なら、CreateDIBSectionを使います。
さっそくのご返事ありがとうございます。
> ビットマップは1ピクセルに必要なビット数が、biBitCountに設定されていますが、
> 8bit以下の場合は、パレットと呼ばれるRGBQUADの配列が必要になります。
> そこに色情報を保持しておいて、実データの中身はそのパレットのインデックス番号
で、
> 表します。
> そのパレットを操作する方法として、BITMAPINFO構造体があります。
> フルカラー(24bit)の場合はパレットはないので、BITMAPINFOは必要ありません。
作成しようとしているものは24ビットフルカラーのものです。
その場合は必要ないとのことですが、CreateDIBitmap() CreateDIBSection() 共に
引数で BITMAPINFO が必要になります。その場合、RGBQUAD にはどのような値を
入れておけばよろしいのでしょうか?
ちなみに、両関数共、引数に DIB_PAL_COLORS か DIB_RGB_COLORS を指定する部分が
ありますが、パレットが無い場合は DIB_PAL_COLORS ということになるんでしょうか?
勘違いしてたらすいません…
> CreateDIBitmap() はDDBです。
> CreateDIBSection() はDIBです。
> DIBの方がファイルに保存する時は便利ですが、表示する時はほんの少し処理が遅いで
す。
> (DIB -DDB変換が行われる為です。今時のパソコンだとフルカラー表示が主流なので、
> ほとんど差はありませんが、昔の256色しか表示できないパソコンだと、わりと時間が
> かかりました。)
なるほど。
やはり処理は早いにこしたことはないので、CreateDIBSection を使ってみようと
思います。( といっても、まだよくわかってないのですが… )
ご迷惑おかけして申し訳ありません。
再度、アドバイスをよろしくお願いします。
Win32APIはC言語で書かれていますのでC言語の考え方でいくと納得しやすいと思います。
C言語でメモリを扱う場合に構造体のポインタを使って操作すると言うのは常套手段です
。
例えば、GlobalAlloc等で確保したメモリアドレスを構造体のポインタに入れて
構造体として操作するなんて事は日常茶飯事です。
この場合、BITMAPINFO構造体は正にこういう使い方をするための構造体で、
BITMAPINFOの最後にあるbmiColorsメンバーはパレット等(必ずしもパレットとは限りま
せん)の情報にアクセスする為の足がかりに使うために配列として宣言されています。
実際に使う場合は、ポインタ扱いで使う事になりますけれど、
ポインタで宣言してしまうとBITMAPINFOHEADERの後ろにくっつくと言う構造が表現できな
くなるので便宜上こうなっているのだと思います。
24bitカラーの場合、bmiColorsの部分のサイズは事実上0になりますから
結果的にはBITMAPINFO構造体とBITMAPINFOHEADER構造体の中身は同じになります。
C言語では他のポインタに読み替えて使用するというのはよくある話なので
覚えておいた方がいいです。
但し、他のポインタに読み替える場合、読み替えた後の動作がうまく行く事は読み替える
側で補償しなくてはなりませんので注意が必要です。
この辺はC言語的な使用法になりますが、Windowsでプログラミングする以上は
Win32APIを全く使わないというわけには行きませんからC言語の知識もないと
躓く事が出てくると思います。
この辺は地道な自己学習が必要って事なんだと思います。
ご返答ありがとうございます。
とりあえず、CreateDIBSection で BITMAPINFOHEADER を BITMAPINFO にキャストして
やってみました。
そして、CreateDIBSection() の4つ目の引数(VOID *ppvBits) のポインタに
素直に memcpy() で ビットマップ実データをコピーして
BitBlt() で 表示してみたのですが、画像がきれいに180度回転してくれています。
これはなぜなのでしょうか?実データを作るときにそれを考慮しろという
ことなのでしょうか?
いまいちわからん!と突っ込まれる前にソースの書かせていただきます。
_h = 0;
for(_i=0 ; _i<m_tga_head.height ; _i++)
{
for(_j=0 ; _j<m_tga_head.width ; _j++, _h++)
{
p_bmp[ _h*4+2 ] = ( p_tex[ _h*4+0 ] * p_tex[ _h*4+3 ] / 0xff );
p_bmp[ _h*4+1 ] = ( p_tex[ _h*4+1 ] * p_tex[ _h*4+3 ] / 0xff );
p_bmp[ _h*4+0 ] = ( p_tex[ _h*4+2 ] * p_tex
[ _h*4+3 ] / 0xff );
// p_bmp[ _h*4+3 ] = 0;
}
}
DIBとGDIは座標系が上下逆と聞いたことがあります。
それが関係してるのではないでしょうか?
こちらは参考になりませんか?
http://www.sm.rim.or.jp/%7Eshishido/
Windowsプログラミング研究室で、DIBについて詳しく書かれています。
すいません!
ソースを書いてるときに手がすべってしまいました!
改めて書かせていただきます。( TABキーのせいです… )
// ビットマップ実データ (わかりやすく黒→白のグラデーション)
_h = 0;
for(_i=0 ; _i<m_tga_head.height ; _i++)
{
for(_j=0 ; _j<m_tga_head.width ; _j++, _h++)
{
p_bmp[ _h*4+2 ] = _i*2;
p_bmp[ _h*4+1 ] = _i*2;
p_bmp[ _h*4+0 ] = _i*2;
p_bmp[ _h*4+3 ] = 0;
}
}
// 表示部分
{
HDC hDC;
HDC hCompatiDC;
CWnd* p_pic;
HBITMAP myDIB;
HBITMAP oldDIB;
void* pBitsDib;
HWND hWnd = p_frame->m_hWnd;
PAINTSTRUCT ps;
p_pic = (CWnd *)GetDlgItem(IDC_PICTURE);
hDC = p_pic->GetDC()->GetSafeHdc();
::BeginPaint(hWnd, &ps);
hCompatiDC = CreateCompatibleDC(hDC);
myDIB = CreateDIBSection( (HDC)0, (BITMAPINFO *)(&p_tex->m_bi),
DIB_RGB_COLORS, (void **)&pBitsDib, NULL, 0 );
memcpy( pBitsDib, p_tex->m_pBmp, 128 * 128 * 4 );
oldDIB = (HBITMAP)SelectObject(hCompatiDC, myDIB);
BitBlt(hDC, 0, 0, 128, 128, hCompatiDC, 0, 0, SRCCOPY );
::EndPaint(hWnd, &ps);
SelectObject( hCompatiDC, oldDIB );
DeleteObject( myDIB );
DeleteObject(hCompatiDC);
}
となっています。
抜き出して編集したので、若干おかしいところがあるかもしれませんが
こんな感じです。
さらに白状してしまうと、この表示部分はどこから呼ぶのが
一番いいのかと迷ってもいます。
不細工なミスをして申し訳ありません。
よろしくお願い致します。
> BitBlt() で 表示してみたのですが、画像がきれいに180度回転してくれています。
> これはなぜなのでしょうか?実データを作るときにそれを考慮しろという
> ことなのでしょうか?
180度回転(本来左上にあるモノが右下にある)と上下反転(左上のモノが左下に)とどちら
でしょうか?
ビットマップの場合、スキャンラインが左下からだったハズなので、
データをそのように用意する必要があったかと思われますが。
# DIBとかあまりいぢらないので後は詳しい方にお願いします。
# BITMAPINFOHEADER構造体のbiHeightの解説に書かれているかと。
# サクっとググってイキのページが参考になりますか?
# http://hp.vector.co.jp/authors/VA023539/tips/bitmap/001.htm
## とか書いているウチにこうかさんからレスが。
大変素早いレス、ありがとうございます。
ただ、こうさんのレスタイミングだけは泣きそうです…
自分で調べていたときは、このようなページは見つけれませんでした。
今からじっくり読んでみます。
そして、大事な事を書き忘れてました。
このビットマップはピクチャボックスに書こうとしています。
(今更すいません…)
描画場所(タイミング)などについても、さらなるアドバイスをいただけると
大変ありがたいです。
よろしくお願い致します。
>この表示部分はどこから呼ぶのが一番いいのかと迷ってもいます。
コントロール(IDC_PICTURE)をサブクラス化して
WM_PAINTがきたときに描くといいんじゃないかなぁ。
試したことないので、うまくいくかは判りませんが……(^^;
ご返答ありがとうございます。
180度回転する件ですが、BITMAPINFOHEADER を作成する際
biHeight の値をマイナス掛けすればうまくいきました。
( って、こんな修正でいいのでしょうかとやや不安 )
描画に関してですが、他にポリゴン等を描画しているのですが、そちらは
マルチメディアタイマーを使って1/60秒で 基本ビュークラスの
OnDraw()の中で行っています。
そこにピクチャボックスの描画をはさむのはさすがになぁ…と思います。
ピクチャボックス再描画が必要になる場合として、
・ウィンドウサイズ変更
・ウィンドウの最小化からの復帰
・ダイアログバーにピクチャボックスを配置し、そのバーをON/OFFしているので
新たにONリクエストがかかったとき
ぐらいだと思われますが、基本ビュークラスにOnPaint()を追加し
その中で描画処理を入れれば、全てに対応できるでしょうか?
あつかましい質問で恐縮ですが
ご教示の程、よろしくお願いします。
前述のピクチャボックスの描画に関してですが。
とりあえず、ウィンドウのサイズ変更と最小化からの復帰に対応させようと
メインのフレームに OnPaint() を追加し、そこで描画処理を入れてみました。
すると、サイズ変更時、最小化からの復帰時共に、一瞬は表示されるのですが
最終的に画像が消えてしまいます。
どのようにすればうまく再描画できるでしょうか?
よろしくお願い致します。
>描画に関してですが、他にポリゴン等を描画しているのですが、そちらは
>マルチメディアタイマーを使って1/60秒で 基本ビュークラスの
>OnDraw()の中で行っています。
>そこにピクチャボックスの描画をはさむのはさすがになぁ…と思います。
HBITMAPやコンパチなHDCをメンバ変数にしておいて、OnDrawではBitBlt転送のみ
にすればそんなに負担にならないと思います。
あと余計な事かもしれませんが、MFCなら普通はCBitmap、CDCにAttatchします。
デストラクタで破棄してくれるので、後始末が楽です。
コンストラクタでBeginPaintを呼んで、デストラクタでEndPaintを呼ぶ
CPaintDCというクラスもあります。
良く考えてみると、BeginPaint、EndPaintはWM_PAINTのみでしか使えない関数だった様な
気がします。GetDC、ReleaseDCを使ってください。