寫在前面的話
WebP是Google開發的一種圖像格式,支持圖像數據的有損和無損壓縮。保留動畫和alpha透明通道數據。
可以創建和JPEG、PNG和GIF圖像格式在質量相同或質量更高,但是數據量更小的一種圖像格式。
如下簡單的分析一下webp圖像格式,並使用sdl顯示圖片。
webp項目地址:https://github.com/webmproject/libwebp
sdl項目地址:https://libsdl.org/
webp格式圖片說明
webp格式圖像數據由兩部分組成,RIFF頭和圖像負載信息。
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 'R' | 'I' | 'F' | 'F' |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| File Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 'W' | 'E' | 'B' | 'P' |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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.
祝玩的開心~