私家版 描画のおさらい
Cocoa の描画はビューを基本(view-based)に行われます。なのでビューへ描画する場合は通常 NSView のサブクラスを作成して drawRect: をオーバーライドします。そして再描画が必要な場合は(ウィンドウサイズが変更されたりとか)メインスレッドのイベントループのなかで自動に drawRect: を呼出します。drawRect: が呼出されているときは Cocoa がすでにそのビューへの描画環境を整えていてくれているので、
ビューへフォーカスがロックされていることや、座標系がそのビューであることを仮定できます。そしてこの再描画が終わると描画環境を再びもとに戻して(ありがとう)くれています。
この drawRect: を直接自分で呼出すことはあまりありません。プログラムのタイミングで再描画が必要になった場合には setNeedsDisplay: に YES を渡して再描画が必要なことを知らせてイベントループの中で再描画を要求するか、displayIfNeeded ですぐに再描画をおこないます。その他の要求するタイミングによっていくつかある display〜 メソッドを使い分けます。
イメージを描画する(1)では再描画がオープンパネルから新しいイメージを指定したときにも行われて欲しいので drawImage を drawRect: の外(setBitmapImageRepWithData:)からも呼んでます。なので lockFocus, unlockFocus を呼出して、描画環境を整えてやる必要がありました。対してイメージを描画する(2)では drawRect: 内なのでロックする必要がありません。
詳しい内容につきましては ”Cocoa Drawing Guide”, ”View Programming Guide” をご参照ください。
どうする?
いままでは bitmapData でポインタを取得して、あれやこれやと行っていました。その他に setColor:atX:y: setPixel:atX:y で各ピクセルにアクセスできますが、これも手間はさほどかわりません。いまは描いた夢をいっきに具体的にしたいのです。
それじゃ NSBitmapImageRep をレシーバに lockFocus, unlockFocus を使えばいいんじゃない?そうだよ、写真家さん、そうしちゃいなよ!...残念なことに NSImage では使えますが、NSBitmapImageRep にはそれがありません。でも写真家はそうゆう星のもとに生まれた人間なので(じゃNSImage 使えよ)こんなことではめげません。そこで NSGraphicsContext を使うのです。
冒頭のリファレンスの通り NSGraphicsContext は描画環境(以下グラフィクス・コンテキスト)を表すオブジェクトへのインターフェイスです。ここを通してどうこうしてやれば NSBitmapImageRep に描画できると期待します。
こうする
グラフィクス・コンテキストはスタック上に保持されていて、saveGraphicsState でスタックにプッシュされ restoreGraphicsState でポップされる。restoreGraphicsState を NSGraphicsContext に送信してスタックから削除しときなさいよと。そうしとけばスタック上の次のグラフィクス・コンテキストがカレントになりますよ。ということでしょうか。
手順は
現在のグラフィクス・コンテキストをスタック上にポップしておいて、その間に NSBitmapImageRep に描画できるようなコンテキストをカレントに設定する。描画が終わったら restoreGraphicsState を呼んで削除する。削除されたら次のグラフィクス・コンテキストが戻ってくる。
じゃあ NSBitmapImageRep に描画できるようなコンテキストはどうする?
+ (NSGraphicsContext *)graphicsContextWithBitmapImageRep:(NSBitmapImageRep *)bitmapRep
ありました。これでちょちょいのちょいです。
では手順を。
- ビューから NSBitmapImageRep のインスタンスを取得する。
- 生成したビットマップ・イメージに描画するためのパスを生成する。
- ビットマップ・イメージのグラフィクス・コンテキストを取得する。
- グラフィクス・コンテキストをスタック上にポップする。
- グラフィクス・コンテキストを変更する。
- ビットマップ・イメージにパスを描画する。
- グラフィクス・コンテキストを元に戻す。
- ビットマップ・イメージをビューに描画する。
Cosine Wave !
今回はビューからビットマップを取得してそれをまたビューに描画しています。写真家はいつも遠回りな人生なのでこうゆうのは慣れっこです。そうです僕が芝生の上で寝っころがりながらみたものは Cosine Wave でした。きれいなコサイン波が描けました。
手順1はメソッド名の通り本来キャッシュのために使用するもののようです。autorelease された NSBitmapImageRep か作成できなければ nil が返ってきます。なので nil をチェックするべきでしょうがしていません。
手順2でコサイン波を NSBezierPath で作成してます。moveToPoint でスタートの位置を決めています。それからループの中で次に線を引くポイントを決め、そこに向けて線を引いて、を繰り返してます。Cocoa の描画モデルは Cocoa Drawing Guide ”The Painter Model” で説明されている通り。いまはまだ図形を作成しただけの状態です。
手順3〜5は前述の通り。”NSGraphicsContext”, ”Cocoa Drawing Gude” にはよく saveGraphicsState と restoreGraphicsState をのバランスを必ずとってね!と書いてあります。
手順6で手順2で作成したパスを実際にグラフィックとして描画しています。パスは適当なグラフィクス・コンテキストのなかで、fill や stroke メッセージを送信してやるとそこに描画されます。ここではグラフィクス・コンテキストが変更されていますので、ビットマップに描画しています。
手順7〜8はグラフィクス・コンテキストを元に戻し、ビューに描画してます。
まだまだ NSGraphicsContext
グラフィクス・コンテキストがもっているグラフィクスの状態(state)をみてみます。
Current transformation matrix (CTM) | 特定のビューの座標系から描画先のデバイスへの座標系へ変換のために指定する。Cocoa はビューの drawRect: を呼出す前に CTM を変更します。CTM の変更は NSAffineTranceform オブジェクトを使います。原点、尺度、回転の座標系を変更できます。 |
Clipping area | 描画されるときに塗りつぶされる描画指定領域を指定する。Cocoa はビューの drawRect: を呼出す前にクリッピング・エリアを可視の領域(visible area)に変更します。NSBezierPath オブジェクトを使って変更できます。 |
Line width | パスの幅を指定する。デフォルトの幅は1.0です。NSBezierPath オブジェクトを使って変更できます。 |
Line join style | 2つの線の結合スタイルを指定する。デフォルトのスタイルは NSMiterLineJoinStyle です。NSBezierPath オブジェクトを使って変更できます。 |
Line cap Style | パスの線端のスタイルを指定する。デフォルトのスタイルは NSButtLineCapStyle です。NSBezierPath オブジェクトを使って変更できます。 |
Line dash style | 破線のパターンを定義する。デフォルトはなく、実線になります。NSBezierPath オブジェクトを使って変更できます。 |
Line miter limit | 線の結合スタイルを NSMiterLineJoinStyle に設定したときのみ適用され、その限度を決定する。結合部分の角は線の幅で決まります。限度を超える場合に角が切り取られます。デフォルトの限度は10.0です。NSBezierPath を使って変更できます。 |
Flatness value | 曲線が描かれるときの精度を指定する。ピクセル単位で計られる最大許容差範囲です。値が小さくなれば曲線が滑らかに描かれますが、計算コストが高くつきます。同じ値でもデバイスによっては解釈がわずかに異なるかも知れません。デフォルトの値は0.6です。NSBezierPath を使って変更できます。 |
Stroke color | 線が描かれるときのカラーを指定する。システムがサポートするカラースペースのカラーが使えます。この値はアルファ値の情報を含んでいます。カラーの情報は NSColor で管理されます。 |
Fill color | 特定の範囲を塗りつぶすカラーを指定する。システムがサポートするカラースペースのカラーが使えます。この値はアルファ値の情報を含んでいます。カラーの情報は NSColor で管理されます。 |
Shadow | 描かれる内容に適用するシャドウの属性を指定する。設定は NSShadow クラスを使っておこないます。 |
Rendering intent | 設定されているカラー・スペースをを現在のカラー・スペースにマッピングする方法を指定する。Cocoa では直接この属性を設定できません。Quartz を使ってください。 |
Font name | テキストを描画するとき使用するフォントを指定する。フォント情報は NSFont クラスを使って変更できます。 |
Font size | テキストを描画するとき使用するフォント・サイズを指定する。フォント情報は NSFont クラスを使って変更できます。 |
Font character spacing | テキストを描画するとき使用する字間(character spacing)を指定する。Cocoa でこの属性は間接的にしかサポートされていません。 |
Text drawing mode | テキストをどのように描画するかを指定する。Cocoa でこの属性は間接的にしかサポートされていません。 |
Image interpolation quality | 画像のサイズを変更した場合の補完の処理の仕方を指定する。NSGraphicsContext クラスでこの設定を変更できます。 |
Compositing operation | ソースと描画先のイメージのブレンド処理の仕方を指定する。Cocoa でサポートされるブレンド・モードは Quartz と関連がありますが、使用方法と動作が異なります。NSGraphicsContext クラスでデフォルト値を指定できます。 |
Global alpha | 透明度を指定する。Cocoa は直接的にこの属性をサポートしてません。値の変更は Quartz の CGContextSetAlpha を使用しなければなりません。 |
Anti-aliasing setting | アンチエイリアシングを指定する。NSGraphicsContext クラスでこの設定を変更できます。 |
ウィンディングの規則(winding rule:開いたパス線が交わるときに囲む領域の処理の仕方)は現在のグラフィクス・コンテキストには保存されません。NSBezierPath オブジェクトでデフォルト値を設定してください、とのこと。今回使った NSColor の set はStroke Color と Fill Color の両方を設定します。それぞれ別に設定する場合は、setStroke, setFill です。
0 件のコメント:
コメントを投稿