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. 深入理解ClassLoader(四)—类的父委托加载机制

    上几次我们介绍到了JVM内部的几个类加载器,我们来重新画一下这个图,再来看一下他们之间的关系.

  2. java类到底是如何加载并初始化的?

    Java虚拟机如何把编译好的.class文件加载到虚拟机里面?加载之后如何初始化类?静态类变量和实例类变量的初始化过程是否相同,分别是如何初始化的呢?这篇文章就 是解决上面3个问题的. 若有不正之处, ...

  3. 修改类不用重启Tomcat加载整个项目

    可以修改类不用重启Tomcat加载整个项目(手工启动) 配置reloadable=true(自动重载) 使用Debug模式,前提是仅限于局部修改.(修改类不用重启--热加载) Tomcat轻小,而We ...

  4. (转)java类到底是如何加载并初始化的?

    Java虚拟机如何把编译好的.class文件加载到虚拟机里面?加载之后如何初始化类?静态类变量和实例类变量的初始化过程是否相同,分别是如何初始化的呢?这篇文章就 是解决上面3个问题的. 若有不正之处, ...

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

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

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

    参加一个笔试,有一个关于类的静态代码块.构造代码块.构造函数的执行顺序的问题.不太清楚,网上百度了一下.在这里记录一下. 一.什么时候会加载类?使用到类中的内容时加载:有三种情况1.创建对象:new ...

  7. Java笔记(十二)……类中各部分加载顺序及存放位置问题

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

  8. Java虚拟机——类的结构与加载

    1.为什么Java可以跨平台 因为有java虚拟机,跨平台是因为字节码即class文件具有平台无关性,java代码会经过java虚拟机转换为字节码 2.class文件的结构 class文件主要是以8位 ...

  9. java类的编译、加载和执行

    一.java类的编译流程 这里主要讲的是从java文件到class文件 下图是java类编译的详细步骤: 1.词法分析:将java源代码的字符流转变为标记(Token)的集合,Token是编译过程中的 ...

随机推荐

  1. oracle 11gR2 如何修改public ip

    修改public ip 修改前后ip信息 修改前ip信息 p570a 192.168.1.10 p570b 192.168.1.11 修改后ip信息 p570a 1.7.3.112 p570a 1.7 ...

  2. USACO 2.1 Ordered Fractions

    Ordered Fractions Consider the set of all reduced fractions between 0 and 1 inclusive with denominat ...

  3. Windows远程桌面和360

    Windows的远程桌面输错了一次密码, 然后就怎么都连接不上了, 查了半天发现 傻缺360会默认屏蔽Windows的远程桌面和数据库连接..... 大家没事都卸载了360吧

  4. iis 部署

    配置错误1: 由于权限不足而无法读取配置文件 建立一个新用户,分配所有权限 http://blog.csdn.net/jaychouliyu/article/details/7237143 配置错误2 ...

  5. 获取sqlserver数据字典的完整sql。

    SELECTsysobjects.name AS 表名称 , --------------as 的作用:为字段起一个别名 --sysproperties.[value] AS 表说明 , ------ ...

  6. 一个基于React整套技术栈+Node.js的前端页面制作工具

    pagemaker是一个前端页面制作工具,方便产品,运营和视觉的同学迅速开发简单的前端页面,从而可以解放前端同学的工作量.此项目创意来自网易乐得内部项目nfop中的pagemaker项目.原来项目的前 ...

  7. ibatiS启动的异常 The content of elements must consist of well-formed character data or markup

    ibatiS启动的异常 The content of elements must consist of well-formed character data or markup 配置的动态SQL语句里 ...

  8. WiFi无线连接过程中有哪几个主要步骤?

    WiFi无线连接过程中有哪几个主要步骤?[1]在使用WIFI功能时,经常性的操作是打开手机上的WiFi设备,搜索到心目中的热点,输入密码,联网成功,成功上网.这个看似简单的过程,背后却是隐藏着大量的无 ...

  9. 转载:常用 Git 命令清单

    转载:常用 Git 命令清单 原文地址:http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html  作者: 阮一峰 我每天使用 Git , ...

  10. to_string函数(将数字转换成字符串)

    一般常用的转换字符串的方法std::stringstream,但是效率较低;目前C ++11提供了std::to_string 效率方面:C风格的sprintf()没有动态分配内存效率最高;std:: ...