● 小池邦人のMac OS Xへの道 2002/05/19

〜 画像のセレクションを実現する その1 〜

マウスドラッグによるセレクションにOverlayウィンドウを利用する例として、今回は「PICT画像の矩形セレクション」にチャレンジしてみます。選択した画像領域は、編集メニューの「Copy」でクリップボードに保存できるようにします。歴代ペイントソフトが実装している「ウニウニ波線」(筆者命名)のMac OS X版と言うことになります。

ペイントアプリケーションで使われてきた「画像領域のセレクション」は、Macintoshらしいユーザインターフェースの代名詞でした。今回は、「UniUni_Demo1」というアプリケーションを使い、ペイントアプリケーションのそれをシミュレーションしてみます。自作アプリケーションへ、Mac OS Xライクな「画像セレクション」をどのように実装すれば良いのかは、Apple社が提供するガイドライン(お手本)がないために、いまいち判断に迷います。iPhotoを見てみると、選択から外れた領域を半透明なフィルーターで覆い隠すような処理がなされています。

 

ただし、選択終了後に、矩形領域がアニメーション表示(ウニウニ波線)されることはありません。まあ、iPhotoが行うセレクションの用途はトリミングですので、ペイントアプリケーションの画像セレクションとは少し意味合いが違うのかもしれません。

昔々、Macintosh用アプリケーションを開発する場合には、「Finder」「MacPaint」「MacWrite」といった優秀なお手本が存在していたので本当に楽ができました(笑)。そういう意味では、Mac OS X Finderぐらいは、開発者のお手本となるようにきっちり作り込んで欲しいものです。それでは、Interface Builderによりメインウィンドウを含んだNibファイルを作成します。ウィンドウに「PICT表示用コントロール」をひとつ配置し、そこにリソースファイルから読み込んだPICT画像を貼り付けておきます。

 

Interface BuilderのInfoウィンドウで、PICT表示用コントロールのSignaitureに'cntl'を、ID番号400に設定しておきます。

 

ウィンドウの下に配置される3つのボタンコントロールのコマンドIDについては「Selection_Demo」の時と同じです。

「全選択」ボタンには'sall'が、「選択解除」ボタンには'selc'が、「終了」ボタンには'quit'が割り付けられています。「全選択」ボタンの'sall'は、編集メニューの「Select All」と同じコマンドIDです。「全選択」ボタンをクリックすると、すべてのPICT画像領域がセレクションされます。逆に「選択解除」ボタンをクリックするとセレクション領域が削除されます。今回は、これらのコマンドIDに加えて、編集メニューにある「Copy」に割り振られたコマンドIDの'copy'にも対応する必要があります。もしPICT画像上にセレクション領域があれば、編集メニューの「Copy」で、その部分をPICTデータとしてクリップボードに保存できるようにします。

 

それでは、サンプルアプリケーションのソースコードを見てみます。まずは、main()ルーチンからです。



このルーチンは「Selection_Demo」の時とほとんど同じで、メニューバー(MenuBar)とメインウィンドウ(MainWindow)をNibファイル(main.nib)から読み込み、setUpWindowEvent()でメインウィンドウにCarbon Event Handlerルーチンをインストールしています。ただし、以前と異なる個所が一つだけあります。それは、setUpTimer()ルーチンにより「Carbon EventTimerルーチン」をインストールしている個所です。また、「セレクション領域」を保存しておくために、外部変数としてQuartz 2D(Core Graphics)で使う矩形構造体(CGRect)のsel_Rectを確保している個所にも注目してください。ここでは、SetWindowGroup()によりウィンドウを特定グループに属するように調整していますが、この処理はアバウト用のSheetウィンドウのためで、今回の選択機能には関係ありません。

以下が、メインウィンドウにCarbon Event HandlerルーチンをインストールしているsetUpWindowEvent()と、Current Event LoopにCarbon EventTimerルーチンをインストールしているsetUpTimer()です。



最初のsetUpWindowEvent()については「Selection_Demo」とまったく同じ内容ですので、今回は説明を省略します。次のsetUpTimer()では、まず外部変数に確保したsel_Rectを初期化(Empty Rect化)し、その後、InstallEventLoopTimer() APIによりEventTimerルーチのエントリーポイントを登録しています。EventTimerルーチとは、システム(Carbon Event Manager)により一定間隔(時間)で呼ばれて実行されるルーチンのことです。

Carbon Eventモデルでは、WaitNextEvent()を利用した自前のイベントループを用意しません。そのため、アプリケーションで定期的に呼ばなければいけなルーチンを、イベントループ内に置いて実行することが不可能となります。これだと、アプリケーション開発時に色々と不都合が出ます。そのため、Carbon Event Managerには、それに取って代わる仕組みとして「Carbon EventTimer」が用意されているわけです。一度インストールされたEventTimerルーチンは、システムにより管理され、どんな状態であっても定期的に実行さます。例えば、マウス位置の座標値を逐次ウィンドウに表示したい場合とか、ウィンドウの状態を逐次監視し、必要であればウィンドウ上オブジェクトのメンテナンスを実行したい場合などを想定すると、理解しやすいかもしれません。

今回は、この仕組みを活用してセレクション領域の「ウニウニ波線」アニメーションの表示を実装します。InstallEventLoopTimer()の3つ目の引き数に、EventTimerルーチンが呼び出される間隔を「秒」で指定します。今回は0.1秒を指定しています。この間隔をあまり短くすると、余分なCPUパワーを使うことになり、同時に起動しているプロセスに迷惑をかけることになりますので注意してください。APIの4つ目の引き数に代入されている「myTimerEvent 」が、システムが定期的に呼び出すルーチンの名称です。今回用意するmyTimerEvent()ルーチンの内容に関しては、次回に詳しく解説します。また、InstallEventLoopTimer()の引き数や、EventTimerに関連するAPIの詳細については、Universal InterfacesのCarbonEvent.hに記載されていますので、そちらを参照してください。



以前紹介した、「Selection_Demo」サンプルアプリケーションでは、セレクション領域を描画するために、Quartz 2D APIとOverlayウィンドウを使用していました。Overlayウィンドウとは、Mac OS X 10.1から採用された下地が透明なウィンドウのことです。下地が透明ということは、Quartz 2Dで図形を描画すると、その内容だけが画面上に浮き上がります。今回実現する画像セレクションにも、Overlayウィンドウのこの特徴を利用します。ユーザが、PICT表示コントロール上でマウスクリックを行うと、おおよそ以下の手順で処理が進みます。

(1)PICT表示コントロールと同位置に同サイズのOverlayウィンドウを作る
(2)Carbon Event Handlerルーチンでマウスクリック座標値を得る
(3)マウスクリック座標値を画像セレクションの担当ルーチンに渡す
(4)TrackMouseLocation()で継続してマウスドラッグ座標値を得る
(5)Quartz 2Dで波線の矩形フレームをOverlayウィンドウへ描画する
(6)マウスドラッグが終了したら確定した矩形情報をsel_Rectに保存する
(7)必要なくなったOverlayウィンドウを削除する

この過程が終了し、sel_Rectに矩形がセット(Empty Rectでない)されている場合にかぎり、EventTimerルーチンが0.1秒置きに「ウニウニ波線」を描画することになります。この時の波線の描画にもQuartz 2D APIを利用しますが、Overlayウィンドウの方は利用しません。「ウニウニ波線」アニメーションでもOverlayウィンドウを利用する方法が考えられますが、こちらについては別のサンプルアプリケーションを使い解説する予定にしています。「UniUni_Demo1」アプリケーションでOverlayウィンドウをオープンしているのが、openOverlayWindow()ルーチンです。



ウィンドウを作成するのにはCreatWindow()を使います。代入するウィンドウクラス(WindowClass)にはkOverlayWindowClassを選択してください。ウィンドウサイズと表示位置をPICT表示コントロールと同等にするために、コントロールの矩形情報を引き数として渡します。そのローカル座標値をLocalToGlobal()でグローバル座標値に変換してからCreatWindow()に渡すわけです。

ここで解説している「UniUni_Demo1」サンプルアプリケーションは、以下のサイトに登録されていますので確認してみてください。動作させるのには、Mac OS X 10.1と最新版のDeveloper Toolsが必要です。

http://www.ottimo.co.jp/library/

次回は、今回の話の続きとなります。「UniUni_Demo1」サンプルアプリケーションのCarbon Event HandlerルーチンやCarbonTimerルーチン、画像領域のセレクションを担当しているルーチンなどについて解説したいと思います。


copyright 2002 Ottimo, Inc. All rights reserved
無断転載・引用禁止
Contact Us: koike@ottimo.co.jp