寫在前面的話
WebP是Google開發的一種影象格式,支援影象資料的有損和無失真壓縮。保留動畫和alpha透明通道資料。
可以建立和JPEG、PNG和GIF影象格式在質量相同或質量更高,但是資料更小的一種影象格式。
如下簡單的分析一下webp影象格式,並使用sdl顯示圖片。
webp專案地址:https://github.com/webmproject/libwebp
sdl專案地址:https://libsdl.org/
webp格式圖片說明
webp格式影象資料由兩部分組成,RIFF頭和影象負載資訊。
RIFF頭資訊由21個位元組組成。
0-3 四個位元組是 RIFF 四個字元,表示 資源交換格式Resource Interchange File Format的簡寫。
4-7 四個位元組是 WEBP檔案的全部長度,這個長度包含RIFF
8-11 四個位元組是 資源交換格式的名稱,填WEBP這四個字元
12-15 四個位元組是資料塊Chunk的負載資訊的編碼格式,取值有VP8表示無損vp8壓縮,VP8L表示有損vp8壓縮
16-19 四個位元組是有失真壓縮時的VP8資料負載資訊的長度
20-以後數vp8格式的影象資料幀。
VP8格式的定義如下
struct VP8Io {
// set by VP8GetHeaders()
int width, height; // picture dimensions, in pixels (invariable).
// These are the original, uncropped dimensions.
// The actual area passed to put() is stored
// in mb_w / mb_h fields. // set before calling put()
int mb_y; // position of the current rows (in pixels)
int mb_w; // number of columns in the sample
int mb_h; // number of rows in the sample
const uint8_t* y, *u, *v; // rows to copy (in yuv420 format)
int y_stride; // row stride for luma
int uv_stride; // row stride for chroma
const uint8_t* a;
};
VP8使用14位表示影象的寬和高,因此webp格式影象寬高最大是2^14 = 16384畫素。
VP8的每一個畫素的值是通過 左上top-left 上top 右上top-right 和左left這4個畫素做預測得到。
預測的方法如下
// L = left pixel, T = top pixel, TL = top left pixel. // ARGB component estimates for prediction.
int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL);
int pRed = RED(L) + RED(T) - RED(TL);
int pGreen = GREEN(L) + GREEN(T) - GREEN(TL);
int pBlue = BLUE(L) + BLUE(T) - BLUE(TL); // Manhattan distances to estimates for left and top pixels.
int pL = abs(pAlpha - ALPHA(L)) + abs(pRed - RED(L)) +
abs(pGreen - GREEN(L)) + abs(pBlue - BLUE(L));
int pT = abs(pAlpha - ALPHA(T)) + abs(pRed - RED(T)) +
abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));
VP8的詳細分析,後面增加一篇對應的文章分析。
環境準備
centos環境可以先安裝png tiff jpeg和sdl
yum install libjpeg-devel
yum install libpng-devel
yum install libtiff-devel
osx系統可以使用brew命令按照對應的包
使用cmake編譯webp專案後會生成如下小工具
cwebp 工具可以將其他格式圖片轉成webp圖片
./cwebp -h
Usage: cwebp [options] -q quality input.png -o output.webp where quality is between 0 (poor) to 100 (very good).
Typical value is around 80.
dwebp 工具可以將webp圖片轉成png jpg tiff ppm等格式
./dwebp -h
Usage: dwebp in_file [options] [-o out_file] -pam ......... save the raw RGBA samples as a color PAM
-ppm ......... save the raw RGB samples as a color PPM
-bmp ......... save as uncompressed BMP format
-tiff ........ save as uncompressed TIFF format
-pgm ......... save the raw YUV samples as a grayscale PGM
file with IMC4 layout
-yuv ......... save the raw YUV samples in flat layout
vwebp_sdl 工具可以將通過sdl顯示webp圖片
做SDL顯示的一個例子
首先找一張png的圖片,將這個圖片轉成webp格式的影象。
./cwebp leopard2.png -o leopard2.webp Saving file 'leopard2.webp'
File: leopard2.png
Dimension: 842 x 1134
Output: 59610 bytes Y-U-V-All-PSNR 40.28 44.99 46.35 41.45 dB
(0.50 bpp)
block count: intra4: 2898 (77.01%)
intra16: 865 (22.99%)
skipped: 1 (0.03%)
bytes used: header: 203 (0.3%)
mode-partition: 13872 (23.3%)
Residuals bytes |segment 1|segment 2|segment 3|segment 4| total
macroblocks: | 12%| 37%| 10%| 42%| 3763
quantizer: | 36 | 32 | 27 | 16 |
filter level: | 11 | 8 | 12 | 15 |
如下是png格式圖片是webp格式圖片大小的3.4倍,png格式的影象大小是1.4M,webp格式影象的大小是58k
1.4M 9 8 16:29 leopard2.png
58K 9 8 16:30 leopard2.webp
顯示webp圖片
./vwebp_sdl leopard2.webp
SDL影象顯示原理
SDL庫在的影象顯示錶面接收RGB或者YUV格式的影象資料。
因此,若顯示webp格式的影象,需要將webp格式的影象轉成RGB或者YUV格式,將影象資料傳遞給SDL的顯示錶面實現顯示效果。
注:其他格式的圖片也是同樣的做法,其他格式轉成RGB或者YUV格式資料交給SDL顯示錶面。
顯示邏輯如下:
結合SDL顯示影象的原理如下:
SDL顯示webp的主要程式碼
int WebpToSDL(const char* data, unsigned int data_size) {
int ok = 0;
// 第一步 宣告webp的配置和屬性
VP8StatusCode status;
WebPDecoderConfig config;
WebPBitstreamFeatures* const input = &config.input;
WebPDecBuffer* const output = &config.output; SDL_Surface* screen = NULL;
SDL_Surface* surface = NULL;
// 第二步 初始化webp解碼配置資訊
if (!WebPInitDecoderConfig(&config)) {
fprintf(stderr, "Library version mismatch!\n");
return 0;
} if (!init_ok) {
SDL_Init(SDL_INIT_VIDEO);
init_ok = 1;
}
// 第三步 獲取webp影象資料
status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &config.input);
if (status != VP8_STATUS_OK) goto Error; screen = SDL_SetVideoMode(input->width, input->height, 32, SDL_SWSURFACE);
if (screen == NULL) {
fprintf(stderr, "Unable to set video mode (32bpp %dx%d)!\n",
input->width, input->height);
goto Error;
} surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
input->width, input->height, 32,
0x000000ffu, // R mask
0x0000ff00u, // G mask
0x00ff0000u, // B mask
0xff000000u); // A mask if (surface == NULL) {
fprintf(stderr, "Unable to create %dx%d RGBA surface!\n",
input->width, input->height);
goto Error;
}
if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface); #if SDL_BYTEORDER == SDL_BIG_ENDIAN
output->colorspace = MODE_BGRA;
#else
output->colorspace = MODE_RGBA;
#endif
output->width = surface->w;
output->height = surface->h;
output->u.RGBA.rgba = surface->pixels;
output->u.RGBA.stride = surface->pitch;
output->u.RGBA.size = surface->pitch * surface->h;
output->is_external_memory = 1;
// 第四步 解碼webp格式成rgb格式的影象資料
status = WebPDecode((const uint8_t*)data, (size_t)data_size, &config);
if (status != VP8_STATUS_OK) {
fprintf(stderr, "Error decoding image (%d)\n", status);
goto Error;
} if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
// 第五步 將rgb的影象資料顯示到SDL的目標表面,實現顯示
if (SDL_BlitSurface(surface, NULL, screen, NULL) ||
SDL_Flip(screen)) {
goto Error;
} ok = 1; Error:
SDL_FreeSurface(surface);
SDL_FreeSurface(screen);
WebPFreeDecBuffer(output);
return ok;
}
參考材料:
https://developers.google.cn/speed/webp/docs/riff_container
done.
祝玩的開心~