提高二維矢量繪圖效率之一般做法

作者:朱金燦
來源:http://blog.csdn.net/clever101

這個(gè)問題很普遍。最近在研究這個(gè)問題,在網(wǎng)上搜了一些資料,再結(jié)合自己的經(jīng)驗(yàn),談?wù)勛约旱囊恍┫敕ā?/p>

一、雙緩存能提高繪圖效率嗎?

網(wǎng)上有篇文章:繪圖效率完整解決方案——三種手段提高GDI/GDI+繪圖效率,其中提到一種方法是:緩存——Bitmap或者DoubleBuffer。緩存就是先把繪制的圖形繪制到一張內(nèi)存位圖上,然后在一次性的貼位圖,他可以提高繪圖速度,也能避免閃爍。DoubleBuffer=true是C#窗體的屬性,設(shè)置了此屬性估計(jì)系統(tǒng)本身會(huì)起用無效區(qū)的內(nèi)存位圖緩存,而不需要程序員Bitmap處理。

這里對(duì)雙緩存的通常做法不作介紹,網(wǎng)上的相關(guān)資料很多。說實(shí)話,我對(duì)使用雙緩存能提升繪圖效率表示懷疑,理由很簡(jiǎn)單,同是DC,同是繪制1000條線段,有什么理由內(nèi)存DC就比窗口DC快(當(dāng)然這個(gè)我沒有作具體的測(cè)試,這個(gè)有空可以測(cè)試下)。我還稍微懷疑使用雙緩存繪圖比直接使用窗口DC繪圖還慢一些,理由有二:一是使用雙緩存需要增加創(chuàng)建內(nèi)存DC和內(nèi)存位圖的操作;二是使用雙緩存還需要增加一個(gè)把內(nèi)存DC拷貝到窗口DC的操作。那么雙緩存的主要作用是什么?其實(shí)就是解決繪圖過程的閃爍問題,改善繪圖效果。

二、Windows環(huán)境下二維繪圖引擎的選取

和繪圖效率的一個(gè)重要相關(guān)因素是繪圖引擎。Windows環(huán)境下二維繪圖引擎有多種選擇:GDI、GDI+、DirectDraw、QT、Agg、Cairo、skia、Direct2D、Direct3D、OpenGL等。下面我逐一作一個(gè)簡(jiǎn)單的分析:

【GDI】
微軟原生的二維繪圖引擎。
優(yōu)點(diǎn):微軟的全力支持,作為操作系統(tǒng)核心層效率方面不用擔(dān)心,支持多種開發(fā)框架(含語言):Win SDK、MFC、Delphi等。
缺點(diǎn):基于過程,缺乏面向?qū)ο?,使用起來不太方便,不支持反鋸齒,不支持復(fù)雜的繪圖效果(這個(gè)相對(duì)于GDI+而言)。

【GDI+】
微軟后來推出的二維繪圖引擎。
優(yōu)點(diǎn):微軟的全力支持,支持多種開發(fā)框架(含語言):Win SDK、MFC、Delphi等,可以實(shí)現(xiàn)復(fù)雜的繪圖效果,如反鋸齒、路徑畫刷等;面向?qū)ο蟮?a target="_blank" rel="nofollow">架構(gòu),使用起來比較方便。
缺點(diǎn):繪圖效率較GDI稍低,繪圖交互性不如GDI(缺少GDI的支持位運(yùn)算的繪圖模式),開啟反鋸齒后效率不如QT。

有關(guān)GDI和GDI+的詳細(xì)比較,請(qǐng)看我以前寫的一篇文章:GDI和GDI+的應(yīng)用場(chǎng)合思考。

【DirectDraw】
從GDI、GDI+到Direct 2D的一個(gè)過渡產(chǎn)品,微軟已明確表示不推薦使用,在MicrosoftDirectX SDK (June 2010)已看不到它的身影,在此不作介紹。

【QT】
開源跨平臺(tái)(基于LGPL協(xié)議),面向?qū)ο蟮姆绞浇M織,使用起來較為方便。

【Agg】
C++編寫的開源繪圖引擎(基于GPL協(xié)議)

【Cairo】
C編寫的開源繪圖引擎(基于LGPL協(xié)議),大名鼎鼎的FireFox就是用這個(gè)繪圖引擎的。

有關(guān)Agg和Cairo請(qǐng)參考這篇文章:Agg vs. Cairo 二維繪圖引擎之比較和選擇。該文作者比較推崇Cairo,但據(jù)我公司的一個(gè)同事介紹:Cairo的繪圖效率很慢。具體我沒有做過測(cè)試。

【Skia】
Google的Android的繪圖引擎。

【Direct2D】
微軟在Windows Vista及之后的Windows版本推出的意在取代GDI、GDI+的二維繪圖引擎,支持硬件加速。

【Direct3D】
微軟開發(fā)的3D繪圖引擎。

【OpenGL】
SGI開發(fā)的3D繪圖引擎。

上面簡(jiǎn)單對(duì) Windows下的二維繪圖引擎作了一個(gè)簡(jiǎn)單介紹。我的推薦是:開發(fā)商業(yè)產(chǎn)品一般情況下在Windows XP及以下Windows版本使用GDI和GDI+,在Windows Vista及其之后的Windows版本(如Win 7)使用Direct2D。理由是:跨平臺(tái)的繪圖引擎如Agg、Cairo之類的,出于跨平臺(tái)封裝的需要,必然會(huì)犧牲一部分性能,就是說它本來就是封裝GDI的,怎么可能超出GDI的繪圖效率呢?還有就是諸如Agg還有開源協(xié)議的限制,這樣就排除了開源二維繪圖引擎。我也不推薦使用Direct3D、OpenGL等三維繪圖引擎進(jìn)行二維繪圖,理由是三維繪圖可以利用硬件加速,繪圖速度應(yīng)該比GDI、GDI+快,但三維繪圖引擎一般是基于三維的數(shù)據(jù)結(jié)構(gòu)進(jìn)行組織的,對(duì)二維繪圖并不合適,比如以前我們?cè)肙penGL進(jìn)行二維繪圖,發(fā)現(xiàn)OpenGL在二維一些操作并不合適,如二維中的點(diǎn)、線捕捉、自定義圖例的添加、打印的支持等等。所以我傾向于使用GDI、GDI+。GDI的一大缺點(diǎn)是由于不是面向?qū)ο蠼M織的,使用起來較為繁瑣。這個(gè)我覺得可以參考GDI+的面向?qū)ο蠓庋b的方式對(duì)GDI進(jìn)行封裝。Direct 2D是微軟在后XP時(shí)代開發(fā)的開發(fā)二維繪圖引擎。微軟出于兼容性的考慮還會(huì)繼續(xù)對(duì)GDI、GDI+進(jìn)行支持,但毫無疑問微軟的策略是要Direct 2D取代GDI和GDI+的,因此在WindowsVista及其之后的Windows上進(jìn)行二維繪圖開發(fā)我建議是直接使用Direct2D。Direct 2D支持硬件加速,在繪圖效率應(yīng)有一定程度的提升。

三.提高GDI繪圖效率的常用做法

提高GDI繪圖效率的一般原則可以簡(jiǎn)單概括為:盡量減少無效繪圖區(qū)域,盡量減少不必要的繪圖操作。

盡量減少繪圖區(qū)域的通常做法是:

  • 設(shè)置裁剪區(qū)。

裁剪區(qū)的作用就是:只有在這個(gè)區(qū)內(nèi)的繪圖過程才會(huì)真正有效,在區(qū)外的是無效的,即使在區(qū)外執(zhí)行了繪圖函數(shù)也是不會(huì)顯示的。因?yàn)槎鄶?shù)情況下窗口重繪的產(chǎn)生大多是因?yàn)榇翱诓糠直徽趽趸蛘叽翱谟袧L動(dòng)發(fā)生,改變的區(qū)域并不是整個(gè)圖形而只有一小部分,這一部分需要改變的就是pDC中的裁剪區(qū)了。因?yàn)轱@示(往內(nèi)存或者顯存都叫顯示)比繪圖過程的計(jì)算要費(fèi)時(shí)得多,有了裁剪區(qū)后顯示的就只是應(yīng)該顯示的部分,大大提高了顯示效率。但是這個(gè)裁剪區(qū)是MFC設(shè)置的,它已經(jīng)為我們提高了顯示效率,在進(jìn)行復(fù)雜圖形的繪制時(shí)如何進(jìn)一步提高效率呢?那就只有去掉在裁剪區(qū)外的繪圖過程了。可以先用pDC->GetClipBox()得到裁剪區(qū),然后在繪圖時(shí)判斷你的圖形是否在這個(gè)區(qū)內(nèi),如果在就畫,不在就不畫。

  • 減少無效區(qū)域。

在GDI繪圖中,被標(biāo)記為無效矩形的區(qū)域直到WM_PAINT消息被處理完之后才會(huì)消失。因此在繪圖中應(yīng)盡量避免使用Invalidate函數(shù)(該函數(shù)使整個(gè)客戶區(qū)設(shè)置為無效區(qū)域),而應(yīng)在多使用InvalidateRect函數(shù)具體比如你想改變某條線的線型,應(yīng)首先精確計(jì)算改線的屏幕范圍,然后改變其線型后調(diào)用InvalidateRect函數(shù)進(jìn)行局部更新。

盡量減少不必要的繪圖操作。比如二維矢量繪圖中一般有一個(gè)滑動(dòng)鼠標(biāo)滾輪進(jìn)行全圖縮放的操作。其實(shí)在縮放過程中,每一次WM_MOUSEWHEEL消息的處理都必須是先把所有的繪圖對(duì)象重繪一次。這個(gè)其實(shí)并沒有必要。一個(gè)優(yōu)化方案是通過設(shè)置一個(gè)標(biāo)記,在繪圖循環(huán)中(一般在復(fù)雜繪圖中都把繪圖對(duì)象保存在一個(gè)數(shù)組或鏈表中),當(dāng)這個(gè)標(biāo)記為TRUE時(shí),就停止繪圖,當(dāng)這個(gè)標(biāo)記為FALSE則不影響繪圖。在處理WM_MOUSEWHEEL消息是,先設(shè)置這個(gè)標(biāo)記為TRUE,然后發(fā)送一個(gè)重繪消息,設(shè)置標(biāo)記為FALSE進(jìn)行重繪。然后在繪圖循環(huán)時(shí)先判斷這個(gè)標(biāo)記是否TRUE,為TRUE則退出繪圖,然后截取鼠標(biāo)消息,優(yōu)先WM_MOUSEWHEEL消息,大致代碼如下:

MSG msg;   
// hView為繪圖窗口句柄  
if(PeekMessage(&msg,hView,WM_MOUSEFIRST,WM_MOUSELAST,PM_REMOVE  
))   
{  
     TranslateMessage(&msg);   
     DispatchMessage(&msg);   
}  

這樣可以造成這樣的效果:就是假如當(dāng)進(jìn)行第二次滾動(dòng),還在進(jìn)行第一次滾動(dòng)時(shí)的操作,就立即退出繪圖循環(huán),這樣就能大大減少不必要的繪圖操作。

參考文獻(xiàn):

繪圖效率完整解決方案——三種手段提高GDI/GDI+繪圖效率,作者:fyhui
GDI和GDI+的應(yīng)用場(chǎng)合思考,作者:朱金燦
Agg vs. Cairo 二維繪圖引擎之比較和選擇,作者:張亮
用MFC如何高效地繪圖
GDI使用經(jīng)驗(yàn)總結(jié),作者:楊濤
DrawCli代碼中雙緩沖,裁剪區(qū)技術(shù)以及坐標(biāo)變換等技術(shù)分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,216評(píng)論 4 61
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,983評(píng)論 25 709
  • 學(xué)習(xí)到了兩句話 < 因小失大 > <貪多嚼不爛> 看了范冰冰演得潘金蓮的電影瞬間顏范轉(zhuǎn)忠實(shí)粉絲,因此記住了不能因小...
    大溪的清晨島嶼閱讀 187評(píng)論 0 1
  • “一旦走出了那些與“習(xí)慣”之間的糾纏和掙扎,自然也就明白,有時(shí)我們只是有太多遲疑,有時(shí)我們只是臆想出了太多的恐懼,...
    樂小檬閱讀 251評(píng)論 0 0
  • 風(fēng)云際會(huì),萬事興。
    10086好閱讀 196評(píng)論 0 0

友情鏈接更多精彩內(nèi)容