BMPファイルは、印刷用データを作成する時によく使用していたが、最近ではe-Paperに画像データを送信する際に使用するデータを作成する場合にも使う。BMPファイルは大昔はMacOSで表示できなかったが現在は何も問題なく表示できるし編集も問題ない。ただ、Windowsのペイントのように1bit深度のモノクロ画像としての保存はできない。WindowsとOS/2のBMPがあるが、OS/2自体が死語だからWindowsだけ考えてたらいいでしょう
構造
ファイルヘッダ、情報ヘッダ、イメージデータの構成になっている。
Windows BMP | |
ファイルヘッダ | 14Bytes |
情報ヘッダ | 40Bytes + カラーパレット |
画像データ | 可変長 |
カラーパレットはない場合もある模様
ファイルヘッダ
ファイルヘッダは14Byte固定で下記の情報からなる。バイトならびはLittleエンディアン(下位バイトが先)になっている。
アドレス(サイズ) | 名称 | 内容 |
0x0000 (2) | bfType | ファイルタイプ 通常は'BM' |
0x0002 (4) | bfSize | ファイルサイズ (byte) |
0x0006 (2) | bfReserved1 | 予約領域 常に 0 |
0x0008 (2) | bfReserved2 | 予約領域 常に 0 |
0x000A (4) | bfOffBits | ファイル先頭から画像データまでのオフセット (byte) |
ファイル先頭から14byteの読み込みphp
<?php
$fp1 = fopen($filename_full,"rb");
echo "<strong>ファイルヘッダ</strong>:";
for($i=0;$i<14;$i++){
$header[$i]=ord(fread($fp1,1));
echo sprintf("%02X",$header[$i]) . ",";
}
echo "<br/>";
$filesize=$header[5]*256*256*256+$header[4]*256*256+$header[3]*256+$header[2];
$fileoffset=$header[13]*256*256*256+$header[12]*256*256+$header[11]*256+$header[10];
echo "Filsize=".sprintf("%d",$filesize)." byte<br/>";
echo "Offset =".sprintf("%d (%02Xh)",$fileoffset,$fileoffset)."<br/>";
出力例
ファイルヘッダ:42,4D,F6,5C,01,00,00,00,00,00,36,00,00,00,
Filsize=89334 byte
Offset =54 (36h)
情報ヘッダ
ファイルヘッダの後に続くのが情報ヘッダになる。
アドレス (サイズ) | 名称 | 内容 |
0x000E (4) | biSize | ヘッダサイズ 40 - Windows Bitmap |
0x0012 (4) | biWidth | 画像の幅 (ピクセル) |
0x0016 (4) | biHeight | 画像の高さ (ピクセル) biHeight の値が正数なら,画像データは下から上へ biHeight の値が負数なら,画像データは上から下へ |
0x001A (2) | biPlanes | プレーン数 常に 1 |
0x001C (2) | biBitCount | 1画素あたりのデータサイズ (bit) 例)256 色ビットマップ = 8 |
0x001E (4) | biCompression | 圧縮形式 0 - BI_RGB (無圧縮) 1 - BI_RLE8 (RunLength 8 bits/pixel) 2 - BI_RLE4 (RunLength 4 bits/pixel) 3 - BI_BITFIELDS (Bitfields) |
0x0022 (4) | biSizeImage | 画像データ部のサイズ (byte) 96dpi ならば3780 0 の場合もあるらしい |
0x0026 (4) | biXPixPerMeter | 横方向解像度 (1mあたりの画素数) 96dpi ならば3780 0 の場合もあるらしい |
0x002A (4) | biYPixPerMeter | 縦方向解像度 (1mあたりの画素数) 96dpi ならば3780 0 の場合もあるらしい |
0x002E (4) | biClrUsed | 格納されているパレット数 (使用色数) 0 の場合もあるらしい |
0x0032 (4) | biCirImportant | 重要なパレットのインデックス 0 の場合もある |
通常のBMPファイルは、画像の左下を起点にして逆さまにデータが並んでいるが、biHeightが負の数字のときは左上が起点で正順に並んでいる。よって読み出し時に場合分けが必要である。少ないデータではあるが、Windowsのペイントで作成したデータはbiHeightが正の数字であるがMacOSで作成した画像(Pixelmeterしか使ってないが・・・)は負の数字になっているようだ
<?php
$i=0;
while( $cnt < $fileoffset ){
$info[$i]=ord(fread($fp1,1));
echo sprintf("%02X",$info[$i]) . ",";
$cnt++;
$i++;
}
echo "<br/>\n";
// $d = fread($fp1,4);
// echo $d.":".sprintf("%08X",$d);
$biSize =$info[ 3]*256*256*256+$info[ 2]*256*256+$info[ 1]*256+$info[ 0];
//$biWidth =$info[ 7]*256*256*256+$info[ 6]*256*256+$info[ 5]*256+$info[ 4];
$d = sprintf("%02X%02X%02X%02X",$info[ 7],$info[ 6],$info[ 5],$info[ 4]);
$biWidth = hexdec($d);
if($biWidth > 0x7FFFFFFF) {
$biWidth -= 0x100000000;
} // 符号付き整数にbit数指定で変換する方法を見つけられない
//$biHeight =$info[11]*256*256*256+$info[10]*256*256+$info[ 9]*256+$info[ 8];
$d = sprintf("%02X%02X%02X%02X",$info[11],$info[10],$info[ 9],$info[ 8]);
$biHeight = hexdec($d);
if($biHeight > 0x7FFFFFFF) {
$biHeight -= 0x100000000;
} // 符号付き整数にbit数指定で変換する方法を見つけられない
$biPlanes =$info[13]*256+$info[12];
$biBitCount =$info[15]*256+$info[14];
$biCompression =$info[19]*256*256*256+$info[18]*256*256+$info[17]*256+$info[16];
$biSizeImage =$info[23]*256*256*256+$info[22]*256*256+$info[21]*256+$info[20];
$biXPixPerMeter=$info[27]*256*256*256+$info[26]*256*256+$info[25]*256+$info[24];
$biYPixPerMeter=$info[31]*256*256*256+$info[30]*256*256+$info[29]*256+$info[28];
$biClrUsed =$info[35]*256*256*256+$info[34]*256*256+$info[33]*256+$info[32];
$biCirImportant=$info[39]*256*256*256+$info[38]*256*256+$info[37]*256+$info[36];
if($biClrUsed===0){
$rgbQuadNum = pow(2,$biBitCount);
} else {
$rgbQuadNum = $biClrUsed;
}
if($biBitCount < 9){ // 色ビット数が1,4,8の時のみパレットデータが存在する。
$rgbQuadAddr=40;
for($i=0;$i<$rgbQuadNum;$i++){
$rgbQuad[$i] = sprintf("%02X%02X%02X",$info[$rgbQuadAddr],$info[$rgbQuadAddr+1],$info[$rgbQuadAddr+2]);
$rgbQuadAddr += 4;
}
}
echo "<div class='disp_table_shukei'>\n";
echo "<table>";
echo "<tr><th>アイテム</th><th>name</th><th>値</th></tr>\n";
echo "<tr><td>情報ヘッダサイズ</td><td>[biSize]</td><td>".sprintf("%d",$biSize )." byte</td></tr>\n";
echo "<tr><td>画像幅ピクセル</td><td>[biWidth]</td><td>".sprintf("%d",$biWidth )." dot</td></tr>\n";
echo "<tr><td>画像縦ピクセル</td><td>[biHeight]</td><td>".sprintf("%d",$biHeight)." dot</td></tr>\n";
echo "<tr><td>プレーン数</td><td>[biPlanes]</td><td>".$biPlanes."</td></tr>\n";
echo "<tr><td>種類</td><td>[biBitCount]</td>";
switch( $biBitCount ){
case 1: echo "<td>2色ビットマップ[".$biBitCount."bit]</td>";break;
case 4: echo "<td>4色ビットマップ[".$biBitCount."bit]</td>";break;
case 8: echo "<td>256色ビットマップ[".$biBitCount."bit]</td>";break;
case 16: echo "<td>65536色ビットマップ[".$biBitCount."bit]</td>";break;
case 24: echo "<td>1677万色ビットマップ[".$biBitCount."bit]</td>";break;
case 32: echo "<td>1677万色ビットマップ[".$biBitCount."bit]</td>";break;
default :echo "<td>わからん![".$biBitCount."bit]</td>";break;
}
echo "</tr>\n";
echo "<tr><td>圧縮形式</td><td>[biCompression]</td><td>".$biCompression."</td></tr>\n";
echo "<tr><td>画像データサイズ</td><td>[biSizeImage]</td><td>".$biSizeImage."</td></tr>\n";
echo "<tr><td>水平解像度</td><td>[biXPixPerMeter]</td><td>".$biXPixPerMeter."</td></tr>\n";
echo "<tr><td>水平解像度</td><td>[biYPixPerMeter]</td><td>".$biYPixPerMeter."</td></tr>\n";
echo "<tr><td>格納パレット数</td><td>[biClrUsed]</td><td>".$biClrUsed."</td></tr>\n";
echo "<tr><td>重要色数</td><td>[biCirImportant]</td><td>".$biCirImportant."</td></tr>\n";
echo "<tr><td>算出パレット数</td><td></td>-<td>".$rgbQuadNum."</td></tr>\n";
if($biBitCount < 9){ // 色ビット数が1,4,8の時のみパレットデータが存在する。
for($i=0;$i<$rgbQuadNum;$i++){
echo "<tr><td>パレット".$i."</td>-<td></td><td>".$rgbQuad[$i]."</td></tr>\n";
}
}
echo "</table>";
echo "</div>\n";
echo "<p></p>\n";
?>
出力例
パレット
パレットはちょっとよくわかってない。変換するときに必要でもないのであまりうまく調べてないが、ビット深度(bitCount)が1〜8の時にパレットが存在する。MacOSでは、カラー画像をグレイスケールに変換しても24bitのままでrgb全ての値が同じになるだけで8bitにはならない。また、1bit深度のモノクロ画像を作れず、0/255の2値になるだけである。
また、Windowsでモノクロ画像を作成するときは、ペイントで生成した場合とphotoshopで生成した場合とではパレットの順番が異なる。これをどのように判断して白は白、黒は黒と生成するのかはまだ謎のままだ。
画像データ
画像データ通常は左下からから右上に向かって記録されている。 (Height の値が負なら左上から右下に向かって記録される。)、なお水平方向の並びは見た目と同じである
そして重要なことは、画像の横のラインのデータ(Byte数)は,4 の倍数でなければならない。よって、4の倍数でない水平ドット数をもつ画像では、4byteの倍数になるように1ドットラインに00が追加される。ということである。
使用できる色数 | 1画素あたりの使用量 | 1画素あたりの内容 |
1 bit Bitmap | 2色 | 1 bit |
4 bit Bitmap | 16色 | 4 bit |
8 bit Bitmap | 256色 | 8 bit (1 byte) |
24 bit Bitmap | 1677万色 | 24 bit (3 byte) |
32 bit Bitmap | 1677万色 | 32 bit (4 byte)RGB+アルファチャンネル |
アルファチャンネルはbit深度が32bitの時に含まれ、透明度をあらわすレイヤとして存在するようだが、どのように扱うかは方言的なものがありそうである。MacOSのプレビューでデータを書き出す時アルファチャンネルを含めるかどうかを選択できる。
また、16bit深度のデータの書き出しはWindowsではGIMPを使うと書き出せる(いくつかパターン方言がありそうであるが)
コメントを追加