WebP是谷歌定义的,如顾名思义(web picture),就是专门优化用于web显示的图片,大概就这样。
定义:
友(you)情(qiang)链接:https://developers.google.com/speed/webp/
无(wu)情(qiang)链接:https://developers.google.cn/speed/webp?hl=zh-cn
GitHub Mirror: https://github.com/webmproject/libwebp
解码:libwebp
显示:GDI+ + Win32
开发环境: VS2015
解码目标是使用libwebp把所有webp帧解析成BGRA流,用来构建后续用来显示的GDI+ Bitmap,部分代码:
//WebP解码
#include "stdafx.h"
#include "WebPLoader.h"
#include "StringUtil.hpp"
BOOL CWebPLoader::LoadImage(const wstring& strImg)
{
//借用了 https://github.com/webmproject/libwebp/blob/master/examples/anim_util.h
return ReadAnimatedImage(CStringUtil::ToStringA(strImg).c_str(), &image);
}
Bitmap* CWebPLoader::GetFrameAt(UINT32 nIndex, UINT32& nDelayMS)
{
if (nIndex >= image.num_frames)
return m_pBmp;
DecodedFrame* pFrame = &image.frames[nIndex];
if (!pFrame->rgba)
return m_pBmp;
Bitmap* pBmp = m_pBmp ? m_pBmp : new Bitmap(image.canvas_width, image.canvas_height);
BitmapData bmd;
Rect rc(0, 0, image.canvas_width, image.canvas_height);
pBmp->LockBits(&rc, ImageLockModeRead | ImageLockModeWrite, PixelFormat32bppARGB, &bmd);
LPBYTE pDst = (LPBYTE)bmd.Scan0;
LPBYTE pSrc = pFrame->rgba;
int rowsize = rc.Width * 4;
for (int h = 0; h < rc.Height; ++h)
{
memcpy(pDst, pSrc, rowsize);
pSrc += rowsize;
pDst += bmd.Stride;
}
pBmp->UnlockBits(&bmd);
m_nCurFrame = nIndex;
m_pBmp = pBmp;
nDelayMS = pFrame->duration;
return pBmp;
}
借用 https://github.com/webmproject/libwebp/blob/master/examples/anim_util.*,并作相应修改,部分代码:
// Read animated WebP bitstream 'webp_data' into 'AnimatedImage' struct.
static int ReadAnimatedWebP(const char filename[],
const WebPData* const webp_data,
AnimatedImage* const image) {
int ok = 0;
int dump_ok = 1;
uint32_t frame_index = 0;
int prev_frame_timestamp = 0;
WebPAnimDecoder* dec;
WebPAnimInfo anim_info;
memset(image, 0, sizeof(*image));
//
//We need BGRA, so we can build gdi+ bitmap directly.
WebPAnimDecoderOptions opt;
memset(&opt, 0, sizeof(opt));
opt.color_mode = MODE_BGRA;
opt.use_threads = 0;
dec = WebPAnimDecoderNew(webp_data, &opt);
if (dec == NULL) {
//WFPRINTF(stderr, "Error parsing image: %s\n", (const W_CHAR*)filename);
goto End;
}
// Main object storing the configuration for advanced decoding
WebPDecoderConfig decoder_config;
// Initialize the configuration as empty
// This function must always be called first, unless WebPGetFeatures() is to be called
if (!WebPInitDecoderConfig(&decoder_config)) {
goto End;
}
// Retrieve features from the bitstream
// The bitstream structure is filled with information gathered from the bitstream
int webp_status = WebPGetFeatures(webp_data->bytes, webp_data->size, &decoder_config.input);
if (webp_status != VP8_STATUS_OK) {
goto End;
}
if (!WebPAnimDecoderGetInfo(dec, &anim_info)) {
fprintf(stderr, "Error getting global info about the animation\n");
goto End;
}
// Animation properties.
image->canvas_width = anim_info.canvas_width;
image->canvas_height = anim_info.canvas_height;
image->loop_count = anim_info.loop_count;
image->bgcolor = anim_info.bgcolor;
// Allocate frames.
if (!AllocateFrames(image, anim_info.frame_count)) return 0;
// Decode frames.
while (WebPAnimDecoderHasMoreFrames(dec)) {
DecodedFrame* curr_frame;
uint8_t* curr_rgba;
uint8_t* frame_rgba;
int timestamp;
if (!WebPAnimDecoderGetNext(dec, &frame_rgba, ×tamp)) {
fprintf(stderr, "Error decoding frame #%u\n", frame_index);
goto End;
}
assert(frame_index < anim_info.frame_count);
curr_frame = &image->frames[frame_index];
curr_rgba = curr_frame->rgba;
curr_frame->duration = timestamp - prev_frame_timestamp;
curr_frame->is_key_frame = 0; // Unused.
memcpy(curr_rgba, frame_rgba,
image->canvas_width * kNumChannels * image->canvas_height);
++frame_index;
prev_frame_timestamp = timestamp;
}
ok = dump_ok;
if (ok) image->format = ANIM_WEBP;
End:
WebPAnimDecoderDelete(dec);
return ok;
}
使用标准Win32工程模板,加个定时器驱动,so easy,部分代码:
#include "stdafx.h"
#include "WebPDemo.h"
#include "GdiplusAutoStartup.hpp"
#include "WebPLoader.h"
// 全局变量:
CWebPLoader g_webpLoader;
UINT32 g_nCurrentFrameID = 0;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// 解码
GDIPLUS_AUTO_STARTUP;
WCHAR szPath[MAX_PATH + 1];
GetModuleFileName(NULL, szPath, _countof(szPath));
PathRemoveFileSpec(szPath);
PathAppend(szPath, L"\\..\\..\\..\\test\\rainbow_cat.webp");
if (!g_webpLoader.LoadImage(szPath))
{
MessageBoxA(NULL, "Error decoding file! Aborting.\n", "Error", MB_ICONERROR|MB_OK);
return 1;
}
//
return 0;
}
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
//first frame
SetTimer(hWnd, 1234, 500, NULL);
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// 绘图代码
RECT rcWin;
GetClientRect(hWnd, &rcWin);
Rect rcBg(0, 0, rcWin.right - rcWin.left, rcWin.bottom - rcWin.top);
// 双缓冲
HDC hMemDC = ::CreateCompatibleDC(hdc);
HBITMAP hBmpOffscreen = ::CreateCompatibleBitmap(hdc, rcBg.Width, rcBg.Height);
HBITMAP hBmpOld = (HBITMAP)::SelectObject(hMemDC, hBmpOffscreen);
Graphics gr(hMemDC);
gr.Clear(0xFFFFFFFFu);
UINT32 nDelayMS = 0;
Bitmap* pBmp = g_webpLoader.GetFrameAt(g_nCurrentFrameID, nDelayMS);
if (pBmp)
{
Rect rc(0, 0, rcWin.right - rcWin.left, rcWin.bottom - rcWin.top);
rc.Offset((rc.Width - (INT)pBmp->GetWidth()) / 2, (rc.Height - (INT)pBmp->GetHeight()) / 2);
rc.Width = pBmp->GetWidth();
rc.Height = pBmp->GetHeight();
gr.DrawImage(pBmp, rc);
}
BitBlt(hdc, 0, 0, rcBg.Width, rcBg.Height, hMemDC, 0, 0, SRCCOPY);
::SelectObject(hMemDC, hBmpOld);
DeleteObject(hBmpOffscreen);
DeleteDC(hMemDC);
EndPaint(hWnd, &ps);
//next frame
SetTimer(hWnd, 1234, nDelayMS, NULL);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_ERASEBKGND:
return 1;
case WM_TIMER:
if (wParam == 1234)
{
g_nCurrentFrameID++;
g_nCurrentFrameID %= g_webpLoader.GetFrameCount();
RECT rc;
GetClientRect(hWnd, &rc);
InvalidateRect(hWnd, &rc, TRUE);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
https://github.com/conn-public/vs-proj/tree/master/project/WebPDemo