ページ

2008年3月2日日曜日

画面キャプチャその3 - 選択時のマスク処理

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

前回のバージョンでは重なりの下にある Windowを選択した場合、それを考慮せずハイライト表示になっていた。




今回は Windowの重なりを考慮したハイライト処理を施すようにする。こんな感じ。



ソース:FullScreenSample-3


重なりを考慮してハイライト処理を行うには、選択された Windowで見えている部分だけハイライト処理する必要がある。

例えば3つのウィンドウがあったとする。



一番下にあるウィンドウが選択された場合は図の青い部分だけハイライトさせる。




これを実現する為に NSBezeirPathを使いマスク処理を施した。
手順は次のとおり。

(1) 選択ウィンドウの上に重なっているすべてのウィンドウの領域を合成した NSBezeirPathを作る。これがマスクとなる。


ウィンドウの合成には NSBezierPath#appendBezierPathWithRect を使う。前回紹介した CGWindowListCopyWindowInfo() から選択ウィンドウの上にあるウィンドウすべての矩形(NSRect)を取り出し NSBezierPathへ追加する。

_clip_path = [[NSBezierPath bezierPath] retain];
for (j=0; j < i; j++) {
window = CFArrayGetValueAtIndex(window_list, j);
window_name = CFDictionaryGetValue(window, kCGWindowName);
if (window_name) {
CGRectMakeWithDictionaryRepresentation(CFDictionaryGetValue(window, kCGWindowBounds), &rect);
NSRect rect2 = NSMakeRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
[_clip_path appendBezierPathWithRect:rect2];
}
}




(2) ビューの描画を行う時に、まずベースとなる半透明の灰色を描画する。これで画面全体が暗くなる。


- (void)drawRect:(NSRect)rect {
[[NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.75] set];
NSRectFill(rect);



(3) 次に選択ウィンドウの領域だけ透明色で塗りつぶす。この時点では余計なところまでハイライトされている。


 [[NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.0] set];
NSRectFill(_spot_rect);



(4) 最後に (1)で用意したNSBezierPathで灰色で描画する。ただしそのまま描画すると (2)で描画したベースの灰色と重なってしまう。



そこで NSBezierPath#setClip を使い、描画を選択ウィンドウの領域だけに限定(クリッピング)する。


 [[NSBezierPath bezierPathWithRect:_spot_rect] setClip];
[[NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.75] set];
[_clip_path fill];


これで、できあがり。




- - - -
「NSBezierPathを使ったマスク処理」といえば聞こえが良いが実際は少々泥臭い方式となってしまった。もっとスマートな方法があるのかもしれない。が、ともかく選択ができるようになったので次はキャプチャ画像の保存処理の実装へ入る。