ページ

2009年3月14日土曜日

範囲選択の範囲を微調整

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

前から気になっていたのだが範囲選択で1ドット単位の細かい範囲指定をやろうとすると少しくせがあって使いにくかった。

例えば次の1ドットの黒枠の画像を、黒枠を含めてピッタリとキャプチャしたいとする。


SimpleCapの範囲選択を起動しラバーバンドの線を黒枠上にぴったり重ねてキャプチャする。


すると左と下の黒枠はキャプチャに含まれるのだが、右と上の黒枠は含まれない。


右と上も含めるにはラバーバンドを右上に1ドットづつ大きめに指定する必要があった。


これでは感覚的ではないのでキャプチャ対象をラーババンドの線上を含む範囲とするように微調整した。
描画する時に右と上の線が1ドット程度内側に表示すれば良いので、描画枠の位置と範囲を調整してみた。

 NSRect d_rect = _rect;
d_rect.origin.y += 0.1;
d_rect.size.height -= 0.2;
d_rect.origin.x += 0.1;
d_rect.size.width -= 0.2;
[path appendBezierPathWithRect:d_rect];



さて試してみる。黒枠上にラバーバンドを重ねてパチリ。


うまくいった。

2009年3月13日金曜日

Screen 改良

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

Screenクラスを改良する。具体的にはシングルトンパターンでインスタンスを作り、それを使い回すようにした。

全画面の範囲は今まではクラスメソッドで取り出していた。

+ (NSRect)frame;

呼出し毎に範囲を計算していた。

これをやめてインスタンス化し、1度計算した値を使い回すようにした。

@interface Screen : NSObject {
NSRect _frame;
}
+ (Screen*)defaultScreen;
- (NSRect)frame;


実装はこんな感じ。
static Screen* _screen = nil;
+ (Screen*)defaultScreen
{
if (!_screen) {
_screen = [[Screen alloc] init];

[[NSNotificationCenter defaultCenter] addObserver:_screen
selector:@selector(screenChanged:)
name:NSApplicationDidChangeScreenParametersNotification
object:nil];
[_screen update];
}
return _screen;
}


スクリーンの構成が変わった時を考慮して NSApplicationDidChangeScreenParametersNotification を受け取るようにしている。この通知を受け取ったら範囲を更新する。

- (void)screenChanged:(NSNotification *)notification
{
[self update];
}

2009年3月12日木曜日

β版バグ修正 - マルチスクリーン(ボタン位置)

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

今日は範囲選択のボタン位置の修正。
これまたマルチスクリーンの場合、画面の位置関係によってはボタンの位置が正しく表示されない。

原因は単純でボタンの位置決めでマルチスクリーンを考慮していないこと。
現在はメイン画面のサイズのみを見ている。

NSSize view_size = [[NSScreen mainScreen] frame].size;


これを変更して、全画面を包含する画面サイズとする。
NSSize view_size = [Screen frame].size;


これで直った。

- - - -
Screen#frame はクラスメソッドで毎回全画面範囲を計算している。利用頻度が高くなってきたのでそろそろインスタンス化した方がよさそうだ。あらかじめ計算しておけばオーバーヘッドを少しでも減らせる。スクリーンの数や配置が変更になった場合は Notificationを受けて再計算すれば良い。

2009年3月11日水曜日

新しい本

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

今日本屋へ寄ったら新しいCocoa本が出ていた。



初心者向けの内容でiPhoneにも少し触れていた。それほど厚みは無い。
少しずつだけど日本語のCocoa関係本が増えてきたのはいいことだ。昔は Ojective-Cをやろうなんて人は少数だったと思うのだけど iPhone効果かな。

- - - -
今日は進展なし。徐々に本業とプライベート両方が忙しくなってきた。

2009年3月10日火曜日

β版バグ修正 - マルチスクリーン(全画面)

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

マルチスクリーンのバグ取り、次は全画面。

全画面キャプチャの時に画面の配置状況によってはカウントダウン時の点線の位置がおかしいとの指摘を受けた。キャプチャ自体は問題なく行えている。

点線がおかしい例


点線が上の方に固まって表示されている。



描画コードはこう。

- (void)drawRect:(NSRect)rect
{
[[NSColor clearColor] set];
NSRectFill(rect);

for (NSScreen* screen in [NSScreen screens]) {
NSRect frame = [screen frame];
frame.origin.y = 0.1;
frame.size.width -= 0.1;
frame.size.height -= 0.1;
[self drawSelectedBoxRect:frame Counter:_animation_counter];
}
}


このコードでは y値を 0に固定し、さらに座標系の違いをまったく考えていない。NSScreen#screens で取得したスクリーン座標系の値を、そのままカスタムビューのローカル座標系で使っている。これではマルチスクリーンで変な描画になるわけだ。単一スクリーンの場合は y値を0(コードでは0.1)にすることで無理矢理?ローカル座標系に合わせているため問題がでなかった。なお 0.1 で調整しているのは、ぴったりの数値にすると線が画面の外にはみ出してしまう為、少しだけ小さくなる様に設定している。

このコードを、スクリーン座標系からローカル座標系に変換するように書き直す。こんな感じ。
- (void)drawRect:(NSRect)rect
{
[[NSColor clearColor] set];
NSRectFill(rect);

for (NSScreen* screen in [NSScreen screens]) {
NSRect frame = [screen frame];
frame.origin = [[_capture_controller view] convertPoint:[[_capture_controller window] convertScreenToBase:frame.origin] fromView:nil];
frame.origin.y -= frame.size.height;
frame.size.width -= 0.1;
frame.origin.y += 0.1;
frame.size.height -= 0.1;

[self drawSelectedBoxRect:frame Counter:_animation_counter];
}
}


基本的には NSWindow#convertScreenToBase: などを使い座標系変換すれば良い。ただし以前書いたように原点の取り方が異なることからその調整が必要になる。コード中、frame.origin.y から frame.size.heightを差し引いているのがその調整。0.1調整は実際の描画を見ながら適当に調整した。

実際にマルチスクリーンで試したところ問題なく点線が描画できた。

- - - -
マルチスクリーンはまだ続く。やれやれ。

2009年3月9日月曜日

β版バグ修正 - マルチスクリーン(検証)

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

昨日までの修正をマルチスクリーン環境で試してみる。

(1) 範囲選択の初期位置がおかしい件

 これは問題なし。スクリーン座標系の情報を変換した結果を使い、意図通りの位置(メイン画面の中心)に表示できている。


(2) 範囲選択がうまく働かない件

これは問題あり。Y軸方向は問題無いがX軸方向で挙動がおかしい。そこで先日の数式を見直してみとX値を求める箇所で符号が逆になっているのに気が付いた。

(誤)cx1 = lx1 - sx1
(正)cx1 = lx1 + sx1

これではキャプチャしても全然変な場所を切り抜いているわけだ。符号を直してやりなおしたところ問題なくキャプチャできた。

メインとサブ画面について一通りの位置関係で範囲選択を試したところ問題無いことが確認できた。

数式の最終版は次のようになる。

・スクリーン座標系上での全画面範囲(左下)-(右上):(sx1, sy1)-(sx2, xy2)
・メイン画面のサイズ(幅・高さ):(sw, sh)
・ローカル座標系上でキャプチャした範囲選択の開始点(左上):(lx1, ly1)
・CGWindow関数へ渡す開始点(左上):(cx1,cy1)

cx1 = lx1 + sx1
cy1 = ly1 - (sy2 - sh)


- - - -
マルチスクリーンがらみの問題はまだある。なのでバグ修正はもう少し続く。

2009年3月8日日曜日

範囲選択の初期位置(マルチスクリーン対応)

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

引き続きマルチスクリーンの話題。

マルチスクリーンで最初に範囲選択した時にメイン画面ではなくサブ画面の中央に範囲選択枠(ラバーバンド)が表示される。これは現在の実装が単純にマルチスクリーンを考慮していないから。

初期位置を決める現在のコードはこんな感じ。

- (void)resetRect
{
NSScreen* screen = [NSScreen mainScreen]; // multi screen ok
NSSize screen_size = [screen frame].size;
_rect = NSMakeRect((screen_size.width - START_WIDTH)/2,
(screen_size.height - START_HEIGHT)/2,
START_WIDTH, START_HEIGHT);
}



単純にローカル座標系の(0,0)を基点としてメイン画面の中央に配置するようにしている。これでは画面の配置状況によっては意図した(メイン画面中央)位置に範囲選択枠がこない。これを直すにはスクリーン座標系上でメイン画面の左上の座標を求め、これをローカル座標系へ変換した上で描画位置の基点とすれば良いと思われる。スクリーン座標系からカスタムViewのローカル座標系への変換には NSWindow#convertScreenToBase:NSView#convertPoint:fromView:が使える。

変換コードを追加するとこんな感じ。
- (void)resetRect
{
NSScreen* screen = [NSScreen mainScreen]; // multi screen ok
NSSize screen_size = [screen frame].size;
_rect = NSMakeRect((screen_size.width - START_WIDTH)/2,
(screen_size.height - START_HEIGHT)/2,
START_WIDTH, START_HEIGHT);
NSPoint bp = NSZeroPoint;
bp = [[_capture_controller view] convertPoint:[[_capture_controller window] convertScreenToBase:NSMakePoint(0, screen_size.height)] fromView:nil];
_rect.origin.x += bp.x;
_rect.origin.y += bp.y;
}

後半 #convertPoint:fromView: と #convertScreenToBase: を使って基点を生成している。

実行してみたところ単一スクリーンでは問題なさそうだ。
ただマルチスクリーンは今は試せる環境が無い。明日以降試してみよう。