プログラミング/BMPファイル仕様/ビットフィールド のバックアップ(No.3)


16ビット及び32ビットのBMPファイルで用いられるビットフィールドの解説。

概要

本文書では、ビットフィールドを用いる16ビットBMP及び32ビットBMPを総称してビットフィールドBMPと呼ぶ。

バイナリ構造のページのビットフィールドセクションでも述べた通り、ビットフィールドは以下の構成要素を持つ。

要素バイト数
赤成分のカラーマスク4
緑成分のカラーマスク4
青成分のカラーマスク4

また、16ビットBMPと32ビットBMPそれぞれの既定値は以下の通りである。

16ビットBMPの既定ビットフィールド
要素
赤成分のカラーマスク0x00007C00
緑成分のカラーマスク0x000003E0
青成分のカラーマスク0x0000001F
1ピクセルあたりのビット数が16であるため、下位16ビットのみが用いられ、上位16ビットは無視される。
なお、この値を持つビットフィールドをRGB555と呼ぶ。
32ビットBMPの既定ビットフィールド
要素
赤成分のカラーマスク0x00FF0000
緑成分のカラーマスク0x0000FF00
青成分のカラーマスク0x000000FF
なお、この値を持つビットフィールドをRGB888と呼ぶ。

これらの値の意味、及び処理の指針について以降のセクションで述べる。

カラーマスクの意味と制約

ビットフィールドの構成要素である各色成分のカラーマスクについて、その意味制約を以下に述べる。

カラーマスク値の意味

各色成分のカラーマスクの値は、ピクセルデータのどのビットが何色にあたるかを示すものである。

例として、16ビットBMPにおける既定値であるRGB555の各構成要素値を16進数表記及び2進数表記した表を以下に示す。
なお、16ビットBMPでは各カラーマスク値の下位16ビットのみを用い、上位16ビットは無視する。

要素16進数表記2進数表記
赤成分のカラーマスク0x00007C0000000000 00000000 01111100 00000000
緑成分のカラーマスク0x000003E000000000 00000000 00000011 11100000
青成分のカラーマスク0x0000001F00000000 00000000 00000000 00011111

16ビットのピクセルデータをこれらのカラーマスク値を用いて解釈すると、以下のようになる。

  • 0ビット目は無視する
  • 1ビット目から5ビット目までは赤成分値
  • 6ビット目から10ビット目までは緑成分値
  • 11ビット目から15ビット目までは青成分値

各色成分値が5ビット(32階調)で表現されているが、これを通常の色表現で用いる8ビット(256階調)の値に変換する方法は、後述のビット数変換セクションで述べる。

もう1つの例として、32ビットBMPにおける既定値であるRGB888の各構成要素値を16進数表記及び2進数表記した表を以下に示す。
更に、このBMPの情報ヘッダV4タイプで、α成分のカラーマスクも指定されているものとする。

要素16進数表記2進数表記
赤成分のカラーマスク0x00FF000000000000 11111111 00000000 00000000
緑成分のカラーマスク0x0000FF0000000000 00000000 11111111 00000000
青成分のカラーマスク0x000000FF00000000 00000000 00000000 11111111
α成分のカラーマスク0xFF00000011111111 00000000 00000000 00000000

32ビットのピクセルデータをこれらのカラーマスク値を用いて解釈すると、以下のようになる。

  • 0ビット目から7ビット目まで(1バイト目)はα成分値
  • 8ビット目から15ビット目まで(2バイト目)は赤成分値
  • 16ビット目から23ビット目まで(3バイト目)は緑成分値
  • 24ビット目から31ビット目まで(4バイト目)は青成分値

カラーマスク値の制約

各色成分のカラーマスクはどのような値でもよいわけではなく、以下に示す2つの制約がある。

カラーマスク値の有効ビット(値が 1 のビット)は連続していなければならない。
例えば、2進数表記で 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 のビット)が連続しているか。
カラーマスク値毎に、以下の手順によってチェックを行う。
ここではカラーマスク値を代入した変数を Mask とする。
  1. 変数 Pos 及び Bit0 で初期化する。
  2. Mask0 ならばこの色成分は存在せず、次以降の処理を行う必要はない。
  3. Mask の最下位ビットが 1 ならば6番目の処理へ飛ぶ。
  4. Pos1 を加え、 Mask を1ビット右シフトする。
  5. 3番目の処理へ戻る。
  6. Bit1 を加え、 Mask を1ビット右シフトする。
  7. Mask の最下位ビットが 0 ならば9番目の処理へ飛ぶ。
  8. 6番目の処理へ戻る。
  9. Mask の値が 0 ならば問題なし0 以外ならば不正なカラーマスク値である。
処理完了時、 Pos には最下位ビットから数えたカラーマスク値のビット位置が、 Bit にはカラーマスク値のビット数が入っている。
これらの値は後述の各色成分値の取得ビット数変換において用いることができる。
カラーマスク値の有効ビット位置が互いに重なっていないか。
任意の2つのカラーマスク値の論理積(AND)が 0 ならば問題なく、 0 以外ならば不正である。
全てのカラーマスク値の組み合わせについてこれを調べる。
論理積は、言語により & 演算子や And 演算子で表される。

上述したチェック処理の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
 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
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
 
 
 
 
 
-
-
!
-
|
|
|
-
-
!
|
-
!
-
|
|
!
|
-
!
-
|
|
!
|
-
!
|
-
!
-
|
!
!
|
-
!
|
!
|
|
!
/**
 * @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ビット右シフトと同じ結果が得られる。
また、ある値の最下位ビットが 01 かを判定する方法として、値が偶数か奇数かによって見分ける方法もある。
偶数ならば最下位ビットは 0 、奇数ならば最下位ビットは 1 である。

各色成分値の取得

カラーマスクの正当性を調べた際に取得したカラーマスク値のビット位置を基に、ピクセルデータから色成分値を取得する方法を以下に示す。
なお、ピクセルデータを代入した変数を Pixel 、カラーマスク値を代入した変数を Mask 、そのビット位置を Pos とする。

  1. Pixel 及び Mask を、 Pos ビット右シフトする。
  2. PixelMask の論理積(AND)が求める色成分値である。

上述した取得処理のC++言語でのコーディング例を以下に示す。
なお、上述のカラーマスクの正当性セクションのコーディング例で記述した関数 check_colormasks を用いている。

すべて開くすべて閉じる
  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
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
 
 
 
 
 
 
-
-
!
|
-
!
|
-
!
|
-
-
!
-
-
!
|
|
-
!
!
!
|
-
!
|
|
!
/**
 * @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階調)の値に変換する方法は、次のビット数変換セクションで述べる。

ビット数変換

例えば、ある5ビット(32階調)の符号無し整数値 Src を8ビット(256階調)の符号無し整数値 Dest に相対変換することを考える。
相対変換であるので、例えば Src が最大値の 31 であった場合、変換した Dest は最大値の 255 になる。

単純に考えれば、次の式によって Dest を求めることができる。
なお、この式中の Round(N)N の小数点以下四捨五入を表す。

Dest=Round(Src×255÷31)

より一般的に言えば、ビット数が BitSrc の符号無し整数値 Src をビット数が BitDest の符号無し整数値 Dest に相対変換するには、 SrcBitDest ビットで表せる値の最大値を掛けた後、その値を BitSrc ビットで表せる値の最大値で割ればよい。

あるビット数 BitCount で表される数値の最大値は、 1BitCount ビット左シフトした後、その値から 1 を引くことで求められる。

あるビット数からあるビット数への相対変換処理の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
-
|
|
|
|
|
|
|
|
|
|
|
!
 
-
-
!
|
|
-
|
|
!
!
/**
 * @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);
}

変換したい数値データが大量にある場合、あらかじめ上のコードにおける max_from 及び max_to を算出しておき、それを利用して計算する方が効率的である。