位圖這個(gè)概念對(duì)于計(jì)算機(jī)圖形學(xué)來(lái)說(shuō)是個(gè)至關(guān)重要的概念,我們?cè)谄聊簧峡吹降娜魏螙|西,對(duì)計(jì)算機(jī)來(lái)說(shuō),其實(shí)都是位圖,簡(jiǎn)單地說(shuō),無(wú)論你是想顯示文字,還是線條,抑或bmp,png,jpg和gif等圖像文件,最終都是要直接或間接轉(zhuǎn)變?yōu)橛?jì)算機(jī)顯示設(shè)備所認(rèn)識(shí)的位圖,才能顯示在屏幕上。
我最近接手了一個(gè)項(xiàng)目,是Windows Mobile平臺(tái)的,主要做UI美化,貼圖是其中的一大塊,我遇到的最大的問(wèn)題就是貼圖的效率問(wèn)題,如何將一張內(nèi)存里的圖片高效繪制出來(lái),實(shí)現(xiàn)平滑流暢的UI動(dòng)畫效果。我嘗試了許多辦法,甚至DirectDraw,但我發(fā)覺(jué)在硬件不支持的情況下,DirectDraw除了讓代碼變得更復(fù)雜之外,沒(méi)有任何優(yōu)點(diǎn)。好,接下去我們來(lái)分析一下如何盡量發(fā)揮GDI的威力。
跟貼圖相關(guān)的函數(shù)有幾個(gè):
BitBlt:最基本的塊傳輸函數(shù)。
StretchBlt:比同BitBlt,它支持圖像的拉伸和壓縮,當(dāng)不需要拉伸和壓縮時(shí)候,它的效果和BitBlt并無(wú)二致。
TransparentBlt:比同StretchBlt,它多了個(gè)“摳色”(Color Keying)功能,能把某種顏色或者某個(gè)范圍的顏色摳去而不作塊傳輸處理,以此來(lái)繪制不規(guī)則圖像。
AlphaBlend:比同StretchBlt,它支持Alpha混合,即“半透明”效果,效果比簡(jiǎn)單的“摳色”更好。
這幾個(gè)函數(shù)是一個(gè)比一個(gè)強(qiáng),但也意味著效率一個(gè)比一個(gè)低,總體上看差不多是這樣的,運(yùn)算量越大,當(dāng)然就越慢,但測(cè)試下來(lái)發(fā)覺(jué)這其實(shí)并不絕對(duì),后面會(huì)提到。
了解了這幾個(gè)函數(shù)之后,我們開始加載一張圖片來(lái)觀察效果,我準(zhǔn)備的是一張320*320的png圖片,利用Windows Mobile 6.0提供的IImage接口來(lái)加載它,并把它轉(zhuǎn)變?yōu)槲粓D,獲得HBITMAP。加載png的代碼可以通過(guò)搜索引擎搜索“IImage用法”等關(guān)鍵詞來(lái)獲取,此處略過(guò)。
加載好圖片后,創(chuàng)建一個(gè)和設(shè)備顯示設(shè)備兼容的DC(Device Context),選入上面加載的位圖,用BitBlt繪制,代碼如下(為簡(jiǎn)潔起見(jiàn),只貼出關(guān)鍵代碼,并且不考慮資源釋放):
我們通過(guò)添加一些debug代碼來(lái)觀察BitBlt的執(zhí)行時(shí)間,在我的模擬器上大約是50 - 60ms,我發(fā)覺(jué)這個(gè)速度并不快,按道理說(shuō),BitBlt應(yīng)該可以在極短的時(shí)間之內(nèi)完成的(1 - 2ms),也只有這樣才能實(shí)現(xiàn)“流暢”的UI動(dòng)畫效果,否則圖一旦多起來(lái),豈不是更慢。

我嘗試修改BitBlt的最后一個(gè)參數(shù),我發(fā)覺(jué)換成NOTSRCCOPY,速度更慢,變成了70多ms,說(shuō)明運(yùn)算量更大了,這不是簡(jiǎn)單的內(nèi)存拷貝;而當(dāng)我把最后一個(gè)參數(shù)換成BLACKNESS或者WHITENESS的時(shí)候,速度則很快,1ms-2ms即可完成,很顯然,對(duì)BitBlt來(lái)說(shuō),把目標(biāo)全部置為黑色或者白色,運(yùn)算量遠(yuǎn)少于像素傳送。在實(shí)驗(yàn)的時(shí)候把BitBlt替換為另外的幾個(gè)函數(shù),效果和預(yù)期的相差不大,如果圖像需要拉伸,則執(zhí)行得更慢一些,但如果圖像不是拉伸,而是壓縮,即縮小顯示,執(zhí)行速度居然比較快,有些意外,這是因?yàn)閴嚎s后圖像變小,需要傳輸?shù)南袼刈兩俚木壒省?br>
我考慮如何提高繪圖效率,經(jīng)過(guò)很多次嘗試,終于有所突破,我最后發(fā)現(xiàn):如果先把位圖存放在一個(gè)和DC兼容的位圖中,再用這個(gè)位圖對(duì)目標(biāo)設(shè)備進(jìn)行像素傳輸,速度十分理想。代碼:
第一次調(diào)用BitBlt,可以看作是初始化,我們計(jì)算第二個(gè)BitBlt的耗時(shí),只有1 - 2ms,非常不錯(cuò),經(jīng)過(guò)分析,我認(rèn)為原因應(yīng)該是這樣(不一定全對(duì),如有不妥請(qǐng)讀者指出):
只有在位圖格式完全一致的情況下,BitBlt才能執(zhí)行真正的內(nèi)存拷貝,否則是要經(jīng)過(guò)轉(zhuǎn)換的,轉(zhuǎn)換是需要消耗CPU時(shí)間的,所以慢。
那如何知道位圖的格式呢?用GetObject可以看出來(lái):

如上圖所示,bmp是從png文件加載進(jìn)來(lái)的位圖的信息,而bmp2是用CreateCompatibleBitmap創(chuàng)建的位圖的信息,從這我們能看到,前者是24bit位圖,即一個(gè)像素用3個(gè)字節(jié)表示,而后者是16bit位圖,一個(gè)像素用兩個(gè)字節(jié)來(lái)表示,這個(gè)BitBlt執(zhí)行過(guò)程中,就需要轉(zhuǎn)換了,因此耗時(shí)。而實(shí)際上位圖的差別可能比這個(gè)還要復(fù)雜些,如果再討論設(shè)備無(wú)關(guān)位圖,那就說(shuō)不完了……
總而言之,為了提高效率,我們要想方設(shè)法把加載進(jìn)來(lái)的位圖轉(zhuǎn)變?yōu)樵O(shè)備兼容位圖,繪制的時(shí)候直接BitBlt這些設(shè)備兼容位圖,來(lái)實(shí)現(xiàn)位圖的高效繪制。
前面討論的主要是BitBlt,那對(duì)于別的幾個(gè)Blt函數(shù)呢?我都嘗試過(guò)了,除了AlphaBlend之外,兼容位圖到設(shè)備的Blt速度上都有顯著的提高,而AlphaBlend則無(wú)法正常工作,因?yàn)榧嫒菸粓D不帶Alpha通道,而AlphaBlend貌似需要32bit的ARGB格式的位圖方可正常工作,這個(gè)問(wèn)題我思考了好久都無(wú)解,如果哪位讀者對(duì)提高AlphaBlend的工作效率有心得,不妨跟我聯(lián)系下,我正急需這方面的技術(shù)資料。
因此,我給出這樣的結(jié)論,階段性結(jié)論:從文件(或資源)加載位圖后,把位圖轉(zhuǎn)為設(shè)備兼容位圖,這樣使得BitBlt在執(zhí)行SRCCOPY的時(shí)候直接使用內(nèi)存拷貝,速度很快,即便需要拉伸壓縮或者摳色等運(yùn)算,使用兼容位圖的速度也是相當(dāng)不錯(cuò)的,而使用AlphaBlend的時(shí)候,如果需要較高的效率,就應(yīng)從設(shè)計(jì)上避免繪制大幅位圖,改用小幅位圖,在不必要對(duì)每一幀都執(zhí)行Alpha混合的時(shí)候,就避免執(zhí)行,以免影響畫面的流暢性。
我最近接手了一個(gè)項(xiàng)目,是Windows Mobile平臺(tái)的,主要做UI美化,貼圖是其中的一大塊,我遇到的最大的問(wèn)題就是貼圖的效率問(wèn)題,如何將一張內(nèi)存里的圖片高效繪制出來(lái),實(shí)現(xiàn)平滑流暢的UI動(dòng)畫效果。我嘗試了許多辦法,甚至DirectDraw,但我發(fā)覺(jué)在硬件不支持的情況下,DirectDraw除了讓代碼變得更復(fù)雜之外,沒(méi)有任何優(yōu)點(diǎn)。好,接下去我們來(lái)分析一下如何盡量發(fā)揮GDI的威力。
跟貼圖相關(guān)的函數(shù)有幾個(gè):
BitBlt:最基本的塊傳輸函數(shù)。
StretchBlt:比同BitBlt,它支持圖像的拉伸和壓縮,當(dāng)不需要拉伸和壓縮時(shí)候,它的效果和BitBlt并無(wú)二致。
TransparentBlt:比同StretchBlt,它多了個(gè)“摳色”(Color Keying)功能,能把某種顏色或者某個(gè)范圍的顏色摳去而不作塊傳輸處理,以此來(lái)繪制不規(guī)則圖像。
AlphaBlend:比同StretchBlt,它支持Alpha混合,即“半透明”效果,效果比簡(jiǎn)單的“摳色”更好。
這幾個(gè)函數(shù)是一個(gè)比一個(gè)強(qiáng),但也意味著效率一個(gè)比一個(gè)低,總體上看差不多是這樣的,運(yùn)算量越大,當(dāng)然就越慢,但測(cè)試下來(lái)發(fā)覺(jué)這其實(shí)并不絕對(duì),后面會(huì)提到。
了解了這幾個(gè)函數(shù)之后,我們開始加載一張圖片來(lái)觀察效果,我準(zhǔn)備的是一張320*320的png圖片,利用Windows Mobile 6.0提供的IImage接口來(lái)加載它,并把它轉(zhuǎn)變?yōu)槲粓D,獲得HBITMAP。加載png的代碼可以通過(guò)搜索引擎搜索“IImage用法”等關(guān)鍵詞來(lái)獲取,此處略過(guò)。
加載好圖片后,創(chuàng)建一個(gè)和設(shè)備顯示設(shè)備兼容的DC(Device Context),選入上面加載的位圖,用BitBlt繪制,代碼如下(為簡(jiǎn)潔起見(jiàn),只貼出關(guān)鍵代碼,并且不考慮資源釋放):
HDC hWndDC = GetDC(hWnd);
HDC hMemDC = CreateCompatibleDC(hWndDC);
SelectObject(hMemDC, hBitmap); //hBitmap是前面加載的圖片
BitBlt(hWndDC, 0, 0, iWidth, iHeight, hMemDC, 0, 0, SRCCOPY);
HDC hMemDC = CreateCompatibleDC(hWndDC);
SelectObject(hMemDC, hBitmap); //hBitmap是前面加載的圖片
BitBlt(hWndDC, 0, 0, iWidth, iHeight, hMemDC, 0, 0, SRCCOPY);
我們通過(guò)添加一些debug代碼來(lái)觀察BitBlt的執(zhí)行時(shí)間,在我的模擬器上大約是50 - 60ms,我發(fā)覺(jué)這個(gè)速度并不快,按道理說(shuō),BitBlt應(yīng)該可以在極短的時(shí)間之內(nèi)完成的(1 - 2ms),也只有這樣才能實(shí)現(xiàn)“流暢”的UI動(dòng)畫效果,否則圖一旦多起來(lái),豈不是更慢。
我嘗試修改BitBlt的最后一個(gè)參數(shù),我發(fā)覺(jué)換成NOTSRCCOPY,速度更慢,變成了70多ms,說(shuō)明運(yùn)算量更大了,這不是簡(jiǎn)單的內(nèi)存拷貝;而當(dāng)我把最后一個(gè)參數(shù)換成BLACKNESS或者WHITENESS的時(shí)候,速度則很快,1ms-2ms即可完成,很顯然,對(duì)BitBlt來(lái)說(shuō),把目標(biāo)全部置為黑色或者白色,運(yùn)算量遠(yuǎn)少于像素傳送。在實(shí)驗(yàn)的時(shí)候把BitBlt替換為另外的幾個(gè)函數(shù),效果和預(yù)期的相差不大,如果圖像需要拉伸,則執(zhí)行得更慢一些,但如果圖像不是拉伸,而是壓縮,即縮小顯示,執(zhí)行速度居然比較快,有些意外,這是因?yàn)閴嚎s后圖像變小,需要傳輸?shù)南袼刈兩俚木壒省?br>
我考慮如何提高繪圖效率,經(jīng)過(guò)很多次嘗試,終于有所突破,我最后發(fā)現(xiàn):如果先把位圖存放在一個(gè)和DC兼容的位圖中,再用這個(gè)位圖對(duì)目標(biāo)設(shè)備進(jìn)行像素傳輸,速度十分理想。代碼:
HDC hWndDC = GetDC(hWnd);
HDC hMemDC = CreateCompatibleDC(hWndDC);
HDC hMemDCToLoad = CreateCompatibleDC(hWndDC);
HBITMAP hMemBmp = CreateCompatibleBitmap(hWndDC, iWidth, iHeight); // The compatible bitmap
HGDIOBJ hOldBmp = SelectObject(hMemDC, hMemBmp);
SelectObject(hMemDCToLoad, hBitmap);
BitBlt(hMemDC, 0, 0, iWidth, iHeight, hMemDCToLoad, 0, 0, SRCCOPY); //Do this in initialization
BitBlt(hWndDC, 0, 0, iWidth, iHeight, hMemDC, 0, 0, SRCCOPY); //This BitBlt's speed is very fast
HDC hMemDC = CreateCompatibleDC(hWndDC);
HDC hMemDCToLoad = CreateCompatibleDC(hWndDC);
HBITMAP hMemBmp = CreateCompatibleBitmap(hWndDC, iWidth, iHeight); // The compatible bitmap
HGDIOBJ hOldBmp = SelectObject(hMemDC, hMemBmp);
SelectObject(hMemDCToLoad, hBitmap);
BitBlt(hMemDC, 0, 0, iWidth, iHeight, hMemDCToLoad, 0, 0, SRCCOPY); //Do this in initialization
BitBlt(hWndDC, 0, 0, iWidth, iHeight, hMemDC, 0, 0, SRCCOPY); //This BitBlt's speed is very fast
第一次調(diào)用BitBlt,可以看作是初始化,我們計(jì)算第二個(gè)BitBlt的耗時(shí),只有1 - 2ms,非常不錯(cuò),經(jīng)過(guò)分析,我認(rèn)為原因應(yīng)該是這樣(不一定全對(duì),如有不妥請(qǐng)讀者指出):
只有在位圖格式完全一致的情況下,BitBlt才能執(zhí)行真正的內(nèi)存拷貝,否則是要經(jīng)過(guò)轉(zhuǎn)換的,轉(zhuǎn)換是需要消耗CPU時(shí)間的,所以慢。
那如何知道位圖的格式呢?用GetObject可以看出來(lái):
如上圖所示,bmp是從png文件加載進(jìn)來(lái)的位圖的信息,而bmp2是用CreateCompatibleBitmap創(chuàng)建的位圖的信息,從這我們能看到,前者是24bit位圖,即一個(gè)像素用3個(gè)字節(jié)表示,而后者是16bit位圖,一個(gè)像素用兩個(gè)字節(jié)來(lái)表示,這個(gè)BitBlt執(zhí)行過(guò)程中,就需要轉(zhuǎn)換了,因此耗時(shí)。而實(shí)際上位圖的差別可能比這個(gè)還要復(fù)雜些,如果再討論設(shè)備無(wú)關(guān)位圖,那就說(shuō)不完了……
總而言之,為了提高效率,我們要想方設(shè)法把加載進(jìn)來(lái)的位圖轉(zhuǎn)變?yōu)樵O(shè)備兼容位圖,繪制的時(shí)候直接BitBlt這些設(shè)備兼容位圖,來(lái)實(shí)現(xiàn)位圖的高效繪制。
前面討論的主要是BitBlt,那對(duì)于別的幾個(gè)Blt函數(shù)呢?我都嘗試過(guò)了,除了AlphaBlend之外,兼容位圖到設(shè)備的Blt速度上都有顯著的提高,而AlphaBlend則無(wú)法正常工作,因?yàn)榧嫒菸粓D不帶Alpha通道,而AlphaBlend貌似需要32bit的ARGB格式的位圖方可正常工作,這個(gè)問(wèn)題我思考了好久都無(wú)解,如果哪位讀者對(duì)提高AlphaBlend的工作效率有心得,不妨跟我聯(lián)系下,我正急需這方面的技術(shù)資料。
因此,我給出這樣的結(jié)論,階段性結(jié)論:從文件(或資源)加載位圖后,把位圖轉(zhuǎn)為設(shè)備兼容位圖,這樣使得BitBlt在執(zhí)行SRCCOPY的時(shí)候直接使用內(nèi)存拷貝,速度很快,即便需要拉伸壓縮或者摳色等運(yùn)算,使用兼容位圖的速度也是相當(dāng)不錯(cuò)的,而使用AlphaBlend的時(shí)候,如果需要較高的效率,就應(yīng)從設(shè)計(jì)上避免繪制大幅位圖,改用小幅位圖,在不必要對(duì)每一幀都執(zhí)行Alpha混合的時(shí)候,就避免執(zhí)行,以免影響畫面的流暢性。
posted on 2010-06-20 20:04 Jiang Guogang 閱讀(3324) 評(píng)論(6) 編輯 收藏 引用 所屬分類: Windows Embedded Programming


