[ATL/WTL]_[Gdiplus]_[Windows窗口如何显示GIF动画]

场景

  1. 在使用 WTLGdiplus 开发界面程序时,我们需要在界面显示 GIF的动画,可惜 WTLGdiplus并没有执行显示 GIF动画的API. 那我们如何做?

说明

  1. GIF文件关键的2个参数, 图片的帧数,每帧的时间间隔. 如果能获取到这2个参数,那么我们就可以让帧索引递增,并逐帧绘制图片。

  2. 获取图片的帧数,可以通过以下方法,我们需要做的是构造方法的参数. 在Image::GetFrameDimensionsList method 里有讲.

Gdiplus::Image::GetFrameCount(const GUID* dimensionID)
  1. 其次是获取每帧的时间间隔,每帧的间隔可能不同,所以我们需要计算并获取每帧的间隔.
image->GetPropertyItem(PropertyTagFrameDelay,nsize,pItem);
  1. 之后通过把下一帧设置为活动帧,这样在绘制图片时就会绘制正确的帧.
image->SelectActiveFrame(&Guid, frame_index++);
  1. 除此以外,我们还需要一个定时器,来在间隔时间绘制图片.
SetTimer(nIDEvent,milliseconds);
  1. 为了减少内存占用, 可以在初始化时把 gif 图片分析完,直接获取所有的帧的时间间隔, 从而在定时器里只是进行选择活动帧和绘制图片.
bool CView::StoreGifInternal(Gdiplus::Bitmap* image,std::vector<long>& internals)
{
	int ncount = image->GetFrameDimensionsCount();
	GUID *pDimensionIDs=(GUID*)new GUID[ncount];
	image->GetFrameDimensionsList(pDimensionIDs,ncount);
	// 获取帧数
	UINT framecount = image->GetFrameCount(&pDimensionIDs[0]);
	delete []pDimensionIDs;
	if (1 == framecount)
		return false;

	int nsize=image->GetPropertyItemSize(PropertyTagFrameDelay);
	Gdiplus::PropertyItem* pItem=NULL;
	pItem = (Gdiplus::PropertyItem*)malloc(nsize);
	
	//// GIF动画图像中两帧之间的时间延迟,以百分之一秒为单位。
	image->GetPropertyItem(PropertyTagFrameDelay,nsize,pItem);
	for(long i = 0;i<framecount;++i){
		// 百分之一秒转换为毫秒,需要*10
		long lPause = ((long*)pItem->value)[i]*10;
		internals.push_back(lPause);
	}

	free(pItem);
	return true;
}

例子

view.h

// View.h : interface of the CView class
//
/////////////////////////////////////////////////////////////////////////////

#pragma once

#include 
#include 

class CView : public CWindowImpl<CView>
{
public:
	DECLARE_WND_CLASS(NULL)

	BOOL PreTranslateMessage(MSG* pMsg);

	BEGIN_MSG_MAP_EX(CView)
		MSG_WM_CREATE(OnCreate)
		MSG_WM_TIMER(OnTimer)
		MESSAGE_HANDLER(WM_PAINT, OnPaint)
		REFLECT_NOTIFICATIONS()
	END_MSG_MAP()

protected:

	int OnCreate(LPCREATESTRUCT lpCreateStruct);
	LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
	LRESULT OnCtlColor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	void OnTimer(UINT_PTR nIDEvent);
	bool StoreGifInternal(Gdiplus::Bitmap* image,std::vector<long>& internals);

private:
	int frame_index_; // 当前GIF的可见帧索引.
	std::vector<long> frame_internal_; // 每帧时间间隔,毫秒.

	Gdiplus::RectF rect_image_;
	Gdiplus::Bitmap* image_;
};

view.cpp

// View.cpp : implementation of the CView class
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "resource.h"

#include "View.h"
#include 
#include 
#include 
#include 
#include 

enum{
	kOneGifTimerID = 1
};

void CView::OnTimer(UINT_PTR nIDEvent)
{
	switch(nIDEvent)
	{
	case kOneGifTimerID:
		{
			frame_index_++;
			if(frame_index_ == frame_internal_.size())
				frame_index_ = 0;

			GUID guid = Gdiplus::FrameDimensionTime;
			// 选择当前GIF哪帧作为当前可见绘制帧.
			image_->SelectActiveFrame(&guid,frame_index_);
			auto milliseconds = frame_internal_[frame_index_];
			SetTimer(nIDEvent,milliseconds);
			CRect rect(rect_image_.X,rect_image_.Y,rect_image_.GetRight(),rect_image_.GetBottom());
			InvalidateRect(rect,FALSE);
			break;
		}
	}
}

bool CView::StoreGifInternal(Gdiplus::Bitmap* image,std::vector<long>& internals)
{
	int ncount = image->GetFrameDimensionsCount();
	GUID *pDimensionIDs=(GUID*)new GUID[ncount];
	image->GetFrameDimensionsList(pDimensionIDs,ncount);
	// 获取帧数
	UINT framecount = image->GetFrameCount(&pDimensionIDs[0]);
	delete []pDimensionIDs;
	if (1 == framecount)
		return false;

	int nsize=image->GetPropertyItemSize(PropertyTagFrameDelay);
	Gdiplus::PropertyItem* pItem=NULL;
	pItem = (Gdiplus::PropertyItem*)malloc(nsize);
	
	//// GIF动画图像中两帧之间的时间延迟,以百分之一秒为单位。
	image->GetPropertyItem(PropertyTagFrameDelay,nsize,pItem);
	for(long i = 0;i<framecount;++i){
		// 百分之一秒转换为毫秒,需要*10
		long lPause = ((long*)pItem->value)[i]*10;
		internals.push_back(lPause);
	}

	free(pItem);
	return true;
}

BOOL CView::PreTranslateMessage(MSG* pMsg)
{
	pMsg;
	return FALSE;
}

LRESULT CView::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	CRect rect;
	GetClientRect(&rect);
	CPaintDC dc(m_hWnd);
	CMemoryDC mdc(dc,dc.m_ps.rcPaint);
	mdc.FillSolidRect(rect,RGB(255,255,255));
	Gdiplus::Graphics graphics(mdc);
	graphics.DrawImage(image_,rect_image_,0,0,
		image_->GetWidth(),image_->GetHeight(),Gdiplus::UnitPixel);

	return 0;
}

static std::wstring GetProductBinDir()
{
	static wchar_t szbuf[MAX_PATH];  
	::GetModuleFileNameW(NULL,szbuf,MAX_PATH);  
    ::PathRemoveFileSpecW(szbuf);
	int length = lstrlen(szbuf);
	szbuf[length] = L'\\';
	szbuf[length+1] = 0;
	return szbuf;
}


int CView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	frame_index_ = 0;
	auto binDir = GetProductBinDir();
	binDir.append(L"wtf.gif");
	image_ = new Gdiplus::Bitmap(binDir.c_str());
	if(image_->GetLastStatus() == Gdiplus::Ok){
		rect_image_.X = 200;
		rect_image_.Y = 200;
		rect_image_.Width = image_->GetWidth();
		rect_image_.Height = image_->GetHeight();
	}
	assert(image_);
	StoreGifInternal(image_,frame_internal_);

	SetTimer(kOneGifTimerID,100);
	return 0;
}

GIF文件

[ATL/WTL]_[Gdiplus]_[Windows窗口如何显示GIF动画]_第1张图片

下载地址

使用vs2010, WTL(Win32)库开发.
https://download.csdn.net/download/infoworld/12391893

参考

PropertyTagFrameDelay

SetTimer function

Copy individual frames from a multiple-frame image

Image::GetFrameDimensionsList method

你可能感兴趣的:(GDI+编程日积月累,ATL/WTL界面开发,gif,gdiplus,wtl,win32,动画)