最終更新 2003 10/01
サンプルのダウンロード → API_Bitmap7.lzh(138k)

全ソースコード
メモリに読み込んだビットマップのピクセルを直接いじるには SetPixel()
というAPI関数を使ってもできます。
でも、このAPI関数は処理速度がめちゃくちゃ遅いです。
そこで少しでも処理速度を上げようと思ったら、メモリに読み込んだ
ビットマップのピクセルを、自分でメモリにアクセスしていじる必要が
出てきます。
自分でいじるのはかなり面倒ですが、SetPixel() を使うよりも10倍以上は
速いです。
メモリに読み込んだビットマップのピクセルデータが格納されている
ポインタを取得するには GetObject() 関数を使います。
使い方はサンプルのように
BITMAP bmp;
if( GetObject( hBmp, sizeof( BITMAP ), &bmp ) == 0 )
{
//失敗
}
LPBYTE p = (LPBYTE)bmp.bmBits;
こんな感じでやると、p にピクセルデータの先頭アドレスのポインタが
入ってきます。
ここまではただ関数を呼べばいいだけなので簡単なのですが、実際に
ピクセルデータをいじるのがとても面倒です。
なぜなら、ビットマップには、モノクロ、16色、256色、16Bit
24Bit、32Bitの6種類のタイプがあって、それぞれでピクセル
データのいじり方が変わります。
サンプルは24Bitのビットマップにしか対応していません。
全てのタイプのビットマップに対応させるのは面倒な上に無駄な手間が
かかるので、あまり考えない方がよいです。
対応させるとしても、24Bitと256色で十分だと思います。
ここでは24Bitのビットマップのいじりかたのみ説明します。
【色の仕組み】
まずは色の仕組みからですが、ビットマップの色は三原色という
3種類の色の組み合わせで全ての色を表現します。
三原色はRGBと呼ばれていて、Rは赤、Gは緑、Bは青に対応して
います。(そのまんま・・・
絵の具のパレットと同じように、赤と青を混ぜると紫になって赤と緑を
混ぜると黄色になります。
絵の具は全部の色をごちゃまぜにすると黒に近い色になりますが
三原色の場合は白になります。
R,G,Bそれぞれに0〜255までのレベルがあって
0が一番暗い色、255が一番明るい色です。
Rを255にすると、一番明るい赤になります。
たとえば、R255、G100、B200 という組み合わせで
色を混ぜるとピンクになります。
白はR255、G255、B255
黒はR0、G0、B0
24BitのビットマップのビットデータはこのRGBのデータが
絵のサイズ分、ずらっと並んでいる形になります。
R、G、Bはそれぞれ1バイト(0〜255)で表現するので
1ピクセル分のデータはRとGとBで3バイトになります。
【データの並び順】
絵の左上(0,0)からRGBの順番で並んでいてくれれば操作は
楽なのですが、どういうわけかウィンドウズのビットマップは
絵の上下左右が逆になった形で保存されています。
これはリトルエンディアンというメモリの格納方法に基づいているの
ですが・・・多分、データを上下逆に格納する方がCPUとメモリとの
やり取りが楽というような理由なんでしょうが・・・使う側にとっては
やりにくくて仕方がないです・・・
実際に図にしてみるとこんな感じです
絵の右下
BGR(4,4) BGR(3,4) BGR(2,4) BGR(1,4) BGR(0,4)
BGR(4,3) BGR(3,3) BGR(2,3) BGR(1,3) BGR(0,3)
BGR(4,2) BGR(3,2) BGR(2,2) BGR(1,2) BGR(0,2)
BGR(4,1) BGR(3,1) BGR(2,1) BGR(1,1) BGR(0,1)
BGR(4,0) BGR(3,0) BGR(2,0) BGR(1,0) BGR(0,0)
絵の左上
本来、RGBの順番で並んでいるデータの後ろから読み取る形になるので
実際はBGRの順番でデータは並んでいます。
【データの並び順の注意】
ウィンドウズ標準のビットマップは先ほど説明した通り、上下左右が
逆になった形で保存されていますが、絵を書くときに使ったソフトに
よっては、そのままの状態で保存されているケースも考えられます。
そういう場合はどうすればよいのかというと、ビットマップのヘッダ
情報を調べます。
BITMAP bmp;
if( GetObject( hBmp, sizeof( BITMAP ), &bmp ) == 0 )
{
//失敗
}
//ヘッダ情報を取得
DIBSECTION dib;
if( GetObject( hBmp, sizeof( DIBSECTION ), &dib ) == 0 )
{
//失敗
}
//ビットデータの先頭ポインタ取得
LPBYTE p = (LPBYTE)bmp.bmBits;
if( dib.dsBmih.biHeight == 0 )
{
//高さが0の絵(エラー)
}
if( dib.dsBmih.biHeight < 0 )
{
/*
ピクセルデータは絵の左上から右下へ向かって
保存されている
(そのまま)
*/
}
else
{
/*
ピクセルデータは絵の右下から左上へ向かって
保存されている
(上下左右逆)
*/
}
|
実際にテストしていないのでどうか分かりませんが、こんな感じで
ビットマップのヘッダ情報はとってこれると思います。
で、ビットデータの並び順がそのままか、上下左右逆かでピクセルの
書き込み位置・読み取り位置を変えれば良いわけです。
【ピクセルをいじる関数を作ってみる】
サンプルとして、メモリに読み込んだビットマップのピクセルを
直接いじる関数を作ってみます。
●ピクセルに色を設定する関数
//ピクセルの色を設定
BOOL SetPixel(
HBITMAP hBmp,
DWORD x, DWORD y,
COLORREF color
)
{
BITMAP bmp;
if( GetObject( hBmp, sizeof( BITMAP ), &bmp ) == 0 )
return FALSE;
//ヘッダ情報が格納されているポインタを取得
DIBSECTION dib;
if( GetObject( hBmp, sizeof( DIBSECTION ), &dib ) == 0 )
return FALSE;
//高さが0の絵・・・ありえないからエラーを返す
if( dib.dsBmih.biHeight == 0 ) return FALSE;
//色のそれぞれの要素(RGB)を取得
BYTE r = GetRValue( color );
BYTE g = GetGValue( color );
BYTE b = GetBValue( color );
//ビットデータの先頭ポインタ取得
LPBYTE p = (LPBYTE)bmp.bmBits;
if( dib.dsBmih.biHeight < 0 )
{
//ビットデータはそのままの形
//書き込み位置を計算
p += ( bmp.bmWidth * y + x ) * 3;
*p = r; //赤の要素を書き込み
*( p + 1 ) = g; //緑の要素を書き込み
*( p + 2 ) = b; //青の要素を書き込み
}
else
{
//ビットデータは上下左右逆の形
//書き込み位置を計算
p += ( bmp.bmWidth * bmp.bmHeight * 3 )
- ( bmp.bmWidth * y + ( bmp.bmWidth - x ) ) * 3;
*( p + 2 ) = r; //赤の要素を書き込み
*( p + 1 ) = g; //緑の要素を書き込み
*p = b; //青の要素を書き込み
}
return TRUE;
}
|
【使い方】
HDC hMemDC;
HBITMAP hBmp;
// hMemDC と hBmp を使ってビットマップをメモリに読み込む
// 読み込み方は前の項を見て下さい
// 読み込んだビットマップの ( 10, 10 ) の位置に赤い点を打つ
SetPixel( hBmp, 10, 10, RGB( 256, 0, 0 ) );
●ピクセルを読み取る関数
//ピクセルの色を読み取り
BOOL GetPixel(
HBITMAP hBmp,
DWORD x, DWORD y,
COLORREF *pColor
)
{
BITMAP bmp;
if( GetObject( hBmp, sizeof( BITMAP ), &bmp ) == 0 )
return FALSE;
LPBYTE p = (LPBYTE)bmp.bmBits;
//ヘッダ情報が格納されているポインタを取得
DIBSECTION dib;
if( GetObject( hBmp, sizeof( DIBSECTION ), &dib ) == 0 )
return FALSE;
//高さが0の絵・・・ありえないからエラーを返す
if( dib.dsBmih.biHeight == 0 ) return FALSE;
//色のそれぞれの要素(RGB)を取得する変数
BYTE r, g, b;
if( dib.dsBmih.biHeight < 0 )
{
//ビットデータはそのままの形
//読み込み位置を計算
p += ( bmp.bmWidth * y + x ) * 3;
r = *p; //赤の要素を読み込み
g = *( p + 1 ); //緑の要素を読み込み
b = *( p + 2 ); //青の要素を読み込み
}
else
{
//ビットデータは上下左右逆の形
//読み込み位置を計算
p += ( bmp.bmWidth * bmp.bmHeight * 3 )
- ( bmp.bmWidth * y + ( bmp.bmWidth - x ) ) * 3;
r = *( p + 2 ); //赤の要素を読み込み
g = *( p + 1 ); //緑の要素を読み込み
b = *p; //青の要素を読み込み
}
//個別に受け取った r,g,b の値を COLORREF 形式に変換
*pColor = ( b << 16 ) + ( g << 8 ) + r;
return TRUE;
}
|
【使い方】
HDC hMemDC;
HBITMAP hBmp;
// hMemDC と hBmp を使ってビットマップをメモリに読み込む
// 読み込み方は前の項を見て下さい
// 読み込んだビットマップの ( 4, 8 ) の位置の色を読み取る
COLORREF color;
GetPixel( hBmp, 4, 8, &color );
|