📄 animator.cpp
字号:
//
// FILE: Animator.cpp
//
// Copyright (c) 1997 by Aaron Michael Cohen and Mike Woodring
//
/////////////////////////////////////////////////////////////////////////
#include "Animator.h"
// CAnimator class message map...
BEGIN_MESSAGE_MAP( CAnimator, CStatic)
ON_WM_PAINT()
ON_WM_DESTROY()
END_MESSAGE_MAP()
// CAnimator class constructor/destructor...
CAnimator::CAnimator() :
m_czFrame(0,0),
m_czBitmap(0,0),
m_nFrames(0),
m_bBackAndForth(FALSE),
m_bTransparent(FALSE),
m_nCurrentFrameIndex(0),
m_dwFrameInterval(0),
m_nFrameIncrement(1) {
return;
}
afx_msg void CAnimator::OnPaint() {
// just paint the current frame...
CPaintDC dc(this);
DrawCurrentFrame(&dc);
}
afx_msg void CAnimator::OnDestroy() {
// make sure that the worker thread has exited
// before our window is destroyed...
Stop();
// make sure that there is no bitmap
// attached to the CBitmap object...
// the animator just uses a passed in HBITMAP,
// it does not manage the resource's lifetime...
m_cBitmap.Detach();
// call the parent function...
CStatic::OnDestroy();
}
void CAnimator::SetupAnimation( HBITMAP hBitmap, CSize czFrame, int nFrames, int nFramesPerSecond, BOOL bBackAndForth, BOOL bTransparent) {
// make sure we have exclusive access to the object
// while we modify its properties...
CMclAutoLock autoLock(m_CritSec);
// make sure that there is no bitmap
// attached to the CBitmap object...
m_cBitmap.Detach();
// attach the passed in bitmap handle...
m_cBitmap.Attach(hBitmap);
// save the initial animation properties...
m_czFrame = czFrame;
m_nFrames = nFrames;
m_dwFrameInterval = 1000 / nFramesPerSecond;
m_bBackAndForth = bBackAndForth;
// compute the overall size of the animation bitmap...
BITMAP bm;
m_cBitmap.GetObject(sizeof(BITMAP), &bm);
m_czBitmap = CSize( bm.bmWidth, bm.bmHeight);
// initialize mask for transparent animation if necessary,,,
m_bTransparent = bTransparent;
if (m_bTransparent)
SetupBitmapsForTransparency();
else
CleanupBitmapsForTransparency();
}
void CAnimator::SetFrameRate( int nFramesPerSecond) {
// make sure we have exclusive access to the object
// while we modify its properties...
CMclAutoLock autoLock(m_CritSec);
// compute the frame interval in milliseconds...
m_dwFrameInterval = 1000 / nFramesPerSecond;
}
void CAnimator::SetTransparency( BOOL bTransparent) {
// make sure we have exclusive access to the object
// while we modify its properties...
CMclAutoLock autoLock(m_CritSec);
if (bTransparent != m_bTransparent) {
// change the transparency...
if (bTransparent) {
SetupBitmapsForTransparency();
}
else {
CleanupBitmapsForTransparency();
}
// change the current mode...
m_bTransparent = bTransparent;
// redraw the control with the new transparency...
RedrawWindow();
}
}
void CAnimator::SetLoopStyle( BOOL bBackAndForth) {
// make sure we have exclusive access to the object
// while we modify its properties...
CMclAutoLock autoLock(m_CritSec);
// change the repeat pattern type...
m_bBackAndForth = bBackAndForth;
}
void CAnimator::Start(void) {
// create an animation thread if there is none...
if (m_apAnimationThread.IsNull()) {
m_apAnimationThread = new CMcl4MfcWorkerThread(this);
}
}
void CAnimator::Stop(void) {
// if there is an animation thread,
// use the event to signal it to stop
// and wait for the thread to exit before
// resetting our auto-pointer to NULL...
if (!m_apAnimationThread.IsNull()) {
m_ceControl.Set();
m_apAnimationThread->Wait(INFINITE);
m_apAnimationThread = NULL;
}
}
void CAnimator::SetFrame( int nFrameNumber) {
// make sure we have exclusive access to the object
// while we modify its properties...
CMclAutoLock autoLock(m_CritSec);
// set the new current frame...
if ((nFrameNumber >= 0) && (nFrameNumber < m_nFrames))
m_nCurrentFrameIndex = nFrameNumber;
// redraw the control at the new frame...
RedrawWindow();
}
void CAnimator::AdvanceFrame(void) {
// make sure we have exclusive access to the object
// while we modify its properties...
CMclAutoLock autoLock(m_CritSec);
if (m_bBackAndForth) {
// oscillate back and forth, increment from frame zero to
// m_nFrames - 1, and then decrement back to frame zero
// and store the current direction in m_nFrameIncrement...
m_nCurrentFrameIndex = (m_nCurrentFrameIndex + m_nFrameIncrement);
if (m_nCurrentFrameIndex >= m_nFrames) {
m_nCurrentFrameIndex = m_nFrames - 1;
m_nFrameIncrement = -1;
}
else if (m_nCurrentFrameIndex < 0) {
m_nCurrentFrameIndex = 1;
m_nFrameIncrement = 1;
}
}
else {
// just cycle around the frames, when we go past the last
// frame, start over at frame zero...
m_nCurrentFrameIndex = (m_nCurrentFrameIndex + 1) % m_nFrames;
}
}
void CAnimator::DrawCurrentFrame(CDC *pDC) {
// all of the CDC calls such as SelectObject, BitBlt, and StretchBlt,
// used in this function are simple wrappers around Win32 API
// function and are safe to call from threads other than
// the one which created the objects...
// make sure we have exclusive access to the object
// while we use its properties, we don't want some
// other thread calling SetupAnimation() while we are
// in the middle of drawing the current frame...
CMclAutoLock autoLock(m_CritSec);
// use the HBITMAP operator to determine if there is
// an attached bitmap,
// if there is no bitmap, we don't draw anything...
if ((HBITMAP)(m_cBitmap) == NULL)
return;
// compute the offset into the bitmap, by counting m_nCurrentFrameIndex images
// into the bitmap by rows and then columns...
int bmx = m_czFrame.cx * (m_nCurrentFrameIndex % (m_czBitmap.cx / m_czFrame.cx));
int bmy = m_czFrame.cy * ((m_nCurrentFrameIndex * m_czFrame.cx) / m_czBitmap.cx);
// create a memory DC and select the animation bitmap...
CDC bdc;
bdc.CreateCompatibleDC(pDC);
CBitmap *pcOldBitmap = bdc.SelectObject(&m_cBitmap);
// get current client dimensions for our window...
CRect cr;
GetClientRect(cr);
// use stretchblt to fill the control client area no matter
// what the size of the animation frames...
if (m_bTransparent) {
// create an offscreen DC and select the offscreen bitmap into it...
CDC odc;
odc.CreateCompatibleDC(pDC);
CBitmap *pcOldOffscreenBitmap = odc.SelectObject(&m_cOffscreenBitmap);
// fill the offscreen bitmap with the background color...
CBrush cbrush(::GetSysColor(COLOR_3DFACE));
odc.FillRect( CRect( 0, 0,m_czFrame.cx, m_czFrame.cy), &cbrush);
// create a memory DC and select in the mask bitmap...
CDC mdc;
mdc.CreateCompatibleDC(pDC);
CBitmap *pcOldMaskBitmap = mdc.SelectObject(&m_cMaskBitmap);
COLORREF crOldBkColor = odc.SetBkColor(RGB(255,255,255));
COLORREF crOldTextColor = odc.SetTextColor(RGB(0,0,0));
odc.BitBlt( 0, 0, m_czFrame.cx, m_czFrame.cy, &mdc, bmx, bmy, SRCAND);
odc.BitBlt( 0, 0, m_czFrame.cx, m_czFrame.cy, &bdc, bmx, bmy, SRCPAINT);
odc.SetBkColor(crOldBkColor);
odc.SetTextColor(crOldTextColor);
// finally, blt the offscreen image into our client area...
pDC->StretchBlt( 0, 0, cr.Width(), cr.Height(), &odc, 0, 0, m_czFrame.cx, m_czFrame.cy, SRCCOPY);
// clean up...
mdc.SelectObject(pcOldMaskBitmap);
odc.SelectObject(pcOldOffscreenBitmap);
}
else {
pDC->StretchBlt( 0, 0, cr.Width(), cr.Height(), &bdc, bmx, bmy, m_czFrame.cx, m_czFrame.cy, SRCCOPY);
}
// restore the orginal bitmap to the bitmap memory DC...
bdc.SelectObject(pcOldBitmap);
}
void CAnimator::SetupBitmapsForTransparency(void) {
// clean up any bitmaps used previously...
CleanupBitmapsForTransparency();
// create a bitmap object for the mask...
// this type of mask only works if the non-image part of the
// bitmap (the "off" pixels) are black and the
// image does not use black (very dark grey is okay)..
m_cMaskBitmap.CreateBitmap( m_czBitmap.cx, m_czBitmap.cy, 1, 1, NULL);
// select the bitmap into a DC...
CDC bdc;
bdc.CreateCompatibleDC(NULL);
CBitmap *pOldBitmap = bdc.SelectObject(&m_cBitmap);
// set up a DC for the mask...
CDC mdc;
mdc.CreateCompatibleDC(NULL);
CBitmap *pOldMaskBitmap = mdc.SelectObject(&m_cMaskBitmap);
// create the mask, "on" pixels are black, "off" pixels are white...
bdc.SetBkColor(RGB(0,0,0));
mdc.BitBlt( 0, 0, m_czBitmap.cx, m_czBitmap.cy, &bdc, 0, 0, SRCCOPY);
// create an offscreen bitmap for use later on...
m_cOffscreenBitmap.CreateCompatibleBitmap( &bdc, m_czBitmap.cx, m_czBitmap.cy);
// clean up...
bdc.SelectObject(pOldBitmap);
mdc.SelectObject(pOldMaskBitmap);
}
void CAnimator::CleanupBitmapsForTransparency(void) {
m_cMaskBitmap.DeleteObject();
m_cOffscreenBitmap.DeleteObject();
}
unsigned CAnimator::ThreadHandlerProc(void) {
// create a DC for our window...
//
// while the CClientDC is not an inline function
// it is a simple wrapper around the Win32
// ::GetDC() API which simply accesses the m_hWnd
// of the CAnimator object...
//
// notice that this is NOT the thread which created
// the m_hWnd, but we can get a valid DC for it
// anyway...
//
// we could have done it like this:
//
// CWnd *pWnd = CWnd::FromHandle(m_hWnd);
// CClientDC dc(pWnd);
//
// which creates a temporary CWnd object but
// otherwise winds up being the same thing as
// what we have done here.
CClientDC dc(this);
// as long as the wait returns timeout, advance to the
// next frame in the animation and draw it...
while (TRUE) {
DWORD dwStatus = m_ceControl.Wait(m_dwFrameInterval);
if (CMclWaitTimeout(dwStatus)) {
// advance m_nCurrentFrameIndex to the next frame...
AdvanceFrame();
// draw the next frame using this worker thread...
DrawCurrentFrame(&dc);
}
else {
return 0;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -