2009年6月30日 星期二

Get Windows Touch/Gesture in an MFC application

在Win32的程式中, 我們可以在WndProc函式中, 接收WM_TOUCHWM_GESTURE的訊息,來撰寫multi-touch application, 今天我們就來介紹,如果在MFC的應用程式中, 如何取得WM_TOUCHWM_GESTURE message.
這是一個很簡單的範例, 首先, 產生一個MFC的Dialog, 當有touch發生時, 視窗的畫面會出現message box, 部分程式碼如下所示:

// Step 1: 宣告這個視窗接收 WM_TOUCH訊息
BEGIN_MESSAGE_MAP(CMFCTouchDlg, CDialog)

ON_MESSAGE(WM_TOUCH, OnTouch)

END_MESSAGE_MAP()


// Step 2: 註冊成 Touch Window

BOOL CMFCTouchDlg::OnInitDialog()
{
CDialog::OnInitDialog();

RegisterTouchWindow(this->GetSafeHwnd(), 0);
... ... ...
}


// Step 3: 加入實作 (當視窗收到 WM_TOUCH 指令時, 呼叫下面 method)

LRESULT CMFCTouchDlg::OnTouch(WPARAM wParam,LPARAM lParam)
{
::AfxMessageBox(L"Touch!!",0,0);

return 0;
}

執行畫面如下所示:






另一個範例則是當有手勢作用時, MFC dialog則會顯示手勢類型的message, 程式碼如下所示:

// Step 1: 宣告這個視窗接收 WM_GESTURE訊息

BEGIN_MESSAGE_MAP(CMFCTouchDlg, CDialog)

ON_MESSAGE(WM_GESTURE, OnGesture)

END_MESSAGE_MAP()


// Step 2: 開啟Rotate手勢
BOOL CMFCTouchDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// Enable rotate gesture
GESTURECONFIG config = { 0 };
config.dwWant = GC_ROTATE;
config.dwID = GID_ROTATE;
config.dwBlock = 0;
BOOL result = SetGestureConfig(
this->GetSafeHwnd(),
0,
1,
&config,
sizeof(GESTURECONFIG));

... ... ...
}



// Step 3: 加入實作 (當視窗收到 WM_GESTURE 指令時, 呼叫下面 method)

LRESULT CMFCTouchDlg::OnGesture(WPARAM wParam,LPARAM lParam)
{
GESTUREINFO gi;
ZeroMemory(&gi,sizeof(GESTUREINFO));
gi.cbSize = sizeof(GESTUREINFO);
BOOL result = GetGestureInfo((HGESTUREINFO)lParam, &gi);

if(result)
{
switch(gi.dwID)
{

case GID_PAN:
::AfxMessageBox(L"Pan",0,0);
break;

case GID_ZOOM:
::AfxMessageBox(L"Zoom",0,0);
break;

case GID_ROTATE:
::AfxMessageBox(L"ROTATE",0,0);
break;

case GID_TWOFINGERTAP:
::AfxMessageBox(L"Two Finger Tap",0,0);
break;

case GID_PRESSANDTAP:
::AfxMessageBox(L"Press and Tap",0,0);
break;
}
}
return 0;
}


執行畫面如下所示:









2009年6月25日 星期四

Windows 7 Gesture Sample

看完了MSDN中Gesture information的介紹, 我們就開始寫一個簡單的Gesture應用程式 Application,
其功能包含利用手勢對物體旋轉,縮放和平移, 如下圖所示:


放 大


縮 小


旋 轉


平 移


部分程式碼如下所示:

1. 利用SetGestureConfig將某些手勢作開啟或關閉(依程式需求)

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;

g_hInst = hInstance; // Store instance handle in our global variable

hWnd = CreateWindow(g_wszWindowClass, g_wszTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

if (!hWnd)
{
return FALSE;
}

//在預設中rotate手勢是被關閉, 如果你想要rotate手勢, 可以透過GESTURECONFIG來開啟
//GESTURECONFIG的dwWant/dwBlock 可以用來打開或關閉某種手勢, 或是手指的細緻控制, 例如單一手指的pan或兩指的pan; 下面的code, 我們將rotate打開, 並將單一手指的pan給關掉
GESTURECONFIG config = { 0 };
config.dwWant = GC_ROTATE; //打開rotate手勢
config.dwID = GID_ROTATE;
config.dwBlock = 0;

BOOL result = SetGestureConfig(
hWnd,
0,
1,
&config,
sizeof(GESTURECONFIG)
);

config.dwID = GID_PAN;
config.dwWant = GC_PAN;
config.dwBlock = GC_PAN_WITH_GUTTER;
config.dwBlock |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; //關掉單一手指垂直平移
config.dwBlock |= GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;//關掉單一手指水平移動

result = SetGestureConfig(
hWnd,
0,
1,
&config,
sizeof(GESTURECONFIG)
);


ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

return TRUE;
}


2. 處理WM_GESTURE訊息

LRESULT CALLBACK WndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;

switch (message)
{
case WM_GESTURE:
// 我們利用GestureEngine class 來處理WM_GESTURE
return g_cGestureEngine.WndProc(hWnd,wParam,lParam);
break;
.....
}


3. 利用GetGestureInfo得到
GESTUREINFO

LRESULT CGestureEngine::WndProc(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
POINT ptZoomCenter;
double k;

// 利用GetGestureInfo函數去取得 GESTUREINFO的資料結構
GESTUREINFO gi;
ZeroMemory(&gi,sizeof(GESTUREINFO));
gi.cbSize = sizeof(GESTUREINFO);
BOOL result = GetGestureInfo((HGESTUREINFO)lParam, &gi)
;

4. 處理平移手勢


// GID_PAN:
// _ptFirst - 兩手指間的中間點
// _dwArguments - 這手勢沒用到

case GID_PAN:
if (gi.dwFlags & GF_BEGIN)
{
_dwID = gi.dwID;
_dwArguments = gi.ullArguments;
_ptFirst.x = gi.ptsLocation.x;
_ptFirst.y = gi.ptsLocation.y;
ScreenToClient(hWnd,&_ptFirst);
break;

}
else
{
// We read the second point of this gesture. It's a middle point
// between fingers in this new position

_ptSecond.x = gi.ptsLocation.x;
_ptSecond.y = gi.ptsLocation.y;
ScreenToClient(hWnd,&_ptSecond);

// We apply move operation the the object
ProcessMove(_ptSecond.x-_ptFirst.x,_ptSecond.y-_ptFirst.y);

InvalidateRect(hWnd,NULL,TRUE);

// We have to copy second point into first one to prepare
// for next pan message.
_ptFirst.x = _ptSecond.x;
_ptFirst.y = _ptSecond.y;

}
fGHandled = TRUE;
break;



5. 處理縮放手勢

// GID_ZOOM:
// _ptFirst - 兩手指間的中間點
// _dwArguments - 兩手指間的距離
case GID_ZOOM:
if (gi.dwFlags & GF_BEGIN)
{

_dwID = gi.dwID;
_dwArguments = gi.ullArguments;
_ptFirst.x = gi.ptsLocation.x;
_ptFirst.y = gi.ptsLocation.y;
ScreenToClient(hWnd,&_ptFirst);

break;
}
else if (gi.dwFlags & GF_END)
{
//at end of gesture, zero the arguments
_dwArguments = 0;
}
else
{
// We read here the second point of the gesture. This is middle point between
// fingers in this new position.
_ptSecond.x = gi.ptsLocation.x;
_ptSecond.y = gi.ptsLocation.y;
ScreenToClient(hWnd,&_ptSecond);

// We have to calculate zooming center point and coefficient of straching
// _dwArguments holds the old distance between two fingers and new
// distance is stored in gi.dwArguments
ptZoomCenter.x = (_ptFirst.x + _ptSecond.x)/2;
ptZoomCenter.y = (_ptFirst.y + _ptSecond.y)/2;
k = (double)gi.ullArguments/_dwArguments; //縮放比例

// Now we process zooming in/out of object
ProcessZoom(k,ptZoomCenter.x,ptZoomCenter.y);

InvalidateRect(hWnd,NULL,TRUE);

// Now we have to store new information as a starting information
// for next step in this gesture.
_ptFirst.x = _ptSecond.x;
_ptFirst.y = _ptSecond.y;
_dwArguments = gi.ullArguments;
}

fGHandled = TRUE;

break;


6. 處理旋轉手勢

// GID_ROTATE:
// _ptFirst - 旋轉中心 (兩手指旋轉時, 位置變化比較少的手指)
// _dwArguments - 初始角度

case GID_ROTATE:
if (gi.dwFlags & GF_BEGIN)
{
_dwID = gi.dwID;
_dwArguments = gi.ullArguments;
_ptFirst.x = gi.ptsLocation.x;
_ptFirst.y = gi.ptsLocation.y;
ScreenToClient(hWnd,&_ptFirst);
ProcessRotate(ArgToRadians(gi.ullArguments),_ptFirst.x,_ptFirst.y);
InvalidateRect(hWnd,NULL,TRUE);

}
else
{

ProcessRotate(ArgToRadians(gi.ullArguments-_dwArguments),_ptFirst.x,_ptFirst.y);
InvalidateRect(hWnd,NULL,TRUE);
_dwArguments = gi.ullArguments;

}

fGHandled = TRUE;

break;

2009年6月24日 星期三

Windows 7 Touch Sample Application - Scratchpad

之前介紹很多關於Windows 7 Touch SDK的用法, 我們就用它來寫一個簡單的multi-touch應用程式,如下圖所示的Scratchpad.



基本上這隻Scratchpad應用程式的功能很簡單, 就是將touch points畫出來, 部分程式碼如下所示:


1.
註冊可以收到multi-touch message的視窗

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;

g_hInst = hInstance; // Store instance handle in our global variable

// Create the application window
hWnd = CreateWindow(g_wszWindowClass, g_wszTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}

// Register application window for receiving multi-touch input. Use default settings.
if(!RegisterTouchWindow(hWnd, 0))
{
MessageBox(hWnd, L"Cannot register application window for multi-touch input", L"Error", MB_OK);
return FALSE;
}
ASSERT(IsTouchWindow(hWnd, NULL));

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

return TRUE;
}

2. 處理Multi-Touch Message

WndProc的函式中, 我們處理收到的WM_TOUCH訊息, 如下所示:

// WM_TOUCH message handlers
case WM_TOUCH:
{
// WM_TOUCH message can contain several messages from different contacts packed together.
// Message parameters need to be decoded:
unsigned int numInputs = (unsigned int) wParam; //將wParam轉換成touch contact的數目
TOUCHINPUT* ti = new TOUCHINPUT[numInputs]; // 產生一組大小為numInputs的TOUCHINPUT structure

if(ti == NULL)
{
break;
}

// 利用GetTouchInputInfo函式取得
TOUCHINPUT structures的相關資訊
if(GetTouchInputInfo((HTOUCHINPUT)lParam, numInputs, ti, sizeof(TOUCHINPUT)))
{

// 根據不同的touch state分別呼叫TouchDown, TouchMove和TouchUp等函式, 而每個函式implement的方法, 會在下面的文字中介紹.

for(unsigned int i=0; i <>numInputs; ++i)
{
if(ti[i].dwFlags & TOUCHEVENTF_DOWN)
{
OnTouchDownHandler(hWnd, ti[i]);
}
else if(ti[i].dwFlags & TOUCHEVENTF_MOVE)
{
OnTouchMoveHandler(hWnd, ti[i]);
}
else if(ti[i].dwFlags & TOUCHEVENTF_UP)
{
OnTouchUpHandler(hWnd, ti[i]);
}
}
}
CloseTouchInputHandle((HTOUCHINPUT)lParam);
delete [] ti;
}
break;


//和處理mouse message的方法類似, 我們可以將touch state作以下處理:

// Handler for touch-down message.
// Starts a new stroke and assigns a color to it.
// hWnd window handle
// ti TOUCHINPUT structure (info about contact)
void OnTouchDownHandler(HWND hWnd, const TOUCHINPUT& ti)
{
// Extract contact info: point of contact and ID
POINT pt = GetTouchPoint(hWnd, ti);
int iCursorId = GetTouchContactID(ti);

// We have just started a new stroke, which must have an ID value unique
// among all the strokes currently being drawn. Check if there is a stroke
// with the same ID in the collection of the strokes in drawing.
ASSERT(g_StrkColDrawing.FindStrokeById(iCursorId) == -1);

// Create new stroke, add point and assign a color to it.
CStroke* pStrkNew = new CStroke;
pStrkNew->Add(pt);
pStrkNew->SetColor(GetTouchColor((ti.dwFlags & TOUCHEVENTF_PRIMARY) != 0));
pStrkNew->SetId(iCursorId);

// Add new stroke to the collection of strokes in drawing.
g_StrkColDrawing.Add(pStrkNew);
}

// Handler for touch-move message.
// Adds a point to the stroke in drawing and draws new stroke segment
// hWnd window handle
// ti TOUCHINPUT structure (info about contact)
void OnTouchMoveHandler(HWND hWnd, const TOUCHINPUT& ti)
{
// Extract contact info: contact ID
int iCursorId = GetTouchContactID(ti);

// Find the stroke in the collection of the strokes in drawing.
int iStrk = g_StrkColDrawing.FindStrokeById(iCursorId);

POINT pt;
pt = GetTouchPoint(hWnd, ti);

// Add contact point to the stroke
g_StrkColDrawing[iStrk]->Add(pt);

// Partial redraw: only the last line segment
HDC hDC = GetDC(hWnd);
g_StrkColDrawing[iStrk]->DrawLast(hDC);
ReleaseDC(hWnd, hDC);
}

// Handler for touch-up message.
// Finishes the stroke and moves it to the collection of finished strokes.:
// hWnd window handle
// ti TOUCHINPUT structure (info about contact)
void OnTouchUpHandler(HWND hWnd, const TOUCHINPUT& ti)
{
// Extract contact info: contact ID
int iCursorId = GetTouchContactID(ti);

// Find the stroke in the collection of the strokes in drawing.
int iStrk = g_StrkColDrawing.FindStrokeById(iCursorId);

g_StrkColFinished.Add(g_StrkColDrawing[iStrk]);

// Remove this stroke from the collection of strokes in drawing.
g_StrkColDrawing.Remove(iStrk);

// Request full redraw.
InvalidateRect(hWnd, NULL, FALSE);
}


3. 視窗重繪時呼叫繪圖功能
每當程式接收到WM_PAINT指令時,就會把整個視窗或部分視窗重繪, 我們可以在處理WM_PAINT訊息時加入以下繪圖指令, 避免視窗放大縮小時圖案被清除.

case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// Full redraw: draw complete collection of finished strokes and
// also all the strokes that are currently in drawing.
g_StrkColFinished.Draw(hdc);
g_StrkColDrawing.Draw(hdc);
EndPaint(hWnd, &ps);
break;

4. 清除資料
在Windows destory時, 我們必需unregister multi-touch
windows, 和清除之前new出來的物件, 以免造成memory leak的問題, 如下所示:

case WM_DESTROY:
// Clean up of application data: unregister window for multi-touch.
if(!UnregisterTouchWindow(hWnd))
{
MessageBox(NULL, L"Cannot unregister application window for touch input", L"Error", MB_OK);
}
ASSERT(!IsTouchWindow(hWnd, NULL));
// Destroy all the strokes
{
int i;
for(i=0; i < > g_StrkColDrawing.Count(); ++i)
{
delete g_StrkColDrawing[i];
}
for(i=0; i <> g_StrkColFinished.Count(); ++i)
{
delete g_StrkColFinished[i];
}
}
PostQuitMessage(0);
break;

2009年6月19日 星期五

Windows 7 Touch SDK - Get Gesture Information

下面的步驟說明如何取得Gestre的資料:

1.
Set up a window for receiving gestures
如果要收到WM_GESTURE Message, 先確保程式中沒有呼叫RegisterTouchWindow.如下所示:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;

hInst = hInstance; // Store instance handle in our global variable.

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

if (!hWnd)
{
return FALSE;
}

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

return TRUE;
}


2. Handling Gesture Messages
和處理WM_TOUCH的方式一樣,你可以在WndProc的函數中取得WM_GESTURE Message, 如下所示:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;

switch (message){
case WM_GESTURE:
/* Insert handler code here to interpret the gesture. */
DecodeGesture(wParam, lParam);
break;
}
}


3. Interpreting the Gesture Messages

可以利用GetGestureInfo函式將Gesture message(lParam)轉換成GESTUREINFO的資料結構. 其資料結構包含gesture的作用點和類型等, 如下所示:

void DecodeGesture(WPARAM wParam, LPARAM lParam)
{
// Create a structure to populate and retrieve the extra message info.
GESTUREINFO gi;

gi.cbSize = sizeof(GESTUREINFO);
ZeroMemory(&gi, sizeof(GESTUREINFO));

BOOL bResult = GetGestureInfo((HGESTUREINFO)lParam, &gi);

if (bResult){
// now interpret the gesture
switch (gi.dwID){
case GID_ZOOM:
// Code for zooming goes here
break;
case GID_PAN:
// Code for panning goes here
break;
case GID_ROTATE:
// Code for rotation goes here
break;
case GID_TWOFINGERTAP:
// Code for two-finger tap goes here
break;
case GID_PRESSANDTAP:
// Code for roll over goes here
break;
default:
// You have encountered an unknown gesture
break;
}
}else{
DWORD dwErr = GetLastError();
if (dwErr > 0){
//GetDlgItem(IDC_STATIC)->SetWindowTextW(L"Error!");
}
}
CloseGestureInfoHandle((HGESTUREINFO)lParam);
}


參考資料 : http://msdn.microsoft.com/en-us/library/dd371578(VS.85).aspx

Windows 7 Touch SDK - Get Touch Point

下面的範例程式介紹如何取得touch的相關資料, 可分成三個步驟:

1.
Create an Application and Enable Windows Touch
首先, 先在VC 2008中產生一個Win32 Project, 然後記得在targetver.h修改Windows Version, 否則會編譯不過.如下所示:
#ifndef WINVER                  // Specifies that the minimum required platform is Windows 7.
#define WINVER 0x0601 // Change this to the appropriate value to target other versions of Windows.
#endif



2.Registering to Receive Multitouch Input
你必須使用RegisterTouchWindow將應用程式註冊成touch window,才能收到WM_TOUCH Message, 否則, 預設只能收到WM_GESTURE Message.範例如下所示:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;

hInst = hInstance; // Store instance handle in our global variable

hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

if (!hWnd){
return FALSE;
}

//註冊成touch window
RegisterTouchWindow(hWnd, 0);

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

return TRUE;
}


3. Handling Multi-touch Messages
你可以在WinProc函式中操作touch messages, 如下所示:
LRESULT OnTouch(HWND hWnd, WPARAM wParam, LPARAM lParam )
{
UINT cInputs = LOWORD(wParam); //將wParam轉換touch點的數目
PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs]; //產生一組大小為cInputs的PTOUCHINPUT資料結構

if (pInputs)
{

//利用GetTouchInputInfo函式取得TOUCHINPUT的資料
if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT)))
{
for (int i=0; i <>(cInputs); i++)
{
TOUCHINPUT ti = pInputs[i];
// do something with your touch input handle
}
}

// if you handled the message and don't want anything else done with it, you can close it
CloseTouchInputHandle((HTOUCHINPUT)lParam);
delete [] pInputs;
}
else
{
/* handle the error here */
}

// if you didn't handle the message, let DefWindowProc handle:
return DefWindowProc(hWnd, WM_TOUCH, wParam, lParam);
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;

switch (message)
{
// pass touch messages to the touch handler
case WM_TOUCH:
OnTouch(hWnd, wParam, lParam);
break;
}
}

參考資料 : http://msdn.microsoft.com/en-us/library/dd744775(VS.85).aspx

2009年6月18日 星期四

Windows 7 Touch SDK - Beginning

Windows 7最大的特色就是支援多點觸控技術, 當然它的SDK也提供 Touch / Gesture的function, 讓programmer開發multi-tocuh相關的應用程式, 下面我就來介紹如何利用Win7 Touch SDK來開發程式.

1:
硬體和軟體需求
a. 機器 - 有支援多點觸碰的電腦(HP TouchSmartDell Latitude XT)或螢幕
b. 作業系統版本 - Windows 7 Ultimate
c. Windows SDK版本 - 安裝Windows SDK v7.0
d. IDE - 安裝Microsoft Visual Studio 2008

2. 編譯環境設定
a. 在VC 2008 設定SDK v7.0的路徑, 包含include 和 lib, 如下圖所示:





完成上面設定後, 我們就可以開始撰寫multi-touch的應用程式

3. 如何取得Touch 和 Gesture的資料?
TouchGesture資料的取得, 可以透過Message(WM_TOUCH, WM_GESTURE)或 Event(Touch Event, Gesture Event)的方式來取得, 如下表所示, 而資料結構的定義 , 可以在WinUser.h中找到


[圖] Touch Platform Overview

參考資料: MSDN-About the Windows Touch SDK






2009年6月16日 星期二

Sleep(1)精準嗎?

在用Sleep(1)函數的時候, 遇到一個怪現象, 就是在不同電腦下, 使用Sleep(1)的精準度會不一樣.

我的測試的方法, 是使用FPS來當測試依據 , 而FPS指的是 "Frame Per Second", 也就是每秒畫面更新的次數, 而FPS對遊戲程式也相當重要, 它會影響畫面的流暢(閃爍和停格等), 下面code為測試程式的source code:

void FPSTest(void)
{
static int Frames = 0; //更新的次數
static int UpdateTime = 1000; //多久算一次FPS
static int LastTime = 0; //上次計算FPS的時間
static double FPS = 0;

Frames++; // 增加更新的次數

if(GetTickCount()-LastTime>UpdateTime) // 如果時間超過,該更新
{
FPS = ((double)Frames/(double)(GetTickCount()-LastTime))*1000.0; // FPS的計算公式
LastTime = GetTickCount(); // 設定時間,給下次用
Frames = 0; // 更新次數歸0

}
}


int _tmain(int argc, _TCHAR* argv[])
{

while(
1)

{
FPSTest();

Sleep(1);

}
return 0;
}


以下是我的測試結果:

(1)
TOSHIBA Intel Core 2 Duo T550 1.83GHZ WinXP SP3
平均 FPS = 512

(2)
ACER Intel Core 2 Duo T7300 2.00GHZ Windows 7 Ultimate
平均 FPS = 64


去請教Google大師, 找到有部份文章提到,多媒體程式庫中有個 timeBeginPeriodAPI,可以設定時間解析度, 用法也很簡單,需要包含mmsystem.h和連結 winmm.lib, 因此將原本測試程式改成 :

int _tmain(int argc, _TCHAR* argv[])
{

timeBeginPeriod(1);
while(1)
{
FPSTest();

Sleep(1);

}
timeEndPeriod(1);
return 0;
}


重新測試結果馬上大不相同,Sleep不再delay,原則上會逼近所要求的等待時間:

(1)
TOSHIBA Intel Core 2 Duo T550 1.83GHZ WinXP SP3
平均 FPS = 512


(2)
ACER Intel Core 2 Duo T7300 2.00GHZ Windows 7 Ultimate
平均 FPS = 1000


注意 :
使用timeBeginPeriod時要注意,原則上調動它會造成系統不必要的負擔,也會改變系統的Sleep()函數的精準度, 記得不用了馬上timeEndPeriod恢復原狀。

參考資料 :