ファイルサイズを削減できる 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 を作るのは次の順序です。
- ファイルのロード
- WebPDecodeARGB を呼び出してデコード
- デコードしたバッファから CGDataProvider を作成
- CGDataProvider から CGImage を作成
- CGImage から UIImage を作成
libwebp で使用するのは WebPDecodeARGB だけです。この関数は WebP をデコードするもので、この戻り値は次のフォーマットになっています。
[a0, r0, g0, b0, a1, r1, g1, b1, ...]
これはそのまま 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倍程度かかってます。一方でファイルサイズは半減しているのでネットワークからファイルを取得する場合は向いているかもしれません。