1.PictureEx.h文件:
//
// PictureEx.cpp: implementation of the CPictureEx class.//// Picture displaying control with support for the following formats:// GIF (including animated GIF87a and GIF89a), JPEG, BMP, WMF, ICO, CUR// // Written by Oleg Bykov ()// Copyright (c) 2001//// To use CPictureEx, follow these steps:// - place a static control on your dialog (either a text or a bitmap)// - change its identifier to something else (e.g. IDC_MYPIC)// - associate a CStatic with it using ClassWizard// - in your dialog's header file replace CStatic with CPictureEx// (don't forget to #include "PictureEx.h" and add // PictureEx.h and PictureEx.cpp to your project)// - call one of the overloaded CPictureEx::Load() functions somewhere// (OnInitDialog is a good place to start)// - if the preceding Load() succeeded call Draw()// // You can also add the control by defining a member variable of type // CPictureEx, calling CPictureEx::Create (derived from CStatic), then // CPictureEx::Load and CPictureEx::Draw.//// By default, the control initializes its background to COLOR_3DFACE// (see CPictureEx::PrepareDC()). You can change the background by// calling CPictureEx::SetBkColor(COLORREF) after CPictureEx::Load().//// I decided to leave in the class the functions to write separate frames from // animated GIF to disk. If you want to use them, uncomment #define GIF_TRACING // and an appropriate section in CPictureEx::Load(HGLOBAL, DWORD). These functions // won't be compiled and linked to your project unless you uncomment #define GIF_TRACING,// so you don't have to worry.// // Warning: this code hasn't been subject to a heavy testing, so// use it on your own risk. The author accepts no liability for the // possible damage caused by this code.//// Version 1.0 7 Aug 2001// Initial release//// Version 1.1 6 Sept 2001// ATL version of the class//// Version 1.2 14 Oct 2001// - Fixed a problem with loading GIFs from resources// in MFC-version of the class for multi-modules apps.// Thanks to Ruben Avila-Carretero for finding this out.//// - Got rid of waitable timer in ThreadAnimation()// Now CPictureEx[Wnd] works in Win95 too.// Thanks to Alex Egiazarov and Wayne King for the idea.//// - Fixed a visual glitch of using SetBkColor.// Thanks to Kwangjin Lee for finding this out.//// Version 1.3 10 Nov 2001// - Fixed a DC leak. One DC leaked per each UnLoad()// (forgot to put a ReleaseDC() in the end of // CPictureExWnd::PrepareDC() function).//// - Now it is possible to set a clipping rectangle using// CPictureEx[Wnd]::SetPaintRect(const LPRECT) function.// The LPRECT parameter tells the class what portion of// a picture should it display. If the clipping rect is // not set, the whole picture is shown.// Thanks to Fabrice Rodriguez for the idea.//// - Added support for Stop/Draw. Now you can Stop() an// animated GIF, then Draw() it again, it will continue// animation from the frame it was stopped on. You can // also know if a GIF is currently playing with the // IsPlaying() function.// // - Got rid of math.h and made m_bExitThread volatile. // Thanks to Piotr Sawicki for the suggestion.////#if !defined(AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_)
#define AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_#if _MSC_VER > 1000
#pragma once#endif // _MSC_VER > 1000#include <vector>
//#define GIF_TRACING // uncomment it if you want detailed TRACEsclass CPictureEx : public CStatic
{ public:struct TFrame // structure that keeps a single frame info
{ IPicture *m_pPicture; // pointer to the interface used for drawing SIZE m_frameSize; SIZE m_frameOffset; UINT m_nDelay; // delay (in 1/100s of a second) UINT m_nDisposal; // disposal method};#pragma pack(1) // turn byte alignment on
enum GIFBlockTypes
{ BLOCK_UNKNOWN, BLOCK_APPEXT, BLOCK_COMMEXT, BLOCK_CONTROLEXT, BLOCK_PLAINTEXT, BLOCK_IMAGE, BLOCK_TRAILER};enum ControlExtValues // graphic control extension packed field values
{ GCX_PACKED_DISPOSAL, // disposal method GCX_PACKED_USERINPUT, GCX_PACKED_TRANSPCOLOR};enum LSDPackedValues // logical screen descriptor packed field values
{ LSD_PACKED_GLOBALCT, LSD_PACKED_CRESOLUTION, LSD_PACKED_SORT, LSD_PACKED_GLOBALCTSIZE};enum IDPackedValues // image descriptor packed field values
{ ID_PACKED_LOCALCT, ID_PACKED_INTERLACE, ID_PACKED_SORT, ID_PACKED_LOCALCTSIZE};struct TGIFHeader // GIF header
{ char m_cSignature[3]; // Signature - Identifies the GIF Data Stream // This field contains the fixed value 'GIF' char m_cVersion[3]; // Version number. May be one of the following: // "87a" or "89a"};struct TGIFLSDescriptor // Logical Screen Descriptor
{ WORD m_wWidth; // 2 bytes. Logical screen width WORD m_wHeight; // 2 bytes. Logical screen heightunsigned char m_cPacked; // packed field
unsigned char m_cBkIndex; // 1 byte. Background color index
unsigned char m_cPixelAspect; // 1 byte. Pixel aspect ratio inline int GetPackedValue(enum LSDPackedValues Value);};struct TGIFAppExtension // application extension block
{ unsigned char m_cExtIntroducer; // extension introducer (0x21) unsigned char m_cExtLabel; // app. extension label (0xFF) unsigned char m_cBlockSize; // fixed value of 11 char m_cAppIdentifier[8]; // application identifier char m_cAppAuth[3]; // application authentication code};struct TGIFControlExt // graphic control extension block
{ unsigned char m_cExtIntroducer; // extension introducer (0x21) unsigned char m_cControlLabel; // control extension label (0xF9) unsigned char m_cBlockSize; // fixed value of 4 unsigned char m_cPacked; // packed field WORD m_wDelayTime; // delay time unsigned char m_cTColorIndex; // transparent color index unsigned char m_cBlockTerm; // block terminator (0x00)public: inline int GetPackedValue(enum ControlExtValues Value);};struct TGIFCommentExt // comment extension block
{ unsigned char m_cExtIntroducer; // extension introducer (0x21) unsigned char m_cCommentLabel; // comment extension label (0xFE)};struct TGIFPlainTextExt // plain text extension block
{ unsigned char m_cExtIntroducer; // extension introducer (0x21) unsigned char m_cPlainTextLabel; // text extension label (0x01) unsigned char m_cBlockSize; // fixed value of 12 WORD m_wLeftPos; // text grid left position WORD m_wTopPos; // text grid top position WORD m_wGridWidth; // text grid width WORD m_wGridHeight; // text grid height unsigned char m_cCellWidth; // character cell width unsigned char m_cCellHeight; // character cell height unsigned char m_cFgColor; // text foreground color index unsigned char m_cBkColor; // text background color index};struct TGIFImageDescriptor // image descriptor block
{ unsigned char m_cImageSeparator; // image separator byte (0x2C) WORD m_wLeftPos; // image left position WORD m_wTopPos; // image top position WORD m_wWidth; // image width WORD m_wHeight; // image height unsigned char m_cPacked; // packed field inline int GetPackedValue(enum IDPackedValues Value);};#pragma pack() // turn byte alignment off
public:
BOOL GetPaintRect(RECT *lpRect); BOOL SetPaintRect(const RECT *lpRect); CPictureEx(); virtual ~CPictureEx(); void Stop(); // stops animation void UnLoad(); // stops animation plus releases all resourcesBOOL IsGIF() const;
BOOL IsPlaying() const; BOOL IsAnimatedGIF() const; SIZE GetSize() const; int GetFrameCount() const; COLORREF GetBkColor() const; void SetBkColor(COLORREF clr);// draws the picture (starts an animation thread if needed)
// if an animation was previously stopped by Stop(), // continues it from the last displayed frame BOOL Draw();// loads a picture from a file
// i.e. Load(_T("mypic.gif")); BOOL Load(LPCTSTR szFileName);// loads a picture from a global memory block (allocated by GlobalAlloc)
// Warning: this function DOES NOT free the global memory, pointed to by hGlobal BOOL Load(HGLOBAL hGlobal, DWORD dwSize);// loads a picture from a program resource
// i.e. Load(IDR_MYPIC,_T("GIFTYPE")); BOOL Load(UINT nResourceID , LPCTSTR szResourceType);protected:
#ifdef GIF_TRACING
void EnumGIFBlocks(); void WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize);#endif // GIF_TRACINGRECT m_PaintRect;
SIZE m_PictureSize; COLORREF m_clrBackground; UINT m_nCurrFrame; UINT m_nDataSize; UINT m_nCurrOffset; UINT m_nGlobalCTSize; BOOL m_bIsGIF; BOOL m_bIsPlaying; volatile BOOL m_bExitThread; BOOL m_bIsInitialized; HDC m_hMemDC;HDC m_hDispMemDC;
HBITMAP m_hDispMemBM; HBITMAP m_hDispOldBM;HBITMAP m_hBitmap;
HBITMAP m_hOldBitmap; HANDLE m_hThread; HANDLE m_hExitEvent; IPicture * m_pPicture; TGIFHeader * m_pGIFHeader; unsigned char * m_pRawData; TGIFLSDescriptor * m_pGIFLSDescriptor; std::vector<TFrame> m_arrFrames;void ThreadAnimation();
static UINT WINAPI _ThreadAnimation(LPVOID pParam);int GetNextBlockLen() const;
BOOL SkipNextBlock(); BOOL SkipNextGraphicBlock(); BOOL PrepareDC(int nWidth, int nHeight); void ResetDataPointer(); enum GIFBlockTypes GetNextBlock() const; UINT GetSubBlocksLen(UINT nStartingOffset) const; HGLOBAL GetNextGraphicBlock(UINT *pBlockLen, UINT *pDelay, SIZE *pBlockSize, SIZE *pBlockOffset, UINT *pDisposal);// Generated message map functions
//{ {AFX_MSG(CPictureEx) afx_msg void OnDestroy(); afx_msg void OnPaint(); //}}AFX_MSG DECLARE_MESSAGE_MAP()};#endif // !defined(AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_)
2.PictureEx.cpp文件:
//
// PictureEx.cpp: implementation of the CPictureEx class.//// Picture displaying control with support for the following formats:// GIF (including animated GIF87a and GIF89a), JPEG, BMP, WMF, ICO, CUR// // Written by Oleg Bykov ()// Copyright (c) 2001//// To use CPictureEx, follow these steps:// - place a static control on your dialog (either a text or a bitmap)// - change its identifier to something else (e.g. IDC_MYPIC)// - associate a CStatic with it using ClassWizard// - in your dialog's header file replace CStatic with CPictureEx// (don't forget to #include "PictureEx.h" and add // PictureEx.h and PictureEx.cpp to your project)// - call one of the overloaded CPictureEx::Load() functions somewhere// (OnInitDialog is a good place to start)// - if the preceding Load() succeeded call Draw()// // You can also add the control by defining a member variable of type // CPictureEx, calling CPictureEx::Create (derived from CStatic), then // CPictureEx::Load and CPictureEx::Draw.//// By default, the control initializes its background to COLOR_3DFACE// (see CPictureEx::PrepareDC()). You can change the background by// calling CPictureEx::SetBkColor(COLORREF) after CPictureEx::Load().//// I decided to leave in the class the functions to write separate frames from // animated GIF to disk. If you want to use them, uncomment #define GIF_TRACING // and an appropriate section in CPictureEx::Load(HGLOBAL, DWORD). These functions // won't be compiled and linked to your project unless you uncomment #define GIF_TRACING,// so you don't have to worry.// // Warning: this code hasn't been subject to a heavy testing, so// use it on your own risk. The author accepts no liability for the // possible damage caused by this code.//// Version 1.0 7 Aug 2001// Initial release//// Version 1.1 6 Sept 2001// ATL version of the class//// Version 1.2 14 Oct 2001// - Fixed a problem with loading GIFs from resources// in MFC-version of the class for multi-modules apps.// Thanks to Ruben Avila-Carretero for finding this out.//// - Got rid of waitable timer in ThreadAnimation()// Now CPictureEx[Wnd] works in Win95 too.// Thanks to Alex Egiazarov and Wayne King for the idea.//// - Fixed a visual glitch of using SetBkColor.// Thanks to Kwangjin Lee for finding this out.//// Version 1.3 10 Nov 2001// - Fixed a DC leak. One DC leaked per each UnLoad()// (forgot to put a ReleaseDC() in the end of // CPictureExWnd::PrepareDC() function).//// - Now it is possible to set a clipping rectangle using// CPictureEx[Wnd]::SetPaintRect(const LPRECT) function.// The LPRECT parameter tells the class what portion of// a picture should it display. If the clipping rect is // not set, the whole picture is shown.// Thanks to Fabrice Rodriguez for the idea.//// - Added support for Stop/Draw. Now you can Stop() an// animated GIF, then Draw() it again, it will continue// animation from the frame it was stopped on. You can // also know if a GIF is currently playing with the // IsPlaying() function.// // - Got rid of math.h and made m_bExitThread volatile. // Thanks to Piotr Sawicki for the suggestion.////#include "stdafx.h"
#include "PictureEx.h"#include <process.h>#ifdef _DEBUG
#undef THIS_FILEstatic char THIS_FILE[]=__FILE__;#define new DEBUG_NEW#endif//
// Nested structures member functions//inline int CPictureEx::TGIFControlExt::GetPackedValue(enum ControlExtValues Value)
{ int nRet = (int)m_cPacked; switch (Value) { case GCX_PACKED_DISPOSAL: nRet = (nRet & 28) >> 2; break;case GCX_PACKED_USERINPUT:
nRet = (nRet & 2) >> 1; break;case GCX_PACKED_TRANSPCOLOR:
nRet &= 1; break; };return nRet;
}inline int CPictureEx::TGIFLSDescriptor::GetPackedValue(enum LSDPackedValues Value)
{ int nRet = (int)m_cPacked;switch (Value)
{ case LSD_PACKED_GLOBALCT: nRet = nRet >> 7; break;case LSD_PACKED_CRESOLUTION:
nRet = ((nRet & 0x70) >> 4) + 1; break;case LSD_PACKED_SORT:
nRet = (nRet & 8) >> 3; break;case LSD_PACKED_GLOBALCTSIZE:
nRet &= 7; break; };return nRet;
}inline int CPictureEx::TGIFImageDescriptor::GetPackedValue(enum IDPackedValues Value)
{ int nRet = (int)m_cPacked;switch (Value)
{ case ID_PACKED_LOCALCT: nRet >>= 7; break;case ID_PACKED_INTERLACE:
nRet = ((nRet & 0x40) >> 6); break;case ID_PACKED_SORT:
nRet = (nRet & 0x20) >> 5; break;case ID_PACKED_LOCALCTSIZE:
nRet &= 7; break; };return nRet;
} //// Construction/Destruction//CPictureEx::CPictureEx()
{ // check structures size ASSERT(sizeof(TGIFImageDescriptor) == 10); ASSERT(sizeof(TGIFAppExtension) == 14); ASSERT(sizeof(TGIFPlainTextExt) == 15); ASSERT(sizeof(TGIFLSDescriptor) == 7); ASSERT(sizeof(TGIFControlExt) == 8); ASSERT(sizeof(TGIFCommentExt) == 2); ASSERT(sizeof(TGIFHeader) == 6);m_pGIFLSDescriptor = NULL;
m_pGIFHeader = NULL; m_pPicture = NULL; m_pRawData = NULL; m_hThread = NULL; m_hBitmap = NULL; m_hMemDC = NULL;m_hDispMemDC = NULL;
m_hDispMemBM = NULL; m_hDispOldBM = NULL;m_bIsInitialized = FALSE;
m_bExitThread = FALSE; m_bIsPlaying = FALSE; m_bIsGIF = FALSE; m_clrBackground = RGB(255,255,255); // white by default m_nGlobalCTSize = 0; m_nCurrOffset = 0; m_nCurrFrame = 0; m_nDataSize = 0; m_PictureSize.cx = m_PictureSize.cy = 0; SetRect(&m_PaintRect,0,0,0,0);m_hExitEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
}CPictureEx::~CPictureEx()
{ UnLoad(); CloseHandle(m_hExitEvent);}BEGIN_MESSAGE_MAP(CPictureEx, CStatic)
//{ {AFX_MSG_MAP(CPictureEx) ON_WM_DESTROY() ON_WM_PAINT() //}}AFX_MSG_MAPEND_MESSAGE_MAP()BOOL CPictureEx::Load(HGLOBAL hGlobal, DWORD dwSize)
{ IStream *pStream = NULL; UnLoad();if (!(m_pRawData = reinterpret_cast<unsigned char*> (GlobalLock(hGlobal))) )
{ TRACE(_T("Load: Error locking memory\n")); return FALSE; };m_nDataSize = dwSize;
m_pGIFHeader = reinterpret_cast<TGIFHeader *> (m_pRawData);if ((memcmp(&m_pGIFHeader->m_cSignature,"GIF",3) != 0) &&
((memcmp(&m_pGIFHeader->m_cVersion,"87a",3) != 0) || (memcmp(&m_pGIFHeader->m_cVersion,"89a",3) != 0)) ) { // it's neither GIF87a nor GIF89a // do the default processing// clear GIF variables
m_pRawData = NULL; GlobalUnlock(hGlobal);// don't delete memory on object's release
if (CreateStreamOnHGlobal(hGlobal,FALSE,&pStream) != S_OK) return FALSE;if (OleLoadPicture(pStream,dwSize,FALSE,IID_IPicture,
reinterpret_cast<LPVOID *>(&m_pPicture)) != S_OK) { pStream->Release(); return FALSE; }; pStream->Release();// store picture's size
long hmWidth;
long hmHeight; m_pPicture->get_Width(&hmWidth); m_pPicture->get_Height(&hmHeight);HDC hDC = ::GetDC(m_hWnd);
m_PictureSize.cx = MulDiv(hmWidth, GetDeviceCaps(hDC,LOGPIXELSX), 2540); m_PictureSize.cy = MulDiv(hmHeight, GetDeviceCaps(hDC,LOGPIXELSY), 2540); ::ReleaseDC(m_hWnd,hDC); } else { // it's a GIF m_bIsGIF = TRUE; m_pGIFLSDescriptor = reinterpret_cast<TGIFLSDescriptor *> (m_pRawData + sizeof(TGIFHeader)); if (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT) == 1) { // calculate the globat color table size m_nGlobalCTSize = static_cast<int> (3* (1 << (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE)+1))); // get the background color if GCT is present unsigned char *pBkClr = m_pRawData + sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + 3*m_pGIFLSDescriptor->m_cBkIndex; m_clrBackground = RGB(pBkClr[0],pBkClr[1],pBkClr[2]); };// store the picture's size
m_PictureSize.cx = m_pGIFLSDescriptor->m_wWidth; m_PictureSize.cy = m_pGIFLSDescriptor->m_wHeight;// determine frame count for this picture
UINT nFrameCount=0; ResetDataPointer(); while (SkipNextGraphicBlock()) nFrameCount++;#ifdef GIF_TRACING
TRACE( _T(" -= GIF encountered\n" "Logical Screen dimensions = %dx%d\n" "Global color table = %d\n" "Color depth = %d\n" "Sort flag = %d\n" "Size of Global Color Table = %d\n" "Background color index = %d\n" "Pixel aspect ratio = %d\n" "Frame count = %d\n" "Background color = %06Xh\n\n" ), m_pGIFLSDescriptor->m_wWidth, m_pGIFLSDescriptor->m_wHeight, m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT), m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_CRESOLUTION), m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_SORT), m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE), m_pGIFLSDescriptor->m_cBkIndex, m_pGIFLSDescriptor->m_cPixelAspect, nFrameCount, m_clrBackground ); EnumGIFBlocks();#endifif (nFrameCount == 0) // it's an empty GIF!
{ m_pRawData = NULL; GlobalUnlock(hGlobal); return FALSE; };// now check the frame count
// if there's only one frame, no need to animate this GIF // therefore, treat it like any other picif (nFrameCount == 1)
{ // clear GIF variables m_pRawData = NULL; GlobalUnlock(hGlobal);// don't delete memory on object's release
if (CreateStreamOnHGlobal(hGlobal,FALSE,&pStream) != S_OK) return FALSE;if (OleLoadPicture(pStream,dwSize,FALSE,IID_IPicture,
(LPVOID *)&m_pPicture) != S_OK) { pStream->Release(); return FALSE; };pStream->Release();
} else { // if, on the contrary, there are several frames // then store separate frames in an arrayTFrame frame;
UINT nBlockLen; HGLOBAL hFrameData; UINT nCurFrame = 0;ResetDataPointer();
while (hFrameData = GetNextGraphicBlock(&nBlockLen, &frame.m_nDelay, &frame.m_frameSize, &frame.m_frameOffset, &frame.m_nDisposal) ) { #ifdef GIF_TRACING // // uncomment the following strings if you want // to write separate frames on disk // // CString szName; // szName.Format(_T("%.4d.gif"),nCurFrame); // WriteDataOnDisk(szName,hFrameData,nBlockLen); // nCurFrame++; #endif // GIF_TRACINGIStream *pStream = NULL;
// delete memory on object's release
if (CreateStreamOnHGlobal(hFrameData,TRUE,&pStream) != S_OK) { GlobalFree(hFrameData); continue; };if (OleLoadPicture(pStream,nBlockLen,FALSE,
IID_IPicture, reinterpret_cast<LPVOID *>(&frame.m_pPicture)) != S_OK) { pStream->Release(); continue; }; pStream->Release(); // everything went well, add this frame m_arrFrames.push_back(frame); };// clean after ourselves
m_pRawData = NULL; GlobalUnlock(hGlobal);if (m_arrFrames.empty()) // couldn't load any frames
return FALSE; }; }; // if (!IsGIF...return PrepareDC(m_PictureSize.cx,m_PictureSize.cy);
}void CPictureEx::UnLoad()
{ Stop(); if (m_pPicture) { m_pPicture->Release(); m_pPicture = NULL; }; std::vector<TFrame>::iterator it; for (it=m_arrFrames.begin();it<m_arrFrames.end();it++) (*it).m_pPicture->Release(); m_arrFrames.clear();if (m_hMemDC)
{ SelectObject(m_hMemDC,m_hOldBitmap); ::DeleteDC(m_hMemDC); ::DeleteObject(m_hBitmap); m_hMemDC = NULL; m_hBitmap = NULL; };if (m_hDispMemDC)
{ SelectObject(m_hDispMemDC,m_hDispOldBM); ::DeleteDC(m_hDispMemDC); ::DeleteObject(m_hDispMemBM); m_hDispMemDC = NULL; m_hDispMemBM = NULL; };SetRect(&m_PaintRect,0,0,0,0);
m_pGIFLSDescriptor = NULL; m_pGIFHeader = NULL; m_pRawData = NULL; m_hThread = NULL; m_bIsInitialized = FALSE; m_bExitThread = FALSE; m_bIsGIF = FALSE; m_clrBackground = RGB(255,255,255); // white by default m_nGlobalCTSize = 0; m_nCurrOffset = 0; m_nCurrFrame = 0; m_nDataSize = 0;}BOOL CPictureEx::Draw()
{ if (!m_bIsInitialized) { TRACE(_T("Call one of the CPictureEx::Load() member functions before calling Draw()\n")); return FALSE; };if (IsAnimatedGIF())
{ // the picture needs animation // we'll start the thread that will handle it for us unsigned int nDummy; m_hThread = (HANDLE) _beginthreadex(NULL,0,_ThreadAnimation,this, CREATE_SUSPENDED,&nDummy); if (!m_hThread) { TRACE(_T("Draw: Couldn't start a GIF animation thread\n")); return FALSE; } else ResumeThread(m_hThread); } else { if (m_pPicture) { long hmWidth; long hmHeight; m_pPicture->get_Width(&hmWidth); m_pPicture->get_Height(&hmHeight); if (m_pPicture->Render(m_hMemDC, 0, 0, m_PictureSize.cx, m_PictureSize.cy, 0, hmHeight, hmWidth, -hmHeight, NULL) == S_OK) { Invalidate(FALSE); return TRUE; }; }; };return FALSE;
}SIZE CPictureEx::GetSize() const
{ return m_PictureSize;}BOOL CPictureEx::Load(LPCTSTR szFileName)
{ ASSERT(szFileName); CFile file; HGLOBAL hGlobal; DWORD dwSize;if (!file.Open(szFileName,
CFile::modeRead | CFile::shareDenyWrite) ) { TRACE(_T("Load (file): Error opening file %s\n"),szFileName); return FALSE; };dwSize = file.GetLength();
hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwSize); if (!hGlobal) { TRACE(_T("Load (file): Error allocating memory\n")); return FALSE; }; char *pData = reinterpret_cast<char*>(GlobalLock(hGlobal)); if (!pData) { TRACE(_T("Load (file): Error locking memory\n")); GlobalFree(hGlobal); return FALSE; };TRY
{ file.Read(pData,dwSize); } CATCH(CFileException, e); { TRACE(_T("Load (file): An exception occured while reading the file %s\n"), szFileName); GlobalFree(hGlobal); e->Delete(); file.Close(); return FALSE; } END_CATCH GlobalUnlock(hGlobal); file.Close();BOOL bRetValue = Load(hGlobal,dwSize);
GlobalFree(hGlobal); return bRetValue;}BOOL CPictureEx::Load(UINT nResourceID , LPCTSTR szResourceType)
{ ASSERT(szResourceType);HINSTANCE hInst = AfxFindResourceHandle(MAKEINTRESOURCE(nResourceID),RT_GROUP_ICON) ;
HRSRC hPicture = NULL ; FindResource(hInst,MAKEINTRESOURCE(nResourceID),szResourceType);HGLOBAL hResData;
if (!hPicture || !(hResData = LoadResource(hInst,hPicture))) { return FALSE; }; DWORD dwSize = SizeofResource(hInst,hPicture);// hResData is not the real HGLOBAL (we can't lock it)
// let's make it realHGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwSize);
if (!hGlobal) { TRACE(_T("Load (resource): Error allocating memory\n")); FreeResource(hResData); return FALSE; }; char *pDest = reinterpret_cast<char *> (GlobalLock(hGlobal)); char *pSrc = reinterpret_cast<char *> (LockResource(hResData)); if (!pSrc || !pDest) { TRACE(_T("Load (resource): Error locking memory\n")); GlobalFree(hGlobal); FreeResource(hResData); return FALSE; }; CopyMemory(pDest,pSrc,dwSize); FreeResource(hResData); GlobalUnlock(hGlobal);BOOL bRetValue = Load(hGlobal,dwSize);
GlobalFree(hGlobal); return bRetValue;}void CPictureEx::ResetDataPointer()
{ // skip header and logical screen descriptor m_nCurrOffset = sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;}BOOL CPictureEx::SkipNextGraphicBlock()
{ if (!m_pRawData) return FALSE;// GIF header + LSDescriptor [+ GCT] [+ Control block] + Data
enum GIFBlockTypes nBlock;
nBlock = GetNextBlock();
while ((nBlock != BLOCK_CONTROLEXT) &&
(nBlock != BLOCK_IMAGE) && (nBlock != BLOCK_PLAINTEXT) && (nBlock != BLOCK_UNKNOWN) && (nBlock != BLOCK_TRAILER) ) { if (!SkipNextBlock()) return NULL; nBlock = GetNextBlock(); };if ((nBlock == BLOCK_UNKNOWN) ||
(nBlock == BLOCK_TRAILER)) return FALSE;// it's either a control ext.block, an image or a plain text
if (GetNextBlockLen() <= 0) return FALSE;
if (nBlock == BLOCK_CONTROLEXT)
{ if (!SkipNextBlock()) return FALSE; nBlock = GetNextBlock();// skip everything until we meet an image block or a plain-text block
while ((nBlock != BLOCK_IMAGE) && (nBlock != BLOCK_PLAINTEXT) && (nBlock != BLOCK_UNKNOWN) && (nBlock != BLOCK_TRAILER) ) { if (!SkipNextBlock()) return NULL; nBlock = GetNextBlock(); };if ((nBlock == BLOCK_UNKNOWN) ||
(nBlock == BLOCK_TRAILER)) return FALSE; };// skip the found data block (image or plain-text)
if (!SkipNextBlock()) return FALSE;return TRUE;
}UINT CPictureEx::GetSubBlocksLen(UINT nStartingOffset) const
{ UINT nRet = 0; UINT nCurOffset = nStartingOffset;while (m_pRawData[nCurOffset] != 0)
{ nRet += m_pRawData[nCurOffset]+1; nCurOffset += m_pRawData[nCurOffset]+1; };return nRet+1;
}enum CPictureEx::GIFBlockTypes CPictureEx::GetNextBlock() const
{ switch(m_pRawData[m_nCurrOffset]) { case 0x21: // extension block switch(m_pRawData[m_nCurrOffset+1]) { case 0x01: // plain text extension return BLOCK_PLAINTEXT; break;case 0xF9:
// graphic control extension return BLOCK_CONTROLEXT; break;case 0xFE:
// comment extension return BLOCK_COMMEXT; break;case 0xFF:
// application extension return BLOCK_APPEXT; break; }; break; case 0x3B: // trailer return BLOCK_TRAILER; break;case 0x2C:
// image data return BLOCK_IMAGE; break; };return BLOCK_UNKNOWN;
}BOOL CPictureEx::SkipNextBlock()
{ if (!m_pRawData) return FALSE;int nLen = GetNextBlockLen();
if ((nLen <= 0) || ((m_nCurrOffset+nLen) > m_nDataSize)) return FALSE;m_nCurrOffset += nLen;
return TRUE;}int CPictureEx::GetNextBlockLen() const
{ GIFBlockTypes nBlock = GetNextBlock();int nTmp;
switch(nBlock)
{ case BLOCK_UNKNOWN: return -1; break;case BLOCK_TRAILER:
return 1; break;case BLOCK_APPEXT:
nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFAppExtension)); if (nTmp > 0) return sizeof(TGIFAppExtension)+nTmp; break;case BLOCK_COMMEXT:
nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFCommentExt)); if (nTmp > 0) return sizeof(TGIFCommentExt)+nTmp; break;case BLOCK_CONTROLEXT:
return sizeof(TGIFControlExt); break;case BLOCK_PLAINTEXT:
nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFPlainTextExt)); if (nTmp > 0) return sizeof(TGIFPlainTextExt)+nTmp; break;case BLOCK_IMAGE:
TGIFImageDescriptor *pIDescr = reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]); int nLCTSize = (int) (pIDescr->GetPackedValue(ID_PACKED_LOCALCT)*3* (1 << (pIDescr->GetPackedValue(ID_PACKED_LOCALCTSIZE)+1)));int nTmp = GetSubBlocksLen(m_nCurrOffset+
sizeof(TGIFImageDescriptor) + nLCTSize + 1); if (nTmp > 0) return sizeof(TGIFImageDescriptor) + nLCTSize + 1 + nTmp; break; };return 0;
}UINT WINAPI CPictureEx::_ThreadAnimation(LPVOID pParam)
{ ASSERT(pParam); CPictureEx *pPic = reinterpret_cast<CPictureEx *> (pParam);pPic->m_bIsPlaying = TRUE;
pPic->ThreadAnimation(); pPic->m_bIsPlaying = FALSE;// this thread has finished its work so we close the handle
CloseHandle(pPic->m_hThread); // and init the handle to zero (so that Stop() doesn't Wait on it) pPic->m_hThread = 0; return 0;}void CPictureEx::ThreadAnimation()
{ // first, restore background (for stop/draw support) // disposal method #2 if (m_arrFrames[m_nCurrFrame].m_nDisposal == 2) { HBRUSH hBrush = CreateSolidBrush(m_clrBackground); if (hBrush) { RECT rect = { m_arrFrames[m_nCurrFrame].m_frameOffset.cx, m_arrFrames[m_nCurrFrame].m_frameOffset.cy, m_arrFrames[m_nCurrFrame].m_frameOffset.cx + m_arrFrames[m_nCurrFrame].m_frameSize.cx, m_arrFrames[m_nCurrFrame].m_frameOffset.cy + m_arrFrames[m_nCurrFrame].m_frameSize.cy }; FillRect(m_hMemDC,&rect,hBrush); DeleteObject(hBrush); }; } else // disposal method #3 if (m_hDispMemDC && (m_arrFrames[m_nCurrFrame].m_nDisposal == 3) ) { // put it back BitBlt(m_hMemDC, m_arrFrames[m_nCurrFrame].m_frameOffset.cx, m_arrFrames[m_nCurrFrame].m_frameOffset.cy, m_arrFrames[m_nCurrFrame].m_frameSize.cx, m_arrFrames[m_nCurrFrame].m_frameSize.cy, m_hDispMemDC,0,0, SRCCOPY); // init variables SelectObject(m_hDispMemDC,m_hDispOldBM); DeleteDC(m_hDispMemDC); m_hDispMemDC = NULL; DeleteObject(m_hDispMemBM); m_hDispMemBM = NULL; };while (!m_bExitThread)
{ if (m_arrFrames[m_nCurrFrame].m_pPicture) { /// // Before rendering a frame we should take care of what's // behind that frame. TFrame::m_nDisposal will be our guide: // 0 - no disposal specified (do nothing) // 1 - do not dispose (again, do nothing) // 2 - restore to background color (m_clrBackground) // 3 - restore to previousdisposal method #3
if (m_arrFrames[m_nCurrFrame].m_nDisposal == 3) { // prepare a memory DC and store the background in it m_hDispMemDC = CreateCompatibleDC(m_hMemDC); m_hDispMemBM = CreateCompatibleBitmap(m_hMemDC, m_arrFrames[m_nCurrFrame].m_frameSize.cx, m_arrFrames[m_nCurrFrame].m_frameSize.cy); if (m_hDispMemDC && m_hDispMemBM) { m_hDispOldBM = reinterpret_cast<HBITMAP> (SelectObject(m_hDispMemDC,m_hDispMemBM)); BitBlt(m_hDispMemDC,0,0, m_arrFrames[m_nCurrFrame].m_frameSize.cx, m_arrFrames[m_nCurrFrame].m_frameSize.cy, m_hMemDC, m_arrFrames[m_nCurrFrame].m_frameOffset.cx, m_arrFrames[m_nCurrFrame].m_frameOffset.cy, SRCCOPY); }; }; ///long hmWidth;
long hmHeight; m_arrFrames[m_nCurrFrame].m_pPicture->get_Width(&hmWidth); m_arrFrames[m_nCurrFrame].m_pPicture->get_Height(&hmHeight);if (m_arrFrames[m_nCurrFrame].m_pPicture->Render(m_hMemDC,
m_arrFrames[m_nCurrFrame].m_frameOffset.cx, m_arrFrames[m_nCurrFrame].m_frameOffset.cy, m_arrFrames[m_nCurrFrame].m_frameSize.cx, m_arrFrames[m_nCurrFrame].m_frameSize.cy, 0, hmHeight, hmWidth, -hmHeight, NULL) == S_OK) { Invalidate(FALSE); }; if (m_bExitThread) break;// if the delay time is too short (like in old GIFs), wait for 100ms
if (m_arrFrames[m_nCurrFrame].m_nDelay < 5) WaitForSingleObject(m_hExitEvent, 100); else WaitForSingleObject(m_hExitEvent, 10*m_arrFrames[m_nCurrFrame].m_nDelay);if (m_bExitThread) break;
// disposal method #2
if (m_arrFrames[m_nCurrFrame].m_nDisposal == 2) { HBRUSH hBrush = CreateSolidBrush(m_clrBackground); if (hBrush) { RECT rect = { m_arrFrames[m_nCurrFrame].m_frameOffset.cx, m_arrFrames[m_nCurrFrame].m_frameOffset.cy, m_arrFrames[m_nCurrFrame].m_frameOffset.cx + m_arrFrames[m_nCurrFrame].m_frameSize.cx, m_arrFrames[m_nCurrFrame].m_frameOffset.cy + m_arrFrames[m_nCurrFrame].m_frameSize.cy }; FillRect(m_hMemDC,&rect,hBrush); DeleteObject(hBrush); }; } else if (m_hDispMemDC && (m_arrFrames[m_nCurrFrame].m_nDisposal == 3) ) { // put it back BitBlt(m_hMemDC, m_arrFrames[m_nCurrFrame].m_frameOffset.cx, m_arrFrames[m_nCurrFrame].m_frameOffset.cy, m_arrFrames[m_nCurrFrame].m_frameSize.cx, m_arrFrames[m_nCurrFrame].m_frameSize.cy, m_hDispMemDC,0,0, SRCCOPY); // init variables SelectObject(m_hDispMemDC,m_hDispOldBM); DeleteDC(m_hDispMemDC); m_hDispMemDC = NULL; DeleteObject(m_hDispMemBM); m_hDispMemBM = NULL; }; }; m_nCurrFrame++; if (m_nCurrFrame == m_arrFrames.size()) { m_nCurrFrame = 0; // init the screen for the first frame, HBRUSH hBrush = CreateSolidBrush(m_clrBackground); if (hBrush) { RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy}; FillRect(m_hMemDC,&rect,hBrush); DeleteObject(hBrush); }; }; };}void CPictureEx::Stop()
{ m_bIsPlaying = FALSE; m_bExitThread = TRUE; SetEvent(m_hExitEvent); if (m_hThread) { // we'll wait for 5 seconds then continue execution WaitForSingleObject(m_hThread,5000); CloseHandle(m_hThread); m_hThread = NULL; }// make it possible to Draw() again
ResetEvent(m_hExitEvent); m_bExitThread = FALSE;}HGLOBAL CPictureEx::GetNextGraphicBlock(UINT *pBlockLen,
UINT *pDelay, SIZE *pBlockSize, SIZE *pBlockOffset, UINT *pDisposal){ if (!m_pRawData) return NULL;// GIF header + LSDescriptor [+ GCT] [+ Control block] + Data
*pDisposal = 0;
enum GIFBlockTypes nBlock; nBlock = GetNextBlock();while (
(nBlock != BLOCK_CONTROLEXT) && (nBlock != BLOCK_IMAGE) && (nBlock != BLOCK_PLAINTEXT) && (nBlock != BLOCK_UNKNOWN) && (nBlock != BLOCK_TRAILER) ) { if (!SkipNextBlock()) return NULL; nBlock = GetNextBlock(); };if ((nBlock == BLOCK_UNKNOWN) ||
(nBlock == BLOCK_TRAILER)) return NULL;// it's either a control ext.block, an image or a plain text
int nStart = m_nCurrOffset;
int nBlockLen = GetNextBlockLen();if (nBlockLen <= 0) return NULL;
if (nBlock == BLOCK_CONTROLEXT)
{ // get the following data TGIFControlExt *pControl = reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]); // store delay time *pDelay = pControl->m_wDelayTime; // store disposal method *pDisposal = pControl->GetPackedValue(GCX_PACKED_DISPOSAL);if (!SkipNextBlock()) return NULL;
nBlock = GetNextBlock(); // skip everything until we find data to display // (image block or plain-text block) while ( (nBlock != BLOCK_IMAGE) && (nBlock != BLOCK_PLAINTEXT) && (nBlock != BLOCK_UNKNOWN) && (nBlock != BLOCK_TRAILER) ) { if (!SkipNextBlock()) return NULL; nBlock = GetNextBlock(); nBlockLen += GetNextBlockLen(); };if ((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER))
return NULL; nBlockLen += GetNextBlockLen(); } else *pDelay = -1; // to indicate that there was no delay valueif (nBlock == BLOCK_IMAGE)
{ // store size and offsets TGIFImageDescriptor *pImage = reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]); pBlockSize->cx = pImage->m_wWidth; pBlockSize->cy = pImage->m_wHeight; pBlockOffset->cx = pImage->m_wLeftPos; pBlockOffset->cy = pImage->m_wTopPos; };if (!SkipNextBlock()) return NULL;
HGLOBAL hGlobal = GlobalAlloc(GMEM_FIXED,
sizeof(TGIFHeader) + sizeof(TGIFLSDescriptor) + m_nGlobalCTSize + nBlockLen + 1); // for the trailerif (!hGlobal) return NULL;
int nOffset = 0;
// GMEM_FIXED means we get a pointer
unsigned char *pGlobal = reinterpret_cast<unsigned char *> (hGlobal);CopyMemory(pGlobal,m_pRawData,
sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize); nOffset += sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;CopyMemory(pGlobal + nOffset,&m_pRawData[nStart], nBlockLen);
nOffset += nBlockLen;pGlobal[nOffset] = 0x3B; // trailer
nOffset++;*pBlockLen = nOffset;
return hGlobal;
}BOOL CPictureEx::IsGIF() const
{ return m_bIsGIF;}BOOL CPictureEx::IsAnimatedGIF() const
{ return (m_bIsGIF && (m_arrFrames.size() > 1));}BOOL CPictureEx::IsPlaying() const
{ return m_bIsPlaying;}int CPictureEx::GetFrameCount() const
{ if (!IsAnimatedGIF()) return 0;return m_arrFrames.size();
}COLORREF CPictureEx::GetBkColor() const
{ return m_clrBackground;}void CPictureEx::OnPaint()
{ CPaintDC dc(this); // device context for paintingLONG nPaintWidth = m_PaintRect.right-m_PaintRect.left;
if (nPaintWidth > 0)
{ LONG nPaintHeight = m_PaintRect.bottom - m_PaintRect.top; ::BitBlt(dc.m_hDC, 0, 0, nPaintWidth, nPaintHeight, m_hMemDC, m_PaintRect.left, m_PaintRect.top, SRCCOPY); } else { ::BitBlt(dc.m_hDC, 0, 0, m_PictureSize.cx, m_PictureSize.cy, m_hMemDC, 0, 0, SRCCOPY); };}BOOL CPictureEx::PrepareDC(int nWidth, int nHeight)
{ SetWindowPos(NULL,0,0,nWidth,nHeight,SWP_NOMOVE | SWP_NOZORDER);HDC hWinDC = ::GetDC(m_hWnd);
if (!hWinDC) return FALSE; m_hMemDC = CreateCompatibleDC(hWinDC); if (!m_hMemDC) { ::ReleaseDC(m_hWnd,hWinDC); return FALSE; };m_hBitmap = CreateCompatibleBitmap(hWinDC,nWidth,nHeight);
if (!m_hBitmap) { ::ReleaseDC(m_hWnd,hWinDC); ::DeleteDC(m_hMemDC); return FALSE; };m_hOldBitmap = reinterpret_cast<HBITMAP>
(SelectObject(m_hMemDC,m_hBitmap)); // fill the background m_clrBackground = GetSysColor(COLOR_3DFACE); RECT rect = {0,0,nWidth,nHeight}; FillRect(m_hMemDC,&rect,(HBRUSH)(COLOR_WINDOW));::ReleaseDC(m_hWnd,hWinDC);
m_bIsInitialized = TRUE; return TRUE;}void CPictureEx::OnDestroy()
{ Stop(); CStatic::OnDestroy();}void CPictureEx::SetBkColor(COLORREF clr)
{ if (!m_bIsInitialized) return;m_clrBackground = clr;
HBRUSH hBrush = CreateSolidBrush(clr);
if (hBrush) { RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy}; FillRect(m_hMemDC,&rect,hBrush); DeleteObject(hBrush); };}#ifdef GIF_TRACING
void CPictureEx::WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize){ CFile file;if (!file.Open(szFileName,
CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone)) { TRACE(_T("WriteData: Error creating file %s\n"),szFileName); return; };char *pData = reinterpret_cast<char *> (GlobalLock(hData));
if (!pData) { TRACE(_T("WriteData: Error locking memory\n")); return; };TRY
{ file.Write(pData,dwSize); } CATCH(CFileException, e); { TRACE(_T("WriteData: An exception occured while writing to the file %s\n"), szFileName); e->Delete(); GlobalUnlock(hData); file.Close(); return; } END_CATCH GlobalUnlock(hData); file.Close();}void CPictureEx::EnumGIFBlocks()
{ enum GIFBlockTypes nBlock;ResetDataPointer();
while(m_nCurrOffset < m_nDataSize) { nBlock = GetNextBlock(); switch(nBlock) { case BLOCK_UNKNOWN: TRACE(_T("- Unknown block\n")); return; break;case BLOCK_TRAILER:
TRACE(_T("- Trailer block\n")); break;case BLOCK_APPEXT:
TRACE(_T("- Application extension block\n")); break;case BLOCK_COMMEXT:
TRACE(_T("- Comment extension block\n")); break;case BLOCK_CONTROLEXT:
{ TGIFControlExt *pControl = reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]); TRACE(_T("- Graphic control extension block (delay %d, disposal %d)\n"), pControl->m_wDelayTime, pControl->GetPackedValue(GCX_PACKED_DISPOSAL)); }; break;case BLOCK_PLAINTEXT:
TRACE(_T("- Plain text extension block\n")); break;case BLOCK_IMAGE:
TGIFImageDescriptor *pIDescr = reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]); TRACE(_T("- Image data block (%dx%d %d,%d)\n"), pIDescr->m_wWidth, pIDescr->m_wHeight, pIDescr->m_wLeftPos, pIDescr->m_wTopPos); break; };SkipNextBlock();
};TRACE(_T("\n"));
}#endif // GIF_TRACINGBOOL CPictureEx::SetPaintRect(const RECT *lpRect)
{ return CopyRect(&m_PaintRect, lpRect);}BOOL CPictureEx::GetPaintRect(RECT *lpRect)
{ return CopyRect(lpRect, &m_PaintRect);}
3.dlg头文件定义:CPictureEx *pAutoGif;
要包含#include "PictureEx.h"
4.dlg的cpp文件实现动态加载gif:
void C**Dlg::OnOK()
{ // TODO: Add extra validation here pAutoGif=new CPictureEx(); pAutoGif->Create(NULL,WS_CHILD | WS_VISIBLE |SS_ENHMETAFILE,CRect(50,50,100,100),this,1234); if(pAutoGif->Load(_T("g1.gif"))) pAutoGif->Draw(); //CDialog::OnOK();}5.静态加载:可以预先加入图片控件,在ClassWiard中为图片控件加入CSatic控制变量m_GifPic,然后将CSatic更改成CPictureEx的变量,当然要加上#include "PictureEx.h",可以将gif预先通过resourceview导入到工程中,之后可以在OnInitDialog()加载:
if (m_GifPic.Load(MAKEINTRESOURCE(IDR_GIF),_T("Gif")))//IDR_GIF导入工程gif的id属性
m_GifPic.Draw();6.可以在onpaint()中移动加载gif的图片控件:
CRect rc =CRect(100,400,150,450);
m_GifPic.MoveWindow(&rc,true);
m_GifPic.Draw();