Tips of VC++ > ビットマップ > DIB Sectionの画像ビットをいじる(16色)
前へ戻る次へ進む


DIB Sectionの画像ビットをいじる(16色)

ここでは、「ビットマップファイルの構造」で紹介した、 画像ビットを操作する方法を説明してみます。 8bitのときは、1ピクセル=1バイトとなって、あまり面白くないので、 16色、つまり1バイト=2ピクセルです。 ビット操作があって多少複雑になります。 ビットフィールドを使っても良いですが、 あえてビット操作演算子を駆使してみます。

「ビットマップファイルを読み込む」で紹介した LoadDIB関数をふまえて説明します。 必要な情報として、1ラインあたりのバイト数(4の倍数)と ビットマップの高さを必要とするので、 以下の変数が与えられているとしましょう。 あと、画像ビットはすでに与えられているとします。

DWORD linesize; // 1ラインあたりのバイト数
SIZE bmpsize; // ビットマップのサイズ(ピクセル)
PBYTE pBits; // 画像ビット

ビットの読み書きを行う関数をそれぞれ作ります。 パラメータとしてビットマップ上での座標を取ります。 ビットマップはボトムアップ型の通常の場合を想定しています。 分かりやすく図で示します。(相変わらず汚い図だな…)

赤い点までのバイト数を数えればいいのですが、 1バイト=2ピクセルであることに注意すると、 x,yはそのまま使えないので、数のようにp,qを導入します

この図から、オフセットはlinesize*(b-q-1)+pで求まることが分かります。
q=y は明らかですね。 さて、pですが、偶奇に関係なくp=[x/2]([]はガウス記号)になります。 つまりp=int(x/2)です。意外と簡単です。 xが偶数のときと奇数のときで考えてみれば分かります。

これで目的のアドレスが取得できます。 次にビット操作です。 今度は、xの偶奇で場合分けが必要です。 まず上位ビット(xが偶数)の読み込みですが、 単純に4ビット右へシフトすればいいです。 下位ビットは、0x0fをandすればいいのです。

書き込みはちょっと面倒なので、先にコーディングします。 オフセットの取得は一つの式で表せるので便利ですね。 かなり短くなります。

char GetPixel(int x,int y)
{
	int offset= linebyte*(bmpsize.cy-y-1)+int(x/2);
	if ( x % 2 == 0 )
		return pBits[offset]>>4;
	else
		return pBits[offset]&0x0f;
}

次に、書き込みです。 オフセットの取得は同じで、次に場合分けが必要です。 いろいろ方法はありますが、 下記のようにビット演算を二度行う方法をとります。 とりあえず図にしたので見てください。 上位バイト、下位バイトを1010で置き換えるサンプルです。

上の図のように、変更したいビットをすべて1に換えてから、 書き替える方法はビットマップの透過処理でも使うことがあるので 覚えておいてください。
上位ビットを変更するときは 0xf0をorしてから、10101111をandします。 00001010から10101111を作る方法は、 平凡に、16を掛けてから00001111を加えればよいでしょう。 下位バイトも似たようにできます。 ソースは以下のようになります。 valueは0から15のインデックス値です。

int SetPixel(int x,int y, unsigned char value)
{
	if (  value<=0x0f )
	{
		int offset= linebyte*(bmpsize.cy-y-1)+int(x/2);
		if ( x % 2 == 0 )
		{
			pBits[offset] |= 0xf0;
			pBits[offset] &= (value*16+15);
		}
		else
		{
			pBits[offset] |= 0x0f;
			pBits[offset] &= (value+0xf0);
		}
		return -1;
	}
	else
		return 0;
}

30 Mar., 2004