今天做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;
}

win32 - 使用GDI+播放gif图片的更多相关文章

  1. 最简单的视音频播放示例2:GDI播放YUV, RGB

    前一篇文章对“Simplest Media Play”工程作了概括性介绍.后续几篇文章打算详细介绍每个子工程中的几种技术.在记录Direct3D,OpenGL这两种相对复杂的技术之前,打算先记录一种和 ...

  2. 【C++自绘控件】如何用GDI+来显示图片

    在我们制作一个应用软件的时候往往需要在窗口或控件中添加背景图.而图片不仅有BMP格式的,还有JPEG.PNG.TIFF.GIF等其它的格式.那么如何用jpg格式的图片来当背景呢? 这里用到了GDI+, ...

  3. Android 播放gif图片

    Android的原生控件并不支持播放GIF格式的图片.我们都知道,在Android中如果想要显示一张图片,可以借助ImageView控件来完成,但是如果将一张GIF图片设置到ImageView里,它只 ...

  4. 非常优秀的swiper插件————幻灯片播放、图片轮播

    http://www.idangero.us/ http://www.swiper.com.cn/ Swiper中文网 2015-10-15 SuperSlide2: (这是个PC用的滚屏插件,看着不 ...

  5. GDI+用PNG图片做半透明异型窗口

    {*******************************************************} {                                          ...

  6. 53、Gif 控件GifView 的使用,播放gif图片

    GifView 是一个为了解决android中现在没有直接显示gif的view,只能通过mediaplay来显示这个问题的项目,其用法和 ImageView一样,支持gif图片.可监视GIF是否加载成 ...

  7. win32用GDI+加载png图片作为背景图

    #include <windows.h> #include <gdiplus.h> /* GDI+ startup token */ ULONG_PTR gdiplusStar ...

  8. GDI+中GIF图片的显示

    某位网友曾经问过我GDI+中Gif图像显示的问题,一直没时间给你写,在此致歉.我把这篇文章送给他. 一.GIF格式介绍 1.概述 GIF(Graphics Interchange Format,图形交 ...

  9. GDI+ 支持的图片文件格式

    您可以使用许多标准格式将位图储存在磁盘文件中.GDI+ 支持以下各种图片文件格式. o 位图 (BMP) 位图是 Windows 用来储存设备无关和与应用程序无关的图片的标准格式.文件头决定了指定的位 ...

  10. windows api(GDI)实现图片旋转

    GDI实现图片旋转,博主在网上找了好多资料,都不太如意. 并且在尝试中发现,如果先用SetViewportOrgEx将HDC上的坐标原点移动到图片中心:再在HDC上的获取每个点,用三角函数进行变换,算 ...

随机推荐

  1. [转帖]容器环境的JVM内存设置最佳实践

    https://cloud.tencent.com/developer/article/1585288 Docker和K8S的兴起,很多服务已经运行在容器环境,对于java程序,JVM设置是一个重要的 ...

  2. [转帖]018、数据库管理之TiDB升级

    升级 使用TiUP进行补丁升级(HotFix) 版本升级流程 升级准备-更新TiUP 升级准备- 编辑TiUP Cluster 升级准备- 集群监控状态检查 升级TiDB 集群 验证TiDB集群升级结 ...

  3. [转帖]Linux中find命令使用示例

    https://zhuanlan.zhihu.com/p/99170116 Linux查找命令是类Unix操作系统中最重要且最常用的命令行实用程序之一. 查找命令可以根据你设定的参数匹配的文件指定的条 ...

  4. [转帖]InnoDB表聚集索引层高什么时候发生变化

    导读 本文略长,主要解决以下几个疑问 1.聚集索引里都存储了什么宝贝 2.什么时候索引层高会发生变化 3.预留的1/16空闲空间做什么用的 4.记录被删除后的空间能回收重复利用吗 1.背景信息 1.1 ...

  5. [转帖]SPECjvm测试工具详解

    ARM服务器测试大纲中指定了要使用specjvm测试Java虚拟机性能,所以就上网找开源的测试套. 简介 SPECjvm2008(java虚拟机基准测试)是用来测试java运行环境(JRE)性能的基准 ...

  6. python中,Microsoft Visual C++ 14.0 or greater is required问题解决方案

    今天在写一个小程序,安装依赖的时候发现这个问题,平时都是直接安装Visual Studio解决,但是这个安装太大了,所以解决看看怎么安装是最方便的,最容易解决的. 下面这个就是出现的问题: build ...

  7. JS ----- JS原型与原型链终极详解(一)

    一. 普通对象与函数对象 JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object .Function 是 JS 自带的函数对象.下面举例说明 var o1 = ...

  8. github clone或访问慢

    做技术的我们经常会访问github.com,有时出现github访问非常慢或者git clone速度很慢,git push也很慢 原因很简单:github被高高的墙屏蔽了. 所以解决方案就是手动把 c ...

  9. 若依、vue三级路由缓存失败

    router.beforeEach((to, from, next) => { NProgress.start() if (getToken()) { // 三级菜单组件无法缓存问题 if (t ...

  10. 10.5 认识XEDParse汇编引擎

    XEDParse 是一款开源的x86指令编码库,该库用于将MASM语法的汇编指令级转换为对等的机器码,并以XED格式输出,目前该库支持x86.x64平台下的汇编编码,XEDParse的特点是高效.准确 ...