利用GDI plus 播放GIF图片

103 阅读2分钟

最近要使用动态gif播放技术,翻了一下没有看见轻量的类,便写了个CStatic子类化的类。
代码如下:

<script type="text/javascript"> </script>

/**/ /*
*  GDI+ 应用 之 GIF播放类
*  GIF; 动态GIF; 播放; GDI plus,GDI+
*  
*  本类作者:webmote 2006-11 原创出版
*  有任何问题,请email: luo31@yahoo.com.cn
*  !!!!引用本类,请不要删除此段注释!!!!
*/
#pragma  once
#include  < gdiplus.h >
using   namespace  Gdiplus;


//  CGifCtrl

class  CGifCtrl :  public  CStatic
... {


    DECLARE_DYNAMIC(CGifCtrl)

public:
    CGifCtrl();
    virtual ~CGifCtrl();
    /**//************************************************************************/
    /**//* SetImg 设置播放的图片路径 播放速度 透明颜色                                  */
    /**//* 路径为空,则只重设 播放速度 和 透明颜色                                        */
    /**//************************************************************************/
    BOOL SetImg(LPCTSTR pszPath,UINT nMilliSeconds=200,COLORREF clrBg=-1);
public:
    //播放gif动画的速度
    CWinThread* m_pThread;
    CRITICAL_SECTION m_csGDILock;
    static UINT DrawGif(LPVOID lpParam);

protected:

    COLORREF m_clrBg;
    Image*    m_pImg;
    GUID*     m_pDimensionIDs;

    UINT      m_nTotalFrame;
    UINT      m_nCurrFrame;
    UINT      m_nWaitTime;
    HANDLE m_hEventKill;
    HANDLE m_hEventDead;
    HANDLE m_thread;



    
protected:
    DECLARE_MESSAGE_MAP()
    virtual void PreSubclassWindow();
public:
    afx_msg void OnDestroy();
    void KillThread(void);
    afx_msg void OnPaint();
} ;



类中使用了线程来定制播放时间。也采用了透明技巧。

//  GifCtrl.cpp : 实现文件
//

#include  " stdafx.h "
#include  " TestAt.h "
#include  " GifCtrl.h "
#include  " .gifctrl.h "

//  CGifCtrl

IMPLEMENT_DYNAMIC(CGifCtrl, CStatic)
CGifCtrl::CGifCtrl()
... {


    m_clrBg=-1;
    m_pImg=NULL;
    m_pDimensionIDs=NULL;
    m_pThread=NULL;
    m_nTotalFrame=0;
    m_nCurrFrame=0;
    m_nWaitTime=0;
    m_thread=NULL;

    ::InitializeCriticalSection(&m_csGDILock);
    // kill event starts out in the signaled state
    m_hEventKill = CreateEvent(NULL, TRUE, FALSE, NULL);
    m_hEventDead = CreateEvent(NULL, TRUE, FALSE, NULL);

}

CGifCtrl:: ~ CGifCtrl()
... {


    if(m_pDimensionIDs!=NULL)
    ...{


        delete m_pDimensionIDs;
        m_pDimensionIDs=NULL;
    }
    CloseHandle(m_hEventKill);
    CloseHandle(m_hEventDead);

}


BEGIN_MESSAGE_MAP(CGifCtrl, CStatic)
    ON_WM_DESTROY()
    ON_WM_PAINT()
END_MESSAGE_MAP()

BOOL CGifCtrl::SetImg(LPCTSTR pszPath,UINT nMilliSeconds,COLORREF clrBg)
... {


    ::EnterCriticalSection(&m_csGDILock);
    m_nWaitTime=nMilliSeconds;
    m_clrBg=clrBg;
    ::LeaveCriticalSection(&m_csGDILock);
    //ASSERT(pszPath!=NULL);

    if(pszPath!=NULL)
    ...{


        USES_CONVERSION;
        UINT count = 0;

        ::EnterCriticalSection(&m_csGDILock);
        m_pImg=Image::FromFile(T2W(pszPath),TRUE);
        
        if(m_pImg!=NULL)
        ...{


            // How many frame dimensions does the Image object have?
            count = m_pImg->GetFrameDimensionsCount();
            //ASSERT(count!=0);
            if(count>0)
            ...{


                if(count!=m_nTotalFrame)
                ...{


                    if(m_pDimensionIDs!=NULL)
                    ...{


                        delete m_pDimensionIDs;
                        m_pDimensionIDs=NULL;
                    }
                    m_pDimensionIDs=new GUID[count]; 
                }
                if(m_pDimensionIDs!=NULL)
                ...{


                    m_nCurrFrame=1;
                    m_pImg->GetFrameDimensionsList(m_pDimensionIDs, count);
                    m_nTotalFrame = m_pImg->GetFrameCount(&m_pDimensionIDs[0]);
                    //m_pImg->SelectActiveFrame(&m_pDimensionIDs[0],m_nCurrFrame);
                    TRACE("The number of frame is %d. ", m_nTotalFrame);
                }

            }
            else
            ...{


                TRACE("载入图片有误 ");
                //delete m_pImg;
                m_pImg=NULL;
            }
        }
        ::LeaveCriticalSection(&m_csGDILock);            
        
        if(m_pImg!=NULL)
        ...{


            //调整控件大小
            CRect rt,rtClient;
            GetWindowRect(&rt);
            GetClientRect(&rtClient);
            int nOffsetX=rt.Width()-rtClient.Width();
            int nOffsetY=rt.Height()-rtClient.Height();
            CWnd* pParent=GetParent();
            if(pParent!=NULL)
            ...{


                pParent->ScreenToClient(&rt);
            }
            rt.SetRect(rt.left,rt.top,rt.left+m_pImg->GetWidth()+nOffsetX,
                       rt.top+m_pImg->GetHeight()+nOffsetY);
            MoveWindow(&rt,TRUE);
        }    
    }
    return (m_pImg!=NULL);
}

//  CGifCtrl 消息处理程序

UINT CGifCtrl::DrawGif(LPVOID lpParam)
... {


    CGifCtrl* pCtrl=static_cast<CGifCtrl*>(lpParam);
    if(pCtrl!=NULL)
    ...{


        TRACE("start gif thread  ");
        while (::WaitForSingleObject(pCtrl->m_hEventKill, pCtrl->m_nWaitTime) == WAIT_TIMEOUT)
        ...{


            //线程体
            //CDC* pDC=pCtrl->GetDC();
            if(/**//*pDC!=NULL &&*/ pCtrl->m_pImg!=NULL && pCtrl->m_pDimensionIDs!=NULL && pCtrl->m_nTotalFrame>1)
            ...{


                ::EnterCriticalSection(&pCtrl->m_csGDILock);
                //Graphics g(pDC->GetSafeHdc());
                pCtrl->m_pImg->SelectActiveFrame(&(pCtrl->m_pDimensionIDs[0]),pCtrl->m_nCurrFrame);                                
                //g.DrawImage(pCtrl->m_pImg,0,0);
                //pCtrl->ReleaseDC(pDC);
                ::LeaveCriticalSection(&pCtrl->m_csGDILock);
                //设定当前播放帧
                pCtrl->m_nCurrFrame++;    
                if(pCtrl->m_nCurrFrame<=0 || pCtrl->m_nCurrFrame>pCtrl->m_nTotalFrame)
                    pCtrl->m_nCurrFrame=1;            
                pCtrl->Invalidate();
            }
            
        }

    }

    TRACE("end gif thread  ");
    VERIFY(SetEvent(pCtrl->m_hEventDead));
    return 0;
}

void  CGifCtrl::PreSubclassWindow()
... {


    // 建立线程
    m_pThread=AfxBeginThread(DrawGif,static_cast<LPVOID>(this),THREAD_PRIORITY_IDLE,CREATE_SUSPENDED);

    if(m_pThread!=NULL)
    ...{


        SetWindowText(_T(""));
        m_pThread->m_bAutoDelete=TRUE;
        //复制线程句柄,以便更好的跟踪线程的关闭
        ::DuplicateHandle(GetCurrentProcess(),m_pThread->m_hThread,
            GetCurrentProcess(),&m_thread,0,FALSE,DUPLICATE_SAME_ACCESS);
        m_pThread->ResumeThread();
    }
    CStatic::PreSubclassWindow();
}


void  CGifCtrl::OnDestroy()
... {


    CStatic::OnDestroy();
    // 摧毁线程
    KillThread();
}

void  CGifCtrl::KillThread( void )
... {


    // reset the m_hEventKill which signals the thread to shutdown
    VERIFY(SetEvent(m_hEventKill));

    // allow thread to run at higher priority during kill process
    SetThreadPriority(m_thread,THREAD_PRIORITY_ABOVE_NORMAL);
    WaitForSingleObject(m_hEventDead, INFINITE);
    WaitForSingleObject(m_thread, INFINITE);
    ::DeleteCriticalSection(&m_csGDILock);
    // now delete CWinThread object since no longer necessary
    //delete this;
}



void  CGifCtrl::OnPaint()
... {


    CPaintDC dc(this); // device context for painting
    // TODO: 在此处添加消息处理程序代码
    // 不为绘图消息调用 CStatic::OnPaint()    
    if(m_pImg!=NULL)
    ...{


        //使用内存DC 加快显示图片
        CRect rt;
        GetClientRect(&rt);
        //1.建立内存DC ,设置透明
        CDC dcMem;
        dcMem.CreateCompatibleDC(&dc);
        dcMem.SetBkMode(TRANSPARENT);

        //2.选入内存图片
        CBitmap bmp;
        bmp.CreateCompatibleBitmap(&dc,rt.Width(),rt.Height());
        CBitmap* pOldBmp=dcMem.SelectObject(&bmp);
        
        //3.建立透明蒙照
        CBrush brush;
        if(m_clrBg==-1)
        ...{


            m_clrBg=dc.GetBkColor();
        }
        brush.CreateSolidBrush(m_clrBg);
        dcMem.FillRect(&CRect(0,0,rt.Width(),rt.Height()),&brush);

        //4.绘制到内存DC中
        Graphics g(dcMem.GetSafeHdc());
        g.DrawImage(m_pImg,0,0);

        ::EnterCriticalSection(&m_csGDILock);
        if(!dc.TransparentBlt(rt.left,rt.top,rt.Width(),rt.Height(),&dcMem,0,0,rt.Width(),rt.Height(),m_clrBg))
            dc.BitBlt(rt.left,rt.top,rt.Width(),rt.Height(),&dcMem,0,0,SRCCOPY);
        ::LeaveCriticalSection(&m_csGDILock);

        //施放资源
        dcMem.SelectObject(pOldBmp);
    }   
}


调用时需要 声明一个CStatic类,
然后使用 子类化为CGifCtrl类, 调用SetImg方法即可。

<script type="text/javascript"> </script>