Edited at

libwebp を使って iOS で WebP を表示する

More than 3 years have passed since last update.

ファイルサイズを削減できる WebP。iOS で WebP を使用する OSS を見かけますが、使用用途にイマイチマッチしなかったので、libwebp を直接使用して描画することにしました。やってみると以外と簡単。


iOS framework のダウンロードとインストール

webp を iOS で使用するために framework 形式で Google が公開しているのでこちらをダウンロードします。



  • downloads repository


    • この記事を書いた時点での最新は libwebp-0.5.0-ios-framework.tar.gz



tar.gz を解凍するとそのまま framework のフォルダ構成になっているので、WebP.framework という名前のフォルダに解凍。

xcode の 「Link Binary With Libraries」 に WebP.framework を追加します。


webp 画像の描画

webp から UIImage を作るのは次の順序です。


  1. ファイルのロード

  2. WebPDecodeARGB を呼び出してデコード

  3. デコードしたバッファから CGDataProvider を作成

  4. CGDataProvider から CGImage を作成

  5. CGImage から UIImage を作成

libwebp で使用するのは WebPDecodeARGB だけです。この関数は WebP をデコードするもので、この戻り値は次のフォーマットになっています。

[a0, r0, g0, b0, a1, r1, g1, b1, ...]

参照:WebP API Documentation

これはそのまま CGImageRef のバッファーとして使用できるので、CGDataProvider でラップして CGImageCreate に渡します。

- (UIImage*)loadImageAtPath:(NSString*)path

{
// ファイルのロード
NSData* data = [[NSData alloc] initWithContentsOfFile:path];
size_t length = data.length;
uint8_t* image = (uint8_t*)data.bytes;

// デコード
int width, height;
uint8_t* decoded = WebPDecodeARGB(image, length, &width, &height);
if (decoded == NULL) {
return nil;
}

// デコードしたバッファから CGDataProvider を作成
self.size = CGSizeMake(width, height);
CGDataProviderRef provider =
CGDataProviderCreateWithData(NULL, decoded, width * 4 * height, releaseBuffer);

// CGDataProvider から CGImage を作成
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGImageRef cgImage = CGImageCreate(width,
height,
8, // ビット深度、デコードバッファは uint8 なので 8bit
32, // ピクセルあたりのビット数、8x4(ARGB) なので 32bit
width * 4, // 1ラインあたりのバイト数、4byte(32bit) かける幅
space,
kCGImageAlphaNoneSkipFirst, // 1byte目のアルファ値を無視する
provider,
NULL,
NO,
kCGRenderingIntentDefault);

// CGImage から UIImage を作成
UIImage* uiImage = [UIImage imageWithCGImage:cgImage];

CGImageRelease(cgImage);
CGColorSpaceRelease(space);
CGDataProviderRelease(provider);

return uiImage;
}

static void releaseBuffer(void *info, const void *data, size_t size)
{
// WebPDecodeARGB で取得したバッファは free で開放します。
free((void*)data);
}


 描画速度

測定したところ JPEG が 1~10ms ぐらいに対して 10 ~ 100ms なので10倍程度かかってます。一方でファイルサイズは半減しているのでネットワークからファイルを取得する場合は向いているかもしれません。