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

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

今回は、前回の話の続きです。「UniUni_Demo1」サンプルアプリケーションのCarbon Event HandlerルーチンやCarbon EventTimerルーチン、画像領域のセレクションを担当しているルーチンについての話です。加えて、Edit(編集)メニューから「Copy」が選択された時に実行されるルーチンも紹介します。

前回の続きとして「UniUni_Demo1」サンプルのソースコードを見ていきます。最初は、メインウィンドウのCarbon Event Handlerルーチンである、myWindowEventHandler()からです。



このHandlerルーチンは、「kEventCommandProcess」、「kEventWindowClickContentRgn」、「kEventWindowClose」の3種類のCarbon Eventに対応しています。受け取ったイベントの種類が「kEventCommandProcess」の場合には、'abou'、'sall'、'selc'、'copy'、'ok 'の5種類のコマンドIDを処理します。前回の「Selection_Demo」からは'copy'が追加されていることになります。これは、Editメニューの「Copy」に対応するコマンドIDです。このうち、'abou'はアバウト用のSheetウィンドウを表示する時に、'ok 'はそれを閉じる時に利用します。この両者の機能は、以前に紹介した「Group_Demo1」サンプルと同じですので、解説は省略します。

続いて、Current(現在の)Carbon Event Loopに登録されるEventTimerルーチンのmyTimerEvent()です。



前回紹介したように、このルーチンはアプリケーションが起動している間システム(Carbon Event Manager)により管理され、0.1秒毎に実行されます。ルーチンの内容は非常に簡単です。FrontWindow()でメインウィンドウのWindowRefを得て、CGRectIsEmpty()でsel_Rectが空(Empty Rect)でない時にかぎりdrawSelection()ルーチンを呼びます。drawSelection()ルーチンが定期的に「ウニウニ波線」を表示している本体なのですが、これについては後から詳しく解説します。また、最初から特定ウィンドウを対象にして処理を実行するのならば、引き数のuserDataからWindowRefを得られるように、インストール時に調整しておくのも一案です。

EventTimeを利用する時に忘れてはいけないことは、メニューを表示している間でも、ウィンドウをドラッグ移動している間でも、EventTimerルーチンは呼ばれるという事実です。この事を考慮に入れていなと、処理内容によっては予想外の結果が生じることがあります。例えば、EventTimerルーチン内でカーソル表示のメンテナンスをしていると、ウィンドウ内部がメニュー表示と重なった場合などは、メニュー上でも矢印意外のカーソルが表示される現象が起こります。ちなみに、この挙動はMac OS X特有のものでして、Mac OS 9では起きません。つまり、Mac OS 9環境では、メニューを表示している間やウィンドウをドラッグしている間は、EventTimerルーチンは呼ばれないのです。

続いて、Event Handlerルーチンの処理分岐を追跡します。'sall'と'selc'のコマンドIDは、それぞれ、メインウィンドウの「全選択」と「選択解除」ボタンがクリックされた時に対応しています。この「全選択」と「選択解除」の処理を担当しているのがhandleSelectAll()ルーチンです。



引数のflagに1かゼロを代入することで、メインウィンドウ上の「矩形セレクション領域」を切り換えます。flagが1の場合は、sel_RectをPICT画像(この場合はPICT表示コントロール)と同サイズに設定します。CGRectInset()で矩形を左右上下1ピクセル小さくしているのは、Quartz 2Dによる矩形表示をウィンドウ枠内にフィットさせるための補正です。この補正は、PICTデータを切り取る(Copyする)時には再補正され元に戻されます。flagが0の場合はsel_Rectを空にします。最後に呼ばれているDrawOneControl()は、直前描画されていた「ウニウニ波線」を消去するために実行されています。

'copy'コマンドIDは、Editメニューの「Copy」が選択された時に送られてきます。この時の処理を担当しているのが、handleCopy()ルーチンです。



まずは、CGRectIsEmpty()でsel_Rectの中身調べ、それが空であれば何もせずに終了します。CGRectGetMinX()やCGRectGetMinY()を使い、Quartz 2Dの矩形構造体(CGRect)をQuickDrawの矩形構造体(Rect)に変換します。続いてInsetRect()で、補正されていた矩形を左右上下1ピクセル大きくして元のサイズに戻します。DrawOneControl()で直前描画されていた「ウニウニ波線」を消し、OpenPicture()とCopyBits()を利用して、セレクション領域内画像をPICTデータに変換します。それをクリップボードに移動させるのには、PutScrapFlavor() APIを用います。最後に、必要なくなったPICTデータを削除しておくことを忘れないでください。

次はセレクションの実態を見てみます。メインウィンドウでマウスクリックが行われると、myWindowEventHandler()にイベントクラスが「kEventClassWindow」でイベントの種類が「 kEventWindowClickContentRgn」のCarbon Eventが送られてきます。最初に、ConvertEventRefToEventRecord()を使い、EventRef(event)からEventRecord構造体(everd)を作ります。その構造体のメンバーであるマウスクリック座標値(everd.where)をhandlSelection()ルーチンに渡し、矩形セレクションの処理を開始します。



最初の仕事は、GlobalToLocal()を使い、引数として受け取った座標値をグローバル座標からウィンドウのローカル座標に変換することです。続いて、Y座標値がPICT表示コントロールより大きい場合には、eventNotHandledErrパラメータを返してCasrbon Eventの処理をシステムに継続させます。これは、ウィンドウの下に配置されている3つのボタンのクリック処理をシステムに任せるためです。もし、この比較処理を削除してしまうと、どのボタンはクリックできなくなりますので注意してください。

マウスクリックが起こると、openOverlayWindow()が呼ばれ、PICT画像とまったく同じ位置にOverlayウィンドウがオープンします。続いて、Quartz 2Dによる図形描画のために、CreateCGContextForPort()でOverlayウィンドウのGrafPtrからCGContextRef(ctx)を得ておきます。今回は、iPhotoのようにセレクション矩形の外側を半透明のグレーフィルタで覆い隠してみます。そのため、CGContextSetRGBFillColor()でその色と透明度の設定をしておきます。APIに渡す3つの引き数でRGBカラーを、もう一つの引き数で透明度(αチャンネル)を決定します。セレクション用の波線矩形の色と透明度については、CGContextSetRGBStrokeColor()の方で決定します。波線の形状(パタン)の設定はCGContextSetLineDash()で行います。どんな波線を描くかは、配列のdash[]に定義された数値が参照されます。今回は、実線部が10ピクセル、空き部が4ピクセルという波線を描画することにします。

 

CGContextTranslateCTM()以下の処理は、前回の「Selection_Demo」とよく似ていますので、そちらも参照してください。ただし、リアルタイムでコントロールの表示状態を切り替えるような処理は行われていません。CGContextClearRect()でPICT表示領域全体を消去してから、矩形が空でない場合にかぎり、CGContextFillRect()で画像全体にグレーのフィルタをペイントします。引き続き、セレクションされている矩形部分のみをCGContextClearRect()で消去してから、CGContextStrokeRect()で波線矩形を描画します。最後にCGContextFlush()を呼べば、4つの描画結果がチラつきなく画面に表示されます。マウスドラッグが終了したら、CGRectIntersection()で、セレクション領域とPICT画像全体の重なり部分のみを抽出します。これは、セレクション領域が画像からはみ出してしまうのを避けるための処理です。最後にCGRectInset()により矩形の左右上下を1ピクセル小さくしているのは「Select All」の時と同じ理由によります。

最後にEventTimerルーチンから呼び出され、実際に「ウニウニ波線」を描画しているdrawSelection()ルーチンを紹介します。



まずは、Quartz 2Dによる描画のために、CreateCGContextForPort()でメインウィンドウ(Overlayウィンドウではない)のGrafPtrからCGContextRefを得ます。最初に、DrawOneControl( )で直前に描画された「ウニウニ波線」を消去していますが、こちらはQuickDrawの仕事です。続いて、CGContextScaleCTM()でhandlSelection()と同様の座標変換を行います。アニメーションを見やすくするために、最初に白色で矩形を描画し、その上に黒色の波線矩形を重ねて描画します。この時、CGContextSetLineDash()に渡すphaseの値を描画毎に少しづつズラしてやります。このパラメータは、波線パタンの書き出し位置を決定しており、これをズラすことで、波線が動いているような効果を得ることが出来るわけです。これが「ウニウニ波線」の正体と言うわけですね。

 

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

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

次回は、「ウニウニ波線」の表示でもOverlayウィンドウを利用する「UniUni_Demo2」サンプルアプリケーションを作成してみます。今回採用した方法と比較して、どんなメリットとデメリットがあるのかを詳しく調べてみることにします。


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