16ビット及び32ビットのBMPファイルで用いられるビットフィールドの解説。
本文書では、ビットフィールドを用いる16ビットBMP及び32ビットBMPを総称してビットフィールドBMPと呼ぶ。
バイナリ構造のページのビットフィールドセクションでも述べた通り、ビットフィールドは以下の構成要素を持つ。
要素 | バイト数 |
---|---|
赤成分のカラーマスク | 4 |
緑成分のカラーマスク | 4 |
青成分のカラーマスク | 4 |
また、16ビットBMPと32ビットBMPそれぞれの既定値は以下の通りである。
要素 | 値 |
---|---|
赤成分のカラーマスク | 0x00007C00 |
緑成分のカラーマスク | 0x000003E0 |
青成分のカラーマスク | 0x0000001F |
要素 | 値 |
---|---|
赤成分のカラーマスク | 0x00FF0000 |
緑成分のカラーマスク | 0x0000FF00 |
青成分のカラーマスク | 0x000000FF |
これらの値の意味、及び処理の指針について以降のセクションで述べる。
ビットフィールドの構成要素である各色成分のカラーマスクについて、その意味と制約を以下に述べる。
各色成分のカラーマスクの値は、ピクセルデータのどのビットが何色にあたるかを示すものである。
例として、16ビットBMPにおける既定値であるRGB555の各構成要素値を16進数表記及び2進数表記した表を以下に示す。
なお、16ビットBMPでは各カラーマスク値の下位16ビットのみを用い、上位16ビットは無視する。
要素 | 16進数表記 | 2進数表記 |
---|---|---|
赤成分のカラーマスク | 0x00007C00 | 00000000 00000000 01111100 00000000 |
緑成分のカラーマスク | 0x000003E0 | 00000000 00000000 00000011 11100000 |
青成分のカラーマスク | 0x0000001F | 00000000 00000000 00000000 00011111 |
16ビットのピクセルデータをこれらのカラーマスク値を用いて解釈すると、以下のようになる。
各色成分値が5ビット(32階調)で表現されているが、これを通常の色表現で用いる8ビット(256階調)の値に変換する方法は後述する。
もう1つの例として、32ビットBMPにおける既定値であるRGB888の各構成要素値を16進数表記及び2進数表記した表を以下に示す。
更に、このBMPの情報ヘッダがV4タイプで、α成分のカラーマスクも指定されているものとする。
要素 | 16進数表記 | 2進数表記 |
---|---|---|
赤成分のカラーマスク | 0x00FF0000 | 00000000 11111111 00000000 00000000 |
緑成分のカラーマスク | 0x0000FF00 | 00000000 00000000 11111111 00000000 |
青成分のカラーマスク | 0x000000FF | 00000000 00000000 00000000 11111111 |
α成分のカラーマスク | 0xFF000000 | 11111111 00000000 00000000 00000000 |
32ビットのピクセルデータをこれらのカラーマスク値を用いて解釈すると、以下のようになる。
各色成分のカラーマスクはどのような値でもよいわけではなく、以下に示す2つの制約がある。
1
のビット)は連続していなければならない。00000000 00000000 00001111 11100000
のカラーマスク値は 1
が連続しているため問題ないが、2進数表記で 00000000 00000000 00001110 11100000
のカラーマスク値は途中に 0
が入っているため不正な値である。00000000 00000000 11111110 00000000
で、緑成分のカラーマスク値が 00000000 00000000 00000011 11110000
の場合、22ビット目がどちらも 1
のため不正な値である。逆に、これらの制約を満たしてさえいればどのような値でもとることができ、有効ビット無し(値 0x00000000
)でも構わない。
カラーマスク値に有効ビットの無い色成分値は 0
として扱われる。ただし、α成分の場合は完全不透明として扱われる。
また、BMPの仕様とは関係ない制約であるが、Windows 95、Windows 98、Windows MeにおいてビットフィールドBMPを直にデバイスコンテキスト上に表示する場合、決まったビットフィールド以外は使用できない。
16ビットBMPの場合は前述のRGB555及び次に述べるRGB565、32ビットBMPの場合は前述のRGB888のみが使用できる。
RGB565は、以下の値を持つビットフィールドのことである。
要素 | 値 |
---|---|
赤成分のカラーマスク | 0x0000F800 |
緑成分のカラーマスク | 0x000007E0 |
青成分のカラーマスク | 0x0000001F |
各カラーマスク値が制約条件を満たしているかどうか調べる方法を以下に示す。
1
のビット)が連続しているか。long
)の変数を Mask としたとき、次のコードによって連続しているか否かを判定できる。-N
との論理積(AND)である (N & -N)
によってビットの立っている最下位ビットだけが 1
となる値を求めることができる(参考:Wikipedia:2の補数#テクニック)。&
演算子や And
演算子などで表される。0
ならば問題なく、 0
以外ならば不正である。上述したチェック処理のC++言語でのコーディング例を以下に示す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | - | | | | | | | | ! - - ! | | - - ! | | - - ! ! | - ! - - ! ! | ! | | ! |
|
ある整数値 N について、 (N & -N)
によってビットの立っている最下位ビットだけが 1
となる値を求めることができると上述したが、この値で元の値を割ることにより連続するビットの位置を右シフトすることが出来る。
例えば、 N の値の2進数表記が 00000000 00000000 11111111 00000000
であるとする。
この時、 (N & -N)
の演算結果の2進数表記は 00000000 00000000 00000001 00000000
となる。
そして (N / (N & -N))
の演算結果の2進数表記は 00000000 00000000 00000000 11111111
となり、ビットの立っている最下位ビットが右端となるように右シフトされる。
ただし、 N の値が 0
である場合はゼロ除算エラーとなってしまうため、最初に値が非ゼロであることをチェックする必要がある。
また、除算は符号を考慮せずに行う必要がある*1。
カラーマスク値に対してこのことを利用し、ピクセルデータから色成分値を取得する方法を以下に示す。
ピクセルデータを代入した変数を Pixel 、カラーマスク値を代入した変数を Mask とする。
0
である場合は既定の色成分値を用いる(α成分ならば完全不透明、それ以外ならば 0
)。上述した取得処理のC++言語でのコーディング例を以下に示す。
なお、引数に渡すカラーマスク値は、上述したカラーマスクの正当性のチェックによって正当であると判別済みであるものとする。
また、カラーマスク値が 0
である要素については常に 0
を設定する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | - | | | | | | | | | | | | | | | ! - - ! - | ! | - ! - - ! | - - ! ! | - - ! | - | ! ! ! | | ! |
|
取得した各色成分値を8ビット(256階調)の値に変換する方法は、次の色成分値のビット数変換セクションで述べる。
例えば、ある5ビット(32階調)の符号無し整数値 Src を8ビット(256階調)の符号無し整数値 Dest に相対変換することを考える。
相対変換であるので、例えば Src が最大値の 31
であった場合、変換した Dest は最大値の 255
になる。
単純に考えれば、次の式によって Dest を求めることができる。
なお、この式中の Round(N)
は N の小数点以下四捨五入を表す。
Dest=Round(Src×255÷31)
より一般的に言えば、ビット数が BitSrc の符号無し整数値 Src をビット数が BitDest の符号無し整数値 Dest に相対変換するには、 Src に BitDest ビットで表せる値の最大値を掛けた後、その値を BitSrc ビットで表せる値の最大値で割ればよい。
あるカラーマスク値 Mask について、そのマスク値で表せる値の最大値は、上述した通り (Mask / (Mask & -Mask))
で求めることができる。
また、あるビット数 BitCount で表せる数値の最大値は、 1
を BitCount ビット左シフトした後、その値から 1
を引くことで求められる。
以上のことを用い、各色成分値の取得セクションで記述した get_maskedcolor_elems
関数を修正したコードを以下に示す。
各色成分値を任意の固定ビット数表現値で取得できるように修正している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | - | | | | | | | | | | | | | | | | | | | ! - - ! | | - | ! | - ! | - ! - - ! | - - ! ! | - - ! | - | ! | | - - ! ! | - - ! | - | | ! ! ! ! | | ! |
|
ビット数変換したい色成分値が大量にある場合、あらかじめ上記のコードにおける max_from 及び max_to を算出しておき、それを利用して計算する方が効率的である。
より正確な計算方法は上述の通りだが、変換後の方がビット数が大きい場合は左ビット複製、小さい場合は右シフト演算により、除算を用いずに近似解を求めることができる。
詳細は次のサイトで述べられている。
ビット数が src_bit の符号無し整数値 src をビット数が dest_bit の符号無し整数値に近似変換する関数のコーディング例は次のようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | - | | | | | | | | | | ! - - ! | | | - | ! | - | ! | - ! | - - | ! ! | - - | ! ! | | ! |
|
LONG_MIN
(符号付き整数型の最小値)である場合、 N / (N & -N)
が LONG_MIN / LONG_MIN
となり、演算結果が環境依存となってしまうため。