如何在 visual studio C++ 中将 GIF 显示为启动画面

How to display GIF as splash in visual studio c++

我正在尝试在 visual studio 中显示一张 GIF 图片作为我的小程序的启动画面。我真的快疯了。我看起来在 Qt IDE 中是可能的,但我在 visual studio 中确实需要它,因为我的其他代码仅适用于 visual studio。是的,我尝试将我的代码转换为 Qt,但出现了太多错误。

我看过this post.

我正在使用 GDI+,但仍然不知道如何简单地显示它而不是播放和停止。显示播放 GIF 文件的小窗体而不是飞溅也没关系,你们能给我一个小代码片段,说明如何在 C++ 中执行此操作吗?

谢谢。

这是一个 MFC window class,它使用 GDI Plus 实现启动画面以显示(动画)GIF。

我已将所有内容封装在一个 header 文件中,以简化它在您的项目中的使用。将其保存为 .h 文件(可能是 "SplashWnd.h"),然后将其包含在您想要设置启动画面的任何位置。您应用的 InitInstance 可能是添加它的好地方 - 类似于此行(在任何 DoModal 调用之前):

SplashWnd splash(_T("Filname.gif"));

构造函数还可以在 auto-closing 之前获取控制延迟的参数,还可以指定在启动关闭时调用的函数。

启动画面 window 没有边框或标题 - 它仅显示为加载的图像,漂浮在任何其他 windows 之上。它的图标不会出现在任务栏中,并且会在超时到期或用户单击 window 或按下某个键时关闭。

#pragma once
#include <functional>
#include <afxwin.h>
#include <gdiplus.h>
#pragma comment(lib,"gdiplus.lib")

inline void ManageGdiPlusInit(bool release=false) {
    static int refcount = 0;
    static ULONG_PTR token;
    if(release) {
        if(--refcount == 0) { 
            Gdiplus::GdiplusShutdown(token); 
        }
    } else if(++refcount == 1) {
        Gdiplus::GdiplusStartupInput startup_input;
        Gdiplus::GdiplusStartup(&token, &startup_input, 0);
}   }

inline void GdiPlusInit()    { ManageGdiPlusInit(false); }
inline void GdiPlusRelease() { ManageGdiPlusInit(true); }

namespace {
class SplashWnd : public CWnd {
protected:
    static CString WindowClass() {
        static CString name;
        if(name.IsEmpty()) {
            name = AfxRegisterWndClass(CS_DROPSHADOW, 0, (HBRUSH)GetStockObject(GRAY_BRUSH), 0);
        }
        return name;
    }

    Gdiplus::Image        *m_pImage;
    UINT                   m_FrameCount;
    unsigned char         *m_FrameDelayData;
    const UINT            *m_FrameDelays;
    UINT                   m_CurFrameIndex;
    UINT                   m_AnimationTimerId;
    UINT                   m_ExpireTimerId;
    CRect                  m_WindowRect;
    std::function<void()>  m_DismissCallback;

    DECLARE_MESSAGE_MAP()

    afx_msg void OnLButtonDown(UINT nFlags, CPoint point) {
        DestroyWindow();
    }

    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
        DestroyWindow();
    }

    afx_msg void OnDestroy() {
        if(m_AnimationTimerId != UINT(-1)) {
            KillTimer(m_AnimationTimerId);
        }
        if(m_ExpireTimerId != UINT(-1)) {
            KillTimer(m_ExpireTimerId);
        }
        if(m_DismissCallback) {
            m_DismissCallback();
        }
        CWnd::OnDestroy();
    }

    afx_msg void OnTimer(UINT nIDEvent) {
        if(nIDEvent == m_AnimationTimerId) {
            if(++m_CurFrameIndex >= m_FrameCount) {
                m_CurFrameIndex = 0;
            }
            DrawCurFrame();
            KillTimer(m_AnimationTimerId);
            m_AnimationTimerId = SetTimer(1, m_FrameDelays[m_CurFrameIndex], 0);
            return;
        }
        if(nIDEvent == m_ExpireTimerId) {
            DestroyWindow();
            return;
    }   }

    void PostNcDestroy() {
        if(m_DeleteSelf) {
            delete this;
        }
    }

    void DrawCurFrame() {
        Gdiplus::Graphics g(m_hWnd);
        GUID dim_select_id = Gdiplus::FrameDimensionTime;
        m_pImage->SelectActiveFrame(&dim_select_id, m_CurFrameIndex);
        g.DrawImage(m_pImage, 0, 0, m_WindowRect.Width(), m_WindowRect.Height());
    }

public:
    // set m_DeleteSelf to true if a SplashWnd is created with new, and you want it to
    // auto-delete itself when the window expires or is dismissed.
    bool m_DeleteSelf;

    // file_path    the gif file path
    // ExpireMs     the time, in milliseconds until the window automatically closes itself
    // WidthFactor  the fraction of the width of the primary display to use as the splash screen's width
    // HeightFactor the fraction of the height of the primary display to use as the height
    // If WidthFactor or HeightFactor are 0, the original image aspect ratio is preserved
    // If both are 0, the original image size, in pixels is used
    SplashWnd(CString file_path, DWORD ExpireMs=2000, double WidthFactor=0.4, double HeightFactor=0) {
        GdiPlusInit();

        m_pImage = new Gdiplus::Image(file_path);

        // Set up an array of frame times for animated images
        UINT dimension_count = m_pImage->GetFrameDimensionsCount();

        GUID dimension_id;
        m_pImage->GetFrameDimensionsList(&dimension_id, 1);
        m_FrameCount = m_pImage->GetFrameCount(&dimension_id);
        UINT frame_delay_size = m_pImage->GetPropertyItemSize(PropertyTagFrameDelay);

        m_FrameDelayData = new unsigned char[frame_delay_size];
        Gdiplus::PropertyItem* frame_delay_item = reinterpret_cast<Gdiplus::PropertyItem*>(m_FrameDelayData);
        m_pImage->GetPropertyItem(PropertyTagFrameDelay, frame_delay_size, frame_delay_item);
        m_FrameDelays = reinterpret_cast<const UINT*>(frame_delay_item->value);

        // Figure out the size and location of the splash window
        int primary_width  = GetSystemMetrics(SM_CXFULLSCREEN);
        int primary_height = GetSystemMetrics(SM_CYFULLSCREEN);

        int splash_width  = int(primary_width * WidthFactor);
        int splash_height = int(primary_height * HeightFactor);

        if(splash_width == 0) {
            if(splash_height == 0) {
                splash_width  = m_pImage->GetWidth();
                splash_height = m_pImage->GetHeight();
            } else {
                splash_width = primary_width * splash_height / primary_height;
            }
        } else if(splash_height == 0) {
            splash_height = primary_height * splash_width / primary_width;
        }

        int l = (primary_width - splash_width) / 2;
        int t = (primary_height - splash_height) / 2;
        int r = l + splash_width;
        int b = t + splash_height;

        m_WindowRect.SetRect(
            (primary_width  - splash_width)  / 2,
            (primary_height - splash_height) / 2,
            (primary_width  + splash_width)  / 2,
            (primary_height + splash_height) / 2);

        // WS_EX_TOPMOST makes the window cover up other, regular windows
        // WS_EX_TOOLWINDOW prevents an icon for this window in the taskbar
        // WS_POPUP prevents caption and border from being drawn
        CreateEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, WindowClass(), _T("Splash"), WS_VISIBLE | WS_POPUP, m_WindowRect, 0, 0);

        // Show the first frame
        m_CurFrameIndex = 0;
        DrawCurFrame();

        // Set up the frame-flipping animation timer
        m_ExpireTimerId = m_AnimationTimerId = UINT(-1);
        if(m_FrameCount > 1) {
            m_AnimationTimerId = SetTimer(1, m_FrameDelays[m_CurFrameIndex], 0);
        }
        // Set up the expiration timer
        if(ExpireMs != INFINITE) {
            m_ExpireTimerId = SetTimer(2, ExpireMs, 0);
        }

        m_DeleteSelf = false;
    }

    // Constructor which takes a callback function which will be called when the splash window closes
    template <typename F>
    SplashWnd(CString file_path, DWORD ExpireMs, double WidthFactor, double HeightFactor, F DismissCallback)
        : SplashWnd(file_path, ExpireMs, WidthFactor, HeightFactor)
    {
         m_DismissCallback = DismissCallback;
    }

    ~SplashWnd() {
        delete [] m_FrameDelayData;
        delete m_pImage;
        GdiPlusRelease();
    }
};

// Message map, usually in an implementation file, but here encapsulated inside the header
// using an anonymous namespace to prevent possible ODR problems.
BEGIN_MESSAGE_MAP(SplashWnd, CWnd)
    ON_WM_KEYDOWN()
    ON_WM_LBUTTONDOWN()
    ON_WM_TIMER()
    ON_WM_DESTROY()
END_MESSAGE_MAP()

}