プログラム講座 中級編5
- PICT画像の白抜きを行う -
 中級編5です。今回は読み込まれたPICT形式(画像)で白の部分を透過させる、つまり背景と合成させるプログラムを作成します。PICT形式の読み込みは初級編2のものを、PICT画像の保存およびオフスクリーンの確保については、中級編3、4を参考にして下さい。ここで使用しているプログラムは初級編2と中級編4で使用したものに手を加えました。このように、後で何回も使い回しができるようにしておくと便利ですしバグも少なく開発効率も良くなります。
◆COPYBITSの転送形式
 ToolBoxの関数COPYBITSにはいくつかの転送モードがあります。以下に転送モードを示します。
- _srcCopy (そのまま転送:最高速)
 - _srcOR (論理和)
 - _srcXOR (排他的論理和:2回転送すると元の状態に戻る)
 - _srcBIC
 - _notSrcCopy (反転してコピー)
 - _notSrcOR
 - _notSrcXOR
 - _notSrcBic
 - _blend (半透明合成)
 - _addPin
 - _addOver
 - _subPin
 - _subOver
 - _adMax
 - _adMin
 - _ditherCopy (ディザ表示)
 - _transparent (背景色透過)
 
都合の良いことに_transparentを指定すると背景色(ここでは白、特に設定しなければ白になります)のピクセルだけ転送しなくなります。つまり背景色以外転送されるので、簡単に白抜きの合成ができるという事です。
 さて、合成するためにはもう一枚余計にオフスクリーンが必要です。今回はoffScreen2&という変数を用意しここに読み込んだPICT画像を表示します。次にoffScreen&の方にCOPYBITSで転送します。これだけで処理はおしまいです。簡単ですね。
◆アップデートイベント
 今回は中級編3、4と違い定期的に画像を書き換えません。ウィンドウを他のウィンドウの下になるように重ね合わせた後に、一番手前に持ってくると、せっかく表示してあった画像が消えてしまっています。
 このようになってしまうとシステムは「ウィンドウリフレッシュ(再描画:通常アップデートイベント)」を要求してきます。このイベントが来たらウィンドウの内容を書き直してやらなければなりません。
 この処理を行っているのが関数doDialogです。アップデートイベントが来たらオフスクリーンからウィンドウに描画してやります。これだけですので、特に複雑なことはありません。ただし、ウィンドウが複数枚ある場合やエディットフィールドがある場合は、処理が複雑になります。ウィンドウを複数枚使用する場合は、ちゃんとした設計をしておいた方がいいでしょう。というかしておくべきです。
◆終わりに
 初級編からこの中級編までで最低限の事は学んだと思います。C言語とかでプログラムすれば面倒なものもFuture BASICではかなり簡単にできるというのが、わかってもらえれば幸いです。本当は本にしてFuture BASIC入門とか書けばいいんですが、それにはまだまだ時間が必要ですね。
 あと、今までの所で「ここがわからない」という所がありましたら、すみませんがメールをいただけると幸いです。
 次回は直接オフスクリーンを操作してみましょう。
◆今回のプログラムリスト
'-----------------------------------------------
' "仮想画面(オフスクリーン)の確保、表示"
'-----------------------------------------------
DIM offScreen&,offScreen2&,cport&,rect;8
DIM header%(256)
END GLOBALS
'-------------------------------------------------------------
' "PICTファイルをオープンしてオフスクリーンに描画する"
' "描画する時に白抜きをして以前の画像と合成する"
'-------------------------------------------------------------
LOCAL FN openPictFile
  DIM rectPICT;8
  
  f$ = FILES$(_fOpen,"PICT",,vRefNum%)
  LONG IF f$<>""
    OPEN "I",#1, f$,,vRefNum%
    fileSize& = LOF(1,1)
    pictHandle& = FN NEWHANDLE(fileSize&+4)
    LONG IF pictHandle&
      err = FN HLOCK(pictHandle&)
      LONG IF err = 0
        READ FILE#1, [pictHandle&], fileSize&
        BLOCKMOVE [pictHandle&]+512,[pictHandle&],fileSize& - 512
        err = FN HUNLOCK(pictHandle&)
        err = FN SETHANDLESIZE(pictHandle&, fileSize&-512)
        err = FN HLOCK(pictHandle&)
        rectPICT;8 = [pictHandle&]+_picFrame
        '----------------------------------------------------
        err% = FN NEWGWORLD(offScreen2&,8,rectPICT,0,0,0)
        LONG IF err%
          BEEP
        XELSE
          CALL SETGWORLD(offScreen2&,0):          '"オフスクリーン2に切り替える"
          CALL DRAWPICTURE(pictHandle&,rectPICT)
          CALL COPYBITS(#offScreen2&+2,#offScreen&+2,rectPICT,rectPICT,_transparent,0)
          CALL SETGWORLD(cport&,0):               '"ウィンドウに切り替える"
          CALL DISPOSEGWORLD(offScreen2&):        '"使用済みなので廃棄する"
        END IF
        '----------------------------------------------------
        err = FN HUNLOCK(pictHandle&)
      END IF
      err = FN DISPOSHANDLE(pictHandle&)
    XELSE 
      BEEP
    END IF
    CLOSE #1
  END IF
END FN
' -----------------------------------------------
'  "オフスクリーンを確保する"
' 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
'--------------------------------------------------------
'   "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,"/Opictファイルを開く..."
  MENU 1,2,_enable,"/S保 存"
  MENU 1,3,_enable,";"
  MENU 1,4,_enable,"/Q終 了"
  
END FN
'---------------------------------------------
LOCAL FN doMenus
  menuID = MENU(_menuID)
  itemID = MENU(_itemID)
  
  SELECT menuID
    CASE 1 :                                      ' File Menu
      SELECT itemID
        CASE 1:
          FN openPictFile
          FN transfer
        CASE 2:
          FN savePict
        CASE 4:                                   ' Quit...
          CALL DISPOSEGWORLD(offScreen&)
          END
      END SELECT
  END SELECT
  MENU
END FN
'--------------------------------------------------------
' "アップデートなどのイベントを取得する"
'--------------------------------------------------------
LOCAL FN doDialog
  evnt = DIALOG(0)
  id = DIALOG(evnt)
  SELECT evnt
    CASE _wndRefresh
      FN transfer:                                '"アップデートイベントなので画面を再描画がする"
  END SELECT
END FN
WINDOW OFF
WINDOW #1,"Main Screen",(16,45)-(16+320,45+240),_dialogMovable
CALL GETPORT(cport&):                             ' "ウィンドウのグラフポートを確保しておきます"
ON MENU FN doMenus
ON DIALOG FN doDialog
FN initMenu
FN setOffscreen
DO
  HANDLEEVENTS
UNTIL theProgramEnds
END