国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
設(shè)置雙緩沖減少窗體閃爍
如何:通過對窗體和控件使用雙緩沖來減少圖形閃爍

 

更新:2007 年 11 月

對于大多數(shù)應(yīng)用程序,.NET Framework 提供的默認(rèn)雙緩沖將提供最佳效果。默認(rèn)情況下,標(biāo)準(zhǔn) Windows 窗體控件是雙緩沖的??梢酝ㄟ^兩種方法對窗體和所創(chuàng)作的控件啟用默認(rèn)雙緩沖。一種方法是將 DoubleBuffered 屬性設(shè)置為 true,另一種方法是通過調(diào)用 SetStyle 方法將 OptimizedDoubleBuffer 標(biāo)志設(shè)置為 true。兩種方法都將為窗體或控件啟用默認(rèn)雙緩沖并提供無閃爍的圖形呈現(xiàn)。建議僅對已為其編寫所有呈現(xiàn)代碼的自定義控件調(diào)用 SetStyle 方法。

------------------------------------------------------------------------------------------------------------------------------------

什么是閃爍

閃爍可以這樣定義:當(dāng)后面一幅圖像以很快的速度畫在前面一幅圖像上時,在后面圖像顯示前,你可以很快看到前面那一個圖像,這樣的現(xiàn)象就是閃爍。我認(rèn)為,閃爍會讓使用者對程序很不滿,原因是:如果用戶接口編碼如此糟糕,那么程序的其他部分呢,如何能相信數(shù)據(jù)的正確性呢?一個具有平滑,快速相應(yīng)的程序會給用戶帶來信心,這個道理很簡單。

程序出現(xiàn)閃爍可以由多種形式造成,最常見的原因是窗口大小發(fā)生改變時,其內(nèi)容重畫造成閃爍。

僅僅畫一次

這是一個黃金法則,在任何計算機(Windows或者你使用的任何操作系統(tǒng))上處理畫法邏輯都需要遵循,即永遠不要將同一像素畫兩次。一個懶惰的程序員常常不愿意在畫法邏輯上投入過多精力,而是采用簡單的處理邏輯。要避免閃爍,就需要確保不會出現(xiàn)重復(fù)繪制的情況發(fā)生?,F(xiàn)在,WIndows和計算機還是很笨的,除非你給他們指令,否則他們不會做任何事情。如果閃爍的現(xiàn)象發(fā)生,那是因為你的程序刻意地多繪制了屏幕的某些區(qū)域造成的. 這個現(xiàn)象可能是因為一些明確的命令,或者一些被你忽視了的地方。如果程序有閃爍的現(xiàn)象出現(xiàn),你需要你知道如何找到好的方案去解決這個問題。

WM_ERASEBKGND

通常,首先需要懷疑的是WM_ERASEBKGND消息。當(dāng)一個窗口的背景需要被擦除時,這個消息會被發(fā)送。這是因為窗口的繪畫通常經(jīng)歷了兩個過程

  • WM_ERASEBKGND: 清除背景
  • WM_PAINT: 在上面繪制內(nèi)容

這兩個過程讓窗體在繪制內(nèi)容時變得很簡單,即:每次當(dāng)收到WM_PAINT消息時,你知道已經(jīng)有了一個新畫布等待去繪制。然而,畫窗口兩次(一次是通過WM_ERASEBKGND畫背景,另外一次是WM_PAINT)將會導(dǎo)致窗口出現(xiàn)比較糟糕的閃爍現(xiàn)象。只要看看標(biāo)準(zhǔn)的編輯框-打開Windows的寫字板并改變窗口大小,就可以看到那種閃爍的效果。

那么,如何避免窗口背景的重刷呢?有如下兩種方法:

  • 設(shè)置窗口背景刷子為NULL(當(dāng)注冊Windows類時,設(shè)置WNDCLASS結(jié)構(gòu)中的hbrBackground成員為零)
  • 在WM_ERASEBKGND消息處理時 返回非零值

以上任何一種方法都可以阻止WM_ERASEBKGND 消息去清除窗口。其中,第二個方案的通常可以以如下代碼實現(xiàn):

case WM_ERASEBKGND:
    return 1;

當(dāng)你標(biāo)記窗口內(nèi)容無效并試圖更新時,還有如下辦法可以防止WM_ERASEBKGND消息:InvalidateRect函數(shù)的最后一個參數(shù)可以指明在下一次窗口重畫時,是否窗口的部分背景會被重刷。將該參數(shù)置為False可以防止當(dāng)窗口需要重畫時系統(tǒng)發(fā)出WM_ERASEBKGND消息。

InvalidateRect(hwnd, &rect, FALSE);

不該畫的時候一定不要畫

有一個比較普遍的現(xiàn)象:即使窗口中只有一個小的部分發(fā)生了改變,往往所有的部分都會被重畫。比如,經(jīng)常地,當(dāng)窗口大小被改變時,一些(不是所有)的程序會重畫所有的窗口。通常,這是個是不必要的,這是因為當(dāng)窗口大小被改變時,經(jīng)常是之前窗口的內(nèi)容是不變的,僅僅是改變大小造成的一個小的邊界區(qū)域需要重畫。此時,沒有必要重畫所有區(qū)域。如果在這里多注意,多考慮,就可以使用好的算法以使得一次只有最小的部分被畫。

系統(tǒng)中每個窗口都有更新區(qū)域。這個區(qū)域描述了窗口中變得無效需要重畫的地方。如果一個窗口僅僅其需要更新的區(qū)域,不多繪制其他地方,那么窗口的繪制效果將會非??臁?/p>

有幾種方法可以獲得窗口的更新區(qū)域。通過GetUpdateRgn 函數(shù)可以獲得準(zhǔn)確的更新區(qū)域,這個函數(shù)返回的結(jié)果可以使矩形的區(qū)域也可以是非矩形的區(qū)域。通過GetUpdateRect 函數(shù)可以獲得需要更新的最小矩形區(qū)域。通常使用矩形的更新區(qū)域比較容易。第三個方法是在BeginPaint/EndPaint中得到PAINTSTRUCT 結(jié)構(gòu),從而得到準(zhǔn)確的更新區(qū)域信息。

一個常規(guī)的畫法函數(shù)是這樣的:

PAINTSTRUCT ps;
HDC          hdc;
case WM_PAINT:
    hdc = BeginPaint(hwnd, &ps);
    // do painting
    EndPaint(hwnd, &ps);
    return 0;

BeginPaint函數(shù)初始化PS(PAINTSTRUCT)結(jié)構(gòu),其中,成員rcPaint是一個RECT結(jié)構(gòu),描述了包含了需要更新的最小矩形區(qū)域(就像GetWindowRect函數(shù))。

如果僅僅在這個矩形區(qū)域上繪制窗口,速度上繪有很好地提高。

現(xiàn)在,當(dāng)使用BeginPaint/EndPaint時Windows會自動剪切掉畫在更新區(qū)域外面的部分。這意味著,你沒有機會畫到更新區(qū)域以外的地方。可能你會認(rèn)為,如果是這樣的話,花功夫確保代碼不試圖畫到更新區(qū)域外是沒有意義的,反正沒有畫出任何東西來。然而,你仍然可以避免不必要的API調(diào)用和相關(guān)計算,所以,我認(rèn)為放一些精力在如何工作地更快上是絕對值得的。

如果還是不能解決

有些時候,當(dāng)你花了很多努力去考慮非常好的畫法時,發(fā)現(xiàn)窗口還是會被全部刷新。這通常是由兩個Window 類的屬性造成的:CS_VREDRAWCS_HREDRAW。如果有其中一個標(biāo)志被設(shè)置時,那么當(dāng)窗口水平或者豎直方向有大小被改變時,其內(nèi)容每次都會被重新刷新。所有,你需要關(guān)掉這兩個標(biāo)志,解決的唯一的方式是在創(chuàng)建窗體和窗體類被注冊時,確保這兩個屬性不被設(shè)置。

WNDCLASSEX wc;
wc.cbSize = sizeof(wc);
wc.style   = 0; /* CS_VREDRAW | CS_HREDRAW; */
...
RegisterClassEx(&wc);

上面的例子描述了當(dāng)窗體類被注冊時,這兩個屬性不被設(shè)置的實現(xiàn)方法。

有一點需要注意:如果主窗體有了這兩個屬性,即使子窗體沒有重畫標(biāo)志,會導(dǎo)致所有子窗體在其大小被改變時會被重繪??梢酝ㄟ^以下方式避免這個情況發(fā)生:

剪切子窗體
有時,閃爍的原因是因為當(dāng)重畫時,父窗體沒有剪切其子窗體區(qū)域。這樣的結(jié)果導(dǎo)致,整個父窗口內(nèi)容被重畫,而子窗體又被顯示在了上面(造成閃爍)。這個可以通過在父窗體上設(shè)置WS_CLIPCHILDREN 來解決。當(dāng)這個標(biāo)志被設(shè)置時,被子窗體占據(jù)的任何區(qū)域?qū)慌懦诟聟^(qū)域外。因此,即使你嘗試在子窗體所在的位置上繪制(父窗口的內(nèi)容),BeginPaint中的剪切區(qū)域也會阻止其繪制效果。
雙緩沖和內(nèi)存設(shè)備描述表(Memory Device Context, 簡稱Memory-DC)

常見的徹底避免閃爍的方法是使用雙緩沖。其基本的思路是:將窗體的內(nèi)容畫在屏幕外的一個緩沖區(qū)內(nèi),然后,將該緩沖區(qū)的內(nèi)容再傳遞到屏幕上(使用BilBlt函數(shù))。這是一個非常好的減少閃爍的方法,但是經(jīng)常被濫用,特別是當(dāng)程序員并不真正地理解如何有效地繪制窗口時。

典型的雙緩沖代碼如下:

HDC          hdcMem;HBITMAP      hbmMem;HANDLE       hOld;PAINTSTRUCT  ps;HDC          hdc;....case WM_PAINT:// Get DC for windowhdc = BeginPaint(hwnd, &ps);// Create an off-screen DC for double-bufferinghdcMem = CreateCompatibleDC(hdc);hbmMem = CreateCompatibleBitmap(hdc, win_width, win_height);hOld   = SelectObject(hdcMem, hbmMem);// Draw into hdcMem// Transfer the off-screen DC to the screenBitBlt(hdc, 0, 0, win_width, win_height, hdcMem, 0, 0, SRCCOPY);// Free-up the off-screen DCSelectObject(hdcMem, hOld);DeleteObject(hbmMem);DeleteDC    (hdcMem);EndPaint(hwnd, &ps);return 0;

這個方法比較慢,因為在每次窗體需要重畫的時候內(nèi)存設(shè)備描述表(Memory-DC)都需要被重新創(chuàng)建。更有效的方法是,僅僅創(chuàng)建內(nèi)存設(shè)備描述表(Memory-DC)一次,并使其足夠大到能滿足任何時候的整個窗體刷新。當(dāng)程序結(jié)束時,再銷毀這個內(nèi)存設(shè)備描述表(Memory-DC)。這兩種方法都存在對內(nèi)存開銷的問題,特別是如果內(nèi)存設(shè)備描述表(Memory-DC)是針對真?zhèn)€屏幕的大小。雙緩沖也需要兩倍的時間去畫。這是因為其第一次是在內(nèi)存設(shè)備描述表(Memory-DC)上畫,然后再使用BitBlt畫回到屏幕上。當(dāng)然,好的顯卡會使BitBlt更快,但是仍然會耗CPU 時間。

如果程序需要顯示相當(dāng)復(fù)雜的信息,比如像網(wǎng)頁,那么你應(yīng)該使用內(nèi)存設(shè)備描述表(Memory-DC)。比如IE,如果不使用雙緩沖,是沒有辦法在繪制網(wǎng)頁時不閃爍的。

沒有必要將雙緩沖技術(shù)用于整個窗體的繪制中。可以這樣設(shè)想,窗口中僅僅有一個小部分包含了復(fù)雜的圖形對象(比如半透明的位圖或者其他)。你應(yīng)該將內(nèi)存設(shè)備描述表(Memory-DC)僅僅用于著一個小區(qū)域,其他區(qū)域使用常規(guī)的方法。 有時,通過仔細的思考,經(jīng)??梢员苊馐褂秒p緩沖而直接將結(jié)果畫到屏幕上。只要你不破壞黃金法則,即“永遠不要將一個像素畫兩次”,就可以防止閃爍的出現(xiàn)。

避免過度繪制

我想說的關(guān)于這個話題是這樣的:有一個需要自己定義畫法的窗體的標(biāo)題欄。首先,你畫了標(biāo)題,接著在上面畫一些其他的圖形?,F(xiàn)在,只要標(biāo)題需要被重畫,就會出現(xiàn)閃爍現(xiàn)象。這是因為你沒有合乎黃金法則。這里,標(biāo)題被很快地顯示在其他圖形在上面繪制時,導(dǎo)致了閃爍。

有兩種技術(shù)可以組織這種類型的閃爍。第一個是使用剪切,第二個是使用你的大腦。

使用剪切時,你可以使用ExcludeClipRect 函數(shù)在設(shè)備描述表中去標(biāo)記一個特定的區(qū)域。當(dāng)一個區(qū)域被標(biāo)記上時,即使在該區(qū)域上面重畫也不會產(chǎn)生效果。一旦背景已經(jīng)被繪制了,可以通過SelectClipRgn移掉該標(biāo)記的區(qū)域,其他圖形能被畫到前面標(biāo)記的區(qū)域上。通過準(zhǔn)確的標(biāo)記(剪切),可以在很多時候被避免過度繪制。

另外一個方案就是找更聰明的解決辦法。比如,當(dāng)你需要畫一個表格,通常應(yīng)該先畫空的背景,再畫網(wǎng)格線從而產(chǎn)生表格。但是,這個方法會使網(wǎng)格線產(chǎn)生閃爍,這是因為在網(wǎng)格線被畫之前,下面背景被很快地顯示了一下。然而可以使用不同的做法達到想要的結(jié)果。即,不是一次畫一個大的空背景,而是畫一系列的空方塊,每一個方塊邊是被一個像素的寬度分開。這樣,當(dāng)畫網(wǎng)格線時,他們剛好能被畫到一個之前沒有畫過的地方。其結(jié)果是不會有閃爍現(xiàn)象,因為沒有像素被畫了超過兩次。

使用你的頭腦去想一個好的算法可能需要長一點的時間,但是卻是值得的,因為這能讓結(jié)果更好。

結(jié)論

希望你再也不會問:“為什么我的窗體會閃爍”這樣的問題。我已經(jīng)講解了閃爍的主要原因和解決辦法。如果你遇到了閃爍的問題,你應(yīng)該能找到原因并且使用這里提到的技術(shù)來解決了。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
VC窗口刷新InvalidateRect和UpdateWindow
VC防止窗口和控件閃爍的方法
轉(zhuǎn):關(guān)于WM
RedrawWindow(NULL, NULL, RDW
Windows窗口刷新機制詳解
Invalidate()與UpdateAllViews()
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服