水曜日, 9月 14, 2011

Cocoaでの上下逆さま

Cocoaでプログラミングを始めて惑ったことのひとつに
座標系(Cordinate system)がWindowsなどと違うことだ。
多くの言語では画面上の座標の原点は左上隅にあり、
垂直方向の座標は下に行くほど大きくなる。
Cocoaでは原点は左下にあり、垂直方向に上にいくほど座標が
大きくなる。(これは数学や、日常的な座標系とおなじである。)
とはいえ、このこと自体は慣れると思う。

ただ、左下隅が原点であるということによる不都合もある。
スクロールビュー(NSScrollView)の中に表示するビュー(NSViewなど)
がデフォルトでは左下隅にくっついてしまうのだ。

外側の青い部分がスクロールビュー
内側の白い部分が内容を表示するビュー






この状態では、アプリケーションの使い勝手が悪くなってしまう。
これをこういう状態になおしたい。


この場合、内側のビュー(NSViewなどを継承したビュー)
のクラスにて isFlipped をオーバーライドし、戻り値としてYESを返すようにするだけでよい。
- (void)isFlipped{
    return YES;
}




ビューに直接描画しているのであれば、これでOKなのであるが、
いったんレイヤー(CGLayer)に描画してからビューに投影している
ような場合、不都合が残っている。


左図のように鏡で上下が逆さまになった状態となってしまう。
レイヤーはFlip(上下反転)していないままそこに Hello と描画している。そのレイヤーをFlipされたビューに描画する際にひっくりかえるのだ。


よって、レイヤーもFlipしておく必要がある。
レイヤーに描画する前にそのコンテキストに対して以下を施す。



CGContextTranslateCTM(layerContext, 0, layerHeight );

CGContextScaleCTM(layerContext, 1, -1);


一つ目で、原点を垂直方向に移動させる座標変換がコンテキストに登録され
二つ目で、垂直方向の座標の符号を逆転する座標変換が登録される。
例えば横幅100 x 縦100 のサイズを持つレイヤー上の
座標(10, 20) はFlip(上下反転)すると
一つ目の変換では 座標(0, 100) を原点とした場合の座標に変換されれるので
(10, -80) となる。
二つ目の変換では垂直方向の符号を逆転するので、(10, 80) となる。

※つまり下から20ポイントの場所は高さ100ポイントの場所からみると
下に向かって80ポイントの場所にあたるということである。

このようにしてFlipされたレイヤーには上下反転して描画される。
そのレイヤーをFlipされたViewに描画すると、さらに上下反転され
正常な状態で表示される。

金曜日, 9月 02, 2011

COCOA備忘

これは自分のための備忘です。すみません。


  • Resources フォルダのファイルパスをプログラム実行時に取得するコード
    • [[NSBundle mainBundle] pathForResource:@"filename" ofType:@"extension"]

  • CGLayer上にテキストを出力する方法。
    NSViewなどに出力する時はNSStringのdrawInRectが使えるが、CGLayerに出力するケースには使えない。この場合CoreTextを使う方法が簡単そうであった。
    • NSAttributedStringを生成
      NSAttributedString *attrStr = [[NSAttributedString allocinitWithString:@"こんにちは" attributes:fontAttributes]; 
    • CTLineCreateWithAttributedStringを使って CTLineRefを生成
      CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrStr);
    • CGContextSetTextPositionを使って出力位置を指定
      CGContextSetTextPosition(context, pt.x, pt.y);
    • CTLineDrawを使って文字を出力
      CTLineDraw(line, context);
    • メモリの解放
      CFRelease(line);
      [attrStr release];

  • イメージの特定の色を透過させる方法。
    このような処理を Maskingと呼ぶ。
    rect はNSRect型で表示したい場所をさしている。
    ctx はグラフィックコンテキストをさしている。
    • 元のイメージが NSImage *nsimg に生成済みだとする。
      これからCGImageRefを取得する。
      CGImageRef cgimg = [nsimg CGImageForProposedRect:&rect context: ctx hints: [[NSDictionary alloc] init]];
    • マスキングカラーを設定する
      CGFloat maskingColors[6] = {255,255,255,255,255,255};
      //左から二つずつ組となりそれぞれR,G,Bの範囲を示している。
    • マスキングをかけたイメージを生成する
      CGImageRef maskedImage = CGImageCreateWithMaskingColors(cgimg, maskingColors);
    • コンテキストに描く
      CGContextDrawImage(ctx, rect, maskedImage);

木曜日, 9月 01, 2011

数学

先日、友人と飲みにいったときに、やっぱり、
数学は大事だよね。ということになった。

プログラムを作る上で、何か変わったことをしたいと
思った時、数学的な知識の不足を感じるのだ。
例えば、画面上に表示する図形を傾けるだけでも
角度を求めるために三角関数を使ったりする。
プログラミング言語には数学関数のライブラリがある訳だから
sin も cos も tan も使えばいいのだが、高校以来
そういうものは忘れてしまっているので、
まずそこから調べねばならなくなる。
先日、QRコードの誤り訂正コードのことを書きましたが、
あれは 有限体とかガロア体というもの性質を応用している訳だが
そういうものは、さっぱりわからぬ。
誤り訂正という技術は、通信データの補正などにも使われている
そうだ。そういうものがあるので、はやぶさのような宇宙探査機と
地球との間で通信ができるのだ。
インターネットで使う暗号技術だって、数学者が生み出した。
数学がないと安心してネットで買い物が出来ないのだ。

そう、実は数学は世の中を支配しているのだ。
という結論で飲み会はお開きになった。