win32 – 使用GDI+播放gif图片

今天做case的时候遇到一个这样的问题,故记录下来。

Codeproject有类似的案例,不过是使用的MFC模板编译的。 因为我们只需要win32程序,所以就....代码如下:

CodeProject: Play GIF using GDI+

另一个是使用双缓冲实现的,我没尝试:win32双缓冲实现gif图片的动态显示 (有兴趣的可以尝试一下)

#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
#define ID_TIMER 101

//Image* m_pImage;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
{
    HWND                hWnd;
    MSG                 msg;
    WNDCLASS            wndClass;
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;

    // Initialize GDI+.
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    wndClass.style = CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = WndProc;
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;
    wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.lpszMenuName = NULL;
    wndClass.lpszClassName = TEXT("GettingStarted");

    RegisterClass(&wndClass);

    hWnd = CreateWindow(
        TEXT("GettingStarted"),   // window class name
        TEXT("Getting Started"),  // window caption
        WS_OVERLAPPEDWINDOW,      // window style
        CW_USEDEFAULT,            // initial x position
        CW_USEDEFAULT,            // initial y position
        CW_USEDEFAULT,            // initial x size
        CW_USEDEFAULT,            // initial y size
        NULL,                     // parent window handle
        NULL,                     // window menu handle
        hInstance,                // program instance handle
        NULL);                    // creation parameters

    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    GdiplusShutdown(gdiplusToken);
    return msg.wParam;
}  // WinMain


PropertyItem* m_pItem = 0;
UINT m_iCurrentFrame = 0;
UINT m_FrameCount = 0;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
    WPARAM wParam, LPARAM lParam)
{
    HDC          hdc;
    PAINTSTRUCT  ps;
    Image* m_pImage = new Image(L"Your_Gif.gif");

    switch (message)
    {
    case WM_CREATE:
    {
        //First of all we should get the number of frame dimensions
        //Images considered by GDI+ as:
        //frames[animation_frame_index][how_many_animation];
        UINT count = m_pImage->GetFrameDimensionsCount();

        //Now we should get the identifiers for the frame dimensions 
        GUID* m_pDimensionIDs = new GUID[count];
        m_pImage->GetFrameDimensionsList(m_pDimensionIDs, count);

        //For gif image , we only care about animation set#0
        WCHAR strGuid[40];
        StringFromGUID2(m_pDimensionIDs[0], strGuid, 40);
        m_FrameCount = m_pImage->GetFrameCount(&m_pDimensionIDs[0]);

        //PropertyTagFrameDelay is a pre-defined identifier 
        //to present frame-delays by GDI+
        UINT TotalBuffer = m_pImage->GetPropertyItemSize(PropertyTagFrameDelay);
        m_pItem = (PropertyItem*)malloc(TotalBuffer);
        m_pImage->GetPropertyItem(PropertyTagFrameDelay, TotalBuffer, m_pItem);
    }
    break;
    case WM_TIMER: {
        //Because there will be a new delay value
        KillTimer(hWnd, ID_TIMER);
        //Change Active frame
        GUID Guid = FrameDimensionTime;
        m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);
        //New timer
        SetTimer(hWnd, ID_TIMER, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, NULL);
        //Again move to the next
        m_iCurrentFrame = (++m_iCurrentFrame) % m_FrameCount;
        InvalidateRect(hWnd, NULL, FALSE);
        break;
    }
    case WM_PAINT:
    {
        HDC hdc;
        PAINTSTRUCT ps;
        hdc = BeginPaint(hWnd, &ps);

        Graphics graphics(hdc);

        //Set Current Frame at #0
        GUID Guid = FrameDimensionTime;
        m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);

        //Use Timer
        //NOTE HERE: frame-delay values should be multiply by 10
        SetTimer(hWnd, ID_TIMER, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, (TIMERPROC)NULL);

        //Move to the next frame
        ++m_iCurrentFrame;
        InvalidateRect(hWnd, NULL, FALSE);

        //Finally simply draw
        graphics.DrawImage(m_pImage, 120, 120, 800, 600);

        EndPaint(hWnd, &ps);
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
} // WndProc

步骤: 创建一个win32的空项目, 然后把代码复制进去。

            代码中你们唯一需要改的就是gif文件路径: Image* m_pImage = new Image(L"Your_Gif.gif"); =》这一行 

            也可以根据需

 

要改变显示图片的大小: graphics.DrawImage(m_pImage, 120, 120, 800, 600);

更新:

gif循环播放代码,

#include <memory>
#include <vector>
#include <algorithm>
#include <windows.h>
#include <objidl.h>
#include <GdiPlus.h>
#include <gdiplusimaging.h>
#include <tchar.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")

#define TIMER_ID 101

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    ULONG_PTR m_gdiplusToken;
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);

    Image gif(_T("spinner.gif"));

    MSG msg = { 0 };
    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hbrBackground = NULL; // <= Do not provide a background brush.
    wc.lpszClassName = L"anim_gif_player";
    if (!RegisterClass(&wc))
        return -1;

    if (!CreateWindow(wc.lpszClassName,
        L"Animated GIF player",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        0, 0, 1080, 800, 0, 0, hInstance, &gif))
        return -2;

    while (GetMessage(&msg, NULL, 0, 0) > 0)
        DispatchMessage(&msg);

    return 0;
}

std::vector<unsigned int> LoadGifFrameInfo(Image* image)
{
    // I think animated gifs will always only have 1 frame dimension...
    // the "dimension" being the frame count, but I could be wrong about this
    int count = image->GetFrameDimensionsCount();
    if (count != 1)
        return std::vector<unsigned int>();

    GUID guid;
    if (image->GetFrameDimensionsList(&guid, 1) != 0)
        return std::vector<unsigned int>();
    int frame_count = image->GetFrameCount(&guid);

    auto sz = image->GetPropertyItemSize(PropertyTagFrameDelay);
    if (sz == 0)
        return std::vector<unsigned int>();

    // copy the frame delay property into the buffer backing an std::vector
    // of bytes and then get a pointer to its value, which will be an array of 
    // unsigned ints
    std::vector<unsigned char> buffer(sz);
    PropertyItem* property_item = reinterpret_cast<PropertyItem*>(&buffer[0]);
    image->GetPropertyItem(PropertyTagFrameDelay, sz, property_item);
    unsigned int* frame_delay_array = (unsigned int*)property_item[0].value;

    // copy the delay values into an std::vector while converting to milliseconds.
    std::vector<unsigned int> frame_delays(frame_count);
    std::transform(frame_delay_array, frame_delay_array + frame_count, frame_delays.begin(),
        [](unsigned int n) {return n * 10; }
    );

    return frame_delays;
}

void GenerateFrame(Bitmap* bmp, Image* gif)
{
    Graphics dest(bmp);

    SolidBrush white(Color::White);
    dest.FillRectangle(&white, 0, 0, bmp->GetWidth(), bmp->GetHeight());

    if (gif)
        dest.DrawImage(gif, 0, 0);
}

std::unique_ptr<Bitmap> CreateBackBuffer(HWND hWnd)
{
    RECT r;
    GetClientRect(hWnd, &r);
    return std::make_unique<Bitmap>(r.right - r.left, r.bottom - r.top);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static Image* animated_gif;
    static std::unique_ptr<Bitmap> back_buffer;
    static std::vector<unsigned int> frame_delays;
    static int current_frame;

    switch (message) {
    case WM_CREATE: {
        animated_gif = reinterpret_cast<Image*>(
            reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams
            );

        if (!animated_gif || animated_gif->GetLastStatus() != 0) {
            MessageBox(hWnd, _T("Unable to load animated gif"), _T("error"), MB_ICONERROR);
            return 0;
        }

        // Create a bitmap the size of the window's clent area
        back_buffer = CreateBackBuffer(hWnd);

        // get the frame delays and thereby test that this is really an animated gif
        frame_delays = LoadGifFrameInfo(animated_gif);
        if (frame_delays.empty()) {
            MessageBox(hWnd, _T("Invalid gif or not an animated gif"), _T("error"), MB_ICONERROR);
            return 0;
        }

        current_frame = 0;
        animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);

        GenerateFrame(back_buffer.get(), animated_gif);

        SetTimer(hWnd, TIMER_ID, frame_delays[0], nullptr);
        InvalidateRect(hWnd, nullptr, FALSE);
    }
                  break;

    case WM_TIMER: {
        KillTimer(hWnd, TIMER_ID);
        current_frame = (current_frame + 1) % frame_delays.size();
        animated_gif->SelectActiveFrame(&FrameDimensionTime, current_frame);
        GenerateFrame(back_buffer.get(), animated_gif);
        SetTimer(hWnd, TIMER_ID, frame_delays[current_frame], nullptr);
        InvalidateRect(hWnd, nullptr, FALSE);
    } break;

    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        Graphics g(hdc);
        g.DrawImage(back_buffer.get(), 0, 0);
        EndPaint(hWnd, &ps);
    } break;

    case WM_SIZE: {
        back_buffer = CreateBackBuffer(hWnd);
        GenerateFrame(back_buffer.get(), animated_gif);
    } break;

    case WM_CLOSE:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

 

原文链接: https://www.cnblogs.com/strive-sun/p/14452464.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    win32 - 使用GDI+播放gif图片

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/404680

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月25日 下午4:40
下一篇 2023年4月25日 下午4:40

相关推荐