描画基礎
なんかの絵が描画できなきゃお話になりません.
そんなわけで、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
サポートされるイメージの種類
そのほかおもしろそうなこと
- ビットマップグラフィックスコンテキストの作成
CGBitmapContextCreate() を持ちて、オフスクリーンビットマップのコンテキスト作成が行える.
- UIImage のデータを jpg/png ファイルとして出力
- ベクタグラフィックス
- PDF描画
- パターン描画