Tinted Image
1 概要
これは、もとになる画像を色付けされたイメ−ジに変換するアプリケーションです。それを行うために、NSImage の描画メソッドを利用しています。オプションを変えることによって、さまざまな合成方法を試してみることができます。このサンプルコードを読む上で、ポイントになるところをピックアップして解説していきます。このサンプルを読むことでカテゴリの追加による NSImage の機能の拡張を学べます。
さて、このサンプルのアプリケーションがどんな機能を持っているのか、まずは実際に動かして確かめてみましょう。起動すると次のような画面になります。
下の Original の部分に画像をドロップして、左上のカラーウェルからカラーを選択して、右上のポップアップメニューから、合成方法を指定すると、左側の Tinted の部分に結果のイメージが表示されます。
合成された画像は保存することができます。
注意:この内容は 2003 年 4 月に、個人的な覚え書きとして作成されました。その後一部修正しているものの、古い内容が残っている可能性があることに注意してください。
2 プロジェクトのファイル構成
では、プロジェクトを構成しているファイルを見ていきます。まず、Project Builder でプロジェクトファイル(Tinted Image.pbproj)を開きます。
Classes のところに、「Controller.h」と「Controller.m」がありますが、これはアプリケ−ション全体の制御を行うコントローラークラスです。(NSObjectのサブクラス)。「TintedImage.h」と「TintedImage.m」は、NSImage に新しいカテゴリを追加して、合成した画像を取得できるようにしたものです。
Resources の「MainMenu.nib」に、ユーザーインターフェイス等の定義がなされています。
Frameworks のところに、ApplicationServices.framework が追加されています。
3 MainMenu.nibの構成
次に、MainMenu.nib をダブルクリックして、Interface Builder でその中身を見てみましょう。
MainMenu.nib のウィンドウを見ると、先程出てきた Controller クラスのインスタンスがあります。この Controller には、5つのアウトレットがあって、それぞれ上の図の水色の部分とつながっています。また、ウインドウ上の各パ−ツからは、オレンジ色で示したようなアクションが接続されています。
4 Controller クラス
まず、Controller について見ていきます。
Controller.h
#import <Cocoa/Cocoa.h>
@interface Controller : NSObject
{
IBOutlet id window;
IBOutlet id inputImageView;
IBOutlet id tintedImageView;
IBOutlet id colorWell;
IBOutlet id opsMenu;
NSColor *color;
NSCompositingOperation op;
}
- (IBAction)takeNewImageFrom:(id)sender;
- (IBAction)takeColorFrom:(id)sender;
- (IBAction)saveTintedImage:(id)sender;
- (IBAction)takeOperationFrom:(id)sender;
- (void) updateViews;
- (void) setColor:(NSColor *) aColor;
@end
最初の5つのアウトレットは、Interface Builder 上で作られたアウトレットで、それぞれ上の図のようなインスタンスを参照します。color はセットされたカラーを保存しておくものです。op は選ばれた合成方法を保存しておくものです。
メソッドのうち、最初の4つは、Interface Builder 上でインスタンスから送られることになっていたアクションです。updateViews は、合成方法やカラーの変化に際して、必要な画面の更新作業をまとめて行うメソッドです。setColor: は実際にカラーをセットするメソッドで、べつに takeColorFrom: で行ってもいいのですが、ここではアクセサメソッドをインプリメントする例として定義されているようです。
5 初期化
Controller は、nib ファイルから読み込まれたときに、インスタンスが作成され初期化されます。そこで、awakeFromNib のなかで初期化を行っているようです。
Controller.m > awakeFromNib
- (void) awakeFromNib
{
[self takeColorFrom: colorWell];
[self takeOperationFrom: opsMenu];
}
ここでは、カラーウェルとポップアップメニュ−から値をとって、その値でインスタンス変数を初期化しているようです。2つのメソッドについては、次に述べます。
6 呼ばれるアクション
ウインドウ上の各パーツから呼ばれるアクションについて見ていきます。
Controller.m > takeNewImageFrom:
- (IBAction)takeNewImageFrom:(id)sender
{
if (sender == tintedImageView)
[inputImageView setImage:[sender image]];
[self updateViews];
}
このメッセ−ジは、2つの NSImageView のどちらかに画像がドロップされた時に呼ばれます。ここでは、sender がオリジナル画像のほうであるかチェックして、その時は画像をイメージビューにセットして、画面を更新しています。ここでは、上のようになっていますが、更新は画像をセットしたときだけで十分なような気もします。
Controller.m > takeColorFrom:
- (IBAction)takeColorFrom:(id)sender
{
[self setColor:[sender color]];
[self updateViews];
}
これはカラーウェルで色が設定されたときに呼ばれます。カラーウェルから色を取得して、それを後述する setColor: メソッドでセットしています。そして画面を更新しています。
Controller.m > takeOperationFrom:
- (IBAction)takeOperationFrom:(id)sender
{
op = [[sender selectedItem] tag];
[self updateViews];
}
これはポップアップメニューが選択されたときに呼ばれます。選択項目のタグ値を取得して、それをインスタンス変数に格納しています。そして画面を更新しています。
7 色のセットと画面の更新
次に、アクションメソッドから呼ばれる内部メソッドを見ていきます。
Controller.m > setColor:
- (void) setColor:(NSColor *) aColor
{
if (aColor) {
[aColor retain];
if (color)
[color release];
color = aColor;
}
}
このメソッドでは、取得したカラーをまず retain して、その後で、以前に取得していたカラーをリリ−スしています。このサンプルには、dealloc がありませんが、このようにするなら、本来は dealloc で、その時に retain しているカラーを release してやらなければなりません。
Controller.m > updateViews
- (void) updateViews
{
[tintedImageView setImage:[[inputImageView image]
tintedImageWithColor:color operation:op]];
[[tintedImageView cell] setHighlighted:NO];
// この呼び出しをしないと、イメ−ジウェルは明灰色の背景をもつ
[[inputImageView cell] setHighlighted:NO];
}
ここでは、画像表示部を更新しています。
まず、合成された画像を表示するイメージビューに、合成した画像をセットしています。このとき、NSImage に追加されたカテゴリのメソッドを利用しています。これについては、後述します。
その後で、2つのイメ−ジビューのセルのハイライトをオフにして、背景が描画されないようにしています。
8 画像の保存
メニューの「Save As ...」から、次のメソッドが呼ばれます。
Controller.m > saveTintedImage:
- (IBAction)saveTintedImage:(id)sender
{
[[NSSavePanel savePanel]
beginSheetForDirectory:NSHomeDirectory()
file:@"Tinted Image"
modalForWindow:window
modalDelegate:self
didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:)
contextInfo:NULL];
}
ここでは、シ−トを表示している。終了時の処理は指定した、savePanelDidEnd: returnCode: contextInfo: のなかで行っている。
Controller.m > savePanelDidEnd:returnCode:contextInfo:
- (void)savePanelDidEnd:(NSSavePanel *)sheet
returnCode:(int)returnCode
contextInfo:(void *)contextInfo
{
if (returnCode == NSOKButton) // "save" が選択されたら
[[[tintedImageView image] TIFFRepresentation]
// TIFF イメ−ジを作成して
writeToFile:[NSString stringWithFormat:@"%@.%@",
[sheet filename], @"tiff"]
// それをシ−トから得たファイル名で保存している
atomically:YES];
}
ここでは、保存がなされようとしたら、イメ−ジビューからイメ−ジを取得して、それをファイルに保存しています。キャンセルの時は何もしません。
9 アプリケ−ションのデリゲ−ト
このサンプルでは、NSApplication のデリゲ−トが2つ実装されています。
Controller.m > applicationDidFinishLaunching:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[NSColor setIgnoresAlpha:NO];
// カラーパネルでアルファチャンネルを利用
}
これは、アプリケ−ションが初期化を終了して、ユ−ザ入力を受けとる前に呼び出されます。ここでは、アルファチャンネルを利用できることを宣言しています。
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:
(NSApplication *)theApplication
{
return YES;
}
これは、最後のウインドウが閉じられたときに、アプリケ−ションを終了するかどうかを返します。このサンプルでは新たにウインドウを作る手段がないので、YES を返すように実装しています。あるいは、ウインドウの閉じるボタンを最初から表示しないようにする方法もあるでしょう。
10 TintedImage カテゴリ
TintedImage.h と TintedImage.m では、NSImage に追加のカテゴリを定義して、そのメソッドで、このサンプルの中心となる画像合成を行っています。
TintedImage.h
#import <Cocoa/Cocoa.h>
@interface NSImage (TintedImage)
- (NSImage *) tintedImageWithColor:(NSColor *) tint;
- (NSImage *) tintedImageWithColor:(NSColor *)
tint operation:(NSCompositingOperation) op;
@end
ここで2つのメソッドを定義しています。
TintedImage.m > tintedImageWithColor:
- (NSImage *) tintedImageWithColor:(NSColor *) tint
{
return [self tintedImageWithColor:tint
operation:NSCompositeSourceAtop];
}
これは、下のメソッドを利用した簡略版です。特定の合成方法のみで合成します。
TintedImage.m > tintedImageWithColor: operation:
- (NSImage *)tintedImageWithColor:(NSColor *)tint
operation:(NSCompositingOperation)op
{
NSSize size = [self size];
NSRect imageBounds = NSMakeRect(0, 0, size.width, size.height);
NSImage *newImage = [[NSImage alloc] initWithSize:size];
// 同じサイズの新しいイメ−ジを作成する
[newImage lockFocus];
// 新しいイメ−ジを描画先にして
[self compositeToPoint:NSZeroPoint
operation:NSCompositeSourceOver];
// まず自分のイメ−ジをそこにコピ−する
[tint set]; // 色をセットして
NSRectFillUsingOperation(imageBounds, op);
// 新しいイメ−ジに、合成方法を指定して四角形を塗りつぶし
[newImage unlockFocus];
// 描画先の指定を解除する
return [newImage autorelease];
// 一時的なオブジェクトとして返す
}
ここでは、同じサイズのイメーじを作成して、そこに自分の画像をコピーし、そのあとで、それを四角形で塗りつぶします。この時に合成方法を使って塗りつぶします。これには、ApplicationKit の関数を使っています。
管理人:神吉 秀典 E-mail: