このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
昨日の説明は文字ばかりで頭が痛いので、
今日は、昨日説明した 16 ビットの BMP を生成してみる。
# gen16bmp.pl
use strict;
use warnings;
use integer;
binmode(select);
my $width = 100;
my $height = 75;
my $bit_count = 16;
my $line_bits = $width * $bit_count;
my $line_size = int(($line_bits + 31) / 32) * 4;
my $pad_size = $line_size - int($line_bits / 8);
my $padding = pack("x$pad_size");
print pack("a2Vx4V", "BM", 54 + $line_size * $height, 54);
print pack("VVVvvx24", 40, $width, $height, 1, $bit_count);
my $r = pack("v", 0b_0_11111_00000_00000);
my $g = pack("v", 0b_0_00000_11111_00000);
my $b = pack("v", 0b_0_00000_00000_11111);
print scalar($b x $width . $padding) x 25;
print scalar($r x $width . $padding) x 25;
print scalar($g x $width . $padding) x 25;
exit;
大分すっきりした。さて、内容を見ていこう。
use strict;
use warnings
昨日は入れなかったが、厳格なコーディングの癖はつけよう。
急いでる時や、使い捨てのコードを書くとき以外は。
use integer;
特に小数が必要ない場合は高速化が見込めるかも。
binmode(select);
Windows では、出力をバイナリ形式にする必要がある。
print 文は、出力ハンドルを間接指定できるんだけど、
省略されることが多い。省略した場合、
「現在選択されている」のハンドルに出力する。
普通、STDOUT だけど、異なるものが選択されることもある。
select 関数は、現在選択されているハンドルを返すので、
それを使って出力をバイナリモードにする。
my $width = 100;
my $height = 75;
my $bit_count = 16;
横幅、高さ、ピクセルあたりのビット数を変数にしておく。
徐々に汎用性を高めていくためだ。
my $line_bits = $width * $bit_count;
my $line_size = int(($line_bits + 31) / 32) * 4;
行は、4 バイト境界になるように埋められる。
行の画素に使用するビット数を求め、
32 で割って切り上げると、「4 バイト」の数が分かる。
それに 4 を掛けて、実際の行のバイト数を求める。
(ここはまだ最適化できるかも)
my $pad_size = $line_size - int($line_bits / 8);
my $padding = pack("x$pad_size");
行のサイズを元に、詰め物のサイズを求め、
バイナリにパックしたものを先に作っておく。
今までに説明した BMP は、16/24/32 ビット単位なので、
詰め物も丁度バイト単位になる。
print pack("a2Vx4V", "BM", 54 + $line_size * $height, 54);
BITMAPFILEHEADER を出力。
行のサイズを計算済みなので、高さを掛けてサイズを求め、
それにヘッダサイズの 54 を加算する。
print pack("VVVvvx24", 40, $width, $height, 1, $bit_count);
BITMAPINFOHEADER を出力。
ほとんど説明不要やね。0 でいいフィールドは、
pack の x テンプレートを使用して埋める。見やすくなった。
my $r = pack("v", 0b_0_11111_00000_00000);
my $g = pack("v", 0b_0_00000_11111_00000);
my $b = pack("v", 0b_0_00000_00000_11111);
16 ビット表現での、赤、青、緑のピクセルを作る。
最近の Perl では、2 進数リテラルが使えるようになった。
そのため、分かりやすく書ける。
アンダーバーは見やすいように入れているが、
どうやら 3 桁ごとに使わないと警告がでるようだ。
このあたり柔軟にしてほしい所だ。
print scalar($b x $width . $padding) x 25;
print scalar($r x $width . $padding) x 25;
print scalar($g x $width . $padding) x 25;
青 25 行、赤 25 行、緑 25 行の順に出力。
x 演算子ってのが Perl らしい。
幅の分繰り返して詰め物を追加、
そしてそれを 25 行分繰り返す。
演算子の優先順位があるので、括弧が必要だが、
括弧をつけると、リストコンテキストとなってしまう。
x 演算子は、スカラコンテキストとリストコンテキストで
動作が異なるため、ここでは scalar を使って、
強制的にスカラコンテキストで解釈させる。
exit;
終わり。