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