genericthread.cpp

来自「windows ce开发技巧与实例光盘代码」· C++ 代码 · 共 470 行

CPP
470
字号
// GenericThread.cpp: implementation of the CGenericThread class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "GenericThread.h"
#include <math.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CGenericThread::CGenericThread()
{
	m_bFirstGfxUpdate = TRUE;
	m_bRequestSaveScreen = FALSE;

	m_dwSpriteCount = 0;
	m_dwSpriteIndex = 0;
	m_dwSpriteWidth = 0;
	m_dwSpriteHeight = 0;

    m_bIsRunning = FALSE;
    m_bThreadIsFinished = TRUE;
	m_bSuspendThread = FALSE;
	m_hGameThread = 0;

	m_dwScreenOrientation = GDDISPMODE_LANDSCAPE1;
}

CGenericThread::~CGenericThread()
{
	KillThread();
}

void CGenericThread::KillThread()
{
    m_bIsRunning = FALSE;
	if (m_hGameThread)
	{
		// Allow Win32 to clean up thread resources asynchronously
		::WaitForSingleObject(m_hGameThread, INFINITE);
		Sleep(100);
		::CloseHandle(m_hGameThread);
		m_hGameThread = 0;
	}
}

BOOL CGenericThread::StartThread(HWND hParentFrame)
{
	// Set display mode
	m_display.SetDisplayMode(m_dwScreenOrientation);

	// Open display
	HRESULT hr = 0;
	if (GAMEPARAM_FULLSCREEN)
	{
		hr = m_display.OpenDisplay(hParentFrame, GDOPENDISPLAY_FULLSCREEN | GDOPENDISPLAY_SIZEX2);
	}
	else
	{
		hr = m_display.OpenDisplay(hParentFrame, GDOPENDISPLAY_SCALEX2);
	}

	if (FAILED(hr))
	{
		::PostMessage(hParentFrame, WM_CLOSE, 0, 0);
		return FALSE;
	}

	m_bIsRunning = TRUE;
	m_bThreadIsFinished = FALSE;
	m_bSuspendThread = FALSE;

	// Start thread - send a pointer to this class as a parameter
	DWORD dwThreadID;
	m_hGameThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) CGenericThread::ThreadFunc, (void*)this, CREATE_SUSPENDED, &dwThreadID);
	if (m_hGameThread == NULL)
	{
		DWORD dw = ::GetLastError();
		m_bIsRunning = FALSE;
		m_bThreadIsFinished = TRUE;
		m_bSuspendThread = TRUE;
		return FALSE;
	}

	::SetThreadPriority(m_hGameThread, THREAD_PRIORITY_ABOVE_NORMAL);

	::ResumeThread(m_hGameThread);

	return TRUE;
}

void CGenericThread::SuspendThread()
{
	m_bSuspendThread = TRUE;
	Sleep(1000);
}

void CGenericThread::ResumeThread()
{
	m_bSuspendThread = FALSE;
}

BOOL CGenericThread::IsRunning()
{
    return m_bIsRunning;
}

BOOL CGenericThread::IsFinished()
{
    return m_bThreadIsFinished;
}

unsigned __stdcall CGenericThread::ThreadFunc(LPVOID pParam)
{
    CGenericThread* pGenericThreadClass = (CGenericThread*) pParam;
    while (pGenericThreadClass->IsRunning())
    {
        pGenericThreadClass->ThreadCallback();
		while (pGenericThreadClass->IsRunning() && pGenericThreadClass->m_bSuspendThread)
		{
			Sleep(100);
		}
    }
    pGenericThreadClass->m_bThreadIsFinished = TRUE;
    return 0;
}

void CGenericThread::ThreadCallback()
{
	DWORD dwScreenWidth = m_display.GetWidth();
	DWORD dwScreenHeight = m_display.GetHeight();

	// Check if we should save back surface
	if (m_bRequestSaveScreen)
	{
		m_backbuffer.SaveSurface(TEXT("backbuffer.bmp"));
		m_bRequestSaveScreen = FALSE;
	}

	if (m_bFirstGfxUpdate)
	{
		// Always sleep when starting thread or changing display mode! Why?
		// (1) On Windows 2000/XP, high priority threads initialize asynchronously
		// (2) On Windows CE, the input buffer for keyboard/pen might be buffered
		Sleep(100);

		m_dwSpriteCount = 0;
		m_display.SetDisplayMode(m_dwScreenOrientation);

		// Get back buffer
		m_display.GetBackBuffer(&m_backbuffer);

		// BEGIN - IF DISPLAYMODE HAS CHANGED ______________________________

		// If display mode has changed, update dwScreenWidth and dwScreenHeight
		dwScreenWidth = m_display.GetWidth();
		dwScreenHeight = m_display.GetHeight();

		m_coordlist.RemoveAll();
		m_velocitylist.RemoveAll();
		m_logoindex.RemoveAll();

		m_backgroundOffset.x = 0;
		m_backgroundOffset.y = 0;
		m_backgroundDirection.x = 0;
		m_backgroundDirection.y = 0;

		// END - IF DISPLAYMODE HAS CHANGED ________________________________

		CPoint koord;
		CPoint dir;
		WORD nLogoIndex;

		// Create intro buffer
		m_introbuffer.CreateSurface(0, dwScreenWidth, dwScreenHeight);
		m_introbuffer.FillRect(NULL, RGB(60, 60, 150), 0, NULL);

		long dwXOffset, dwYOffset;
		CString welcome = TEXT("Creating background buffer, please wait...");
		dwXOffset = (dwScreenWidth >> 1);
		dwYOffset = (dwScreenHeight >> 1);
		m_introbuffer.DrawText(dwXOffset, dwYOffset, welcome, RGB(255, 255, 255), GDDRAWTEXT_BORDER | GDDRAWTEXT_CENTER, NULL, NULL);
		m_introbuffer.DrawRect(NULL, RGB(255, 255, 255), 0, NULL);
		dwYOffset += 10;

		// Move in...
		for (int n=0; n<=(int)(dwScreenHeight); n+=4)
		{
			m_backbuffer.BltFast(0, 0, &m_introbuffer, CRect(0, m_introbuffer.GetHeight()-n, m_introbuffer.GetWidth(), m_introbuffer.GetHeight()), 0, NULL);
			if (m_display.Flip() == GDERR_BACKBUFFERLOST)
				m_display.GetBackBuffer(&m_backbuffer);
		}

		// Create bitmap surfaces
		HINSTANCE hInst = AfxGetInstanceHandle();
		const COLORREF TRANSPARENTCOLOR = RGB(255, 0, 255);
		m_spritelist.CreateSurface(hInst, IDB_SPRITELIST, TEXT("PNG"));
		m_plant1.CreateSurface(hInst, IDB_PLANT1, TEXT("PNG"));
		m_plant2.CreateSurface(hInst, IDB_PLANT2, TEXT("PNG"));
		m_spritelist.SetColorKey(TRANSPARENTCOLOR);
		m_plant1.SetColorKey(TRANSPARENTCOLOR);
		m_plant2.SetColorKey(TRANSPARENTCOLOR);

		// Get Sprite size
		m_dwSpriteWidth = m_spritelist.GetWidth();
		m_dwSpriteHeight = m_spritelist.GetHeight() / SPRITE_ANIMATION_FRAMES;

		// Create sprite coordinate list
		float nDir;
		for (int i=0; i<(SPRITECOUNT_MAX+1); i++)
		{
			koord.x = (DWORD) (2+ rand() * (dwScreenWidth - m_dwSpriteWidth - 2)   / RAND_MAX);
			koord.y = (DWORD) (2+ rand() * (dwScreenHeight - m_dwSpriteHeight - 2) / RAND_MAX);
			nLogoIndex = (WORD) (rand() * (SPRITE_ANIMATION_FRAMES - 1) / RAND_MAX);
			nDir = 2 * (float)rand() / (float)RAND_MAX;
			dir.x = (nDir > 0.5) ? 2 : -2;
			nDir = 2 * (float)rand() / (float)RAND_MAX;
			dir.y = (nDir > 0.5) ? 2 : -2;
			m_coordlist.AddTail(koord);
			m_logoindex.AddTail(nLogoIndex);
			m_velocitylist.AddTail(dir);
		}

		// Set background velocity
		m_backgroundDirection.x = 1;
		m_backgroundDirection.y = 1;

		// Allocate buffer for background area
		m_background.CreateSurface(0, BACKGROUND_WIDTH, BACKGROUND_HEIGHT);
		m_background.FillRect(NULL, RGB(83, 129, 80), 0, NULL);

		// Check to see if we can use direct display access to update the progress bar
		DWORD dwDispCaps;
		BOOL bDirectDisplay = FALSE;
		if (m_display.GetDisplayCaps(&dwDispCaps) == GD_OK)
		{
			bDirectDisplay = ((dwDispCaps & GDDISPCAPS_DIRECT) == GDDISPCAPS_DIRECT);
		}

		// Plants
		DWORD plantX, plantY;
		FLOAT nStepOffset = ((FLOAT)(dwScreenWidth-1)/(FLOAT)PLANTCOUNT);
		for (n=0; n<PLANTCOUNT; n++)
		{
			// Place random plants on the background, alter between plant1 and 2
			// Plant1
			plantX = (DWORD) (2 + (m_background.GetWidth() - m_plant1.GetWidth() - 2) * rand() / RAND_MAX);
			plantY = (DWORD) (2 + (m_background.GetHeight() - (m_plant1.GetHeight()>>1) - 2) * rand() / RAND_MAX);
			m_background.AlphaBlt(CRect(plantX, plantY, plantX+m_plant1.GetWidth(), plantY+(m_plant1.GetHeight()>>1)), &m_plant1, NULL, &m_plant1, CRect(0, (m_plant1.GetHeight()>>1), m_plant1.GetWidth(), m_plant1.GetHeight()), 0, NULL);
			// Plant2
			plantX = (DWORD) (2 + (m_background.GetWidth() - m_plant2.GetWidth() - 2) * rand() / RAND_MAX);
			plantY = (DWORD) (2 + (m_background.GetHeight() - (m_plant2.GetHeight()>>1) - 2) * rand() / RAND_MAX);
			m_background.AlphaBlt(CRect(plantX, plantY, plantX+m_plant2.GetWidth(), plantY+(m_plant2.GetHeight()>>1)), &m_plant2, NULL, &m_plant2, CRect(0, (m_plant2.GetHeight()>>1), m_plant2.GetWidth(), m_plant2.GetHeight()), 0, NULL);

			m_introbuffer.FillRect(CRect((DWORD)nStepOffset+1, dwYOffset, (DWORD)(nStepOffset*n), dwYOffset+2), RGB(255, 255, 255), 0, NULL);
			if (bDirectDisplay)
			{
				// No need to "flip" since we can write directly to the screen area
				m_display.FillRect(CRect((DWORD)nStepOffset+1, dwYOffset, (DWORD)(nStepOffset*n), dwYOffset+2), RGB(255, 255, 255), 0, NULL);
			}
			else
			{
				m_backbuffer.FillRect(CRect((DWORD)nStepOffset+1, dwYOffset, (DWORD)(nStepOffset*n), dwYOffset+2), RGB(255, 255, 255), 0, NULL);
				if (m_display.Flip() == GDERR_BACKBUFFERLOST)
					m_display.GetBackBuffer(&m_backbuffer);
			}
		}

		// Move out
		for (n=0; n<=(int)(dwScreenHeight); n+=4)
		{
			m_backbuffer.BltFast(0, n, &m_introbuffer, CRect(0, 0, dwScreenWidth, dwScreenHeight-n), 0, NULL);
			m_backbuffer.FillRect(CRect(0, 0, dwScreenWidth, n), RGB(0, 0, 0), 0, NULL);
			if (m_display.Flip() == GDERR_BACKBUFFERLOST)
				m_display.GetBackBuffer(&m_backbuffer);
		}

		// Start timer
		m_timer.StartTimer(GAMEPARAM_TARGETFPS);

		m_bFirstGfxUpdate = FALSE;
	}

	CPoint koord;
	CPoint dir;
	long nLogoIndex;
	for (int i=0; i<(int)m_dwSpriteCount; i++)
	{
		koord = m_coordlist.GetAt(m_coordlist.FindIndex(i));
		dir = m_velocitylist.GetAt(m_velocitylist.FindIndex(i));
		nLogoIndex = m_logoindex.GetAt(m_logoindex.FindIndex(i));

		// Update koordinate
		koord.x += dir.x;
		koord.y += dir.y;

		// Update direction X
		if (koord.x < 2)
			dir.x = 2;
		else if (koord.x > (long)(dwScreenWidth - m_dwSpriteWidth - 2))
			dir.x = -2;

		// Update direction Y
		if (koord.y < 2)
			dir.y = 2;
		else if (koord.y > (long)(dwScreenHeight - m_dwSpriteHeight - 2))
			dir.y = -2;

		// Update logo index
		if ((dir.x * dir.y) < 0)
			nLogoIndex++;
		else
			nLogoIndex--;

		if (nLogoIndex < 0)
			nLogoIndex = (SPRITE_ANIMATION_FRAMES-1);
		if (nLogoIndex > (SPRITE_ANIMATION_FRAMES-1))
			nLogoIndex = 0;

		m_coordlist.SetAt(m_coordlist.FindIndex(i), koord);
		m_velocitylist.SetAt(m_velocitylist.FindIndex(i), dir);
		m_logoindex.SetAt(m_logoindex.FindIndex(i), nLogoIndex);
	}

	m_backgroundOffset.x += m_backgroundDirection.x;
	m_backgroundOffset.y += m_backgroundDirection.y;

	// Update direction X
	if (m_backgroundOffset.x < 1)
		m_backgroundDirection.x = 1;
	else if (m_backgroundOffset.x > (long)(m_background.GetWidth() - dwScreenWidth - 1))
		m_backgroundDirection.x = -1;

	// Update direction Y
	if (m_backgroundOffset.y < 1)
		m_backgroundDirection.y = 1;
	else if (m_backgroundOffset.y > (long)(m_background.GetHeight() - dwScreenHeight - 1))
		m_backgroundDirection.y = -1;

	// Clean up background after last pass
	m_backbuffer.BltFast(0, 0, &m_background, CRect(m_backgroundOffset.x, m_backgroundOffset.y, m_backgroundOffset.x+dwScreenWidth, m_backgroundOffset.y+dwScreenHeight), 0, NULL);

	// Draw sprites
	for (i=0; i<(int)m_dwSpriteCount; i++)
	{
		koord = m_coordlist.GetAt(m_coordlist.FindIndex(i));
		nLogoIndex = m_logoindex.GetAt(m_logoindex.FindIndex(i));
		m_backbuffer.BltFast(koord.x, koord.y, &m_spritelist, CRect(0, nLogoIndex * m_dwSpriteHeight, m_dwSpriteWidth, nLogoIndex * m_dwSpriteHeight+m_dwSpriteHeight), GDBLTFAST_KEYSRC, NULL);
	}

	CString str;

	str.Format(TEXT("Spritecount: [%d]"), m_dwSpriteCount);
	DrawFrameInfo(dwScreenHeight - 60, str, RGB(50, 130, 60));

	FLOAT nActualFrameTime = 0;
	m_timer.GetActualFrameTime(&nActualFrameTime);
	str.Format(TEXT("Current frame: [%.3f ms]"), nActualFrameTime);
	DrawFrameInfo(dwScreenHeight - 40, str, RGB(170, 90, 60));

	FLOAT nFrameRate = 0;
	m_timer.GetActualFrameRate(&nFrameRate);
	str.Format(TEXT("Current framerate: [%.3f fps]"), nFrameRate);
	DrawFrameInfo(dwScreenHeight - 20, str, RGB(90, 60, 170));

	// Draw logo text
	CString str1, str2, str3;
	str1 = TEXT("[1] = Quit");
	str2 = TEXT("-www.gapidraw.com-");
	str3 = TEXT("[2] = Rotate");
	GDFILLRECTFX fillrectfx;
	fillrectfx.dwOpacity = 128;
	m_backbuffer.FillRect(CRect(0, 0, dwScreenWidth, 10), RGB(0, 0, 0), GDFILLRECT_OPACITY, &fillrectfx);
	m_backbuffer.DrawText(2, 2, str1, RGB(255, 255, 255), GDDRAWTEXT_BORDER | GDDRAWTEXT_LEFT, 0, NULL);
	m_backbuffer.DrawText(dwScreenWidth/2, 2, str2, RGB(255, 255, 255), GDDRAWTEXT_BORDER | GDDRAWTEXT_CENTER, 0, NULL);
	m_backbuffer.DrawText(dwScreenWidth-3, 2, str3, RGB(255, 255, 255), GDDRAWTEXT_BORDER | GDDRAWTEXT_RIGHT, 0, NULL);
	m_backbuffer.DrawLine(0, 10, dwScreenWidth-1, 10, RGB(255, 255, 255), 0, NULL);

	// FLIP BUFFERS AND WAIT FOR NEXT FRAME _________________

	if (m_display.Flip() == GDERR_BACKBUFFERLOST)
	{
		m_display.GetBackBuffer(&m_backbuffer);
	}

	BOOL bAddSprite = TRUE;
	BOOL bOldDevice = FALSE;
	if (m_timer.WaitForNextFrame() == GDERR_FRAMETIMEOVERFLOW)
	{
		// The computer cannot keep up with the frame rate.
		// Decrease the number of sprites, sleep 1 ms and try again...
		if (m_dwSpriteCount <= SPRITECOUNT_MIN)
			bOldDevice = TRUE;
		else
			bAddSprite = FALSE;
		Sleep(1);
	}

	// ______________________________________________________

	// Always decrease if we are exceeding frame target time,
	// unless we use an older device which cannot even display SPRITECOUNT_MIN
	if ((bAddSprite == FALSE) || (++m_dwSpriteIndex > 5))
	{
		m_dwSpriteIndex = 0;
		if (bOldDevice)
		{
			m_dwSpriteCount += (m_dwSpriteCount < SPRITECOUNT_MIN) ? 1 : 0;
		}
		else
		{
			if (bAddSprite)
				m_dwSpriteCount += (m_dwSpriteCount < SPRITECOUNT_MAX) ? 1 : 0;
			else
				m_dwSpriteCount -= (m_dwSpriteCount > 0) ? 1 : 0;
		}
	}

}

void CGenericThread::SaveScreenshot()
{
	m_bRequestSaveScreen = TRUE;
}

void CGenericThread::ChangeScreenOrientation()
{
	if (!m_bFirstGfxUpdate)
	{
		switch (m_dwScreenOrientation)
		{
		case GDDISPMODE_NORMAL:
			m_dwScreenOrientation = GDDISPMODE_LANDSCAPE1;
			break;
		case GDDISPMODE_LANDSCAPE1:
			m_dwScreenOrientation = GDDISPMODE_LANDSCAPE2;
			break;
		case GDDISPMODE_LANDSCAPE2:
			m_dwScreenOrientation = GDDISPMODE_NORMAL;
			break;
		}
		m_bFirstGfxUpdate = TRUE;
	}
}

__forceinline
void CGenericThread::DrawFrameInfo(DWORD dwY, const TCHAR* pText, COLORREF color)
{
	DWORD strX = 20;
	DWORD strWidth;

	GDFILLRECTFX fillrectfx;
	fillrectfx.dwOpacity = 128;

	m_backbuffer.DrawText(0, 0, pText, RGB(0, 0, 0), GDDRAWTEXT_CALCWIDTH, 0, &strWidth);
	m_backbuffer.DrawLine(0, dwY-2, strX+strWidth+3, dwY-2, RGB(0, 0, 0), 0, NULL);
	m_backbuffer.DrawLine(strX+strWidth+3, dwY-2, strX+strWidth+3, dwY+8, RGB(0, 0, 0), 0, NULL);
	m_backbuffer.DrawLine(0, dwY+8, strX+strWidth+3, dwY+8, RGB(0, 0, 0), 0, NULL);
	m_backbuffer.FillRect(CRect(0, dwY-1, strX+strWidth+3, dwY+8), color, GDFILLRECT_OPACITY, &fillrectfx);
	m_backbuffer.DrawText(strX, dwY, pText, RGB(255, 255, 255), GDDRAWTEXT_BORDER, 0, 0);
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?