プログラム講座 中級編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