09 月 23 日(金)

初歩的なグラフィックスインタプリタ

CGComputer Graphics)用 プログラミング言語の初歩的な処理系について, プログラミングを開始します.


画像ファイル

CG 画像を取り扱うための最も単純なファイル形式 PBMPortable BitMap) について理解しよう. まずは,PBM ファイルの具体例として, List 1(a) のような内容のファイル c.pbm を作ってみよう.

List 1(a). 画像ファイル c.pbm の内容
P1
10 11
0000000000
0001111000
0011111100
0111001110
0110000110
0110000000
0110000110
0111001110
0011111100
0001111000
0000000000						

この PBM ファイルの構成は次の通り:

コンピュータでは, 画素(pixel)と呼ばれる点(微小な四角形)の集合によって, 画像を表現している.

では次に,このデータを画像として表示してみよう. 今回は画像表示用プログラムとして,display を利用する:

$  display  c.pbm  &
このコマンドラインの末尾の記号「&」は, このコマンドをバックグラウンドで実行 (シェルと同時に実行)するための指定だ. これによって,display を終了しなくても, 別のコマンドを入力できるようになる. (「&」を付けずに実行してみれば,その効果がわかるだろう.)

さて,表示結果は Fig.1 である. ただし,サイズが非常に小さいので,拡大して観察しよう. この display のウィンドウ内で左クリックするとメニューが現れる:

Fig.1. 画像ファイル c.pbm の拡大表示例

さあ,PBM 形式で他の画像を作るのも簡単そうだ. オリジナルな画像の PBM ファイルを作成してみよう.

ただし,PBM ファイルを作成する場合には, 次の制約があることに注意しよう:

画像の横幅はヘッダ部で指定しているので, データ部ではどこで改行しても構わない. (もちろん,71 文字未満なら,まったく改行しなくてもよい.) たとえば,List 1(a) の改行位置を List 1(b) のように変えてもまったく問題なく, 画像としての表示結果はまったく変わらない. 試してみよう.

List 1(b). 画像ファイル c.pbm の別な表現
P1
10 11
0000000000000111100000111111000111001110
0110000110011000000001100001100111001110
001111110000011110000000000000				

なお,PBM で表現できるのは白黒二値画像だけだが, PBM の親戚には,カラー画像用の PPM と グレースケール画像(白黒濃淡画像)用の PGM もある. 興味のある人はオンラインマニュアルや WWW 等で調べてみよう.

$  man  pbm
$  man  pgm
$  man  ppm

CG の基本プログラム

PBM 画像ファイルを自動生成するプログラムを用意しておいた. まずは,次のソースファイルをダウンロードし, 使ってみよう:

ソースコードは長いが... とりあえず,main( ) 関数から読んでみれば, 何が行なわれているのか理解できるハズだ.

ただし,直線描画関数 DrawLine( ) の処理内容については, 複雑に感じるかもしれない. 今は,気にしないでおいてよい.

なお,abs( ) は整数の絶対値を計算するライブラリ関数であり, 対応するヘッダファイルは stdlib.h である.

そして,理解の確認として,基本プログラム cg.c を改造し, 自分の思い通りのグラフィックスを描いてみよう. 作業内容は,main() 内の関数呼び出し部分を書き換えるだけだ.

なお,コンピュータ画面の座標は次のように設定されている.

つまり,数学などで普段考えている座標軸とは y 軸の方向が異なっている.

また,cg.c では画像のサイズは, 横(x 方向)に 320, 縦(y 方向)に 240 と設定されている. (WIDTHHEIGHT としてマクロ定義.) したがって,位置を指定するには, たとえば,左上の角が (0, 0), 右下の角が (319, 239) となる. (320, 240) は範囲外だ!!


インタプリタの例

プログラミング言語の処理系の初歩的な例として, (スーパーやコンビニにある)レジスタの機能を実現するインタプリタを作ってみた.

インタプリタ(interpreter)とは, 入力された命令にしたがって処理を実行するプログラムのことだ. 入力を変えれば,処理の内容も変わる. たとえば,Scheme や POV-Ray やシェルもインタプリタだ. (ちなみにCは,インタプリタではなく,コンパイラだ.)

コンパイル方法や実行方法については,ソースファイル内に記述されている.


本日の課題

基本プログラム cg.c の関数 main() を改造し, 以下に説明するような仕様のグラフィックスインタプリタを作成せよ. また,そのプログラム用のオリジナルな命令ファイルを作成せよ.

基本プログラムでは,グラフィックスを変えるためには, ソースを書き換えてから再コンパイルしなければならなかった. この課題では,グラフィックスの変更の際に, 再コンパイルの必要が無いようなプログラムを作る. つまり,グラフィック命令をファイルから読み取って, それに応じた処理を実行するように.

たとえば,List 2(a) のようなソースコードを利用する代わりに, List 2(b) のような命令ファイルを読み取って, Fig.2 のような画像を出力できるようにしよう. コマンドラインについては,次のように使うことにする:

$  ./cg  sample.cg  |  display  &	# 画像の表示用のコマンド
入力ファイル sample.cg の内容を書き換えたり, 別の入力ファイルを指定するだけで, 異なる画像を出力できるようにする. 出力画像を変更するだけなら, ソース cg.c の書き換え,再度のコンパイル (そしてエラーの場合,デバッグ)といった面倒な作業は不要になる.
List 2(a). 改造前のソースコードの例
int main()
{
	...

	Clear(pbm, 0);
	DrawLine(pbm,  60, 120,  260, 120,  1);
	DrawLine(pbm, 160,  20,  160, 220,  1);
	DrawCircle(pbm, 110,  70,  40,  1);
	DrawCircle(pbm, 210, 170,  40,  1);

	...
} 							

List 2(b). 命令ファイルの例 sample.cg
clear 0
line  60 120  260 120  1
line 160  20  160 220  1
circle 110  70  40 1
circle 210 170  40 1
							

Fig.2. 出力画像の例

レポートには,変更した関数 main() のソースコードとともに, オリジナルな命令ファイルも載せること.

オリジナル」とは何なのか? 明確な答えは無さそうだが... 今年のオリンピックエンブレム,昨年のSTAP細胞論文,等の騒動を知っているよね? コピペして少し変えただけでは, 世間的にはオリジナルとは見なされない.

また,余裕のある人は,描画関数のバリエーションを増やしてみよう. たとえば,弧(arc),四角形(rectangle)などがあると便利だろう. さらに上級者は,カラー画像へ対応させてみてはどうだろう.

ヒント:

ありがちなトラブルとその対策: display で画像表示できない場合, 出力データが PBM 形式として正しくない可能性が高い. その場合には,まず,less で出力データの内容を確認すること. これで原因を特定できるかもしれない.

$  ./cg sample.cg  |  less		# 画像データの出力テスト用のコマンド
レポート提出 注意事項

(c) 2016, yanagawa@kushiro-ct.ac.jp