2006 年 1 月 20 日 23 時 25 分

16 ビット BMP 生成


このアーカイブは同期化されません。 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;

終わり。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.