【可以显示gif图片的控件】
闲暇之余,自己做个东西,需要显示gif图片,于是乎,有了今天的这个作品,首发于【编程论坛】。花费一天时间打造,时间仓促,难免有不足之处,欢迎真正懂得人拍砖!
先说一下起初的一些想法吧,让图片动态显示,其实就是把图片中的一帧一帧取出来,然后根据每帧的延迟时间,觉得显示下一帧图像,这样看起来就是动态的。
这个过程难免要考虑:1)线程(网上有个GifPicture使用的是线程,代码量比较多,感兴趣的朋友可以去找一下)、2)定时器
在线程中显示图片,这个好像不太方便,而且为一个小小的控件专门开一个线程,未免有点大材小用了,同时,如果我要显示10张gif图片,岂不是开10个线程,线程之间来回的切换,这个效率可想而知了。当然了,有些人就是愿意开辟线程,我也没招!
对于我个人而言,我喜欢使用定时器来完成这件事,它的不好处就是精度不够(除非你开多媒体定时器),同时定时器消息的优先级比较低,可能会出现画面偶尔的不流畅,但是容易控制,所以我采纳了定时器。
在图片操作方面,GDI+比GDI好的不是一点半点,简单易学,因此我采用了Image这个类来加载显示gif图片(具体的细节,代码中有注释,要想了解更多的gif知识或Image知识,请百度)
不足之处:在调用Image::SelectActiveFrame()函数时,会报告 0x4aeee2e0 处最可能的异常: 0x80000001: 尚未实现的问题,根别人回答应该是Image不是线程安全的,但是起码我实验的时候,程序未出现异常(使用我代码的朋友,如果发现什么问题,请告知我,先谢过您的支持!),对于这块,我打算有时间尝试另一种方法(但是别的方法是否可行,尚未得知),好了,废话不多说,直接贴代码(因为代码量不多,所以直接贴出来)
测试程序:
程序代码:
#ifndef CGIF_STATIC_H
#define CGIF_STATIC_H
#include <GdiPlus.h>
using namespace Gdiplus;
#pragma comment(lib,"GdiPlus.lib")
// 定时器ID
#define IDT_DISPLAY_GIF 5000
class CGifStatic : public CStatic
{
DECLARE_DYNAMIC(CGifStatic)
public:
CGifStatic();
virtual ~CGifStatic();
DECLARE_MESSAGE_MAP()
protected:
/**自定义函数**/
// 显示gif图片
void DisplayGifImage();
// 绘制背景色
void DrawBackground();
protected:
/**重载函数**/
virtual void PreSubclassWindow();
virtual BOOL PreTranslateMessage(MSG *pMsg);
afx_msg BOOL OnEraseBkgnd(CDC *pDC);
afx_msg void OnPaint();
afx_msg void OnTimer(UINT_PTR nIDEvent);
public:
/**对外方法**/
// 加载gif图片
BOOL LoadGifImage(LPCTSTR lpszFileName);
// 开始显示gif图片
BOOL StartDisplay();
// 暂停/显示
void PauseDisplay(BOOL bPause);
// 停止显示gif图片
void StopDisplay();
private:
//内存DC 对象
CDC m_MemDC;
// CBitmap 对象
CBitmap m_MemBmp;
// 控件的大小
int m_cxWnd;
int m_cyWnd;
// 当前图像帧的延迟时间
LONG m_lCurDelayTime;
// 标记是否删除WM_TIMER消息
BOOL m_bDelTimerMsg;
// 要显示的gif图片
Image *m_pGifImage;
// gif图片的帧的个数
UINT m_nNumOfFrame;
// 图像帧的序号
UINT m_nIndexOfFrame;
// 保存gif图片属性
PropertyItem *m_pPropertyItem;
ULONG_PTR gdiplusToken;
};
#endif //CGIF_STATIC_H
程序代码:#include "CGifStatic.h"
IMPLEMENT_DYNAMIC(CGifStatic, CStatic)
CGifStatic::CGifStatic()
{
m_bDelTimerMsg = FALSE;
m_pGifImage = NULL;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
}
CGifStatic::~CGifStatic()
{
if(NULL != m_pPropertyItem)
{
delete []m_pPropertyItem;
m_pPropertyItem = NULL;
}
GdiplusShutdown(gdiplusToken);
}
BEGIN_MESSAGE_MAP(CGifStatic, CStatic)
ON_WM_TIMER()
ON_WM_ERASEBKGND()
ON_WM_PAINT()
END_MESSAGE_MAP()
BOOL CGifStatic::LoadGifImage(LPCTSTR lpszFileName)
{
// 加载gif图片之前,应确保调用StopDisplay()终止当前显示的gif图片
if(NULL != m_pGifImage)
{
return FALSE;
}
m_pGifImage = new Image(lpszFileName);
if(m_pGifImage->GetLastStatus() == Ok)
{
UINT nNumOfDimensions;
// 获取gif图像的“维度”个数,一个维度中包含有不定数目的图像帧,对于gif图像而言,其维度为1
nNumOfDimensions = m_pGifImage->GetFrameDimensionsCount();
if(nNumOfDimensions)
{
GUID *pDimensionsIDs = new GUID[nNumOfDimensions];
// 获取gif图像“维度”所读应的GUID列表
m_pGifImage->GetFrameDimensionsList(pDimensionsIDs, nNumOfDimensions);
// GetFrameCount()获取指定“维度”中所包含的图像帧的个数,因为gif图像只有一个
// “维度”,所以可以直接调用GetFrameCount(&pDimensionsIDs[0]),但是最好
// 使用一个while循环
UINT i = 1;
m_nNumOfFrame = 0;
while(i <= nNumOfDimensions)
{
m_nNumOfFrame += m_pGifImage->GetFrameCount(&pDimensionsIDs[i-1]);
i++;
}
delete []pDimensionsIDs;
// 每个图像帧之间存在时间间隔,GetPropertyItemSize()函数获取gif图像中存在
// 多少个时间间隔,每个时间间隔存放在PropertyItem结构体中
int FrameDelayNums = m_pGifImage->GetPropertyItemSize(PropertyTagFrameDelay);
m_pPropertyItem = new PropertyItem[FrameDelayNums];
m_pGifImage->GetPropertyItem(PropertyTagFrameDelay, FrameDelayNums, m_pPropertyItem);
return TRUE;
}
}
m_pGifImage = NULL;
return FALSE;
}
void CGifStatic::PreSubclassWindow()
{
CRect rtWindow;
GetClientRect(&rtWindow);
m_cxWnd = rtWindow.Width();
m_cyWnd = rtWindow.Height();
CDC *pDC = GetDC();
m_MemDC.CreateCompatibleDC(pDC);
m_MemBmp.CreateCompatibleBitmap(pDC, m_cxWnd, m_cyWnd);
m_MemDC.SelectObject(&m_MemBmp);
m_MemDC.SetBkMode(TRANSPARENT);
ReleaseDC(pDC);
CStatic::PreSubclassWindow();
}
BOOL CGifStatic::PreTranslateMessage(MSG *pMsg)
{
if(pMsg->hwnd == m_hWnd)
{
if(TRUE == m_bDelTimerMsg && WM_TIMER == pMsg->message)
{
return TRUE;
}
}
return CStatic::PreTranslateMessage(pMsg);
}
BOOL CGifStatic::OnEraseBkgnd(CDC *pDC)
{
return TRUE;
}
void CGifStatic::OnPaint()
{
CPaintDC dc(this);
if(NULL == m_pGifImage)
{
dc.FillSolidRect(0, 0, m_cxWnd, m_cyWnd, RGB(0, 255, 255));
}
else
{
dc.BitBlt(0, 0, m_cxWnd, m_cyWnd, &m_MemDC, 0, 0, SRCCOPY);
}
CStatic::OnPaint();
}
void CGifStatic::OnTimer(UINT_PTR nIDEvent)
{
KillTimer(nIDEvent);
m_nIndexOfFrame++;
if(m_nIndexOfFrame + 1 == m_nNumOfFrame)
{
m_nIndexOfFrame = 0;
}
DisplayGifImage();
}
BOOL CGifStatic::StartDisplay()
{
if(NULL == m_pGifImage)
{
return FALSE;
}
m_bDelTimerMsg = FALSE;
m_nIndexOfFrame = 0;
DisplayGifImage();
return TRUE;
}
void CGifStatic::DisplayGifImage()
{
GUID Guid = FrameDimensionTime;
m_pGifImage->SelectActiveFrame(&Guid, m_nIndexOfFrame);
Gdiplus::Graphics graphics(m_MemDC.GetSafeHdc());
graphics.DrawImage(m_pGifImage, 0, 0, m_cxWnd, m_cyWnd);
CDC *pDC = GetDC();
pDC->BitBlt(0, 0, m_cxWnd, m_cyWnd, &m_MemDC, 0, 0, SRCCOPY);
ReleaseDC(pDC);
m_lCurDelayTime = ((LONG *)m_pPropertyItem->value)[m_nIndexOfFrame] * 10;
SetTimer(IDT_DISPLAY_GIF, m_lCurDelayTime, NULL);
}
void CGifStatic::DrawBackground()
{
CDC *pDC = GetDC();
pDC->BitBlt(0, 0, m_cxWnd, m_cyWnd, &m_MemDC, 0, 0, SRCCOPY);
ReleaseDC(pDC);
}
void CGifStatic::PauseDisplay(BOOL bPause)
{
/*
bPause表示暂停当前播放,需要kill掉定时器,因为可能此时消息队列中存在了WM_TIMER消息,
所以同时从消息队列中清除已经存在的定时器消息
对于PeekMessage传递参数PM_REMOVE可以清除WM_TIMER消息,但是如果此时消息队列中存在一个
WM_PAINT消息,则PeekMessage永远返回TRUE,成为了死循环,所以这种方式存在一定的问题
可以通过PreTranslateMessage这个函数实现,该函数时对于送达消息队列中的消息在发送给
特定窗口时会做一个预先的处理,因此在处理消息前直接让PreTranslateMessage函数返回TRUE,
不送达窗口回调函数处理
*/
if(bPause)
{
KillTimer(IDT_DISPLAY_GIF);
m_bDelTimerMsg = TRUE;
}
else
{
SetTimer(IDT_DISPLAY_GIF, m_lCurDelayTime, NULL);
m_bDelTimerMsg = FALSE;
}
}
void CGifStatic::StopDisplay()
{
if(NULL == m_pGifImage)
{
return;
}
KillTimer(IDT_DISPLAY_GIF);
m_bDelTimerMsg = TRUE;
m_MemDC.FillSolidRect(0, 0, m_cxWnd, m_cyWnd, RGB(0, 255, 255));
DrawBackground();
delete m_pGifImage;
m_pGifImage = NULL;
m_nIndexOfFrame = 0;
m_nNumOfFrame = 0;
m_lCurDelayTime = 0;
if(NULL != m_pPropertyItem)
{
delete []m_pPropertyItem;
m_pPropertyItem = NULL;
}
}





哈哈
表扬一下。楼主再接再厉