描画基礎

なんかの絵が描画できなきゃお話になりません.

そんなわけで、2D描画をやってみました


drawRect

描画のコードは、 UIView::drawRect をオーバーライドすることで実装する.

drawRect の引数となっている CGPoint には、更新された矩形のサイズが入っている.
(描画速度向上のためには、該当部分以外の描画は控えた方がよい?)

再描画を行う際には、 UIView::setNeedsDispay もしくは、 UIView::setNeedsDisplayInRect
メッセージを用いる

durty rect に関して

UIView::setNeedsDisplayInRect メッセージを送信すると、指定矩形に対して drawRect の発行が
行える

複数回の setNeedDisplayInRect の発行があった場合、システムはある周期で dyrty rect の
収集を行い、まとめた drawRect の発行を行う.

# 断片化された、durty rect 更新メッセージを送ったとき、拡張された形で drawRect の矩形が
# 決定されると思われる.

レンダリングコンテキスト

drawRect が呼び出されると、ビューオブジェクトは自動的にレンダリングコンテキストの作成を行う.

レンダリングコンテキストの取得は、以下のように行う

    CGContextRef a_gc = UIGraphicsGetCurrentContext();

CGContextRef は、 CGContext* の typedef.

CGContext を用いることで、C言語関数で実装された CoreGrphics を利用することができる.

C/C++ コードでの描画の実装

CoreGraphics は、Objective-C のオブジェクトで構成されているのではなく、C言語関数で実装されている
ため、そのまま C/C++ コードから呼び出すことができる.

Objective-C は、C/C++のスーパーセットなので、C++クラスの呼び出しが可能.

例として、 CGContext を drawRect から、C++クラスに引き渡す例を挙げる

- (void) drawRect:(CGPoint) i_rect
{
    CGContectRef a_gc = UIGraphicsGetCurrentContext();
    m_myobj->Render( a_gc, m_image );
}
#include <CoreGraphics/CGContext.h>

class MyObj
{
  public:
    MyObj( float i_x, float i_y )
      : m_x( i_x ),
        m_y( i_y )
    {
    }
    ~MyObj(){}

    void Render( CGContextRef i_gc, CGImageRef i_image )
    {
	CGRect a_imageRect;
	a_imageRect.origin.x = m_x;
	a_imageRect.origin.y = m_y;

	a_imageRect.size   = CGSizeMake( 64.0, 64.0 );
        GContextDrawImage( i_gc, a_imageRect, i_image );
    }
  private:
    float m_x;
    float m_y;
};

イメージの読み込みから描画

UIImage を経由して読み込みを行う.

@interface MyView : UIView
{
    CGImageRef m_image;
}
@end

@implementation MyView

- (id)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        UIImage* a_img = [UIImage imageNamed:@"hoge.png"];
        m_image = CGImageRetain( a_img.CGImage );
    }
    return self;
}

- (void) drawRect:(CGPoint) i_rect
{
    CGContextRef a_gc = UIGraphicsGetCurrentContext();
    CGRect a_imageRect;
    a_imageRect.origin.x = 10.f;
    a_imageRect.origin.y = 10.0f;
    a_imageRect.size     = CGSizeMake( 64.0, 64.0 );
    CGContextDrawImage( a_gc, a_imageRect, m_image );
}

@end

イメージ描画時の注意点

CGContextDrawImage を用いた描画の場合、画像が Y軸反転した形で表示される.
これは、画像データが左下を原点(0,0)と置いき、右上を(width,height)と置く座標系で定義されているためである.

UIImage オブジェクトを利用した描画では、座標空間の違いを補正した描画が行えるが、CGContextDrawImage を
用いた場合、自分で補正を行う必要がある.

補正のサンプルを以下に示す

void DrawImage(
               CGContextRef i_gc,
               CGImage* i_image,
               CGRect i_image_rect
               )
{
    CGRect a_imageRect = i_image_rect;
    a_imageRect.origin.y = 0.0f;           // CTM で設定する

    CGContextSaveGState( i_gc );

    CGAffineTransform a_tr = CGAffineTransformIdentity;
    a_tr.d = -1.0f;
    a_tr.ty = i_image_rect.origin.y + i_image_rect.size.height;

    CGContextConcatCTM( i_gc, a_tr );
    CGContextDrawImage( i_gc, a_imageRect, i_image );

    CGContextRestoreGState( i_gc );
}

注: CTM = Current Transformation Matix

サポートされるイメージの種類

  • *.png(推奨)
  • *.tif
  • *.jpg
  • *.bmp
  • *.ico(windows iconファイル)
  • *.cur(windows カーソルファイル)
  • *.xbm(XWindow ビットマップ)

そのほかおもしろそうなこと

  • ビットマップグラフィックスコンテキストの作成

CGBitmapContextCreate() を持ちて、オフスクリーンビットマップのコンテキスト作成が行える.

  • UIImage のデータを jpg/png ファイルとして出力
  • ベクタグラフィックス
  • PDF描画
  • パターン描画