• vc应用CPictureEx类(重载CStatic类)加载gif动画


    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 "));
      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 "
          "Logical Screen dimensions = %dx%d "
          "Global color table = %d "
          "Color depth = %d "
          "Sort flag = %d "
          "Size of Global Color Table = %d "
          "Background color index = %d "
          "Pixel aspect ratio = %d "
          "Frame count = %d "
          "Background color = %06Xh "
         ),
       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() "));
      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 "));
       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 "),szFileName);
      return FALSE;
     };

     dwSize = file.GetLength();
     hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwSize);
     if (!hGlobal)
     {
      TRACE(_T("Load (file): Error allocating memory "));
      return FALSE;
     };
     
     char *pData = reinterpret_cast<char*>(GlobalLock(hGlobal));
     if (!pData)
     {
      TRACE(_T("Load (file): Error locking memory "));
      GlobalFree(hGlobal);
      return FALSE;
     };

     TRY
     {
      file.Read(pData,dwSize);
     }
     CATCH(CFileException, e);                                          
     {
      TRACE(_T("Load (file): An exception occured while reading the file %s "),
       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 "));
      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 "));
      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 "),szFileName);
      return;
     };

     char *pData = reinterpret_cast<char *> (GlobalLock(hData));
     if (!pData)
     {
      TRACE(_T("WriteData: Error locking memory "));
      return;
     };

     TRY
     {
      file.Write(pData,dwSize);
     }
     CATCH(CFileException, e);                                          
     {
      TRACE(_T("WriteData: An exception occured while writing to the file %s "),
       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 "));
       return;
       break;

      case BLOCK_TRAILER:
       TRACE(_T("- Trailer block "));
       break;

      case BLOCK_APPEXT:
       TRACE(_T("- Application extension block "));
       break;

      case BLOCK_COMMEXT:
       TRACE(_T("- Comment extension block "));
       break;

      case BLOCK_CONTROLEXT:
       {
       TGIFControlExt *pControl = 
        reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]);
       TRACE(_T("- Graphic control extension block (delay %d, disposal %d) "),
         pControl->m_wDelayTime, pControl->GetPackedValue(GCX_PACKED_DISPOSAL));
       };
       break;

      case BLOCK_PLAINTEXT:
       TRACE(_T("- Plain text extension block "));
       break;

      case BLOCK_IMAGE:
       TGIFImageDescriptor *pIDescr = 
        reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
       TRACE(_T("- Image data block (%dx%d  %d,%d) "),
         pIDescr->m_wWidth,
         pIDescr->m_wHeight,
         pIDescr->m_wLeftPos,
         pIDescr->m_wTopPos);
       break;
      };

      SkipNextBlock(); 
     };

     TRACE(_T(" "));
    }
    #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();

  • 相关阅读:
    C#编写ActiveX在WEB中应用
    Asp.net中Array的用法
    CAB打包方法
    C#获取文件路径的几种方法
    使用MakeCAB.exe命令创建CAB文件
    VS2005 工具里添加“OLE/COM 对象查看器”命令
    【转载】ASP.NET MVC中Controller与View之间的数据传递总结
    F如何配置外向交货单的定价过程
    解决 局域网XP无权限访问的问题 (转)
    U盘装XP系统(含截图,2012最新原创超简单方法)
  • 原文地址:https://www.cnblogs.com/lidabo/p/6894259.html
Copyright © 2020-2023  润新知