- 追加された行はこの色です。
- 削除された行はこの色です。
16ビット及び32ビットのBMPファイルで用いられる@dfn{ビットフィールド};の解説。
#contents
*概要 [#about]
本文書では、ビットフィールドを用いる16ビットBMP及び32ビットBMPを総称して@dfn{ビットフィールドBMP};と呼ぶ。
[[バイナリ構造>../バイナリ構造]]のページの[[ビットフィールド>../バイナリ構造#bitfields]]セクションでも述べた通り、ビットフィールドは以下の構成要素を持つ。
|~要素|~バイト数|h
|LEFT:|RIGHT:|c
|赤成分のカラーマスク|4|
|緑成分のカラーマスク|4|
|青成分のカラーマスク|4|
また、16ビットBMPと32ビットBMPそれぞれの既定値は以下の通りである。
:16ビットBMPの既定ビットフィールド|
|~要素|~値|h
|LEFT:|RIGHT:|c
|~赤成分のカラーマスク| @code{0x00007C00}; |
|~緑成分のカラーマスク| @code{0x000003E0}; |
|~青成分のカラーマスク| @code{0x0000001F}; |
なお、この値を持つビットフィールドを@dfn{RGB555};と呼ぶ。
:32ビットBMPの既定ビットフィールド|
|~要素|~値|h
|LEFT:|RIGHT:|c
|~赤成分のカラーマスク| @code{0x00FF0000}; |
|~緑成分のカラーマスク| @code{0x0000FF00}; |
|~青成分のカラーマスク| @code{0x000000FF}; |
なお、この値を持つビットフィールドを@dfn{RGB888};と呼ぶ。
これらの値の意味、及び処理の指針について以降のセクションで述べる。
*カラーマスクの意味と制約 [#color-mask]
ビットフィールドの構成要素である@dfn{各色成分のカラーマスク};について、その[[意味>#color-mask-mean]]と[[制約>#color-mask-restrict]]を以下に述べる。
**カラーマスク値の意味 [#color-mask-mean]
各色成分のカラーマスクの値は、[[ピクセルデータ>../バイナリ構造#image-data-pixel]]のどのビットが何色にあたるかを示すものである。
例として、16ビットBMPにおける既定値であるRGB555の各構成要素値を16進数表記及び2進数表記した表を以下に示す。~
なお、16ビットBMPでは各カラーマスク値の下位16ビットのみを用い、上位16ビットは無視する。
|~要素|~16進数表記|~2進数表記|h
|LEFT:|RIGHT:|RIGHT:|c
|~赤成分のカラーマスク| @code{0x00007C00}; | @code{00000000 00000000 01111100 00000000}; |
|~緑成分のカラーマスク| @code{0x000003E0}; | @code{00000000 00000000 00000011 11100000}; |
|~青成分のカラーマスク| @code{0x0000001F}; | @code{00000000 00000000 00000000 00011111}; |
16ビットの[[ピクセルデータ>../バイナリ構造#image-data-pixel]]をこれらのカラーマスク値を用いて解釈すると、以下のようになる。
-0ビット目は無視する
-1ビット目から5ビット目までは赤成分値
-6ビット目から10ビット目までは緑成分値
-11ビット目から15ビット目までは青成分値
各色成分値が5ビット(32階調)で表現されているが、これを通常の色表現で用いる8ビット(256階調)の値に変換する方法は、後述の[[ビット数変換>#bits2bits]]セクションで述べる。
もう1つの例として、32ビットBMPにおける既定値であるRGB888の各構成要素値を16進数表記及び2進数表記した表を以下に示す。~
更に、このBMPの[[情報ヘッダ>../バイナリ構造#info-header]]が[[V4タイプ>../バイナリ構造#info-header-v4]]で、[[α成分のカラーマスク>../バイナリ構造#info-header-v4-amask]]も指定されているものとする。
|~要素|~16進数表記|~2進数表記|h
|LEFT:|RIGHT:|RIGHT:|c
|~赤成分のカラーマスク| @code{0x00FF0000}; | @code{00000000 11111111 00000000 00000000}; |
|~緑成分のカラーマスク| @code{0x0000FF00}; | @code{00000000 00000000 11111111 00000000}; |
|~青成分のカラーマスク| @code{0x000000FF}; | @code{00000000 00000000 00000000 11111111}; |
|~α成分のカラーマスク| @code{0xFF000000}; | @code{11111111 00000000 00000000 00000000}; |
32ビットの[[ピクセルデータ>../バイナリ構造#image-data-pixel]]をこれらのカラーマスク値を用いて解釈すると、以下のようになる。
-0ビット目から7ビット目まで(1バイト目)はα成分値
-8ビット目から15ビット目まで(2バイト目)は赤成分値
-16ビット目から23ビット目まで(3バイト目)は緑成分値
-24ビット目から31ビット目まで(4バイト目)は青成分値
**カラーマスク値の制約 [#color-mask-restrict]
各色成分のカラーマスクはどのような値でもよいわけではなく、以下に示す2つの制約がある。
:カラーマスク値の有効ビット(値が @code{1}; のビット)は連続していなければならない。|
例えば、2進数表記で @code{00000000 00000000 00001111 11100000}; のカラーマスク値は @code{1}; が連続しているため問題ないが、2進数表記で @code{00000000 00000000 0000111''0'' 11100000}; のカラーマスク値は途中に @code{0}; が入っているため不正な値である。
:あるカラーマスク値の有効ビットが他のカラーマスク値のそれと重なっていてはならない。|
これは当然ではあるが、例えば赤成分のカラーマスク値が @code{00000000 00000000 111111''1''0 00000000}; で、緑成分のカラーマスク値が @code{00000000 00000000 000000''1''1 11110000}; の場合、22ビット目がどちらも @code{1}; のため不正な値である。
逆に、これらの制約を満たしてさえいればどのような値でもとることができ、有効ビット無し(値 @code{0x00000000}; )でも構わない。~
カラーマスク値に有効ビットの無い色成分値は @code{0}; として扱われる。ただし、α成分の場合は完全不透明として扱われる。
また、BMPの仕様とは関係ない制約であるが、Windows 95、Windows 98、Windows MeにおいてビットフィールドBMPを直にデバイスコンテキスト上に表示する場合、決まったビットフィールド以外は使用できない。~
16ビットBMPの場合は前述のRGB555及び次に述べるRGB565、32ビットBMPの場合は前述のRGB888のみが使用できる。
@dfn{RGB565};は、以下の値を持つビットフィールドのことである。
|~要素|~値|h
|LEFT:|RIGHT:|c
|~赤成分のカラーマスク| @code{0x0000F800}; |
|~緑成分のカラーマスク| @code{0x000007E0}; |
|~青成分のカラーマスク| @code{0x0000001F}; |
*カラーマスクの正当性 [#valid-mask]
各カラーマスク値が[[制約条件>#color-mask-restrict]]を満たしているかどうか調べる方法を以下に示す。
:各カラーマスク値の有効ビット(値が @code{1}; のビット)が連続しているか。|
カラーマスク値毎に、以下の手順によってチェックを行う。~
ここではカラーマスク値を代入した変数を @var{Mask}; とする。
:|
++変数 @var{Pos}; 及び @var{Bit}; を @code{0}; で初期化する。
++ @var{Mask}; が @code{0}; ならば''この色成分は存在せず''、次以降の処理を行う必要はない。
++ @var{Mask}; の最下位ビットが @code{1}; ならば6番目の処理へ飛ぶ。
++ @var{Pos}; に @code{1}; を加え、 @var{Mask}; を1ビット右シフトする。
++3番目の処理へ戻る。
++ @var{Bit}; に @code{1}; を加え、 @var{Mask}; を1ビット右シフトする。
++ @var{Mask}; の最下位ビットが @code{0}; ならば9番目の処理へ飛ぶ。
++6番目の処理へ戻る。
++ @var{Mask}; の値が @code{0}; ならば''問題なし''、 @code{0}; 以外ならば''不正なカラーマスク値''である。
:|
処理完了時、 @var{Pos}; には最下位ビットから数えたカラーマスク値のビット位置が、 @var{Bit}; にはカラーマスク値のビット数が入っている。~
これらの値は後述の[[各色成分値の取得>#read-pixel]]や[[ビット数変換>#bits2bits]]において用いることができる。
:カラーマスク値の有効ビット位置が互いに重なっていないか。|
任意の2つのカラーマスク値の論理積(AND)が @code{0}; ならば問題なく、 @code{0}; 以外ならば不正である。~
全てのカラーマスク値の組み合わせについてこれを調べる。~
論理積は、言語により @code{&}; 演算子や @code{And}; 演算子で表される。
上述したチェック処理のC++言語でのコーディング例を以下に示す。
#code(c){{
/**
* @brief
* 各色成分のカラーマスク値とその要素数を受け取り、その値が正しい値かどうか、
* 及び各カラーマスクのビット位置とビット数を返す。
*
* @param[in] masks
* カラーマスク値の配列。
* @param[in] mask_num
* masks の要素数(=カラーマスク数)。
* 通常は 3 で、α成分のカラーマスクもある場合は 4 。
* @param[out] positions
* 各カラーマスクのビット位置格納先の配列。不要ならば NULL 。
* @param[out] bitcounts
* 各カラーマスクのビット数格納先の配列。不要ならば NULL 。
*
* @retval true カラーマスク値が正しい場合。
* @retval false カラーマスク値が不正である場合。
*/
bool check_colormasks(
const unsigned long* const masks,
int mask_num = 3,
int* positions = NULL,
int* bitcounts = NULL)
{
// カラーマスク毎の処理
for (int i = 0; i < mask_num; i++)
{
int pos = 0, bit = 0;
if (masks[i] != 0)
{
// 有効ビットが連続しているか調べる
unsigned long mask = masks[i];
// マスクの最下位ビットが 0 の間ループ
while ((mask & 0x00000001) == 0)
{
pos++;
mask >>= 1;
}
// マスクの最下位ビットが 1 の間ループ
while ((mask & 0x00000001) != 0)
{
bit++;
mask >>= 1;
}
// マスクが 0 でないなら不正
if (mask != 0) { return false; }
// 他のカラーマスク値と重なっていないか調べる
for (int j = i + 1; j < mask_num; j++)
{
if ((masks[i] & masks[j]) != 0) { return false; }
}
}
// ビット位置及びビット数を保存
if (positions != NULL) { positions[i] = pos; }
if (bitcounts != NULL) { bitcounts[i] = bit; }
}
return true;
}
}}
なお、右シフト演算が行えない言語では、値を2で割ることで1ビット右シフトと同じ結果が得られる。~
また、ある値の最下位ビットが @code{0}; か @code{1}; かを判定する方法として、値が偶数か奇数かによって見分ける方法もある。~
偶数ならば最下位ビットは @code{0}; 、奇数ならば最下位ビットは @code{1}; である。
*各色成分値の取得 [#read-pixel]
[[カラーマスクの正当性>#valid-mask]]を調べた際に取得したカラーマスク値のビット位置を基に、[[ピクセルデータ>../バイナリ構造#image-data-pixel]]から色成分値を取得する方法を以下に示す。~
なお、ピクセルデータを代入した変数を @var{Pixel}; 、カラーマスク値を代入した変数を @var{Mask}; 、そのビット位置を @var{Pos}; とする。
+ @var{Pixel}; 及び @var{Mask}; を、 @var{Pos}; ビット右シフトする。
+ @var{Pixel}; と @var{Mask}; の論理積(AND)が求める色成分値である。
上述した取得処理のC++言語でのコーディング例を以下に示す。~
なお、上述の[[カラーマスクの正当性>#valid-mask]]セクションのコーディング例で記述した関数 @code{check_colormasks}; を用いている。
#code(c){{
/*
* @brief
* ピクセルデータ、各色成分のカラーマスク値、及びその要素数を受け取り、
* 各カラーマスクに対応する色成分値を返す。
*
* @param[in] pixel
* ピクセルデータ。
* @param[in] masks
* カラーマスク値の配列。
* @param[in] mask_num
* masks の要素数(=カラーマスク数)。
* 通常は 3 で、α成分のカラーマスクもある場合は 4 。
* @param[out] colors
* 各カラーマスクに対応する色成分値格納先の配列。
* @param[out] bitcounts
* 各カラーマスクのビット数格納先の配列。不要ならば NULL 。
*
* @retval true カラーマスク値が正しい場合。
* @retval false カラーマスク値が不正である場合。
*/
bool get_maskedcolors(
unsigned long pixel,
const unsigned long* const masks,
int mask_num,
unsigned long* colors,
int* bitcounts = NULL)
{
// カラーマスク数が 0 以下は不可
if (mask_num <= 0) { return false; }
// ビット位置保存先配列作成
int* positions = new int[mask_num];
// カラーマスク値のチェックとビット位置取得
bool valid = check_colormasks(masks, mask_num, positions, bitcounts);
if (valid)
{
// カラーマスク毎の処理
for (int i = 0; i < mask_num; i++)
{
// ピクセルデータとマスクを右シフト
unsigned long px = (pixel >> positions[i]);
unsigned long mask = (masks[i] >> positions[i]);
// 論理積した値が色成分値
colors[i] = (px & mask);
}
}
// 配列解放
delete [] positions;
return valid;
}
}}
取得した各色成分値を8ビット(256階調)の値に変換する方法は、次の[[ビット数変換>#bits2bits]]セクションで述べる。
*ビット数変換 [#bits2bits]
例えば、ある5ビット(32階調)の符号無し整数値 @var{Src}; を8ビット(256階調)の符号無し整数値 @var{Dest}; に相対変換することを考える。~
相対変換であるので、例えば @var{Src}; が最大値の @code{31}; であった場合、変換した @var{Dest}; は最大値の @code{255}; になる。
単純に考えれば、次の式によって @var{Dest}; を求めることができる。~
なお、この式中の @code{Round(N)}; は @var{N}; の小数点以下四捨五入を表す。
Dest=Round(Src×255÷31)
より一般的に言えば、ビット数が @var{BitSrc}; の符号無し整数値 @var{Src}; をビット数が @var{BitDest}; の符号無し整数値 @var{Dest}; に相対変換するには、 @var{Src}; に @var{BitDest}; ビットで表せる値の最大値を掛けた後、その値を @var{BitSrc}; ビットで表せる値の最大値で割ればよい。
あるビット数 @var{BitCount}; で表される数値の最大値は、 @code{1}; を @var{BitCount}; ビット左シフトした後、その値から @code{1}; を引くことで求められる。
あるビット数からあるビット数への相対変換処理のC++言語でのコーディング例を以下に示す。
#code(c){{
/*
* @brief
* あるビット数で表されている数値を他のビット数に相対変換して返します。
*
* @param[in] src
* bit_from ビットで表されている元データを指定します。
* @param[in] bit_from
* src のビット数を指定します。
* @param[in] bit_to
* 相対変換するビット数を指定します。
*
* @return 相対変換された値を返します。
*/
unsigned long bits2bits(unsigned long src, int bit_from, int bit_to)
{
// 指定したビット数で表される数値の最大値を算出
unsigned long max_from = (1UL << bit_from) - 1;
unsigned long max_to = (1UL << bit_to) - 1;
// 割る数の半分を割られる数に足すことで四捨五入を近似
// ※ unsigned long x, y; に対して次の式が成り立つ。
// ※ (unsigned long)round((double)x/y) ≒ (x+(y/2))/y
return ((src * max_to + (max_from / 2)) / max_from);
}
}}
変換したい数値データが大量にある場合、あらかじめ上のコードにおける @var{max_from}; 及び @var{max_to}; を算出しておき、それを利用して計算する方が効率的である。