动态图(三) 动态WebP解码与播放

动态图(三) 动态WebP解码与播放

    • 概述
      • 环境
        • 搭建工程
          • libwebp
      • 解码
      • 播放
      • GitHub

概述

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
  1. Clone libwebp
  2. 用vs新建一个lib工程,名为 libwebp.vcxproj,把llibwebp\src\目录整个拷到工程目录,加入src*.*到工程去

解码

解码目标是使用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, &timestamp)) {
      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;
}

GitHub

https://github.com/conn-public/vs-proj/tree/master/project/WebPDemo

你可能感兴趣的:(图片解码,webp)