プログラム講座 中級編3

- オフスクリーン(仮想画面)の使い方 -

 中級編3です。今回はMacで必ずといっていい程、使用されるグラフィック、それもオフスクリーンの初歩について説明します。



◆仮想画面(オフスクリーン)とは
 仮想画面(オフスクリーン)というのは、メモリ内部に確保された見えない画面、つまり仮想的な画面です。ゲームなどには結構使われるのですが、オフスクリーンについて丁寧に書かれた本が少なかったりして苦労する人も多いのではないでしょうか? オフスクリーンの使い方が分かれば、作るプログラムの幅が一気に拡大します。とは言っても大事なのは元ネタ。でも、アイデアを実現するだけの技術も必要ですね。
 今回は仮想画面(オフスクリーン)の作り方、描画の仕方、転送の仕方、仮想画面(オフスクリーン)の廃棄についてそれぞれ説明します。



◆仮想画面の作り方
 仮想画面(オフスクリーン)を作るのは実は簡単です。まず、グローバル変数の宣言で次のようにしておきます。

DIM offScreen&

 別にoffScreen&でなくても構いません。Directorのように大量にオフスクリーンを扱うソフトであれば、配列にしておくと便利です。要するに配列(本当は違うのだが)で宣言しておけばOKという事です。
 これだけでは当然仮想画面(オフスクリーン)は、確保できません。仮想画面(オフスクリーン)を確保するには「仮想画面の大きさを指定」して「NEWGWORLD」を呼び出す事でメモリ内部に仮想画面(オフスクリーン)が作成されます。

CALL SETRECT(rect,0,0,320,240)
err% = FN NEWGWORLD(offScreen&,8,rect,0,0,0)

 rectもグローバル変数の所でDIM rect;8としてありますが、当然ローカル変数として定義しても構いません。
NEWGWORLDを実行するとoffScreen&に確保したオフスクリーンのアドレス(グラフポートの先頭アドレス)が入ります。次の8という数字は、色深度の指定です。つまり以下のようになっています。

1
白黒
2
4色
4
16色
8
256色
16
32768色
32
1670万色

 残念ながら64ビットを指定するとエラーになって戻ってきてしまいます。
rectは確保する仮想画面(オフスクリーン)の大きさです。ここでは320*240ピクセルのオフスクリーンを確保しています。
 これで仮想画面(オフスクリーン)が作成され準備が整いました。

注意:エラーで戻ってくる場合は、ほとんどの場合「メモリ不足」から来ています。使用可能なメモリサイズを増やして下さい。そうすれば解決します。



◆ウィンドウのグラフポートの取得
 仮想画面(オフスクリーン)を確保しても実際に表示する画面に転送しないと見ることはできません。そこで必要になってくるのが、ウィンドウの仮想画面(オフスクリーン)のアドレスです。まあ、実画面なので仮想画面という呼び方は変ですが、実際に表示されているかいないか程度の差違しかありません。本当はアドレスとは呼ばずにグラフポートという具合に呼びますが、まあそういうような呼び方なんだな程度で十分です。
 さて、ウィンドウのグラフポートを取得するには以下のようにします。

CALL GETPORT(cport&)

 こうするとcport&にグラフポートが取得されます。おっと、このcport&もグローバル変数のところでDIM cport&と定義しておきましょう。名前は別にcport&でなくても構いません。わかりやすい名前を付けて下さい。



◆描画するグラフポートの切替
 さて、今度は実際にオフスクリーンに描画してみましょう。四角形を書こうと思いましたが面白くないので(笑)、楕円を描画させてみましょう。表示する場所は乱数で、表示する色も乱数で指定します。乱数は以下のようにすると求めることが出来ます。

RND(発生させたい乱数の最大値)

 座標はcall setrectで行っています。
肝心の描画するグラフポートの切替は次のようにします。

CALL SETGWORLD(offScreen&,0)

 描画させたいオフスクリーンのグラフポートを指定するだけです。ウィンドウ側に描画させるのであれば、offScreen&でなくcport&にすればOKという事です。Future BASICにwindow outputという命令がありますが、やっている処理は同じです。
 あとはCALL PAINTOVALを呼び出して描画させます。PAINTRECTとか他のQuickDraw関数を呼び出して見るのもいいでしょう。
 描画し終わったら、忘れずにウィンドウ側に描画するようにグラフポートを戻しておきましょう。



◆仮想画面(オフスクリーン)からウィンドウに転送
 実際に描画させたのは仮想画面ですから、そのままでは見ることが出来ません。実際に見るためにはウィンドウに仮想画面の内容を転送する必要があります。
「なんか面倒くさそうだなあ」
と思うでしょうが、2〜3行で終わってしまいます。まず転送元と転送先の大きさを指定します。今回はどちらも同じ大きさにしてあります。
 次に転送先と転送元、そして転送方法を指定します。ここでは転送方法はベタ描き、つまり_srcCopyを指定しています。ちなみにこれが最高速です。AMIGAではここらへんはカスタムチップが行うため非常に高速に行えました。余談はともかく実際は以下の2行しかありません。

CALL SETRECT(rect,0,0,320,240)
CALL COPYBITS(#offScreen&+2,#cport&+2,rect,rect,_srcCopy,0)

 _srcCopyの後の0はマスクリージョンといって描画する場所としない場所を切り分ける(?)ためのものです。通常は0を指定しておけばいいでしょう。
 これで仮想画面の内容を無事にウィンドウに転送し終わりました。
 ウィンドウを動かしても、PRINT文などのように、消えてしまったりする事はありません。他のウィンドウと重ね合わせても大丈夫ですし、バックグラウンドでも動作します。バッチリですね(^-^)/



◆使い終わったら必ず破棄すべし
 仮想画面はメモリ内部に確保されますので、使い終わったら廃棄しておかないとメモリにゴミが残ったままになり、トラブルの原因になります。破棄するのは非常に簡単です。

CALL DISPOSEGWORLD(offScreen&)

とすれば破棄できます。忘れずに破棄してください。



◆終わりに
 やっと?Macらしいグラフィック関係のネタですが、直接オフスクリーンにデータを書き込む方法やrowByteを求める方法などなど、説明する所はたくさんあります。今回は初歩の初歩という事で説明しましたが、いかがでしたでしょうか。

 それでは、また次回。次回のネタは何にしよう(^^;



◆今回のプログラムリスト
'----------------------------------------------- ' "仮想画面(オフスクリーン)の確保、表示" '----------------------------------------------- DIM offScreen&,cport&,rect;8 END GLOBALS ' ----------------------------------------------- ' "オフスクリーンを確保する" ' offScreen& = "オフスクリーンのアドレス" ' ----------------------------------------------- CLEAR LOCAL LOCAL FN setOffscreen CALL SETRECT(rect,0,0,320,240): '"320x240の画面を作成" err% = FN NEWGWORLD(offScreen&,8,rect,0,0,0) LONG IF err% BEEP END: ' "多くの場合、メモリ不足" END IF END FN '-------------------------------------------------------- ' "仮想画面(オフスクリーン)からウィンドウへ転送" '-------------------------------------------------------- CLEAR LOCAL LOCAL FN transfer CALL SETRECT(rect,0,0,320,240) CALL COPYBITS(#offScreen&+2,#cport&+2,rect,rect,_srcCopy,0) END FN '-------------------------------------------------------- ' "楕円を描画" '-------------------------------------------------------- LOCAL FN drawChar c = RND(256) x1 = RND(320) y1 = RND(240) x2 = RND(320) y2 = RND(240) CALL SETRECT(rect,x1,y1,x2,y2) CALL SETGWORLD(offScreen&,0): ' "描画側をオフスクリーン側に" COLOR c CALL PAINTOVAL(rect) CALL SETGWORLD(cport&,0): ' "描画側を元に戻す" END FN LOCAL FN initMenu 'File Menu MENU 1,0,_enable,"ファイル" MENU 1,1,_enable,"/Q終 了" END FN '--------------------------------------------- LOCAL FN doMenus menuID = MENU(_menuID) itemID = MENU(_itemID) SELECT menuID CASE 1 : ' File Menu SELECT itemID CASE 1: ' Quit... CALL DISPOSEGWORLD(offScreen&) END END SELECT END SELECT MENU END FN WINDOW OFF WINDOW #1,"Main Screen",(16,45)-(16+320,45+240),_dialogMovable CALL GETPORT(cport&): ' "ウィンドウのグラフポートを確保しておきます" ON MENU FN doMenus FN initMenu FN setOffscreen DO HANDLEEVENTS FN drawChar FN transfer UNTIL theProgramEnds END