传统的PictureControl无法动态的显示GIF图片, 只能显示其中的某一帧, 此处通过PictureEx类来实现PictureControl控件显示GIF图片.

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 (oleg_bykoff@rsdn.ru)
// 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 drawingSIZE     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 onenum 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 methodGCX_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 widthWORD m_wHeight; // 2 bytes. Logical screen heightunsigned char m_cPacked;      // packed field  unsigned char m_cBkIndex;     // 1 byte. Background color indexunsigned char m_cPixelAspect; // 1 byte. Pixel aspect ratioinline 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 11char m_cAppIdentifier[8];   // application identifierchar 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 4unsigned char m_cPacked;    // packed fieldWORD m_wDelayTime;    // delay timeunsigned char m_cTColorIndex; // transparent color indexunsigned 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 12WORD m_wLeftPos;    // text grid left positionWORD m_wTopPos;     // text grid top positionWORD m_wGridWidth;  // text grid widthWORD m_wGridHeight; // text grid heightunsigned char m_cCellWidth;  // character cell widthunsigned char m_cCellHeight; // character cell heightunsigned char m_cFgColor; // text foreground color indexunsigned 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 positionWORD m_wTopPos;  // image top positionWORD m_wWidth;   // image widthWORD m_wHeight;  // image heightunsigned char m_cPacked; // packed fieldinline int GetPackedValue(enum IDPackedValues Value);
};#pragma pack() // turn byte alignment offpublic:BOOL GetPaintRect(RECT *lpRect);BOOL SetPaintRect(const RECT *lpRect);CPictureEx();virtual ~CPictureEx();void Stop();   // stops animationvoid 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 frameBOOL 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 hGlobalBOOL Load(HGLOBAL hGlobal, DWORD dwSize);// loads a picture from a program resource// i.e. Load(MAKEINTRESOURCE(IDR_MYPIC),_T("GIFTYPE"));BOOL Load(LPCTSTR szResourceName,LPCTSTR szResourceType);protected:#ifdef GIF_TRACINGvoid 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_MSGDECLARE_MESSAGE_MAP()
};#endif // !defined(AFX_PICTUREEX_H__0EFE5DE0_7B68_4DB7_8B34_5DC634948438__INCLUDED_)

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 (oleg_bykoff@rsdn.ru)
// 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_FILE
static 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 sizeASSERT(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 defaultm_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_MAP
END_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 variablesm_pRawData = NULL;GlobalUnlock(hGlobal);// don't delete memory on object's releaseif (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 sizelong 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 GIFm_bIsGIF = TRUE;m_pGIFLSDescriptor = reinterpret_cast<TGIFLSDescriptor *>(m_pRawData + sizeof(TGIFHeader));if (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT) == 1){// calculate the globat color table sizem_nGlobalCTSize = static_cast<int>(3* (1 << (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE)+1)));// get the background color if GCT is presentunsigned 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 sizem_PictureSize.cx = m_pGIFLSDescriptor->m_wWidth;m_PictureSize.cy = m_pGIFLSDescriptor->m_wHeight;// determine frame count for this pictureUINT nFrameCount=0;ResetDataPointer();while (SkipNextGraphicBlock())nFrameCount++;#ifdef GIF_TRACINGTRACE(_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 variablesm_pRawData = NULL;GlobalUnlock(hGlobal);// don't delete memory on object's releaseif (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 releaseif (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 framem_arrFrames.push_back(frame);};// clean after ourselvesm_pRawData = NULL;GlobalUnlock(hGlobal);if (m_arrFrames.empty()) // couldn't load any framesreturn 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 defaultm_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 usunsigned 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_CATCHGlobalUnlock(hGlobal);file.Close();BOOL bRetValue = Load(hGlobal,dwSize);GlobalFree(hGlobal);return bRetValue;
}BOOL CPictureEx::Load(LPCTSTR szResourceName, LPCTSTR szResourceType)
{ASSERT(szResourceName);ASSERT(szResourceType);HRSRC hPicture = FindResource(AfxGetResourceHandle(),szResourceName,szResourceType);HGLOBAL hResData;if (!hPicture || !(hResData = LoadResource(AfxGetResourceHandle(),hPicture))){TRACE(_T("Load (resource): Error loading resource %s\n"),szResourceName);return FALSE;};DWORD dwSize = SizeofResource(AfxGetResourceHandle(),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 descriptorm_nCurrOffset = sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;
}BOOL CPictureEx::SkipNextGraphicBlock()
{if (!m_pRawData) return FALSE;// GIF header + LSDescriptor [+ GCT] [+ Control block] + Dataenum 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 textif (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 blockwhile ((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 blockswitch(m_pRawData[m_nCurrOffset+1]){case 0x01:// plain text extensionreturn BLOCK_PLAINTEXT;break;case 0xF9:// graphic control extensionreturn BLOCK_CONTROLEXT;break;case 0xFE:// comment extensionreturn BLOCK_COMMEXT;break;case 0xFF:// application extensionreturn BLOCK_APPEXT;break;};break;case 0x3B:// trailerreturn BLOCK_TRAILER;break;case 0x2C:// image datareturn 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 handleCloseHandle(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 #2if (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 #3if (m_hDispMemDC && (m_arrFrames[m_nCurrFrame].m_nDisposal == 3) ){// put it backBitBlt(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 variablesSelectObject(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 #3if (m_arrFrames[m_nCurrFrame].m_nDisposal == 3){// prepare a memory DC and store the background in itm_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 100msif (m_arrFrames[m_nCurrFrame].m_nDelay < 5) WaitForSingleObject(m_hExitEvent, 100);elseWaitForSingleObject(m_hExitEvent, 10*m_arrFrames[m_nCurrFrame].m_nDelay);if (m_bExitThread) break;// disposal method #2if (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);};} elseif (m_hDispMemDC && (m_arrFrames[m_nCurrFrame].m_nDisposal == 3) ){// put it backBitBlt(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 variablesSelectObject(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 executionWaitForSingleObject(m_hThread,5000);CloseHandle(m_hThread);m_hThread = NULL;}// make it possible to Draw() againResetEvent(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 textint nStart = m_nCurrOffset;int nBlockLen = GetNextBlockLen();if (nBlockLen <= 0) return NULL;if (nBlock == BLOCK_CONTROLEXT){// get the following dataTGIFControlExt *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 offsetsTGIFImageDescriptor *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 pointerunsigned 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; // trailernOffset++;*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 backgroundm_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_CATCHGlobalUnlock(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);
}

将PictureEx.h 和 PictureEx.cpp文件添加至项目中.

void CShowDlg::ShowGifPicture(CString path) {int cx, cy;image.Load(path);                                         cx = image.GetWidth();//  获取gif图片的原始大小cy = image.GetHeight();//  获取gif图片的原始大小int nScreenWidth, nScreenHeight;//获得屏幕尺寸nScreenWidth = ::GetSystemMetrics(SM_CXSCREEN);nScreenHeight = ::GetSystemMetrics(SM_CYSCREEN); ShowWindow(SW_MAXIMIZE); //最大化显示CWnd *pWnd = NULL;pWnd = GetDlgItem(IDC_STATIC);//获取控件句柄 PictureControl的IDpWnd->MoveWindow(CRect((nScreenWidth - cx) / 2, (nScreenHeight - cy) / 2, (nScreenWidth + cx) / 2, (nScreenHeight + cy) / 2));CRect imageRect((nScreenWidth - cx) / 2, (nScreenHeight - cy) / 2, (nScreenWidth + cx) / 2, (nScreenHeight + cy) / 2);            //  将GIF图片居中显示m_GifPic.Create(NULL, WS_CHILD | WS_VISIBLE | SS_ENHMETAFILE, imageRect, this, 1234);m_GifPic.Load(path);m_GifPic.Draw();
}

MFC PictureControl 空间实现GIF图片的动态显示相关推荐

  1. mfc中插入PNG透明图片

    mfc中插入PNG透明图片 记录下在mfc中插入png透明图片的方法 新建项目 在对话框界面中插入两个 Picture Control 在MFC_pngDlg.h中添加 在MFC_pngDlg.cpp ...

  2. Android---仿QQ空间动态九宫格图片预览(一)

    Android-仿QQ空间动态九宫格图片预览(一) 文章目录 Android---仿QQ空间动态九宫格图片预览(一) NineGridImageView(九宫格图片控件) 预览效果 特性 用法 dem ...

  3. Android开发之QQ空间效果(QQ空间下拉图片放大,松手后回弹)

    Android开发之QQ空间效果(QQ空间下拉图片放大,松手后回弹) 腾讯QQ空间的下拉图片放大,松手后回弹的效果带来的视觉差异效果让许多移动开发者心动不已,经本人一段时间的研究,终于实现了该视差效果 ...

  4. vs2015+opencv3.2+mfc读取摄像头显示在图片控件Picture Control

    vs2015 + opencv3.3 + mfc读取摄像头显示在图片控件Picture Control 要开始做一个行人检测的小项目,首先读取出摄像头显示,参考了这位博主的教程:http : //bl ...

  5. C语言用句柄显示bmp图片,VC编程之VC MFC界面上显示BMP图片

    本文主要向大家介绍了VC编程之VC MFC界面上显示BMP图片,通过具体的内容向大家展示,希望对大家学习VC编程有所帮助. 1.通过点击界面浏览按钮选择BMP图像文件. 点击浏览按钮打开文件对话框选择 ...

  6. MFC 加载并显示图片

    方法一  用API OleLoadPicture来加载JPG.GIF格式的图片(注:不支持PNG格式,另外GIF只能加载第一帧,且不支持透明)OleLoadPicture 函数实际上创建了一个IPic ...

  7. MFC 加载jpg等图片

    MFC提供的CWnd只有默认加载BMP文件的接口,对JPG等图像是不支持的,而实际中经常需要用到非BMP的图片,在VC中加载.JPG格式的图片,有两种方法,用流对象加载和用IPicture接口加载. ...

  8. 安卓仿QQ空间实现(含图片的动态编辑、发表、点赞、评论)

    之前做项目的时候需要用到仿空间动态的功能,在此做一下记录,简单介绍一下listview自定义适配器以及各相关功能实现方法. 一.效果图 这里添加了两条初始动态,其他都是现编现发的.头像这里后面使用了圆 ...

  9. QQ空间的“神奇”图片

    昨天女友说遇到了一个奇怪的问题,就是在别人的QQ空间的转载上看到了自己的名字,头像,问我是怎么回事.晚饭后,闲来无事,想到那个问题,就让女友打开那个看到自己头像,名字的网页(因为本人几乎不上空间,所以 ...

最新文章

  1. 2019机器学习比赛_2019顶尖的机器学习课程
  2. MapReduce骚气分析
  3. java中保存图片到本地_java保存网络图片到本地
  4. migration vmware vms to openstack kvm 修改vmware windows scsi to ide
  5. antd 函数组件_react函数组件中引用antd<Form/>组件demo
  6. tr的display属性出现td的colspan无效问题
  7. hadoop-2.7.2 分布式集群搭建
  8. [转载] ANTLR——嵌入文法的Actions
  9. [转]Linux TCP/IP 协议栈的关键数据结构Socket Buffer(sk_buff )
  10. 分页 Paginator
  11. 华为系列设备优先级总结(二)
  12. 7.业务架构·应用架构·数据架构实战 --- 业务架构书
  13. OLDX-FC开源飞控
  14. 折腾黑苹果 - 制作四叶草CLOVER引导U盘
  15. MATLAB R2016b + VS2017专业版安装spams
  16. qrcode二维码生成工具
  17. 【论文阅读】时间序列中的变量是一张知识图谱
  18. 易支付程序对接固码支付
  19. 超详细的VMware虚拟机安装Linux图文教程保姆级
  20. 毕业论文排版及格式修改“骚操作”和常见误区(Word基础)

热门文章

  1. window7安装oracle9i,win 7安装oracle11g企业版
  2. snagit 如何对录取的视频进行剪切,剪切掉不需要的部分
  3. linux服务器中毒排查--基础篇
  4. 橘子为何变成枳?除名崔鹏乃足协无能!
  5. NOKIA X的跨界而来,诺基亚的自我救赎?
  6. html偷图片,ASP小偷偷取地址并生成html-ASP教程,ASP应用
  7. 一度智信:电商店铺爆款打造,新手快速上手!
  8. 计算机网络改变人们的思维,2012年4月浙江自学考试网络艺术真题
  9. 织梦表单html模板,dedecms织梦模板 自定义表单分页+模版显示的源码
  10. 铁氧体磁珠相关知识总结