[iOS] グラフィックス関係のおおざっぱな説明 Quarz 2D, Core Graphics, Core Animation

2012/04/20

こんにちは。きんくまです。

iOSのグラフィックス関係はすごく大きくて、最初はどこから手をつけていいのかわかりませんでした。
そんで、概要みたいのが合った方が、理解が早いと思い、私のおおざっぱな理解をメモしておきます。

グラフィックスには、大きく2つの要素があります。
描画の方法と、描画される入れ物です。

描画について

大きく分けて、2つです。
Quartz 2D APIとOpenGL ES。

Quartz 2D APIはCore Graphics frameworkの一部です。
メソッドの頭文字にCGとつくものがそれだったりします。

OpenGL ESはAppleとは別のところで決めているものです。

今回はQuarz 2Dについてもう少し書きます。

Quarz 2DにはGraphics Contextというのがあります。
グラフィックスの文脈なんですが、これって何なのでしょう?

これは、簡単に言うと、描画先を指定するものです。
例えば、画面だったり、ビットマップだったり、PDFだったり、
描画先を指定できるのです。

同じ描画命令(例えば矩形を描けとか)を書いたとしても、
その描画先を変えることで、PDFに描画したり、画面の中のあるパーツに描画したり
といったことが可能になるんですね。

Quarz 2Dの実際の描画としてはこんな感じです。

1. 描画されるものの指定
2. 描画の開始命令
3. 各描画命令
4. 描画の終了命令

具体例を2つほど。

+ (UIImage *)imageWithClipedRect:(UIImage *)sourceImage clipedRect:(CGRect)rect
{
    UIImage *clipedImage;
    UIGraphicsBeginImageContext(rect.size); //1. 描画されるものの指定 + 2. 描画の開始命令
    //3. 各描画命令
    [sourceImage drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    clipedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext(); //4. 描画の終了命令
    return clipedImage;
}

上のコードは、あるUIImageの中から指定した矩形部分のUIImageを作り出して返すものです。
この場合は、GraphicsContextがビットマップになっています。

- (void)drawRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext(); //1. 描画されるものの指定

    //3. 各描画命令
    CGContextSetAllowsAntialiasing(ctx, NO);

    CGContextSetRGBStrokeColor(ctx, 1, 1, 0, 1.0);
    CGContextMoveToPoint(ctx, 0, 1);
    CGContextSetLineWidth(ctx, 1.0);
    CGContextAddLineToPoint(ctx, self.frame.size.width, 1);
    CGContextStrokePath(ctx);
}

今度のコードはUIViewの中のコードです。2と4が省略されています。
UIViewの中のdrawRect:は特別なメソッドで、デフォルトだと自分の中のCALayerに描画します。
描画先がわかっているので、描画開始命令と終了命令が省略されているのだと思います。
あと、CALayerというのは、描画される入れ物の方です。あとで説明します。

入れ物について

入れ物については、Core Animationというのが関係しています。
Core Animationというので、アニメーションのことだけかと思うのですが、アニメーションされる
入れ物のこと(CALayer)も扱っています。

UIViewには次の性質があります。

1. UIViewの子供を持つことができる(コンテナ)
2. CALayerという描画できる入れ物を持っている
3. タッチイベントを受け取ることができる

1と3は、普通に出てくることなので、ご存知の方も多いと思います。ではCALayerとは何でしょうか?

CALayerの性質

1. CALayerの子供を持つことができる(コンテナ)
2. Quarz 2Dを使って中に描画することができる
3. タッチイベントは受け取れない

UIViewにはCALayerを自分直下にひとつだけもつことが可能です。
そのCALayerには、さらにいくつも子供のCALayerを吊るすことが可能です。
CALayerは描画処理だけを担当しているので、非常に速い描画が可能です。
描画のことだけを考えた場合は、UIViewをいくつも入れ子にするのではなく、
CALayerを入れ子にした方が良いです。

また、CALayerはそれ自体に、3Dのアフィン変換を適用したりできます。
3Dモデリングを表示したりするのには、OpenGLが必要ですが、
3Dのエフェクト処理、例えば手前と奥に矩形を表示するとか、
Appleのカバーフローなんかも、これだけで作成可能です。

あとは、CALayerにアニメーション処理をつけることも可能です。
Core Animationにはimplicit(暗黙)とexplicit(明示)の2つのタイプのアニメーションがあります。
layerのプロパティ、例えばframeを変えたりすると、自分でexplicitしていなければ、
implicitが適用されて勝手にアニメーションされます。

具体的なコードはまたの機会に説明しますが、概要がわかってしまえば、
いろんなところにあるサンプルコードを読みやすくなると思います。

今回はデモとして、タップするたびに、くるくるまわるCALayerを作ってみました。
今回はアニメーションの設定を自分でしていないのでimplicitのアニメーションが適用されます。

CATransform3d-demo1

#import "KKViewController.h"
#import <QuartzCore/QuartzCore.h>

@interface MyLayer : CALayer
- (void)flipToAngle:(float)angle;
@end

@implementation MyLayer

- (id)initWithLayer:(id)layer
{
    self = [super initWithLayer:layer];
    if(self){
        [self setNeedsDisplay];
    }
    return self;
}

- (void)flipToAngle:(float)angle
{
    CATransform3D trans = CATransform3DIdentity;
    trans.m34 = -1.0 / 100; //パースペクティブ
    trans = CATransform3DRotate(trans, angle / 180.0 * M_PI, 0, 1, 0);
    self.transform = trans;
}

//CALayerはdrawRectでなくこっち
- (void)drawInContext:(CGContextRef)ctx
{
    CGContextSetRGBFillColor(ctx, (rand() % 90)/90.0 + 0.1, 0.0, 0.0, 0.5);
    CGContextFillRect(ctx, CGRectMake(0, 0, 50, 50));
}

@end


@interface KKViewController (){
    NSMutableArray *mylayers;
    int numberOfLayer;
}

@end

@implementation KKViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    numberOfLayer = 50;
    srand(time(NULL));
    mylayers = [[NSMutableArray alloc] init];
    CGRect rect;
    int layerW = 50;
    for(int i = 0; i < numberOfLayer; i++){
        rect = CGRectMake(rand() % (320 - layerW), rand() % (480 - layerW), layerW, layerW);
        MyLayer *myLayer = [[MyLayer alloc] initWithLayer:[CALayer layer]];
        myLayer.frame = rect;
        [self.view.layer addSublayer:myLayer];
        [myLayer release];
        [mylayers addObject:myLayer];
    }

}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    for(int i = 0; i < numberOfLayer; i++){
        MyLayer *myLayer = [mylayers objectAtIndex:i];
        [myLayer flipToAngle:rand() % 360];
    }
}

@end

■追記

CALayerの具体的な使い方は、こないだ買った本がすごくわかりやすかったです。
>> iPhoneアプリ開発のコツとツボ35

あとは、何といってもAppleのドキュメントです。
私は日本語の方は読んでませんが、日本語訳もされてます。
>> https://developer.apple.com/jp/devcenter/ios/library/japanese.html

いっぱいあるのですが、今回関係するのは以下の2つの項目です。
iOS描画および印刷ガイド(Drawing and Printing Guide for iOS)
Core Animationプログラミングガイド(Core Animation Programming Guide)


自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る