CG(Computer Graphics)用プログラミング言語の 初歩的な処理系について, プログラミングを開始します.
CG 画像を取り扱うための最も単純なファイル形式 PBM(Portable BitMap) について理解しよう. まずは,PBM ファイルの具体例として, List 1(a) のような内容のファイル c.pbm を作ってみよう.
P1 10 11 0000000000 0001111000 0011111100 0111001110 0110000110 0110000000 0110000110 0111001110 0011111100 0001111000 0000000000
この PBM ファイルの構成は次の通り:
では次に,このデータを画像として表示してみよう. 今回は画像表示用プログラムとして,display を利用する:
$ display c.pbm &
さて,表示結果は Fig.1 である. ただし,サイズが非常に小さいので,拡大して観察しよう. この display のウィンドウ内で左クリックするとメニューが現れる:
さあ,PBM 形式で他の画像を作るのも簡単そうだ. オリジナルな画像の PBM ファイルを作成してみよう.
ただし,PBM ファイルを作成する場合には, 次の制約があることに注意しよう:
画像の横幅はヘッダ部で指定しているので, データ部ではどこで改行しても構わない. (もちろん,71 文字未満なら,まったく改行しなくてもよい.) たとえば,List 1(a) の改行位置を List 1(b) のように変えてもまったく問題なく, 画像としての表示結果はまったく変わらない. 試してみよう.
P1 10 11 0000000000000111100000111111000111001110 0110000110011000000001100001100111001110 001111110000011110000000000000
なお,PBM で表現できるのは白黒二値画像だけだが, PBM の親戚には,カラー画像用の PPM と グレースケール画像(白黒濃淡画像)用の PGM もある. 興味のある人はオンラインマニュアルや WWW 等で調べてみよう.
$ man pbm $ man pgm $ man ppm
PBM 画像ファイルを自動生成するプログラムを用意しておいた. まずは,次のソースファイルをダウンロードし, 使ってみよう:
$ cc cg.c -o cg -lm -Wall
$ ./cg | display & # 画像を表示 $ ./cg | less # 画像データを表示
ソースコードは長いが... とりあえず,main( ) 関数から読んでみれば, 何が行なわれているのか理解できるハズだ.
ただし,直線描画関数 DrawLine( ) の処理内容については, 複雑に感じるかもしれない. 今は,気にしないでおいてよい.
なお,abs( ) は整数の絶対値を計算するライブラリ関数であり, 対応するヘッダファイルは stdlib.h である.
そして,理解の確認として,基本プログラム cg.c を改造し, 自分の思い通りのグラフィックスを描いてみよう. 作業内容は,main() 内の関数呼び出し部分を書き換えるだけだ.
なお,コンピュータ画面の座標は次のように設定されている.
つまり,数学などで普段考えている座標軸とは y 軸の方向が異なっている.
また,cg.c では画像のサイズは, 横(x 方向)に 320, 縦(y 方向)に 240 と設定されている. (WIDTH,HEIGHT としてマクロ定義.) したがって,位置を指定するには, たとえば,左上の角が (0, 0), 右下の角が (319, 239) となる. (320, 240) は範囲外だ!!
プログラミング言語の処理系の初歩的な例として, (スーパーやコンビニにある)レジスタの機能を実現するインタプリタを作ってみた.
コンパイル方法や実行方法については,ソースファイル内に記述されている.
基本プログラム cg.c の関数 main() を改造し, 以下に説明するような仕様のグラフィックスインタプリタを作成せよ. また,そのプログラム用のオリジナルな命令ファイルを作成せよ.
基本プログラムでは,グラフィックスを変えるためには, ソースを書き換えてから再コンパイルしなければならなかった. この課題では,グラフィックスの変更の際に, 再コンパイルの必要が無いようなプログラムを作る. つまり,グラフィック命令をファイルから読み取って, それに応じた処理を実行するように.
たとえば,List 2(a) のようなソースコードを利用する代わりに, List 2(b) のような命令ファイルを読み取って, Fig.2 のような画像を出力できるようにしよう. コマンドラインについては,次のように使うことにする:
$ ./cg sample.cg | display & # 画像の表示用のコマンド
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); ... }
clear 0 line 60 120 260 120 1 line 160 20 160 220 1 circle 110 70 40 1 circle 210 170 40 1
レポートには,変更した関数 main() のソースコードとともに, オリジナルな命令ファイルも載せること.
また,余裕のある人は,描画関数のバリエーションを増やしてみよう. たとえば,弧(arc),四角形(rectangle)などがあると便利だろう. さらに上級者は,カラー画像へ対応させてみてはどうだろう.
ヒント:
char *cmd; ... while (fgets(...) ... ) { ... cmd = strtok(...); ... if (strcmp(cmd, "circle") == 0) { ... atoi(strtok(...)); .... DrawCircle( ... ); } else if (strcmp(cmd, "line") == 0) { .... DrawLine( ... ); } else if (strcmp(cmd, "point") == 0) { .... DrawPoint( ... ); } else if (strcmp(cmd, "clear") == 0) { .... Clear( ... ); } }
この基本形のコードは,羅列的なので良いコードではない. 同じようなコードを多数回(コマンドの個数分だけ) 書き並べるという労力が浪費され, バグが混入し易くなっている.
高度な技を使えば,コンパクト化も可能だが, この授業では割愛する. (上級者向情報: 各コマンドの文字列と関数との対応表を構造体配列で作り, 反復処理でコマンドを検索し, 関数ポインタで関数を間接的に呼び出す.)
ありがちなトラブルとその対策: display で画像表示できない場合, 出力データが PBM 形式として正しくない可能性が高い. その場合には,まず,less で出力データの内容を確認すること. これで原因を特定できるかもしれない.
$ ./cg sample.cg | less # 画像データの出力テスト用のコマンド