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 (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 TRACEs

class 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 height

unsigned 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 resources

BOOL 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_TRACING

RECT 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 (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 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_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 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();
#endif

if (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 pic

if (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 array

TFrame 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_TRACING

IStream *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 real

HGLOBAL 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 previous

disposal 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 value

if (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 trailer

if (!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 painting

LONG 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_TRACING

BOOL 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();

vc应用CPictureEx类(重载CStatic类)加载gif动画相关推荐

  1. 你知道 Java 类是如何被加载的吗?

    一:前言 最近给一个非Java方向的朋友讲了下双亲委派模型,朋友让我写篇文章深度研究下JVM的ClassLoader,我确实也好久没写JVM相关的文章了,有点手痒痒,涂了皮炎平也抑制不住. 我在向朋友 ...

  2. 面试官:你真的知道 Java 类是如何被加载的吗?

    来自:https://yq.aliyun.com/articles/710407 一:前言 最近给一个非Java方向的朋友讲了下双亲委派模型,朋友让我写篇文章深度研究下JVM的ClassLoader, ...

  3. Java 类中各成分加载顺序和内存中的存放位置

    一.什么时候会加载类? 使用到类中的内容时加载:有三种情况 1.创建对象:new StaticCode(); 2.使用类中的静态成员:StaticCode.num=9; StaticCode.show ...

  4. java使用哪个类,怎么知道 java类从哪个jar 加载

    如何知道 java类从哪个jar 加载 Java的类装载器使用的是优先策略,类加载器加载类的时候先找到哪个就加载哪个 从JSP页面就行测试 JBuilder Generated JSP if(claN ...

  5. 懒加载和预加载的区别_类的动态创建(ro,rw)amp; 懒加载类和非懒加载类底层加载的区别 amp; 类和分类的搭配分析...

    黑客技术点击右侧关注,了解黑客的世界! Java开发进阶点击右侧关注,掌握进阶之路! Python开发点击右侧关注,探讨技术话题!作者丨OSMin链接:https://juejin.im/post/5 ...

  6. [JAVA冷知识]动态加载不适合数组类?那如何动态加载一个数组类?

    写在前面 今天和小伙伴分享一些java小知识点,主要围绕下面几点: 既然数组是一个类, 那么编译后类名是什么?类路径呢? 为什么说动态加载不适合数组? 那应该如何动态加载一个数组? 部分内容参考 &l ...

  7. java重写源码类不生效(自己重写的类没有被jvm加载)

    背景 很多情况下我们需要调试源码或者修改源码以满足我们的开发需求 我们会项目的java目录下创建一个类,目录和类名都与源码的一致,然后将源码中的代码全部拷贝到我们自己创建的类中,再加入一些我们自己的逻 ...

  8. MFC加载GIF动画

    在一个项目中需要使用MFC直接加载GIF动画.加载GIF动画网上有多种方式,大多数是将GIF填进资源,使用PictureEx类load资源文件  如下: PictureEx图片显示类支持以下格式的图片 ...

  9. [Swift通天遁地]五、高级扩展-(11)图像加载Loading动画效果的自定义和缓存

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ ➤微信公众号:山青咏芝(shanqingyongzhi) ➤博客园地址:山青咏芝(https://www.cnblog ...

最新文章

  1. Runloop, 多线程
  2. BZOJ-2242-计算器-SDOI2011-BSGS
  3. 《树莓派实战秘籍》——1.14 技巧14初学GPIO
  4. Jmeter 时间函数工具汇总
  5. 提取特征点的方式,opencv2.x和opencv3.x的区别
  6. 七月算法机器学习2 数理统计与参数估计1
  7. Vmware里安装Ubuntu时由于分辨率问题,界面显示不全解决办法
  8. 【图像配准】基于surf算法实现图像配准附Matlab代码
  9. 服务器防御DDoS的方法,一文解决DDoS攻击
  10. 装备状态鉴定文件清单
  11. 智慧城市将成为推进我国新型城镇化的新思路、新手段、新模式
  12. 大数据分析与应用技术创新平台
  13. 三维绕任意轴旋转矩阵
  14. 直流有刷电机驱动项目需求分析
  15. 【网络同步】浅析帧同步和状态同步
  16. Excel一键将-替换为--的批量操作
  17. 火狐扩展(Firefox Extension)FillForm开发手记(更新中)
  18. 进程和线程.View椭圆运动
  19. Navicat报错connection is being used
  20. xe5 android 模拟器,Delphi xe5如何使用Bluestacks模拟器。

热门文章

  1. 和smi_nvidia-smi 报错:无法与 nvidia driver 通信
  2. Python3基础-分数运算
  3. Spring-Security (学习记录四)--配置权限过滤器,采用数据库方式获取权限
  4. WPF ,listbox,平滑滚动的2种方式。
  5. mysql_fetch_row,mysql_fetch_array,mysql_fetch_assoc的区别
  6. 搜索引擎爬虫蜘蛛的UserAgent收集
  7. mx:button加skin光晕点击时,大小不一样
  8. web标准页面知识必备
  9. Fedora Core 4配置本地yum源
  10. Android平台 Psensor传感器调试方法