..::Plynulß animace v MFC 2::..

Tady m∙₧ete stßhnout ukßzkovou aplikaci.

V minulΘm dφle tohoto Φlßnku jsem vßm ukßzal jeden zp∙sob jak zrychlit animaci ve vaÜφ aplikaci. Od tΘ doby m∞ napadl jeÜt∞ jeden. Ten sice nedosahuje takovΘ frekvence p°ekreslovßnφ jako p°epsßnφ funkce Run, ale Φasto nßm dostaΦuje. Tento zp∙sob vyu₧φvß pracovnφho vlßkna ve kterΘm se vyvolßvß p°ekreslenφ. Nßsleduje porovnßnφ t∞chto t°φ metod.

Metoda PoΦet cykl∙ Relativnφ rychlost v∙Φi timeru
Timer 52120 na 1000 p°ekreslenφ 1
Vlßkno (zprßva) 18950 na 1000 p°ekreslenφ 2,75
Vlßkno (funkce) 3240 na 100 000 p°ekreslenφ 1608
Run 1640 na 100 000 p°ekreslenφ 3178

Jak vidφte, pou₧itφm vlßkna a zprßvy nezφskßme takovΘ obrovskΘ zrychlenφ jako p°epsßnφm funkce Run, ale velice Φasto nßm dosa₧enΘ zrychlenφ  dostaΦuje. A je mnohem jednoduÜÜφ pro ka₧dΘ okno vytvo°it vlßkno ne₧ upravovat funkci Run. A pokud ve vlßkn∞ volßme p°φmo kreslφcφ funci je rychlost s Run srovnatelnß.

A te∩ jak se to provßdφ. Nejd°φve vlßkno se zprßvou.

Nadefinujte si funkci vlßkna. Nap°φklad takto:

UINT RedrawThread(LPVOID pParam)

{

HWND hWnd = (HWND)pParam;    // zkonvertujeme parametr zpßtky na HWND

 

while (IsWindow(hWnd))                    // dokud okno existuje

{

::SendMessage(hWnd,WM_USER+1,0,0);    // poÜleme zprßvu, ₧e chceme p°ekreslit a Φekßme dokud na ni okno nezareaguje

}

return 0;    // Okno u₧ neexistuje, m∙₧eme ukonΦit vlßkno

}

Te∩ vlßkno musφme spustit a to nejlΘpe ve funkci OnCreate (reakce na zprßvu WM_CREATE).

...

AfxBeginThread(RedrawThread,(LPVOID)m_hWnd,THREAD_PRIORITY_NORMAL);

...

A jeÜt∞ musφme odchytit zprßvu (WM_USER+1) po₧adujφcφ p°ekreslenφ a p°ekreslit okno. K tomu m∙₧eme vyu₧φt mapu zprßv a nebo p°φmo proceduru okna (virtußlnφ funkci WindowProc).

LRESULT CTstView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)

{

// TODO: Add your specialized code here and/or call the base class

switch (message)

{

case WM_USER+1:

Redraw();        // Zavolßme svoji funkci, kterß zp∙sobφ p°ekreslenφ

break;

}

return CView::WindowProc(message, wParam, lParam);

}

Jinß mo₧nost jak vyu₧φt vlßkna  je p°edat vlßknu mφsto handle okna p°φmo ukazatel na t°φdu okna a funkci Redraw volat p°φmo z vlßkna. Tφm zφskßme frekvenci srovnatelnou s Run (asi poloviΦnφ), ale ve funkci OnDraw p°i kontrole platnosti ukazatele na dokument v debug m≤du mi tato kontrola nedopadala dob°e --> ASSERT_VALID schazovalo aplikaci. V release m≤du, ale ₧ßdnΘ problΘmy nebyly. A pokud nepou₧φvßte architekturu dokument/pohled, nebo okno nemßte propojeno na dokument, nebudou ₧ßdnΘ problΘmy ani v debug m≤du. (Ve skuteΦnosti staΦφ zakomentß°ovat v OnDraw °ßdek ASSERT_VALID(pDoc);)

Potom jednoduchou ·pravou p°edchozφho k≤du zφskßme podstatn∞ v∞tÜφ rychlost.

P°i spouÜt∞nφ vlßkna p°edßme mφsto handle okna ukazatel na t°φdu okna.

AfxBeginThread(RedrawThread,(LPVOID)this,THREAD_PRIORITY_NORMAL);

a trochu upravφme funkci vlßkna.

UINT RedrawThread(LPVOID pParam)

{

CTstView* pView = (CTstView*)pParam;    // zkonvertujeme parametr zpßtky na CTstView*

 

while (IsWindow(pView->hWnd))                    // dokud okno existuje

{

pView->Redraw();    // zavolßme funkci, kterß zp∙sobφ p°ekreslenφ

}

return 0;    // Okno u₧ neexistuje, m∙₧eme ukonΦit vlßkno

}

Zprßvu WM_USER+1 u₧ nepou₧φvßme, tak₧e ji nemusφme odchytßvat.

Ale pozor. Tahle metoda mß jeÜt∞ jeden zßdrhel. Proto₧e funkce pro p°ekreslenφ se volß ve vlßkn∞ a tφm se i p°ekreslenφ provßdφ ve vlßkn∞, mohlo by se stßt, ₧e se bude p°ekreslovat jak ve vlßkn∞, tak "souΦasn∞" ve vlastnφ aplikaci a to by mohlo vΘst ke chybnΘmu vykreslenφ. Ale pomoc je jednoduchß. Do t°φdy okna °idßme kritickou sekci a vlßkna synchronizujeme. A to takto.

Do t°φdy naÜeho okna p°idßme prom∞nnou kritickΘ sekce (pot°ebuje hlaviΦkov² soubor afxmt.h):

...

CCriticalSection cs;

...

a na zaΦßtku vykreslovßnφ ji uzamkneme a na konci odemkneme. Nap°φklad takto:

void CTstView::Redraw()

{

cs.Lock();                // Uzamkneme kritickou sekci

CClientDC dc(this);  // Zφskßme DC okna

OnDraw(&dc);         // Zavolßme funkci OnDraw pro vykreslenφ

cs.Unlock();            // Odemkneme kritickou sekci

}

Tφm zajistφme, ₧e se na obrazovku nebude kreslit souΦasn∞ z naÜeho vlßkna a hlavnφho vlßkna aplikace. (I kdy₧ cs.Lock() a cs.Unlock() by m∞li b²t spφÜe uvnit° funkce OnDraw(...).)