2007/02/20

次のゴール

前回のゴールは達成できたので、次のゴールを設定することにする。

次のゴールは、「ファイルの出力先ディレクトリを指定できるようにする」にしよう。

Export Thumbnailsボタンを押した時に、いきなり処理を行うのでなくダイアログボックスを表示し、設定を変更した後、そのダイアログボックスにある「実行」ボタンを押してエクスポートを行うようにする。ダイアログボックスにはデフォルトのディレクトリを表示しておき、その変更は標準のファイル選択ダイアログボックスを使うようにする。

箇条書きにすると、
  • ボタンクリックでカスタムダイアログボックスの表示
  • ダイアログボックスからのファイル選択ダイアログボックスの表示
  • 前回選択したディレクトリをアプリケーション終了後もどこかに保持しておく
以上の3点だ。

Xcodeでソースコード管理: Subversionを使う

しばらくCocoaアプリケーションの開発をやってきて、そろそろソースコード管理システムなしで開発するのがおっかなくなってきた。Xcodeのメニューを見ると、SCMという文字が見えるからソースコード管理システムとの統合が実現されているように思う。実際、CVS, Subversion, Perforceといったソースコード管理システムと連動できるようになっている。

そこで、XCodeのSubversion連動機能を使えるよう設定してみることにした。設定手順については詳しくは書かないが、一筋縄ではいかないのでやってみようと思う人はがんばってほしい。

大まかな手順は以下の通り
  • apache2をインストールする。Mac OS Xインストール時に動いているのは1.3ベースのapacheなのでこれを止め、apache2をMacPortsからインストールする。
  • subversionもMacPortsから入れ直す。この時、mod_dav_svn.soがインストールされるようなオプションを付けてインストールする。
  • apache2で、mod_dav_svn.soを使うように設定。
  • subversionのリポジトリを作る。
  • XCodeのプロジェクトの複製を作り、subversionリポジトリにインポートする。
  • subversionリポジトリからプロジェクトをcheckoutする。
  • Xcodeのプロジェクト設定からSCMをenableする。subversionのバイナリの位置を指定する。が、Subversion 1.4とXcode 2.4ではNSCFArray addObject attempt to insert nilというエラーが出るので、これを直すために次の作業が必要。
  • SIMBLをインストールする。
  • Xcode+svn-1.4というパッチをインストールする。
と大変手間が掛かるが、動き出すとなかなか便利そうだ。

2007/02/18

画像縮小

目標通り、今作っているアプリケーションでボタンを押すと、現在読み込まれている画像を縮小したファイルを吐き出す機能が実装できた。

縮小の操作については動作を完全に理解できていないので、あまり参考にしない方がよいと思うが、一応以下の操作でオリジナルの20%のサイズの画像を生成できる。

  NSImage* originalImage = [[imageModel objectAtIndex:i] copy];
NSBitmapImageRep* originalImageRep = [NSBitmapImageRep imageRepWithData:[originalImage TIFFRepresentation]];

// producing a scaled image
// scale to 20%
NSSize rect = NSMakeSize([originalImageRep pixelsWide], [originalImageRep pixelsHigh]);
// [originalImageRep release];

NSSize thumbRect;
thumbRect.width = rect.width * 0.2;
thumbRect.height = rect.height * 0.2;

NSImage* image = [[[NSImage alloc] initWithSize:thumbRect] autorelease];
[image lockFocus];
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
[originalImage setScalesWhenResized:YES];
[originalImage setSize:thumbRect];
[originalImage compositeToPoint:NSZeroPoint operation:NSCompositeCopy];
[image unlockFocus];

次に、生成した画像をJPEGファイルとして保存する手順についてメモしておく。手順としては、NSImageからNSBitmapImageRepを取得する。NSBitmapImageRepをJPEGデータとしてNSDataを生成する。これをファイルに書き出せばOKだ。

// Saving to disk
NSData* imageData = [image TIFFRepresentation];
NSBitmapImageRep* imageRep = [NSBitmapImageRep imageRepWithData: imageData];
NSDictionary* imageProps = [NSDictionary dictionaryWithObject: [NSNumber numberWithFloat: 0.9] forKey:NSImageCompressionFactor];
imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps];

NSString* outputFilename = [NSString stringWithFormat:@"/tmp/output%d.jpg", i];
[imageData writeToFile:outputFilename atomically:NO];

2007/02/15

Exposeの設定

Exposeを使いたいシチュエーションが増えてきたので設定を調整してみた。


ExposeはCtrl-右ボタンとしてみた。

画面右下にマウスを移動するのは意図的にやる場合以外ないので、それでDashboardを起動する。一応、キーボードでも起動できるようにはしたが、多分使わないだろう。

追記(2007/02/20): 右下にマウスを動かすのは意図的な場合と書いたものの、実は誤って起動されてしまうことが何度かあった。現在Dashboardの起動は左下に割り当ててみた。

Happy Hacking Keyboard Lite 2を使っていて、右コマンドキーはほぼ使わないので、これをアプリケーションウィンドウに割り当ててみた。

2007/02/14

画像サイズの取得: NSImageのサイズがおかしい

画像を読み込んで縮小するコードを書いている時におかしな現象に出くわした。

ある2592 x 1944ピクセルのJPEG画像を読み込んで画像のサイズをNSImage:sizeで取得すると、なぜか1036.80005 x 777.599976になってしまう。別の画像だとこの現象は起こらず、2592 x 1944となる。

おかしい原因はプレビューアプリケーションで、2つの画像を比べてみることで分かった。サイズがおかしくなる画像は、180 dpi (ドット/インチ)で、うまくいく方は72 dpiだった。どうやらdpiに応じた処理が行われているらしい。

180 dpiにおける2592ピクセルは、14.4インチ。これは72 dpiでは1036.8ピクセルとなる。というわけでsizeで採れる値と一致する。つまり、NSImage:sizeが返す値は、画像を72 dpiで表示したときのサイズなわけだ。

ではどうやって画像のピクセルサイズを取得するか?

これはNSImageからビットマップオブジェクト(NSBitmapImageRep)を生成し、その寸法を取得すれば良い。

NSImage* image = ...
NSBitmapImageRep* imageRep = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]];
NSSize aSize = NSMakeSize([imageRep pixelsWide], [imageRep pixelsHigh]);
こんな感じでめでたくピクセル数がとれた。

2007/02/13

warning: 'MyController' may not respond to '-loadImageFile:'

ドラッグアンドドロップの実装のためのコードを書いていて次のようなエラーが出た。

warning: 'MyController' may not respond to '-loadImageFile:'
warning: (Messages without a matching method signature will be assumed to return 'id' and accept '...' as arguments.)

実際にソースコード(MyController.m) には、-(BOOL)loadImageFile:(NSString*) filenameというメソッドがある。

この警告が出る理由は、ヘッダファイルにこのメソッドの宣言が書かれていないからだ。コンパイラが出したのは警告であってエラーではないのでコンパイルは終了しコードは動くのだが、ヘッダは書いた方が良い。特に2行目のエラーは、「宣言がないので適当に計らいまっせ」と言っている。

その警告を直し、ドラッグアンドドロップの動作を確認してみる。うまく出来ているようでちゃんと画像をドラッグしたらサムネイルが表示されるようになった。


これで2月2日に書いたゴールを達成。ちょっと当初の目的とはずれてしまうが、[サムネイル生成]ボタンを追加し、それをクリックしたら適当なディレクトリにサムネイルファイルを生成するようにしてみよう。考えられる作業は次の通り。
  1. ボタンの追加
  2. ボタンからControllerへのアクションの追加
  3. NSImageあたりを使って画像の操作(画像サイズ取得と画像縮小)
  4. ファイルの書き出し
  5. ダイアログボックスを表示して、作業の完了をお知らせする

ドラッグアンドドロップの実装

Cocoaアプリケーションにドラッグアンドドロップを実装するのは比較的簡単だ。

今回はドラッグ対象をファイル名とし、そのコピー操作をドラッグアンドドロップで受け付けることにする。

ドラッグアンドドロップ操作を受け付けるには最低2つのメソッドを実装する必要がある。1つ目は、-(NSDragOperation)draggingEntered:(id )sender、もう1つは-(BOOL)performDragOperation:(id )senderだ。

draggingEnteredはウィンドウやビューにドラッグされたオブジェクトが重なった時に呼び出され、それを受け付けるかどうか応答するメソッドだ。ドラッグされてきたオブジェクトが何なのかは、NSPasteboard型のペーストボードから取得できる。このメソッドで、NSDragOperationCopyを返せばコピー操作を受け付けることになり、ドラッグされてきたオブジェクトの画像の+サインがつく。

ドラッグアンドドロップでオブジェクトが離された時にドラッグアンドドロップの操作が実行されるが、それを司るのがperformDragOperationメソッドだ。こちらもペーストボードを参照し、ドラッグされたもの、その操作に応じた処理を行う。処理に応じてYES/NOのBOOL値を返す。

ということで、この2つを実装し、ドラッグされて来たものが離された時にファイル名をログに出力するところまで実装した。