ランレングスデータは、以下の5種類の要素から構成されます。
- エンコードデータ
- 絶対モードデータ
- 行データの終わり
- イメージデータの終わり
- 相対位置に移動
- エンコードデータ
1バイト目:繰返し回数(0x01〜0xFF)、2バイト目:ピクセルデータ
例
RLE8 0x04 0x56 → 0x56 0x56 0x56 0x56
RLE4 0x05 0x67 → 0x67 0x67、0x6?
(繰返し回数が奇数のときは、末尾4bitは不確定のまま次のピクセルへ進みます) - 絶対モードデータ
1バイト目:0x00、2バイト目:データ個数(0x03〜0xFF)、3バイト目以降:ピクセルデータ
例
RLE8 0x00 0x03 0x45 0x67 0x89 0x00 → 0x45 0x67 0x89
RLE4 0x00 0x05 0x45 0x67 0x80 0x00 → 0x45 0x67 0x8?
(圧縮されているコードは常に2バイトの境界で揃えられるので、
奇数個の圧縮データの末尾には必ず0x00が一つ付加されています。) - 行データの終わり
1バイト目:0x00、2バイト目:0x00 - イメージデータの終わり
1バイト目:0x00、2バイト目:0x01 - 相対位置に移動
1バイト目:0x00、2バイト目:0x02、3バイト目:x軸移動距離、4バイト目:y軸移動距離
例 0x00 0x02 0x03 0x04 → 現在の位置からx軸方向に3ピクセル、y軸方向に4ピクセル、正方向に移動する。
ビットマップバイト幅を算出するためのマクロ定義です。
//ビットマップバイト幅の算出マクロ
#ifndef WIDTHBYTES
#define WIDTHBYTES(bits) (((bits)+31)/32*4)
#endif//WIDTHBYTES
8ビットランレングス圧縮されたデータを解凍します。
//ランレングス8ビット解凍
BOOL __stdcall _DecodeRle8(LPVOID pBuffer,DWORD dwBufferSize,
UINT width,UINT height,CFile* infile)
{
ASSERT(AfxIsValidAddress(infile,sizeof(CFile),0));
ASSERT(AfxIsValidAddress(pBuffer,dwBufferSize));
DWORD widthBytes=WIDTHBYTES(8*width);//ビットマップバイト幅の算出
DWORD dwSizeImage=widthBytes*height; //ピクセルデータのバイト数
ASSERT(dwBufferSize>=dwSizeImage);
::ZeroMemory(pBuffer,dwBufferSize); //ピクセルデータのクリア
PBYTE dst=(PBYTE)pBuffer; //ピクセルデータの先頭位置
PBYTE end=dst+dwBufferSize; //ピクセルデータの末尾位置
BOOL bEndOfLine=FALSE; //終了フラグをクリア
int x=0;int y=0; //座標位置
BYTE code[4]; //読み込んだコードを一次保持する
//終了フラグが立ってないならループ
while(!bEndOfLine){
UINT uSize=infile->Read(&code,2); //2バイトコードを読む
if (uSize<2) return FALSE; //読み込みに失敗したらFALSEを返して終了
switch(code[0]){
case 0: //エスケープコード(0)の時
switch(code[1]){
case 0: //識別コード0:行末
x=0; //復帰
y++; //改行
dst=(PBYTE)pBuffer+widthBytes*y;//座標計算
//ピクセルデータ末尾に到達したら終了フラグを立てる
if (dst>=end) bEndOfLine=TRUE;
break;
case 1: // 識別コード1:データの末尾
bEndOfLine=TRUE; //データ末尾フラグを立てる
break;
case 2: // 識別コード2:座標移動
uSize=infile->Read(&code[2],2);//更に2バイト読む
if (uSize<2) return FALSE;//読み込み失敗ならFALSEを返して終了
x+=code[2]; //x軸方向に加算
y+=code[3]; //y軸方向に加算
dst=(PBYTE)pBuffer+widthBytes*y+x/2;//座標計算
//ピクセルデータ末尾に到達したら終了フラグを立てる
if (dst>=end) bEndOfLine=TRUE;
break;
default: // 絶対値モード(後ろに続くn ピクセルをそのまま使う)
for(int i=0;i<code[1];i++){
uSize=infile->Read(&code[2],1);//1バイトコード読み込む
if (uSize<1) return FALSE;//読み込みに失敗したらFALSEを返して終了
*dst++=code[2]; //コードをピクセルに格納する
x++; //x軸をインクリメント
}
//奇数個目なら1バイト読み飛ばす
if (code[1]%2==1) infile->Seek(1,CFile::current);
break;
}
break;
default: // エンコードデータ 1〜255のとき
for(int i=0;i<code[0];i++){//1番目のコード分繰り返す
*dst++=code[1]; //2番目のコードをピクセルに格納する
x++; //x軸をインクリメント
}
break;
}
}
return TRUE;
}
4ビットランレングス圧縮されたデータを解凍します。
//ランレングス4ビット解凍
BOOL __stdcall _DecodeRle4(LPVOID pBuffer,DWORD dwBufferSize,
UINT width,UINT height,CFile* infile)
{
ASSERT(AfxIsValidAddress(infile,sizeof(CFile),0));
ASSERT(AfxIsValidAddress(pBuffer,dwBufferSize));
DWORD widthBytes=WIDTHBYTES(4*width);//ビットマップバイト幅の算出
DWORD dwSizeImage=widthBytes*height; //ピクセルデータのバイト数
ASSERT(dwBufferSize>=dwSizeImage);
::ZeroMemory(pBuffer,dwBufferSize); //ピクセルデータのクリア
PBYTE dst=(PBYTE)pBuffer; //ピクセルデータの先頭位置
PBYTE end=dst+dwBufferSize; //ピクセルデータの末尾位置
BOOL bEndOfLine=FALSE; //終了フラグをクリア
int x=0;int y=0; //座標位置
int n; //パディングを判定する時に使う変数
BYTE code[4]; //読み込んだコードを一次保持する
//終了フラグが立ってないならループ
while(!bEndOfLine){
UINT uSize=infile->Read(&code,2); //2バイトコードを読む
if (uSize<2) return FALSE; //読み込みに失敗したらFALSEを返して終了
switch(code[0]){
case 0: //エスケープコード(0)の時
switch(code[1]){
case 0: //識別コード0:行末
x=0; //復帰
y++; //改行
dst=(PBYTE)pBuffer+widthBytes*y;//座標計算
//ピクセルデータ末尾に到達したら終了フラグを立てる
if (dst>=end) bEndOfLine=TRUE;
break;
case 1: // 識別コード1:データの末尾
bEndOfLine=TRUE; //データ末尾フラグを立てる
break;
case 2: // 識別コード2:座標移動
uSize=infile->Read(&code[2],2);//更に2バイト読む
if (uSize<2) return FALSE;//読み込み失敗ならFALSEを返して終了
x+=code[2]; //x軸方向に加算
y+=code[3]; //y軸方向に加算
dst=(PBYTE)pBuffer+widthBytes*y+x/2;//座標計算
//ピクセルデータ末尾に到達したら終了フラグを立てる
if (dst>=end) bEndOfLine=TRUE;
break;
default: // 絶対値モード(後ろに続くn ピクセルをそのまま使う)
for(int i=0;i<code[1];i++){
//偶数個目コードの時読み込む
if ((i%2)==0) {
uSize=infile->Read(&code[2],1);
if (uSize<1) return FALSE;
}
//偶数個目の時は上位4ビット、奇数個目は下位4ビットを取る
code[3]=(i&1)? code[2]:code[2]>>4;
//偶数ピクセルのときは上位4ビットに、
//それ以外は下位4ビットに格納する
if ((x%2)==0) *dst=(code[3]<<4)&0xF0;
else *dst++|=(code[3]&0x0F);
x++; //x軸をインクリメント
//ピクセルデータ末尾に到達したら終了フラグを立てる
if (dst>=end){
bEndOfLine=TRUE;
break;
}
}
//奇数個目なら1バイト読み飛ばす
n=(code[1]/2)%2+code[1]%2;
if (n%2) infile->Seek(1,CFile::current);
break;
}
break;
default: // エンコードデータ 1〜255のとき
for(int i=0;i<code[0];i++){
//偶数個目の時は上位4ビット、奇数個目は下位4ビットを取る
code[3]=(i&1)? code[1]:code[1]>>4;
//偶数ピクセルのときは上位4ビットに、それ以外は下位4ビットに格納する
if ((x%2)==0) *dst=(code[3]<<4)&0xF0;
else *dst++|=(code[3]&0x0F);
x++; //x軸をインクリメント
//ピクセルデータ末尾に到達したら終了フラグを立てる
if (dst>=end) {
bEndOfLine=TRUE;
break;
}
}
break;
}
}
return TRUE;
}
戻り値がTRUEならpBufferで示されるメモリーに、ランレングスデータを解凍したピクセルデータが格納されます。
サンプルプログラム(VisualC++net2003ソリューション)DIBFileTest03.zip
上記のzipファイルをダウンロードしてからWinRAR等で解凍し、DIBFileTest03フォルダー内にある「DIBFileTest.sln」を開きます。「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。
ビルドが終わると直ちにプログラムが自動起動してウィンドウが表示されます。ウィンドウのメニューで「ファイル」/「開く」を選択して、「ファイルを開く」ダイヤログを表示させます。
WindowsXPの場合
"C:\WINDOWS\system32"フォルダー(※XPのシステムファイルがドライブC:にある場合)内の「browseui.dll」を選択し、「開く」ボタンを押すと、「リソース名を選択してください。」ダイヤログが表示されるので、
コンボボックスでリソース名261番(RLE4)を選択します。
そして下記のようなDIB画像が表示されるかどうか確認します。
ツールバーの新規作成ボタンを押して先ほどの画像を消してから、同様にリソース名262番(RLE8)も確認します。
Windows7の場合
resフォルダー内の「browseui261RLE4.bmp」を選択し、
「開く」ボタンを押して、正しく画像が表示されるかどうか確認します。
「browseui262RLE8.bmp」についても同様にしてください。