ページ

2008年10月31日金曜日

アプリケーションを開く(3)拡張子から利用可能なアプリを調べる?

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

前回は Launch Services API の LSCopyApplicationURLsForURLを使ってオープン可能なアプリケーションの一覧を取得した。
例えば pro.jpg というファイルの場合、次のリストが取得できた。

    file://localhost/Applications/QuickTime%20Player.app/,
file://localhost/Developer/Applications/Xcode.app/,
file://localhost/Applications/Gimp.app/,
file://localhost/Applications/OpenOffice.org%202.1.app/,
file://localhost/Applications/Safari.app/,
file://localhost/Applications/Preview.app/,
file://localhost/Developer/Applications/Graphics%20Tools/Core%20Image%20Fun%20House.app/,
file://localhost/Applications/Adobe%20Illustrator%20CS2/Adobe%20Illustrator.app/,
file://localhost/Applications/%E3%83%84%E3%83%BC%E3%83%AB/Firefox.2.app/,
file://localhost/Applications/tools/Firefox.app/,
file://localhost/Applications/Utilities/ColorSync%20Utility.app/,
file://localhost/Users/hashi/development/study/package/build/Release/package.app/,
file://localhost/Applications/Firefox.app/


SimpleCapで扱うファイルは GIF/JPEG/PNG なので、特定のファイルを開けるアプリの一覧よりは、これらの種類を開けるアプリの一覧が欲しい。API の中に LSCopyAllRoleHandlersForContentType があったのでこれを使ってみる。結果は下記の通り。
    "com.apple.CoreImageFunHouse.app",
"org.gimp.Gimp",
"com.apple.Preview",
"com.apple.ColorSyncUtility"


むむ。返される形式が異なるのは別として、アプリの数が少ない。

これは結局2つの関数の検索条件が異なることに起因しているようだ。前者は拡張子かクリエータを対象に、後者は Info.plistのLSCopyDefaultRoleHandlerForContentTypeエントリを対象として検索している。

この話題はメーリングリストでも取り上げられていた。
Re: Get a list of all Apps for a File


なるほど。

だったらダミーファイルを GIF/JPEG/PNGの3種類、バンドル内に用意してこれを使って対応アプリを取得することにしよう。

かなり安易だが。。

#ちなみに試しに実在しないファイルを LSCopyApplicationURLsForURL を渡したところ nullが返ってきた。

2008年10月30日木曜日

アプリケーションを開く(2)「このアプリ..で開く」

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

プレビューは開くことができたので、こんどは起動するアプリケーションを環境設定で自由に選べるようにする。

イメージはファインダの「このアプリケーションで開く」


さてこれを実現するにはファイルを開くのに適したアプリケーションを取得する必要がある。デフォルトのアプリは NSWorkspace#getInfoForFile:application:type: で取れそうだが、オープン可能なアプリ一覧となると NSWorkspace では役不足のようだ。

MacOSXには Launch Services という API が用意されていれていて、このあたりを扱うことができる。

Launch Services Programming Guide

このAPIの中に LSCopyApplicationURLsForURL という関数が用意されていて、これが使えそうだ。

(参考)Re: Get list of possible applications


早速サンプルアプリを作って試してみる。

サンプル:FindingAllApps-1.zip

実行するとソースに付随している画像ファイル pro.jpg を開くことができるアプリの一覧がコンソールへ出力される。


コードはこんな感じ。

 NSString* path = [[NSBundle mainBundle] pathForImageResource:@"pro"];
NSURL* url = [NSURL fileURLWithPath:path];
NSArray* array = [(NSArray*)LSCopyApplicationURLsForURL((CFURLRef)url, kLSRolesAll) autorelease];
NSLog(@"%@", array);


実行結果。

2008年10月29日水曜日

アプリケーションを開く(1)Preview.appで開く

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

キャプチャ後に特定のアプリケーションを開く様にする。まずはプレビュー(Preview.app)で開いてみよう。
これは NSWorkspace#openFile:withApplication: を使えば簡単にできる。

[[NSWorkspace sharedWorkspace] openFile:filename withApplication:@"Preview.app"];


これをキャプチャコントローラのファイル保存後の位置に差し込んでやる。ただ開くかどうかはプリファレンスで制御したいので IB で制御用のチェックボックスを追加し、この値によって開く制御を行う。


キャプチャを行うと直後にプレビューで画像が表示されるようになった。

2008年10月28日火曜日

範囲選択履歴(その9)履歴の保存

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

履歴を NSUserDefaultsに保存する。その為に履歴のモデルにあたる SelectionHistoryに手を入れる。
履歴は NSSize を NSValueでラップし、これを NSArray で持っている。NSUserDefaultsは NSSizeを扱えないので NSStringに変換して保存する。とりだすときはその逆に NSStringから NSSizeへ戻してやる。標準でその為の関数が用意されている。

NSStringFromSize( )
NSSizeFromString


これらを使って読み込みと保存のメソッドを用意する。
SelectionHistory
- (void)load
{
if (_history_list) {
[_history_list release];
}
_history_list = [[NSMutableArray alloc] init];
NSArray* array = [UserDefaults valueForKey:UDKEY_SELECTION_HISTORY];
for (NSString* str in array) {
NSSize size = NSSizeFromString(str);
[_history_list addObject:[NSValue valueWithSize:size]];
}
}
- (void)save
{
NSMutableArray* array = [NSMutableArray array];
for (NSValue* value in _history_list) {
NSString* size = NSStringFromSize([value sizeValue]);
[array addObject:size];
}
[UserDefaults setValue:array forKey:UDKEY_SELECTION_HISTORY];
[UserDefaults save];
}


それぞれを適切な位置で呼出せば履歴の永続化が完了する。
NSUserDefaulsへのアクセスには以前作成した NSUserDefaultsController のラッパークラス UserDefaults を使う。
NSUserDefaultsController を使う場合は(デフォルトでは)しかるべきタイミングで save: を投げてやる必要がある。UserDefaultsにはその為のメソッド #saveを用意した。

UserDefaults
+ (void)save
{
[(NSUserDefaultsController*)[NSUserDefaultsController sharedUserDefaultsController] save:self];
}

2008年10月27日月曜日

範囲選択履歴(その8)履歴の選択

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

範囲選択履歴は続く。

今回はメニューで履歴を選んだときの動作を実装する。どのメニューが選ばれたかは #setTag を使って NSMenuItemにインデックス値を紐づけておき、選択時に #tag で取り出す。

ThinButtonBar.m

 NSInteger idx = 0;
for (NSString* title in list) {
item = [[[NSMenuItem alloc] initWithTitle:title
action:@selector(selectMenuItem:)
keyEquivalent:@""] autorelease];
[item setTag:idx++];
[item setRepresentedObject:tag_number];
[menu addItem:item];
}

tag_number はボタンの種類、すなわち範囲選択履歴が押されていることを表す。

選択されたらデリゲート先へ selectMenuAtTag:atIndex: を投げる。
- (void)selectMenuItem:(NSMenuItem *)menu_item
{
[_delegate performSelector:@selector(selectMenuAtTag:atIndex:)
withObject:[menu_item representedObject]
withObject:[NSNumber numberWithInt:[menu_item tag]]];
}


で、受け取ったら範囲を変更する。この時アンドゥ処理の為の準備を他の処理同様に行っておく。
SelectionHandler.m
-(void)selectMenuAtTag:(NSNumber*)tag atIndex:(NSNumber*)index
{
NSSize size;
switch ([tag intValue]) {
case TAG_SELECTION_HISTORY:
size = [_selection_history sizeAtIndex:[index intValue]];
[_selection_history setSize:size];
NSRect frame = _rect;
frame.size = size;
CaptureView* view = [_capture_controller view];
NSUndoManager* undoManager = [view undoManager];
[[undoManager prepareWithInvocationTarget:self]
setRubberBandFrame:_rect];
[undoManager setActionName:NSLocalizedString(@"UndoResizeSelection", @"")];
[self setRubberBandFrame:frame];
break;

default:
break;
}
}


これで履歴を選択した時に範囲の大きさが変わる。Undoも効く。



なお履歴に何もない場合には「なし」を表示する。


これはタイトルだけの NSMenuItemを1つ用意してやれば良い。ポップアップメニューは賢くてメソッドが存在しない(nil)の場合はメニュー項目をディゼーブルにしてくれる。
 if (idx == 0) {
[menu insertItemWithTitle:NSLocalizedString(@"SelectionHistoryNone", @"")
action:nil
keyEquivalent:@""
atIndex:0];
}


- - - -
残りは履歴の永続化(UserDefaults)だな。

2008年10月26日日曜日

範囲選択履歴(その7)履歴の追加

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

範囲選択でキャプチャを実行した時にそのサイズを履歴に追加する。履歴の管理は最近使ったものを一番上に持ってくる様にする。

設定メソッドは次の通り。

- (void)setSize:(NSSize)size
{
NSValue* value = [NSValue valueWithSize:size];
[_history_list addObject:value];
if ([_history_list containsObject:value]) {
[_history_list removeObject:value]; // (1)
}
if ([_history_list count] == MAX_HISTORY) {
[_history_list removeObjectAtIndex:(MAX_HISTORY-1)]; // (2)
}

[_history_list insertObject:value atIndex:0]; // (3)
}

渡されたサイズが既に存在する場合は重複を避けるために削除しておく(1)。また履歴がいっぱいになった場合は最後のエントリを削除する(2)。追加は先頭(index=0)に行う(3)。

こんな感じ。

2008年10月25日土曜日

範囲選択履歴(その6)モデル作成

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

今度は履歴を格納するモデルクラスを作成する。
こんな感じ。

SelectionHistory.h

@interface SelectionHistory : NSObject {

NSMutableArray* _history_list;
}

- (void)setSize:(NSSize)size;
- (NSSize)sizeAtIndex:(int)index;
- (NSArray*)menuList;

@end

#menuList はポップアップメニューに表示する為の文字列一覧を返す。

このクラスを前回のコードへ追加し機能させる。
まずは前回同様のポップアップメニューを表示させてみる。
イニシャライザの中でテスト用にサイズを追加する。

- (id)init
{
self = [super init];
if (self) {
_history_list = [[NSMutableArray alloc] init];
[self setSize:NSMakeSize(500, 500)];
[self setSize:NSMakeSize(600, 600)];
[self setSize:NSMakeSize(700, 700)];
}
return self;
}


#setSize は次の様になる。
- (void)setSize:(NSSize)size
{
NSValue* value = [NSValue valueWithSize:size];
[_history_list addObject:value];
}


NSSizeは構造体なのでそのままでは NSMutableArrayに入れられない。そこでラッパーとなる NSValue を利用する。

#menuListではポップアップメニュー表示用に文字列の配列を作る。
- (NSArray*)menuList
{
NSMutableArray* results = [NSMutableArray array];
for (NSValue* value in _history_list) {
NSSize size = [value sizeValue];
[results addObject:[NSString stringWithFormat:@"%.0fx%.0f", size.width, size.height]];
}
return results;
}


前回のコードで、この #menuList を呼ぶように書き換える。

メニューでた。


- - -
表示系は基本的なものができた。
次は履歴への追加に着手する。

2008年10月24日金曜日

範囲選択履歴(その5)ポップアップメニューのハンドリング

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

検証ができたので、組み込みに入る。アーキテクチャは下図のようにする。


三角を押す、メニューを選ぶ、と2つのイベントを処理する必要がある為に少し複雑になっている。
イベントのやりとりは Viewが中心になることから、イベントの直接的な処理を Viewにまかせ、Controller は表示するメニューの提供や、Viewからのイベント伝達を待つような役割分担とする。実際にはこの後、履歴を管理するモデルもこの中に登場することになる。

(1)から(4)までの処理を行っている Viewのコードはこんな感じ。

- (void)displayMenuWithTag:(UInt)tag event:(NSEvent*)theEvent
{
NSArray* list = [_delegate performSelector:@selector(menuWithTag:)
withObject:[NSNumber numberWithInt:tag]];
NSMenu* menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
NSInteger idx = 0;
for (NSString* title in list) {
[menu insertItemWithTitle:title
action:@selector(selectMenu:)
keyEquivalent:@""
atIndex:idx++];
}
[NSMenu popUpContextMenu:menu withEvent:theEvent forView:self];
}

_delegateに対して投げている menuWithTag: はこんな感じ。現在は動作確認用に固定の文字配列を返す。
-(NSArray*)menuWithTag:(NSNumber*)tag
{
NSLog(@"tag=%@", tag);
NSMutableArray* array = [NSMutableArray array];
switch ([tag intValue]) {
case TAG_SELECTION_HISTORY:
[array addObject:@"100x100"];
[array addObject:@"200x200"];
[array addObject:@"300x300"];
break;

default:
break;
}
return array;
}


これでポップアップメニューが出る。


今回はメニューが押された時に呼出されるメソッドも用意したので、全メニューが有効(Enable)になって選択できるようになっている。

2008年10月23日木曜日

範囲選択履歴(その4)ポップアップメニュー

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

下三角のアイコンをクリックした時にポップアップメニューを表示したい。ADCのマニュアルを調べていたらずばり目的のコードが掲載されていた。

Application Menu and Pop-up List Programming Topics for Cocoa
この中の "Listing 3 Displaying a contextual menu upon receiving a left-mouse event" が参考になる。

これを参考にしてまずは動作確認目的で、アイコンが押されたらサンプルのメニューを表示するだけの処理を加えてみる。
下三角のアイコンを押したら、次のメソッドを呼出す。

-(void)displaySelectionHistoryMenuWithEvent:(NSEvent*)event
{
NSMenu *menu = [[[NSMenu alloc] initWithTitle:@"HELLO"] autorelease];
[menu insertItemWithTitle:@"100x100" action:@selector(selectHistory:) keyEquivalent:@"" atIndex:0];
[menu insertItemWithTitle:@"200x200" action:@selector(selectHistory:) keyEquivalent:@"" atIndex:1];
[menu insertItemWithTitle:@"300x300" action:@selector(selectHistory:) keyEquivalent:@"" atIndex:2];

[NSMenu popUpContextMenu:menu withEvent:event forView:_button_bar2];
}


NSMenu のインスタンスを作成し、ここへサンプルのメニュー項目3つを追加する。そして最後に NSMenu#popUpContextMenu:withEvent:forView: を呼出すと、ポップアップメニューが現れる。_button_bar2 は下三角アイコンを表示しているカスタムビュー。

メニュー出た。

2008年10月22日水曜日

範囲選択履歴(その3)

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

文字の描画サイズは NSString#sizeWithAttributes: で取得できる。

文字描画のコードは次のようになっていた。

- (void)drawInformation
{
NSString *info =
[NSString stringWithFormat:@"%.0f x %.0f",
fabs(_rect.size.width), fabs(_rect.size.height)];
NSMutableDictionary *stringAttributes = [NSMutableDictionary dictionary];

[stringAttributes setObject:[NSFont boldSystemFontOfSize:18.0]
forKey: NSFontAttributeName];
[stringAttributes setObject:[NSColor whiteColor]
forKey:NSForegroundColorAttributeName];
[stringAttributes setObject:[NSColor blackColor]
forKey:NSStrokeColorAttributeName];
[stringAttributes setObject:[NSNumber numberWithFloat: -1.5]
forKey:NSStrokeWidthAttributeName];
:
:


文字属性の設定に使っているこのstringAttributes を先ほどのメソッドへ渡してやれば良い。

NSSize size = [info sizeWithAttributes:stringAttributes];


これで描画しようとしている文字の大きさがわかる。このサイズを前回のボタン表示のオフセット位置として使ってやれば良い。

桁が大きくなっても大丈夫。


もちろん小さくなっても大丈夫。



よし。次はプルダウンだな。

2008年10月21日火曜日

範囲選択履歴(その2)

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

履歴を呼出す為のボタンをつける。
まず Illustratorで下向きの矢印を作る。



それを以前作った ButtonBar クラスを使って左下に表示する。




よし。ただ今はまだ位置が固定になっている。数値の幅に合わせてX位置を移動させる必要がある。次回はそれをやろう。

2008年10月20日月曜日

範囲選択履歴(その1)

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

波紋もいい加減長くなったので一旦離れて別の機能実装に入る(波紋は細々と進める)。

範囲選択の指定ができるようにとの要望が以前あった。値を入力させることも考えたが、SimpleCapでは 5px単位の拡大縮小をサポートしている(コマンドキーを押しながらで有効)ので、直接大きさを変えた方が速い。その為、値入力は今回見合わせる。ただ何パターンかの大きさを履歴として持っておけると便利だと思うのでその機能を付けよう。

まず手始めに標準で表示していなかったサイズをデフォルトで表示するようにした。また文字が見づらいので大きくして影を付けた。位置も左上から左下へ移動。

こんな感じ。

2008年10月19日日曜日

QuarzComposer

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

CoreImageの効果は QuartzComposerで手軽に確認できる。



# いまさらながらだが。

2008年10月18日土曜日

波紋(その19)

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

今回は情報を少し紹介。

OmniDazzle

Omni Group が提供している CoreImageを使った視覚エフェクト集。
波紋もある。



動画デモを見ることができる。
$14.95 なり。


Ripplin'

Dashboardの波紋をまねて作ったというデモソース。ビルドして実行すると次のようなウィンドウが現れる。ボタンを押すと波紋が出る、はずなのだが Leopard環境ではウィンドウが拡大縮小するだけで波紋は出なかった。


ソースコードを見ると CIShapedWaterRipple という見慣れないフィルタを使っていた。
AWRipper.m 抜粋

    rippleFilter = [[CIFilter filterWithName:@"CIShapedWaterRipple"] retain];
[rippleFilter setDefaults];
[rippleFilter setValue:[NSNumber numberWithFloat:40.0] forKey:@"inputCornerRadius"];
[rippleFilter setValue:[CIVector vectorWithX:rippleRect.origin.x-40.0 Y:(rippleRect.origin.y - 40.0)] forKey:@"inputPoint0"];
[rippleFilter setValue:[CIVector vectorWithX:(rippleRect.origin.x + rippleRect.size.width + 40.0) Y:(rippleRect.origin.y + rippleRect.size.height + 40.0)] forKey:@"inputPoint1"];


Googleで調べたところ、どうもこのフィルタはプライベート扱いで、かつ Tigerまでしか使えないようだ。
うーん残念。

2008年10月17日金曜日

GIF保存(2)

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

昨日の続き。GIFで保存すると影の部分が GIFのアルファチャネル(1bit, いわゆる透明情報)として書き出されてしまっていた。


結局これは GIFの仕様上避けられないことなので、発想を変えて背景色を与えて、そこに影を落とす様にしむけてみた。アルファチャネルを持たない JPEGでは(潔く?)初めからこのようになっている。


GIFの保存には NSBitmapImagRep#representationUsingType:properties: を使っている。これを使う前に白い背景と合成させる。その為のメソッドを書く。

- (NSBitmapImageRep*)fillBackground:(NSBitmapImageRep*)bitmap_rep
{
NSImage *src_image = [[[NSImage alloc] init] autorelease];
[src_image addRepresentation:bitmap_rep];
NSSize image_size = [src_image size];

NSImage *bg_image = [[[NSImage alloc] initWithSize:image_size] autorelease];
[bg_image lockFocus];
[[NSColor whiteColor] set];
[NSBezierPath fillRect:NSMakeRect(0, 0, image_size.width, image_size.height)];
[src_image compositeToPoint:NSZeroPoint operation:NSCompositeSourceOver];
[bg_image unlockFocus];
NSBitmapImageRep* output = [[[NSBitmapImageRep alloc] initWithData:[bg_image TIFFRepresentation]] autorelease];
return output;
}


このメソッドでは、入力された NSBitmapImageRep と同じ大きさの NSImageを作り、白く塗りつぶす。その上に入力画像を NSImage#compositeToPoint:operation: で上書きする。

これを保存前に呼出して使う。


結果はこんな感じ。影の部分は背景(白色)と合成され多少マシになっている。


NSBitmapImagRep#representationUsingType:properties:のオプションに NSImageDitherTransparency を指定してみる。あまり変わらない。



影を付けない場合。




なお最後にPreview.appと比較してみた。最初に PNGで書き出し、これを Preview.app で GIFへ変換して書き出す。
結果は下の通り。



Preview.appの場合、ディザが奇麗にかかると期待したがプログラムで書き出したものと見た目は変わらない。恐らく同じメソッドを使っていると思われる。また自動的に白い背景が付くところも同じ発想だ。

- - - -
直接は関係ないがこのブログで使っている Bloggerでは、プログラムやPreview.appで作成した GIF画像をアップロードするとエラーになって受け入れてくれない。GIF形式が特殊なんだろうか?Windowsなどで確認する必要があるな。

2008年10月16日木曜日

GIF保存

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

GIFで保存すると影の部分が汚くなってしまう。



NSBitmapImageRep#representationUsingType:properties: のプロパティに NSImageDitherTransparency を指定してみたが改善しない。何故だろう。

- - - -
今日は飲み会で頭が回らず。

2008年10月15日水曜日

波紋(その18)

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

もう少し波がほしい。試しにフィルタを複数段重ねてみる。

こんな感じ。

 CIImage *output_image = [_filter valueForKey:kCIOutputImageKey];
[_filter2 setValue:output_image forKey:kCIInputImageKey];
output_image = [_filter2 valueForKey:kCIOutputImageKey];
[_filter3 setValue:output_image forKey:kCIInputImageKey];
output_image = [_filter3 valueForKey:kCIOutputImageKey];
    :


まずは1段の場合。



次に2段。徐々に波が多くなる。



3段。いい感じだ。



ここまでは入力画像を、inputImageだけに当てていた。これをtargetImageにも適用してみる。



いっそう複雑な波が現れる。


視覚効果としては複数段重ねた方が見栄えがする。SimpleCapでも複数段を採用しよう。

2008年10月14日火曜日

波紋(その17)

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

前回からの続き。

画像の反転(flip)は CGAffineTransform を使えば良い。
こんな感じ。

 CGAffineTransform transform;
transform = CGAffineTransformMakeTranslation(0.0, CGImageGetHeight(cgimage));
transform = CGAffineTransformScale(transform, 1.0, -1.0);
ciimage = [ciimage imageByApplyingTransform:transform];


元ネタはこちら。
Theocacao: Convert an NSImage to CIImage


続いて画像がおかしかった件。NSBitmapImageRepを経由せず、CIImage#initWithCGImage を使ったらこれは直った。
(前回)
NSBitmapImageRep *bitmap_rep = [[[NSBitmapImageRep alloc] initWithCGImage:cgimage] autorelease];
CIImage* ciimage = [[CIImage alloc] initWithBitmapImageRep:bitmap_rep];


(今回)
CIImage* ciimage = [[CIImage alloc] initWithCGImage:cgimage];



波紋出た。

2008年10月13日月曜日

波紋(その16)

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

SimpleCapへ組み入れてみる。まずはウィンドウキャプチャで選択したウィンドウを波立たせてみよう。視覚効果が良さそうなら採用することにする。

見た目の確認が重要なので動作を最優先させる。そこで前回までのコードをコピーして動くものを組み立てる。
手始めに主要部分を Transaction という新クラスに記述する。

Transaction.h

@interface Transition : NSObject {

NSNumber *_inputTime;
NSNumber *_inputWidth;
NSNumber *_inputScale;
NSNumber *_framePerSec;
NSNumber *_totalSec;

CIFilter* _filter;
NSInteger _count;

NSView* _view;
BOOL _finished;
id _target;
}

@property (retain) NSNumber* inputTime;
@property (retain) NSNumber *inputWidth;
@property (retain) NSNumber *inputScale;
@property (retain) NSNumber *framePerSec;
@property (retain) NSNumber *totalSec;

- (id)initWithView:(NSView*)view;
- (void)draw;
- (void)startWithTarget:(id)target CGImage:(CGImageRef)cgimage;

@end


トランジションで使うパラメータをメンバ変数にもち、イニシャライザに、描画メソッド(draw)、トランジション開始のメソッドを用意した。これと SimpleCap既存のウィンドウキャプチャを組み合わせてみる。

実行してみる。



うーむ。波だっているようだが、色がおかしい上に上下反転(フリップ)している。
一筋縄ではいかないようだ。

2008年10月12日日曜日

波紋(その15)

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

とりあえずサンプルを公開。

サンプルソース: RippleTransition-3.zip

環境マップ画像は透明なものを用意している。それなりに波紋に見える。

2008年10月11日土曜日

波紋(その14)

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

NSView#dataWithPDFInsideRect の代わりに #bitmapImageRepForCachingDisplayInRect: を使うこともできる。

HDMT(July,2006)によれば「再現性、使用メモリ、パフォーマンスともに、かなりいい線いってる」とのこと。

旧コード
NSData* data = [_view2 dataWithPDFInsideRect:[_view2 bounds]];
NSImage *image1a = [[[NSImage alloc] initWithData:data] autorelease];

data = [image1a TIFFRepresentation];
NSBitmapImageRep* bitmap = [NSBitmapImageRep imageRepWithData:data];

これが下記の様になる。

新コード
NSBitmapImageRep* bitmap = [_view2 bitmapImageRepForCachingDisplayInRect:[_view2 bounds]];
[_view2 cacheDisplayInRect:[_view2 bounds] toBitmapImageRep:bitmap];


動作は問題ないようだ。コードもすっきりして良い。

NSView Class Reference - bitmapImageRepForCacingDisplayInRect:

2008年10月10日金曜日

波紋(その13)

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

NSViewに描画した内容を CIImage へ変換し、トランジションをかける。

 NSData* data = [_view2 dataWithPDFInsideRect:[_view2 bounds]];
NSImage *image1a = [[[NSImage alloc] initWithData:data] autorelease];

data = [image1a TIFFRepresentation];
NSBitmapImageRep* bitmap = [NSBitmapImageRep imageRepWithData:data];
CIImage* image1b = [[CIImage alloc] initWithBitmapImageRep:bitmap];
CIImage* image2b = [[CIImage alloc] initWithBitmapImageRep:bitmap];
[_filter setValue:image1b forKey:kCIInputImageKey];
[_filter setValue:image2b forKey:kCIInputTargetImageKey];


_view2 がトランジションをかけたいビュー。これを dataWithPDFInsideRect: で一旦 NSData へ変換した後、NSImage、NSBitmapImageRepを経由して最後に CIImage を作成する。CIImage#initWithData はうまくいかなかった。

作ってみたのがこれ。



下の NSViewに描画し、これを CIImageへ変換し、上のエリアでトランジションをかけている。

2008年10月9日木曜日

波紋(その12)

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

さてそろそろSimpleCapへの組み込みに入っていこう。使いどころはいくつかアイディアがあるが、いずれも NSView の描画内容に対して波紋のトランジションを適用することになる。トランジションへは CIImage で渡す必要がある為、NSViewの内容をなんらかの方法で変換してやる必要がある。

ネットで調べていると次のメソッドが使えそうな事がわかった。

NSView#dataWithPDFInsideRect:


戻り値は NSData になるので、これを CIImage#imageWithData: へ渡したらどうだろうか。

と、今日は時間切れ。
明日検証してみる。

2008年10月8日水曜日

波紋(その11)

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

アニメーションさせてみる。初めの方では CoreAnnimationを使ったが、NSTimerを使い自前でタイミングを計る。

STARTボタンでタイマーを起動。

- (IBAction)start:(id)sender
{
_count = 0;
float interval = 1.0/[_framePerSec floatValue];
[NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(fire:)
userInfo:nil
repeats:YES];
}


タイマーが発火したら(前回までで用意した)トランジション画像描画メソッドを呼ぶ。
-(void)fire:(NSTimer*)theTimer
{
_count++;
float max_count = [_totalSec floatValue] * [_framePerSec floatValue];
if (_count > max_count) {
[theTimer invalidate];
}
_inputTime = [NSNumber numberWithFloat:(_count / max_count)];
[self redrawImage];
}



単に動かすだけでは調査にならないので、1秒当たりのフレーム数(_framePerSec)と、全体の時間(_totalSec)を調整できるようにした。

こんな感じ。


パラメータをいじりながらトランジションを確認していくと、だんだんいい感じになってきた。
今いい感じなのは上画像のパラメータセット(inputWith:102.0, inputScale:26.1, framePerSec: 20, totalSec: 2.0)。

後は環境マップのカスタマイズが必要。

- - - -
なお、今回もサンプルコード提供はありません。
実はトランジションで使っている環境マップ画像は
他サイトから勝手に借りてきて使っている為です。
環境マップが自前で用意できるようになったら公開します。

2008年10月7日火曜日

波紋(その10)

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

いつのまにか Dashboard に CI Filter Browser なる Widgetが追加されている。



この中にパラメータの説明が日本語で書かれていた。

これによれば inputScale は、
「波紋が開始する突起(値が高い時)またはくぼみ(値が低い時)を決定するための値」

inputWithは
「波紋の幅」
とある。

2008年10月5日日曜日

波紋(その9)

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

トランジションについてリファレンスを調べるといくつかパラメータがあることがわかる。
Core Image Filter Reference - CIRippleTransition

下記の2つはこのリファレンスを見て初めてわかった。

inputWidth
An NSNumber class whose attribute type is CIAttributeTypeDistance and whose display name is Width.

Default value: 100.00 Minimum: 1.00 Maximum: 0.00 Slider minimum: 10.00 Slider maximum: 300.00 Identity: 0.00

inputScale
An NSNumber class whose attribute type is CIAttributeTypeScalar and whose display name is Scale.

Default value: 50.00 Minimum: -50.00 Maximum: 0.00 Slider minimum: -50.00 Slider maximum: 50.00 Identity: 0.00



これを調整できるスライダを用意して動きを見てみよう。

最初はデフォルト値。


時間(inputTime)を進めて波紋が広がりかけた途中で止める。ここから他のパラメータをいじる。


inputWidth を減らす ( 100 ⇒ 23 )


inputWidth を増やす ( 100 ⇒ 226 )


inputScaleを減らす( 50 ⇒ 24 )



inputScaleを減らす( 50 ⇒ -1.40 )



今度は inputScaleを減らす( 50 ⇒ -50 )



パラメータをいじるとそれっぽくなってきた気がする。

波紋(その8)

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

スライダでトランジションの状態を制御できるようにした。

NSNumber* _transition;

をスライダとバインドさせ、

[_filter setValue:_transition forKey:kCIInputTimeKey];

としてフィルタのタイムキーに設定するようにした。

kCIInputTimeKey の値は 0から 1の間の値を取る。これをスライダで制御してみる。

こんなかんじ。左の数字が kCIInputTimeKey の値。













- - - - -
いろいろ分かってきたが Dashboardのような波紋はまだほど遠い。Dashboardは環境マップに球ではなく四角を使っているようにみえる。また大きさも Widgetに合わせてその四隅で波紋がおきえいる。