您的位置:首页 > 其它

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类的另外两个构造函数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: