LoginSignup
107

More than 5 years have passed since last update.

UIGraphicsBeginImageContextの無駄

Last updated at Posted at 2014-03-21

よく、以下のようなコードでオフスクリーンコンテキストを作成し、
描画の後画像を得るサンプルを見ます。
コードが単純なためか、非常に広く使われていると思います。

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(1024, 1024), NO, 1.0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextFillEllipseInRect(context, CGRectMake(0, 0, 1024, 1024));

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

ですが、これは本来やらなくていい処理をしていると思いませんか?
というのも、
描画のためのコンテキストを一度作成し、UIGraphicsGetImageFromCurrentImageContextでわざわざコピーしてきているのです。
メモリを用意して、そこに描画して、それをそのまま画像にすれば、
この無駄なコピーは本来発生しないはずなんです。
その点を考慮に入れると、本来最小限のコードは例えば以下のようになるかと思います。

補助関数

static void bufferFree(void *info, const void *data, size_t size)
{
    free((void *)data);
}
static size_t align16(size_t size)
{
    if(size == 0)
        return 0;

    return (((size - 1) >> 4) << 4) + 16;
}

メイン処理

    size_t width = 1000;
    size_t height = 1000;
    size_t bitsPerComponent = 8;
    size_t bytesPerRow = align16(4 * width);
    size_t bufferSize = bytesPerRow * height;
    uint8_t *bytes = malloc(bufferSize);
    CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrderDefault;

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = CGBitmapContextCreate(bytes, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);
    CGContextClearRect(context, CGRectMake(0, 0, width, height));
    CGContextFillEllipseInRect(context, CGRectMake(0, 0, width, height));
    CGContextRelease(context);
    context = NULL;

    CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, bytes, bufferSize, bufferFree);
    size_t bitsPerPixel = 32;
    CGImageRef image = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitmapInfo, dataProvider, NULL, NO, kCGRenderingIntentDefault);
    CGDataProviderRelease(dataProvider);
    dataProvider = NULL;
    CGColorSpaceRelease(colorSpace);
    colorSpace = NULL;

    CGImageRelease(image);

ポイントはCGBitmapContextCreateに自分で用意したメモリを使ってもらう点です。
描画が終われば、即座にコンテキストは捨ててしまい、
ラスターデータだけが残るので、そのままCGImageのバッファとして、CGDataProviderCreateWithDataを通して使用します。
bytesPerRowを16バイトアラインメントが推奨されているので、対応してあげる事、
など少し気を配っておいた方がいい点もあります。

というわけで、これにより無駄なコピーを避ける事ができます。
若干コードが多いため、
使い回すなら、関数やクラスに加工して使うと良いでしょう。

画像の扱いは負荷が高いため、
気づくとCPU負荷があがりすぎていたりすると思いますので、気になる場合は試してみてはいかがでしょうか。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
107