16ビットDIBを作る

概要:カラーテーブルを使わず32768色表現できるDIB

16ビットDIBはカラーテーブルを使わないので手軽に、
そして固定の32768色を使うことができるという微妙なDIBです。
あまり使われていないように思いますが、
色の表現方法が独特なので一応解説しておきます。

■5ビットのRGB成分

16ビットというと65536色使えそうですが、
最上位ビットは無視して各成分 5 ビット(2の15乗=32768)でRGBを構成します。

ところでどうやって 0〜31 で 0〜255 を表現するのかが最大の謎でしょう。
それには 0 を 0 に、31 を 255 に対応させて、
残りの30個を全ての値が等間隔になるように配置しています。

間隔は約 8 です(8より少し大きい)。
つまり (1)5 は (8)8 を意味する事になります(括弧の右下の数字はビット数)。
このトーンジャンプが問題にならなければ16ビットを使う価値があるかもしれません。

■ピクセル列の型

32 ビットの時は DWORD 型、
24 ビットの時は BYTE 型、
8 ビットの時は BYTE 型を使ってきました。
普通に考えれば 16 ビットは WORD 型を使うべきでしょう。

実は、32 ビットに BYTE 型を使っても全く問題ないのですが、
16 ビットに BYTE 型を使うと、RGBのG成分が二つの BYTE にまたがってしまいます。
もちろん型キャストすれば問題ありませんが、
あえて 16 ビットにBYTE 型を使う利点は無いということです。

■ビット変換

8ビットから5ビットに変換する際には誤差が発生します。
8ビットにはあるけど5ビットには無い値がある為です。
従って、近似することになります。

また、5ビットから8ビットへの変換には理論上の誤差はありませんが、
小数の打ち切り誤差などがあります。

従って、8ビットから5ビット変換、
そして8ビットに復号しても元の値にはならないことに注意して下さい。

これらの変換をマクロとして実現すれば次のようになります。

//8ビットを5ビットに近似する
//本質:bit8/(255.0/31.0)
#define TO85(bit8)      ((BYTE)(bit8*31.0/255.0+0.5))

//5ビットを8ビットに変換する
#define TO58(bit5)      ((BYTE)(bit5*255.0/31.0+0.5))

ただし、何故だか実行結果と±1だけ違うことがあります……。
ま、あまり細かいことは気にしないということで。

■描画

描画には上記のマクロを使用しています。
変換によって誤差が生じていることを実行結果から確認して下さい。
[Print Screen]キーを押せばスクリーンキャプチャする事ができます。

#define WIDTH           197
#define HEIGHT          100
#define BITCOUNT        16

static LPWORD lpPixel;
int x,y,r,g,b,rgb;
static int length;

//4の倍数に補正
if(WIDTH*(BITCOUNT/8)%4) length=WIDTH*(BITCOUNT/8)+(4-WIDTH*(BITCOUNT/8)%4);
else length=WIDTH*(BITCOUNT/8);

//描画
r=TO85(255); g=TO85(128); b=0; rgb=(r<<10)+(g<<5)+b;
for(y=25;y<=50;y++){
    for(x=25;x<=50;x++){
        CopyMemory((LPBYTE)lpPixel+x*2+y*length,&rgb,2);
    }   //(255,128,0)8 → (31,16,0)5 → (255,132,0)8
}

length は一行分のバイト数なので、LPWORD 型の lpPixel を LPBYTE 型にキャストしてから
アドレス計算をしています。
こういう面倒な事があるから32ビットが好まれるんですよねー。

他は32ビットや24ビットと同じです。

★☆ ソースファイル表示 ☆★


戻る / ホーム