プログラム講座 中級編4

- オフスクリーンをPICT形式で保存する -

 中級編4です。今回はオフスクリーン(仮想画面)に書かれた内容(画像)をPICT形式で保存してみましょう。



◆PICT形式とは
 PICT形式(PICTフォーマット)はMacintoshの標準画像形式です。Windows ならばBMPといった画像形式があります。他にもTIFF, EPSなどがありますが、PICTはMac専用の画像形式と言ってもいいでしょう。そのため、他の機種でPICT画像を表示させようとすると、かなり苦労します。
 PICT形式を説明すると長くなってしまいますし、中級編の範囲を超えてしまいますので、概略だけを説明します。



◆PICT形式の構造
 PICT形式は先頭に512バイト、そしてその次に「オプコード」と呼ばれるデータ参照コードが続きます。オプコードが00FFHならばエンド、0001なら云々という具合に全て定義されていてインサイドマック、またはグラフィックファイルフォーマット(アスキー出版)などの本にそのコードが掲載されています。


 問題は、そのオプコードで全部インサイドマックを見ながら、頭をかかえて書き出すような事をするのか、どうか?と言った部分でしょう。Mac以外の機種ではこのようにオプコードを解析して表示させたり保存するのですが、Macの場合は「PICTハンドル」さえあれば書き出す事ができます。
 幸いにしてFuture BASICには、PICT形式で保存するサンプルプログラムが掲載されています。でも、よく見ると読み込んだPICTハンドルをそのまま書き出しているだけで、これでは画像にエフェクトをかけたりユーザーが描いた画像を保存するような事ができません。
 PICTハンドルを取得するにはFuture BASICの命令にあるPICTURE ON, PICTURE OFFを使用するか、直接ToolBox(QuickDraw)を呼び出す方法があります。はっきり言って、どっちを使っても行数も変わりませんし手間も変わりません。せっかくですから、ToolBoxを使ってみましょう。



◆前回(その3)のプログラムに保存機能を付ける
 1から作るのも面倒なので前回(その3)で使ったプログラムに保存機能を付けてみましょう。前回のプログラムは直接オフスクリーンに描画させているので、丁度よいサンプルですね。
 まずは実際のプログラムを見てみましょう。

'--------------------------------------------------------
'   "Pict画像の保存"
'--------------------------------------------------------
LOCAL FN savePict
  DEF OPEN "PICT":                                ' "ファイルタイプをPICTにする"
  CALL SETRECT(rect,0,0,320,240)
  saveFile$ = FILES$(_fSave,"保存ファイル名:","名称未設定",volRefNum%)
  LONG IF LEN(saveFile$)
    OPEN "O",#1,saveFile$,,volRefNum%
    

    CALL SETGWORLD(offScreen&,0):                 ' "描画側をオフスクリーン側に"
    savePicture& = FN OPENPICTURE(rect)
    COLOR _zWhite
    CALL COPYBITS(#offScreen&+2,#offScreen&+2,rect,rect,_srcCopy,0)
    CALL CLOSEPICTURE
    CALL SETGWORLD(cport&,0):                     ' "描画側を元に戻す"
    


    WRITE FILE #1,@header%(0),512:                ' "Header Write"
    bytes& = FN GETHANDLESIZE(savePicture&)
    err% = FN HLOCK(savePicture&)
    WRITE FILE #1,[savePicture&],bytes&
    CLOSE #1
    CALL KILLPICTURE(savePicture&)

  END IF
END FN
 色を変えてある部分が今回のポイントです。赤色がオフスクリーンからPICTハンドルを求めるところです。その中にある

savePicture& = FN OPENPICTURE(rect)

 は、これ以降呼び出されるQuickDrawによる描画をsavePicture&ハンドルに記録して、という命令です。この命令が出されるとCALL CLOSEPICTURE命令が見つかるまでメモリの内部に描画方法(これがオプコード&データ)を記録していきます。
 オフスクリーンの内容をそのままオフスクリーンに転送しています。こうするだけで自動的にオフスクリーンの内容が記録されます。やり方がわかれば簡単ですね。



◆PICT形式の書き出し
 PICTハンドルが用意できれば後はFuture BASICのマニュアルにあるサンプルどおりにやるだけです。(ハンドブックの309ページに掲載されています)
 まず、512バイトの空ヘッダーを書き出します。@header%(0)は配列header%の0番目(一番最初)のアドレス(@:アットです←という覚えやすい具合になってます^^;)を示します。そこから512バイトファイルに書き出します。
 あとはbytes& = FN GETHANDLESIZE(savePicture&)で記録されたPICTのハンドルサイズを求めてファイルに書き出すだけです。


◆終わりに
 やり方がわからなくてNiftyServeのホームパーティ上で質問し、快く回答して下さった後藤寿庵さんに感謝します。
 PICT画像を解析して表示するプログラムならMZ-2861で作成したので原理は分かっていたのですが、こういう手軽なやり方があるとは知りませんでした。
 PICT形式で保存できるようになりましたので、次回はPICT画像を読み込んで「白い部分だけ透過させて合成していく」プログラムを作成してみましょう。



◆今回のプログラムリスト
'----------------------------------------------- ' "仮想画面(オフスクリーン)の確保、表示" '----------------------------------------------- DIM offScreen&,cport&,rect;8 DIM header%(256) 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 '-------------------------------- ' Copy Offscreen -> Window '-------------------------------- CLEAR LOCAL LOCAL FN transfer CALL SETRECT(rect,0,0,320,240) CALL COPYBITS(#offScreen&+2,#cport&+2,rect,rect,_srcCopy,0) END FN '-------------------------------------------------------- ' Display Character '-------------------------------------------------------- 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 '-------------------------------------------------------- ' "Pict画像の保存" '-------------------------------------------------------- LOCAL FN savePict DEF OPEN "PICT": ' "ファイルタイプをPICTにする" CALL SETRECT(rect,0,0,320,240) saveFile$ = FILES$(_fSave,"保存ファイル名:","名称未設定",volRefNum%) LONG IF LEN(saveFile$) OPEN "O",#1,saveFile$,,volRefNum% CALL SETGWORLD(offScreen&,0): ' "描画側をオフスクリーン側に" savePicture& = FN OPENPICTURE(rect) CALL COPYBITS(#offScreen&+2,#offScreen&+2,rect,rect,_srcCopy,0) CALL CLOSEPICTURE CALL SETGWORLD(cport&,0): ' "描画側を元に戻す" WRITE FILE #1,@header%(0),512: ' "Header Write" bytes& = FN GETHANDLESIZE(savePicture&) err% = FN HLOCK(savePicture&) WRITE FILE #1,[savePicture&],bytes& CLOSE #1 CALL KILLPICTURE(savePicture&) END IF END FN '-------------------------------------------------------- ' "メニューの構築" '-------------------------------------------------------- LOCAL FN initMenu 'File Menu MENU 1,0,_enable,"ファイル" MENU 1,1,_enable,"/S保 存" MENU 1,2,_enable,";" MENU 1,3,_enable,"/Q終 了" END FN '--------------------------------------------- LOCAL FN doMenus menuID = MENU(_menuID) itemID = MENU(_itemID) SELECT menuID CASE 1 : ' File Menu SELECT itemID CASE 1: FN savePict CASE 3: ' 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