ページ

2008年12月30日火曜日

NSWindowリサイズ時だけ画像を荒く、終了後奇麗に

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

縮小画像を奇麗に見せる件を以前取り上げた。NSGraphicsContext#setImageInerporation: に NSImageInterpolationNone を設定すれば良い。ただ縮小画像のを奇麗にする(補完する)処理は重いため、例えば QuickLookパネルではリサイズ時のみ荒くして、終了後に奇麗な画像を表示するようにしている。

この仕組みを SimpleViewでも対応することにした。最初は NSWindowの delegateメソッドである、windowWillResize:toSize:windowDidResize: を使えば容易にできるとたかをくくっていたが、やってみるとこれらでは役不足であることがわかった。

これらのメソッドはリサイズの前後のタイミングを正確に拾う事はできるのだが、実際にユーザがリサイズする場合には右下のリサイズボックスのドラッグと静止を繰り返す為、これらのメソッドが頻繁に呼ばれてしまい今回の用途では使えない(粗い設定にした直後に、奇麗な設定に戻す為、見た目は荒くならない)。理想的なのはユーザがリサイズボックスのドラッグを開始した後、マウスを放すまでの間だけ画像を荒くしたい。その為には、(1)リサイズボックスのドラッグの開始イベントと、(2)マウスを離すイベントが必要。

NSWindow にはリサイズボックスのイベントに関するメソッドは先の delegateぐらいで他に役に立ちそうなものはない。マウスを離すイベントも、リサイズボックスに関する操作は mouseUp: で受け取れない。

結局、NSWindowやNSResponderのリファレンスを眺めるもいい方法が見つからずネットを調べてみた。すると欲しかった情報そのものずばりが見つかった。

Apple Mailing Lists
Re: nswindow resize question

これによると NSView にはライブリサイズ用のイベントを扱うメソッドが用意されているとのこと。NSViewのリファレンスを見てみる。ああ、あった。
Managing Live Resize

さっそく試してみる。
SimpleViewerImageView.m

- (void)viewWillStartLiveResize
{
_interpolation = NSImageInterpolationNone;
}

- (void)viewDidEndLiveResize
{
_interpolation = NSImageInterpolationHigh;
[self setNeedsDisplay:YES];
}


NSView#viewWillStartLiveResize はユーザがリサイズボックスのドラッグを開始した時に1回だけ呼ばれる。そしてマウスを離した時に NSView#viewDidEndLiveResize が最後に1回だけ呼ばれる。まさに欲しかったイベントがずばり得られる。これらのメソッドをオーバーライドして、画質を切り替えるようにした。viewDidEndLiveResize が呼ばれた後は、再描画は行われないのでこのメソッド内で #setNeedsDisplay:YES を呼んでおく。

設定したメンバ変数 _interpolation は #drawRect: 内で使用する。
[[NSGraphicsContext currentContext] setImageInterpolation:_interpolation];


この辺りは以前のブログ「SimpleViewer(その5)画像の拡大縮小を奇麗に(2)」を参照のこと。


さて実行してみよう。

まずはキャプチャ直後に原寸大(100%)で画像を表示している状態。


右下のリサイズボックスをドラッグして縮小する。画像が荒くなる。


リサイズを終えてマウスを放す。画像が奇麗になる。


わかってみれば実装は5分もかからなかった。ここに至るまでは時間がかかったのだが。。。