- // 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類的另外兩個構造函數