GDI+显示gif动画ImageEx类
2011-02-25 21:51
330 查看
Code:
// GDIPlusHelper.h: interface for the CGDIPlusHelper class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_GDIPLUSHELPER_H__BD5F6266_5686_43E2_B146_5EA1217A56FE__INCLUDED_)
#define AFX_GDIPLUSHELPER_H__BD5F6266_5686_43E2_B146_5EA1217A56FE__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <windows.h>
//注意:此类当创建的图像对象是多帧动画时,没有考虑线程同步问题,不能在其他地方使用,
//比如所有的Graphics操作函数,以及从基类继承过来的接口函数,你只能使用ImageEx类的public接口函数;
//而当创建的是单帧图像时,线程同步性等价于基类Image的同步性,
//则你可以像以前使用Image类那样使用该类,包括所有的Graphics操作函数,以及从基类继承过来的接口函数。
//其实你会发现下面的public成员函数操作的成员变量都是新增的成员变量。
class ImageEx : public Image
{
public:
//以长度为nSize的内存pBuff中的内容构造图像
ImageEx(const void* pBuff, size_t nSize, BOOL useEmbeddedColorManagement = FALSE);
//以类型为sResourceType,名称为sResource的资源构造图像
ImageEx(LPCTSTR sResourceType, LPCTSTR sResource, BOOL useEmbeddedColorManagement = FALSE);
//以文件构造图像
ImageEx(LPCTSTR filename, BOOL useEmbeddedColorManagement = FALSE);
//调用Destroy成员函数
~ImageEx();
public:
//如果已经构造的对象是动画,则创建动画线程,并返回true,
//如果为静态图像或已经创建过动画线程,则也返回false
// 图像将绘制在m_hWnd客户区的rect区域,会拉伸,支持镜像
bool InitAnimation(HWND hWnd, RECT rect);
//判断是否为动画
bool IsAnimatedGIF() { return m_nFrameCount > 1; }
//设置动画暂停与否
void SetPause(bool bPause);
//判断动画是否处于暂停状态
bool IsPaused() { return m_bPause; }
//关闭动画,事实上基类Image中还有的两个成员变量没有关闭,因为析构函数会调用基类析构函数进行关闭的
void Destroy();
protected:
//测试图像是否为动画,是的话成员变量m_pPropertyItem将会malloc分配内存
bool TestForAnimatedGIF();
//给所有成员变量初始化,构造函数中调用
void Initialize();
//在客户区画出当前帧,返回值表示是否要退出线程函数
bool DrawFrameGIF();
//把长度为nSize的内存pBuff中的内容创建为IStream流保存在类的m_pStream成员变量中,
//返回成功与否,不检查参数的非法性
bool LoadFromBuffer(const void* pBuff, size_t nSize);
//装载名称为lpName,类型为lpType的资源到pResource中,返回装载成功与否。
//nBufSize表示pResource缓存的长度。pResource为NULL时,nBufSize返回所需内存大小,
//不为NULL时,返回实际资源大小,长度不够时,相当于pResource为NULL时的作用,只是返回值为false。
bool GetResource(LPCTSTR lpName, LPCTSTR lpType, void* pResource, size_t& nBufSize);
//装载类型为sResourceType,名称为sResource的资源到流对象成员变量m_pStream中,返回装载成功与否,
//m_pStream在该函数中装载成功与否保存在成员变量m_bIsInitialized中,
//同时,如果该类的对象是以文件构造的,则m_bIsInitialized表示构造成功与否
bool Load(LPCTSTR sResourceType, LPCTSTR sResource); //类型,名字
//实际的线程函数
void ThreadAnimation();
//代理线程函数,实际调用ThreadAnimation成员函数
static UINT WINAPI _ThreadAnimationProc(LPVOID pParam);
protected:
IStream* m_pStream; //记得Release
HANDLE m_hThread; //线程创建时是挂起的,须调用ResumeThread
HANDLE m_hPause; //手工重置,初始有信号
HANDLE m_hExitEvent; //手工重置,初始无信号
HINSTANCE m_hInst;
HWND m_hWnd;
UINT m_nFrameCount;
UINT m_nFramePosition;
bool m_bIsInitialized; //表示对象构造成功与否,不管是以资源还是文件的形式
bool m_bPause;
PropertyItem* m_pPropertyItem; //如果为动画,会malloc内存,记得free
HDC m_hdcMem; //双缓存用,只在是多帧动画且创建了播放线程时才创建
HBITMAP m_hbmpBack; //双缓存用,只在是多帧动画且创建了播放线程时才创建
RECT m_rect; // 图像在m_hWnd客户区绘制的区域,会拉伸,支持镜像
//另外,基类Image中还有两个成员变量:nativeImage(GpImage*类型)和lastResult(Status类型)
};
#endif // !defined(AFX_GDIPLUSHELPER_H__BD5F6266_5686_43E2_B146_5EA1217A56FE__INCLUDED_)
// GDIPlusHelper.cpp: implementation of the CGDIPlusHelper class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ImageEx.h"
#include <process.h>
#include <string>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
////////////////////////////////////////////////////////////////////////////////
int A2U(const char* szA,wchar_t* szU,size_t cnt)
{
return MultiByteToWideChar (CP_ACP, 0, szA, -1, szU, cnt) ;
}
std::wstring A2U(const char* szA)
{
int nRetCode=A2U(szA,0,0);
if(0==nRetCode)
return std::wstring();
std::wstring str(nRetCode-1,'/0');
A2U(szA,(wchar_t*)(str.c_str()),nRetCode);
return str;
}
////////////////////////////////////////////////////////////////////////////////
//以长度为nSize的内存pBuff中的内容构造图像
////////////////////////////////////////////////////////////////////////////////
ImageEx::ImageEx(const void* pBuff, size_t nSize, BOOL useEmbeddedColorManagement)
{
Initialize();
bool bResult = LoadFromBuffer(pBuff, nSize);
m_bIsInitialized = bResult;
if (m_bIsInitialized)
{
nativeImage = NULL;
if (useEmbeddedColorManagement)
lastResult = DllExports::GdipLoadImageFromStreamICM(m_pStream, &nativeImage);
else
lastResult = DllExports::GdipLoadImageFromStream(m_pStream, &nativeImage);
TestForAnimatedGIF();
}
}
////////////////////////////////////////////////////////////////////////////////
//以类型为sResourceType,名称为sResource的资源构造图像
////////////////////////////////////////////////////////////////////////////////
ImageEx::ImageEx(LPCTSTR sResourceType, LPCTSTR sResource, BOOL useEmbeddedColorManagement)
{
Initialize();
if (Load(sResourceType, sResource))
{
nativeImage = NULL;
if (useEmbeddedColorManagement)
lastResult = DllExports::GdipLoadImageFromStreamICM(m_pStream, &nativeImage);
else
lastResult = DllExports::GdipLoadImageFromStream(m_pStream, &nativeImage);
TestForAnimatedGIF();
}
}
////////////////////////////////////////////////////////////////////////////////
//以文件构造图像
////////////////////////////////////////////////////////////////////////////////
ImageEx::ImageEx(LPCTSTR filename, BOOL useEmbeddedColorManagement) :
#ifndef UNICODE
Image(A2U(filename).c_str(), useEmbeddedColorManagement)
#else
Image(filename, useEmbeddedColorManagement)
#endif
{
Initialize();
m_bIsInitialized = (Ok == lastResult);
if(m_bIsInitialized)
TestForAnimatedGIF();
}
////////////////////////////////////////////////////////////////////////////////
//析构函数,调用Destroy成员函数
////////////////////////////////////////////////////////////////////////////////
ImageEx::~ImageEx()
{
this->Destroy();
}
////////////////////////////////////////////////////////////////////////////////
//如果已经构造的对象是动画,则创建动画线程,并返回true,
//如果为静态图像或已经创建过动画线程,则也返回false
// 图像将绘制在m_hWnd客户区的rect区域,会拉伸,支持镜像
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::InitAnimation(HWND hWnd, RECT rect)
{
m_hWnd = hWnd;
m_rect = rect;
if (!m_bIsInitialized)
return false;
if (IsAnimatedGIF())
{
if (m_hThread == NULL)
{
unsigned int nTID = 0;
HDC hDC = GetDC(m_hWnd);
m_hdcMem = CreateCompatibleDC(hDC);
m_hbmpBack = CreateCompatibleBitmap(hDC,GetWidth(),GetHeight());
SelectObject(m_hdcMem,m_hbmpBack);
ReleaseDC(m_hWnd, hDC);
m_hThread = (HANDLE) _beginthreadex( NULL, 0, _ThreadAnimationProc, this, CREATE_SUSPENDED,&nTID);
if (!m_hThread)
return false;
else
{
ResumeThread(m_hThread);
return true;
}
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
//把长度为nSize的内存pBuff中的内容创建为IStream流保存在类的m_pStream成员变量中,
//返回成功与否,不检查参数的非法性
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::LoadFromBuffer(const void* pBuff, size_t nSize)
{
bool bResult = false;
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, nSize);
if (hGlobal)
{
void* pData = GlobalLock(hGlobal);
if (pData)
memcpy(pData, pBuff, nSize);
GlobalUnlock(hGlobal);
if (CreateStreamOnHGlobal(hGlobal, TRUE, &m_pStream) == S_OK)
bResult = true;
}
return bResult;
}
////////////////////////////////////////////////////////////////////////////////
//装载名称为lpName,类型为lpType的资源到pResource中,返回装载成功与否。
//nBufSize表示pResource缓存的长度。pResource为NULL时,nBufSize返回所需内存大小,
//不为NULL时,返回实际资源大小,长度不够时,相当于pResource为NULL时的作用,只是返回值为false。
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::GetResource(LPCTSTR lpName, LPCTSTR lpType, void* pResource, size_t& nBufSize)
{
HRSRC hResInfo;
HGLOBAL hRes;
void* lpRes = NULL;
size_t nLen = 0;
bool bResult = FALSE;
// Find the resource
hResInfo = FindResource(m_hInst , lpName, lpType);
if (hResInfo == NULL)
{
DWORD dwErr = GetLastError();
return false;
}
// Load the resource
hRes = LoadResource(m_hInst , hResInfo);
if (hRes == NULL)
return false;
// Lock the resource
lpRes = LockResource(hRes);
if (lpRes != NULL)
{
nLen = SizeofResource(m_hInst , hResInfo);
if (pResource == NULL)
{
nBufSize = nLen;
bResult = true;
}
else
{
if (nBufSize >= nLen)
{
memcpy(pResource, lpRes, nLen);
bResult = true;
}
nBufSize = nLen;
}
UnlockResource(hRes);
}
// Free the resource
FreeResource(hRes);
return bResult;
}
////////////////////////////////////////////////////////////////////////////////
//装载类型为sResourceType,名称为sResource的资源到流对象成员变量m_pStream中,返回装载成功与否,
//m_pStream在该函数中装载成功与否保存在成员变量m_bIsInitialized中,
//同时,如果该类的对象是以文件构造的,则m_bIsInitialized表示构造成功与否
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::Load(LPCTSTR sResourceType, LPCTSTR sResource)
{
bool bResult = false;
BYTE* pBuff = NULL;
size_t nSize = 0;
if (GetResource(sResource, sResourceType, pBuff, nSize))
{
if (nSize > 0)
{
pBuff = new BYTE[nSize];
if (GetResource(sResource, sResourceType, (void*)pBuff, nSize))
{
bResult = LoadFromBuffer((const void*)pBuff, nSize);
}
delete [] pBuff;
}
}
m_bIsInitialized = bResult;
return bResult;
}
////////////////////////////////////////////////////////////////////////////////
//测试图像是否为动画,是的话成员变量m_pPropertyItem将会malloc分配内存
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::TestForAnimatedGIF()
{
UINT count = 0;
count = GetFrameDimensionsCount();
GUID* pDimensionIDs = new GUID[count];
// Get the list of frame dimensions from the Image object.
GetFrameDimensionsList(pDimensionIDs, count);
// Get the number of frames in the first dimension.
m_nFrameCount = GetFrameCount(&pDimensionIDs[0]);
// Assume that the image has a property item of type PropertyItemEquipMake.
// Get the size of that property item.
if (m_nFrameCount > 1)
{
int nSize = GetPropertyItemSize(PropertyTagFrameDelay);
// Allocate a buffer to receive the property item.
m_pPropertyItem = (PropertyItem*) malloc(nSize);
GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem);
}
delete pDimensionIDs;
return m_nFrameCount > 1;
}
////////////////////////////////////////////////////////////////////////////////
//给所有成员变量初始化,构造函数中调用
////////////////////////////////////////////////////////////////////////////////
void ImageEx::Initialize()
{
m_pStream = NULL;
m_hThread = NULL;
m_hPause = CreateEvent(NULL,TRUE,TRUE,NULL); //手工重置,初始有信号
m_hExitEvent = CreateEvent(NULL,TRUE,FALSE,NULL); //手工重置,初始无信号
m_hInst = GetModuleHandle(NULL);
m_hWnd = NULL;
m_nFrameCount = 0;
m_nFramePosition = 0;
m_bIsInitialized = false;
m_bPause = false;
m_pPropertyItem = NULL;
m_hdcMem = NULL;
m_hbmpBack = NULL;
SetRect(&m_rect,0,0,0,0);
//以下两句不能有,因为Initialize函数是构造函数中调用,下面两个属于基类的成员变量不能再次被修改
// nativeImage = NULL;
// lastResult = InvalidParameter;
}
////////////////////////////////////////////////////////////////////////////////
//代理线程函数,实际调用ThreadAnimation成员函数
////////////////////////////////////////////////////////////////////////////////
UINT WINAPI ImageEx::_ThreadAnimationProc(LPVOID pParam)
{
ImageEx *pImage = reinterpret_cast<ImageEx *> (pParam);
pImage->ThreadAnimation();
return 0;
}
////////////////////////////////////////////////////////////////////////////////
//实际的线程函数
////////////////////////////////////////////////////////////////////////////////
void ImageEx::ThreadAnimation()
{
m_nFramePosition = 0;
bool bExit = false;
while (!bExit)
{
bExit = DrawFrameGIF();
}
}
////////////////////////////////////////////////////////////////////////////////
//在客户区画出当前帧,返回值表示是否要退出线程函数
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::DrawFrameGIF()
{
::WaitForSingleObject(m_hPause, INFINITE); //m_hPause手工重置,初始有信号
//pageGuid的值在显示GIF为FrameDimensionTime,显示TIF时为FrameDimensionPage
GUID pageGuid = FrameDimensionTime;
RECT rect;
UINT width, height;
HDC hDC = GetDC(m_hWnd);
int BltMode = SetStretchBltMode (hDC, COLORONCOLOR) ;
if (m_hdcMem)
{
width = GetWidth();
height = GetHeight();
SetRect(&rect, 0, 0, width, height);
FillRect(m_hdcMem,&rect,(HBRUSH) (COLOR_WINDOW+1));
Graphics graphics(m_hdcMem);
graphics.DrawImage(this, 0, 0, width, height);
StretchBlt(hDC, m_rect.left, m_rect.top,
m_rect.right-m_rect.left, m_rect.bottom-m_rect.top,
m_hdcMem, 0, 0, width, height, SRCCOPY);
}
SetStretchBltMode (hDC, BltMode) ;
ReleaseDC(m_hWnd, hDC);
long lPause = ((long*) m_pPropertyItem->value)[m_nFramePosition] * 10;
if(lPause<10)
lPause=80;
else if(lPause>5000)
lPause=80;
DWORD dwErr = WaitForSingleObject(m_hExitEvent, lPause); //m_hExitEvent手工重置,初始无信号
if(WAIT_OBJECT_0 != dwErr)
{
if(++m_nFramePosition == m_nFrameCount)
m_nFramePosition = 0;
SelectActiveFrame(&pageGuid, m_nFramePosition);
return false;
}
else
return true; // 如果在lPause时间内产生信号,则返回true,表示要退出线程
}
////////////////////////////////////////////////////////////////////////////////
//设置动画暂停与否
////////////////////////////////////////////////////////////////////////////////
void ImageEx::SetPause(bool bPause)
{
if (!IsAnimatedGIF())
return;
if (!m_bPause && bPause) //本来没暂停且要设置为暂停
{
ResetEvent(m_hPause);
}
else
{
if (m_bPause && !bPause) //本来已暂停且要设置为非暂停
{
SetEvent(m_hPause);
}
}
m_bPause = bPause;
}
////////////////////////////////////////////////////////////////////////////////
//关闭动画,事实上基类Image中还有的两个成员变量没有关闭,因为析构函数会调用基类析构函数进行关闭的
////////////////////////////////////////////////////////////////////////////////
void ImageEx::Destroy()
{
if (m_hThread)
{
this->SetPause(false);
SetEvent(m_hExitEvent);
WaitForSingleObject(m_hThread, INFINITE);
CloseHandle(m_hThread);
DeleteDC(m_hdcMem);
DeleteObject(m_hbmpBack);
m_hThread = NULL;
m_hdcMem = NULL;
m_hbmpBack = NULL;
}
if(m_hExitEvent)
CloseHandle(m_hExitEvent);
if(m_hPause)
CloseHandle(m_hPause);
free(m_pPropertyItem);
m_hExitEvent = NULL;
m_hPause = NULL;
m_pPropertyItem = NULL;
if (m_pStream)
{
m_pStream->Release();
m_pStream = NULL;
}
}
使用GDI+库显示gif动态图片,该类接口如下:
可以看出,该ImageEx完全继承了基类的接口函数。
说明:
如果打开非多帧图片,该类几乎完全等价于基类,比如你可以把该类的对象代入Graphics类系列的成员函数中;
如果打开的是多帧的图片,你只要打开图片后不调用InitAnimation函数(它会创建线程),则上述做法依然可以;
但如果调用InitAnimation函数后(单帧图像没关系,因为不会创建线程),则不可以了,
所有的基类继承过来的接口成员函数和配合gdi+库其他类的函数调用都是不可以的,因为没有作线程同步,
你只能调用下面位数不多的几个public成员函数,调用Destroy成员函数后,则就可以了,因为它会关闭线程。
其实你会发现下面的public成员函数操作的成员变量都是新增的成员变量,没涉及到线程同步问题。
class ImageEx : public Image
{
public:
//以长度为nSize的内存pBuff中的内容构造图像
ImageEx(const void* pBuff, size_t nSize, BOOL useEmbeddedColorManagement = FALSE);
//以类型为sResourceType,名称为sResource的资源构造图像
ImageEx(LPCTSTR sResourceType, LPCTSTR sResource, BOOL useEmbeddedColorManagement = FALSE);
//以文件构造图像
ImageEx(LPCTSTR filename, BOOL useEmbeddedColorManagement = FALSE);
//调用Destroy成员函数
~ImageEx();
public:
//如果已经构造的对象是动画,则创建动画线程,并返回true,
//如果为静态图像或已经创建过动画线程,则也返回false
// 图像将绘制在m_hWnd客户区的rect区域,会拉伸,支持镜像
bool InitAnimation(HWND hWnd, RECT rect);
//判断是否为动画
bool IsAnimatedGIF() { return m_nFrameCount > 1; }
//设置动画暂停与否
void SetPause(bool bPause);
//判断动画是否处于暂停状态
bool IsPaused() { return m_bPause; }
//关闭动画,事实上基类Image中还有的两个成员变量没有关闭,因为析构函数会调用基类析构函数进行关闭的
void Destroy();
//另外的非public的东西省略..
};
用法:
MFC对话框程序在下面添加:
BOOL CTestDlgDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//其它的初始化代码
// GDI+
//m_image为ImageEx指针类型成员变量,"GIF"为资源类型,"HEARTS"为资源名称
m_image = new ImageEx( _T("GIF"), _T("HEARTS") );
RECT rc;
GetClientRect(&rc);
m_image->InitAnimation(this->m_hWnd, rc);//创建gif播放线程
return TRUE; // return TRUE unless you set the focus to a control
}
CTestDlgDlg::~CTestDlgDlg()
{
// GDI+
delete m_image;
}
其中的m_image = new ImageEx( _T("GIF"), _T("HEARTS") );你可以换成ImageEx类的另外两个构造函数
// GDIPlusHelper.h: interface for the CGDIPlusHelper class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_GDIPLUSHELPER_H__BD5F6266_5686_43E2_B146_5EA1217A56FE__INCLUDED_)
#define AFX_GDIPLUSHELPER_H__BD5F6266_5686_43E2_B146_5EA1217A56FE__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <windows.h>
//注意:此类当创建的图像对象是多帧动画时,没有考虑线程同步问题,不能在其他地方使用,
//比如所有的Graphics操作函数,以及从基类继承过来的接口函数,你只能使用ImageEx类的public接口函数;
//而当创建的是单帧图像时,线程同步性等价于基类Image的同步性,
//则你可以像以前使用Image类那样使用该类,包括所有的Graphics操作函数,以及从基类继承过来的接口函数。
//其实你会发现下面的public成员函数操作的成员变量都是新增的成员变量。
class ImageEx : public Image
{
public:
//以长度为nSize的内存pBuff中的内容构造图像
ImageEx(const void* pBuff, size_t nSize, BOOL useEmbeddedColorManagement = FALSE);
//以类型为sResourceType,名称为sResource的资源构造图像
ImageEx(LPCTSTR sResourceType, LPCTSTR sResource, BOOL useEmbeddedColorManagement = FALSE);
//以文件构造图像
ImageEx(LPCTSTR filename, BOOL useEmbeddedColorManagement = FALSE);
//调用Destroy成员函数
~ImageEx();
public:
//如果已经构造的对象是动画,则创建动画线程,并返回true,
//如果为静态图像或已经创建过动画线程,则也返回false
// 图像将绘制在m_hWnd客户区的rect区域,会拉伸,支持镜像
bool InitAnimation(HWND hWnd, RECT rect);
//判断是否为动画
bool IsAnimatedGIF() { return m_nFrameCount > 1; }
//设置动画暂停与否
void SetPause(bool bPause);
//判断动画是否处于暂停状态
bool IsPaused() { return m_bPause; }
//关闭动画,事实上基类Image中还有的两个成员变量没有关闭,因为析构函数会调用基类析构函数进行关闭的
void Destroy();
protected:
//测试图像是否为动画,是的话成员变量m_pPropertyItem将会malloc分配内存
bool TestForAnimatedGIF();
//给所有成员变量初始化,构造函数中调用
void Initialize();
//在客户区画出当前帧,返回值表示是否要退出线程函数
bool DrawFrameGIF();
//把长度为nSize的内存pBuff中的内容创建为IStream流保存在类的m_pStream成员变量中,
//返回成功与否,不检查参数的非法性
bool LoadFromBuffer(const void* pBuff, size_t nSize);
//装载名称为lpName,类型为lpType的资源到pResource中,返回装载成功与否。
//nBufSize表示pResource缓存的长度。pResource为NULL时,nBufSize返回所需内存大小,
//不为NULL时,返回实际资源大小,长度不够时,相当于pResource为NULL时的作用,只是返回值为false。
bool GetResource(LPCTSTR lpName, LPCTSTR lpType, void* pResource, size_t& nBufSize);
//装载类型为sResourceType,名称为sResource的资源到流对象成员变量m_pStream中,返回装载成功与否,
//m_pStream在该函数中装载成功与否保存在成员变量m_bIsInitialized中,
//同时,如果该类的对象是以文件构造的,则m_bIsInitialized表示构造成功与否
bool Load(LPCTSTR sResourceType, LPCTSTR sResource); //类型,名字
//实际的线程函数
void ThreadAnimation();
//代理线程函数,实际调用ThreadAnimation成员函数
static UINT WINAPI _ThreadAnimationProc(LPVOID pParam);
protected:
IStream* m_pStream; //记得Release
HANDLE m_hThread; //线程创建时是挂起的,须调用ResumeThread
HANDLE m_hPause; //手工重置,初始有信号
HANDLE m_hExitEvent; //手工重置,初始无信号
HINSTANCE m_hInst;
HWND m_hWnd;
UINT m_nFrameCount;
UINT m_nFramePosition;
bool m_bIsInitialized; //表示对象构造成功与否,不管是以资源还是文件的形式
bool m_bPause;
PropertyItem* m_pPropertyItem; //如果为动画,会malloc内存,记得free
HDC m_hdcMem; //双缓存用,只在是多帧动画且创建了播放线程时才创建
HBITMAP m_hbmpBack; //双缓存用,只在是多帧动画且创建了播放线程时才创建
RECT m_rect; // 图像在m_hWnd客户区绘制的区域,会拉伸,支持镜像
//另外,基类Image中还有两个成员变量:nativeImage(GpImage*类型)和lastResult(Status类型)
};
#endif // !defined(AFX_GDIPLUSHELPER_H__BD5F6266_5686_43E2_B146_5EA1217A56FE__INCLUDED_)
// GDIPlusHelper.cpp: implementation of the CGDIPlusHelper class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ImageEx.h"
#include <process.h>
#include <string>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
////////////////////////////////////////////////////////////////////////////////
int A2U(const char* szA,wchar_t* szU,size_t cnt)
{
return MultiByteToWideChar (CP_ACP, 0, szA, -1, szU, cnt) ;
}
std::wstring A2U(const char* szA)
{
int nRetCode=A2U(szA,0,0);
if(0==nRetCode)
return std::wstring();
std::wstring str(nRetCode-1,'/0');
A2U(szA,(wchar_t*)(str.c_str()),nRetCode);
return str;
}
////////////////////////////////////////////////////////////////////////////////
//以长度为nSize的内存pBuff中的内容构造图像
////////////////////////////////////////////////////////////////////////////////
ImageEx::ImageEx(const void* pBuff, size_t nSize, BOOL useEmbeddedColorManagement)
{
Initialize();
bool bResult = LoadFromBuffer(pBuff, nSize);
m_bIsInitialized = bResult;
if (m_bIsInitialized)
{
nativeImage = NULL;
if (useEmbeddedColorManagement)
lastResult = DllExports::GdipLoadImageFromStreamICM(m_pStream, &nativeImage);
else
lastResult = DllExports::GdipLoadImageFromStream(m_pStream, &nativeImage);
TestForAnimatedGIF();
}
}
////////////////////////////////////////////////////////////////////////////////
//以类型为sResourceType,名称为sResource的资源构造图像
////////////////////////////////////////////////////////////////////////////////
ImageEx::ImageEx(LPCTSTR sResourceType, LPCTSTR sResource, BOOL useEmbeddedColorManagement)
{
Initialize();
if (Load(sResourceType, sResource))
{
nativeImage = NULL;
if (useEmbeddedColorManagement)
lastResult = DllExports::GdipLoadImageFromStreamICM(m_pStream, &nativeImage);
else
lastResult = DllExports::GdipLoadImageFromStream(m_pStream, &nativeImage);
TestForAnimatedGIF();
}
}
////////////////////////////////////////////////////////////////////////////////
//以文件构造图像
////////////////////////////////////////////////////////////////////////////////
ImageEx::ImageEx(LPCTSTR filename, BOOL useEmbeddedColorManagement) :
#ifndef UNICODE
Image(A2U(filename).c_str(), useEmbeddedColorManagement)
#else
Image(filename, useEmbeddedColorManagement)
#endif
{
Initialize();
m_bIsInitialized = (Ok == lastResult);
if(m_bIsInitialized)
TestForAnimatedGIF();
}
////////////////////////////////////////////////////////////////////////////////
//析构函数,调用Destroy成员函数
////////////////////////////////////////////////////////////////////////////////
ImageEx::~ImageEx()
{
this->Destroy();
}
////////////////////////////////////////////////////////////////////////////////
//如果已经构造的对象是动画,则创建动画线程,并返回true,
//如果为静态图像或已经创建过动画线程,则也返回false
// 图像将绘制在m_hWnd客户区的rect区域,会拉伸,支持镜像
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::InitAnimation(HWND hWnd, RECT rect)
{
m_hWnd = hWnd;
m_rect = rect;
if (!m_bIsInitialized)
return false;
if (IsAnimatedGIF())
{
if (m_hThread == NULL)
{
unsigned int nTID = 0;
HDC hDC = GetDC(m_hWnd);
m_hdcMem = CreateCompatibleDC(hDC);
m_hbmpBack = CreateCompatibleBitmap(hDC,GetWidth(),GetHeight());
SelectObject(m_hdcMem,m_hbmpBack);
ReleaseDC(m_hWnd, hDC);
m_hThread = (HANDLE) _beginthreadex( NULL, 0, _ThreadAnimationProc, this, CREATE_SUSPENDED,&nTID);
if (!m_hThread)
return false;
else
{
ResumeThread(m_hThread);
return true;
}
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
//把长度为nSize的内存pBuff中的内容创建为IStream流保存在类的m_pStream成员变量中,
//返回成功与否,不检查参数的非法性
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::LoadFromBuffer(const void* pBuff, size_t nSize)
{
bool bResult = false;
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, nSize);
if (hGlobal)
{
void* pData = GlobalLock(hGlobal);
if (pData)
memcpy(pData, pBuff, nSize);
GlobalUnlock(hGlobal);
if (CreateStreamOnHGlobal(hGlobal, TRUE, &m_pStream) == S_OK)
bResult = true;
}
return bResult;
}
////////////////////////////////////////////////////////////////////////////////
//装载名称为lpName,类型为lpType的资源到pResource中,返回装载成功与否。
//nBufSize表示pResource缓存的长度。pResource为NULL时,nBufSize返回所需内存大小,
//不为NULL时,返回实际资源大小,长度不够时,相当于pResource为NULL时的作用,只是返回值为false。
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::GetResource(LPCTSTR lpName, LPCTSTR lpType, void* pResource, size_t& nBufSize)
{
HRSRC hResInfo;
HGLOBAL hRes;
void* lpRes = NULL;
size_t nLen = 0;
bool bResult = FALSE;
// Find the resource
hResInfo = FindResource(m_hInst , lpName, lpType);
if (hResInfo == NULL)
{
DWORD dwErr = GetLastError();
return false;
}
// Load the resource
hRes = LoadResource(m_hInst , hResInfo);
if (hRes == NULL)
return false;
// Lock the resource
lpRes = LockResource(hRes);
if (lpRes != NULL)
{
nLen = SizeofResource(m_hInst , hResInfo);
if (pResource == NULL)
{
nBufSize = nLen;
bResult = true;
}
else
{
if (nBufSize >= nLen)
{
memcpy(pResource, lpRes, nLen);
bResult = true;
}
nBufSize = nLen;
}
UnlockResource(hRes);
}
// Free the resource
FreeResource(hRes);
return bResult;
}
////////////////////////////////////////////////////////////////////////////////
//装载类型为sResourceType,名称为sResource的资源到流对象成员变量m_pStream中,返回装载成功与否,
//m_pStream在该函数中装载成功与否保存在成员变量m_bIsInitialized中,
//同时,如果该类的对象是以文件构造的,则m_bIsInitialized表示构造成功与否
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::Load(LPCTSTR sResourceType, LPCTSTR sResource)
{
bool bResult = false;
BYTE* pBuff = NULL;
size_t nSize = 0;
if (GetResource(sResource, sResourceType, pBuff, nSize))
{
if (nSize > 0)
{
pBuff = new BYTE[nSize];
if (GetResource(sResource, sResourceType, (void*)pBuff, nSize))
{
bResult = LoadFromBuffer((const void*)pBuff, nSize);
}
delete [] pBuff;
}
}
m_bIsInitialized = bResult;
return bResult;
}
////////////////////////////////////////////////////////////////////////////////
//测试图像是否为动画,是的话成员变量m_pPropertyItem将会malloc分配内存
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::TestForAnimatedGIF()
{
UINT count = 0;
count = GetFrameDimensionsCount();
GUID* pDimensionIDs = new GUID[count];
// Get the list of frame dimensions from the Image object.
GetFrameDimensionsList(pDimensionIDs, count);
// Get the number of frames in the first dimension.
m_nFrameCount = GetFrameCount(&pDimensionIDs[0]);
// Assume that the image has a property item of type PropertyItemEquipMake.
// Get the size of that property item.
if (m_nFrameCount > 1)
{
int nSize = GetPropertyItemSize(PropertyTagFrameDelay);
// Allocate a buffer to receive the property item.
m_pPropertyItem = (PropertyItem*) malloc(nSize);
GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem);
}
delete pDimensionIDs;
return m_nFrameCount > 1;
}
////////////////////////////////////////////////////////////////////////////////
//给所有成员变量初始化,构造函数中调用
////////////////////////////////////////////////////////////////////////////////
void ImageEx::Initialize()
{
m_pStream = NULL;
m_hThread = NULL;
m_hPause = CreateEvent(NULL,TRUE,TRUE,NULL); //手工重置,初始有信号
m_hExitEvent = CreateEvent(NULL,TRUE,FALSE,NULL); //手工重置,初始无信号
m_hInst = GetModuleHandle(NULL);
m_hWnd = NULL;
m_nFrameCount = 0;
m_nFramePosition = 0;
m_bIsInitialized = false;
m_bPause = false;
m_pPropertyItem = NULL;
m_hdcMem = NULL;
m_hbmpBack = NULL;
SetRect(&m_rect,0,0,0,0);
//以下两句不能有,因为Initialize函数是构造函数中调用,下面两个属于基类的成员变量不能再次被修改
// nativeImage = NULL;
// lastResult = InvalidParameter;
}
////////////////////////////////////////////////////////////////////////////////
//代理线程函数,实际调用ThreadAnimation成员函数
////////////////////////////////////////////////////////////////////////////////
UINT WINAPI ImageEx::_ThreadAnimationProc(LPVOID pParam)
{
ImageEx *pImage = reinterpret_cast<ImageEx *> (pParam);
pImage->ThreadAnimation();
return 0;
}
////////////////////////////////////////////////////////////////////////////////
//实际的线程函数
////////////////////////////////////////////////////////////////////////////////
void ImageEx::ThreadAnimation()
{
m_nFramePosition = 0;
bool bExit = false;
while (!bExit)
{
bExit = DrawFrameGIF();
}
}
////////////////////////////////////////////////////////////////////////////////
//在客户区画出当前帧,返回值表示是否要退出线程函数
////////////////////////////////////////////////////////////////////////////////
bool ImageEx::DrawFrameGIF()
{
::WaitForSingleObject(m_hPause, INFINITE); //m_hPause手工重置,初始有信号
//pageGuid的值在显示GIF为FrameDimensionTime,显示TIF时为FrameDimensionPage
GUID pageGuid = FrameDimensionTime;
RECT rect;
UINT width, height;
HDC hDC = GetDC(m_hWnd);
int BltMode = SetStretchBltMode (hDC, COLORONCOLOR) ;
if (m_hdcMem)
{
width = GetWidth();
height = GetHeight();
SetRect(&rect, 0, 0, width, height);
FillRect(m_hdcMem,&rect,(HBRUSH) (COLOR_WINDOW+1));
Graphics graphics(m_hdcMem);
graphics.DrawImage(this, 0, 0, width, height);
StretchBlt(hDC, m_rect.left, m_rect.top,
m_rect.right-m_rect.left, m_rect.bottom-m_rect.top,
m_hdcMem, 0, 0, width, height, SRCCOPY);
}
SetStretchBltMode (hDC, BltMode) ;
ReleaseDC(m_hWnd, hDC);
long lPause = ((long*) m_pPropertyItem->value)[m_nFramePosition] * 10;
if(lPause<10)
lPause=80;
else if(lPause>5000)
lPause=80;
DWORD dwErr = WaitForSingleObject(m_hExitEvent, lPause); //m_hExitEvent手工重置,初始无信号
if(WAIT_OBJECT_0 != dwErr)
{
if(++m_nFramePosition == m_nFrameCount)
m_nFramePosition = 0;
SelectActiveFrame(&pageGuid, m_nFramePosition);
return false;
}
else
return true; // 如果在lPause时间内产生信号,则返回true,表示要退出线程
}
////////////////////////////////////////////////////////////////////////////////
//设置动画暂停与否
////////////////////////////////////////////////////////////////////////////////
void ImageEx::SetPause(bool bPause)
{
if (!IsAnimatedGIF())
return;
if (!m_bPause && bPause) //本来没暂停且要设置为暂停
{
ResetEvent(m_hPause);
}
else
{
if (m_bPause && !bPause) //本来已暂停且要设置为非暂停
{
SetEvent(m_hPause);
}
}
m_bPause = bPause;
}
////////////////////////////////////////////////////////////////////////////////
//关闭动画,事实上基类Image中还有的两个成员变量没有关闭,因为析构函数会调用基类析构函数进行关闭的
////////////////////////////////////////////////////////////////////////////////
void ImageEx::Destroy()
{
if (m_hThread)
{
this->SetPause(false);
SetEvent(m_hExitEvent);
WaitForSingleObject(m_hThread, INFINITE);
CloseHandle(m_hThread);
DeleteDC(m_hdcMem);
DeleteObject(m_hbmpBack);
m_hThread = NULL;
m_hdcMem = NULL;
m_hbmpBack = NULL;
}
if(m_hExitEvent)
CloseHandle(m_hExitEvent);
if(m_hPause)
CloseHandle(m_hPause);
free(m_pPropertyItem);
m_hExitEvent = NULL;
m_hPause = NULL;
m_pPropertyItem = NULL;
if (m_pStream)
{
m_pStream->Release();
m_pStream = NULL;
}
}
使用GDI+库显示gif动态图片,该类接口如下:
可以看出,该ImageEx完全继承了基类的接口函数。
说明:
如果打开非多帧图片,该类几乎完全等价于基类,比如你可以把该类的对象代入Graphics类系列的成员函数中;
如果打开的是多帧的图片,你只要打开图片后不调用InitAnimation函数(它会创建线程),则上述做法依然可以;
但如果调用InitAnimation函数后(单帧图像没关系,因为不会创建线程),则不可以了,
所有的基类继承过来的接口成员函数和配合gdi+库其他类的函数调用都是不可以的,因为没有作线程同步,
你只能调用下面位数不多的几个public成员函数,调用Destroy成员函数后,则就可以了,因为它会关闭线程。
其实你会发现下面的public成员函数操作的成员变量都是新增的成员变量,没涉及到线程同步问题。
class ImageEx : public Image
{
public:
//以长度为nSize的内存pBuff中的内容构造图像
ImageEx(const void* pBuff, size_t nSize, BOOL useEmbeddedColorManagement = FALSE);
//以类型为sResourceType,名称为sResource的资源构造图像
ImageEx(LPCTSTR sResourceType, LPCTSTR sResource, BOOL useEmbeddedColorManagement = FALSE);
//以文件构造图像
ImageEx(LPCTSTR filename, BOOL useEmbeddedColorManagement = FALSE);
//调用Destroy成员函数
~ImageEx();
public:
//如果已经构造的对象是动画,则创建动画线程,并返回true,
//如果为静态图像或已经创建过动画线程,则也返回false
// 图像将绘制在m_hWnd客户区的rect区域,会拉伸,支持镜像
bool InitAnimation(HWND hWnd, RECT rect);
//判断是否为动画
bool IsAnimatedGIF() { return m_nFrameCount > 1; }
//设置动画暂停与否
void SetPause(bool bPause);
//判断动画是否处于暂停状态
bool IsPaused() { return m_bPause; }
//关闭动画,事实上基类Image中还有的两个成员变量没有关闭,因为析构函数会调用基类析构函数进行关闭的
void Destroy();
//另外的非public的东西省略..
};
用法:
MFC对话框程序在下面添加:
BOOL CTestDlgDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//其它的初始化代码
// GDI+
//m_image为ImageEx指针类型成员变量,"GIF"为资源类型,"HEARTS"为资源名称
m_image = new ImageEx( _T("GIF"), _T("HEARTS") );
RECT rc;
GetClientRect(&rc);
m_image->InitAnimation(this->m_hWnd, rc);//创建gif播放线程
return TRUE; // return TRUE unless you set the focus to a control
}
CTestDlgDlg::~CTestDlgDlg()
{
// GDI+
delete m_image;
}
其中的m_image = new ImageEx( _T("GIF"), _T("HEARTS") );你可以换成ImageEx类的另外两个构造函数
相关文章推荐
- GDI+显示GIF动画
- 利用GDI+显示GIF动画
- GDI+显示GIF动画
- GDI+显示GIF动画
- GDI+显示GIF动画
- GDI+显示GIF动画
- GDI+显示GIF动画
- GDI+显示GIF动画
- 用GDI+显示GIF动画的一个类
- GDI+显示GIF动画
- 用GDI+显示GIF动画的一个类
- GDI+显示GIF动画
- 用VC读取gif动画,并显示之
- Android显示GIF动画
- GifView——Android显示GIF动画
- EVC编程点滴-GIF动画显示类
- 使用Qt显示GIF格式动画
- android中显示Gif动画 GifView
- Android开源项目:GifView——Android显示GIF动画
- GDI+中GIF图片的显示