透明位圖
摘要
這篇文章討論了在 Microsoft Windows 圖形環(huán)境中用位圖達(dá)到透明和屏蔽效果的幾種方法,包括通過(guò)仿真和使用特殊的驅(qū)動(dòng)器功能。包含其中的一個(gè)小樣本應(yīng)用程序 TRANSBLT 詳細(xì)闡明了這篇文章討論的大多數(shù)方法。
介紹
使用透明(TRANSPARENT)背景模式(用SetBrMode函數(shù)設(shè)置),一個(gè)應(yīng)用程序就可以用透明文本,透明風(fēng)格的線條和透明形狀的刷子。令人悲傷的是,Microsoft Windows圖形環(huán)境并沒(méi)有為透明位圖提供一個(gè)簡(jiǎn)單的接口。(是的,它提供了,但是并沒(méi)有對(duì)它進(jìn)行廣泛的支持,就象在下文中“容易的位圖透明性”中提到的)。幸運(yùn)的是,通過(guò)使用一個(gè)屏蔽位圖和幾次調(diào)用具有經(jīng)過(guò)仔細(xì)選擇的光柵操作的BitBlt,一個(gè)應(yīng)用出現(xiàn)可以模仿這種效果。
到底什么是透明的位圖呢?它是一個(gè)位圖,通過(guò)它目的文件的一部分仍然可以看得見(jiàn)。一個(gè)簡(jiǎn)單的例子就是類似于控制面板圖象等基于Windows的圖象。控制面板圖象本身基本上是個(gè)長(zhǎng)方形。當(dāng)它被最小化時(shí),通過(guò)這個(gè)長(zhǎng)方形圖象位圖的部分可以看見(jiàn)桌面。從理想化的角度講,這圖象位圖被設(shè)計(jì)成長(zhǎng)方形其中有些象素被指定為透明的以至于當(dāng)位圖被使用,那些象素就不會(huì)擋住目的文件。透明的位圖可以通過(guò)移動(dòng)的、非矩形圖象變得更為有趣。下面將要描述得模仿方式可以用來(lái)完成這些透明效果。
符號(hào)
這篇文章使用透明和不透明這兩個(gè)詞來(lái)描繪源位圖中得象素。透明象素是那些不會(huì)影響目標(biāo)文件的象素。不透明象素是那些畫(huà)在目標(biāo)文件上并取代該位置上原來(lái)的東西的象素。
白色和黑色分別被假定為全1和全0的值。這在所有已知的Windows顯示驅(qū)動(dòng)器上都是正確的,包括調(diào)色板設(shè)備。
基本的操作涉及到從源文件到目標(biāo)文件 的塊傳遞,額外的與單色屏蔽有關(guān)的塊傳遞也是需要的。源文件和目標(biāo)文件由他們的設(shè)備上下文代表hdcSrc和hdcDest即可以是位圖也可以是設(shè)備表面本身。有hdcMask提到的屏蔽被假定為被選進(jìn)兼容DC的單色位圖。
背景概念
在討論實(shí)際的透明模仿之前,我們應(yīng)該定義和復(fù)習(xí)一些基本圖形概念。
光柵操作
BitBlt函數(shù)的最后一個(gè)參數(shù)指定了一個(gè)光柵操作(ROP),它明確定義了如何將源文件、目標(biāo)文件和模式(由現(xiàn)在選出的刷子畫(huà)筆定義)的位組合去形成一個(gè)目標(biāo)文件。因?yàn)橐粋€(gè)位圖只是一個(gè)位值的集合,光柵操作(ROP)只是一個(gè)在位上操作的布爾等式。相應(yīng)使用的設(shè)備,位圖中的代表不同的事物。
- 在多色設(shè)備上,每個(gè)象素由一個(gè)位集合代表,他們要么形成一個(gè)指向顏色的索引,要么直接代表一種顏色。
- 在單色設(shè)備上,每個(gè)象素由一個(gè)位來(lái)代表,0表示黑色并且1代表白色。
對(duì)于所有的設(shè)備類型,光柵操作(ROP)只簡(jiǎn)單地在位展示上進(jìn)行而不考慮他們的實(shí)際意義。
有一個(gè)技巧,就是用一種有意義的方式合并位。在Windows3.1版軟件開(kāi)發(fā)包中的程序員參考,第三頁(yè):消息、結(jié)構(gòu)和宏中的附錄A列舉了256種可能的三重光柵操作(ROP)。光柵操作(ROP)提供了多種合并位圖數(shù)據(jù)的方法,而且你經(jīng)??梢允褂貌恢灰环N的方法得到你想要的效果。這篇文章只討論其中的四種。
預(yù)先定義的名字 |
布爾操作 |
透明仿真中的用途 |
SRCCOPY |
src |
直接將源拷貝到目的 |
SRCAND |
src AND dest |
將目標(biāo)文件中對(duì)應(yīng)于源文件黑色區(qū)域的部分變黑,將對(duì)應(yīng)于白色區(qū)域的部分留著不動(dòng) |
SRCINVERT |
src XOR dest |
將源插入到目標(biāo)。二次使用時(shí),將目標(biāo)恢復(fù)到它原來(lái)的狀態(tài)。在某種條件下可以代替SRCPAINT 操作 |
SRCPAINT |
src OR dest |
將源文件中的非白色區(qū)域刷到目標(biāo)文件中。源中的黑色區(qū)域不轉(zhuǎn)換到目標(biāo)中。 |
一些打印機(jī)不支持某些光柵操作,尤其是那些涉及到目標(biāo)文件的光柵操作。因?yàn)檫@一點(diǎn),這篇文章中描寫(xiě)的技巧特地以顯示為目的而且有可能在某些打印機(jī)設(shè)備上不能工作,比如PostScript打印機(jī)。
透明屏蔽
在這篇文章中,“面具”一詞不是指蝙蝠俠戴在臉上的東西。它指的是一個(gè)限制其他位圖可見(jiàn)部分的一個(gè)位圖。“面具”有兩個(gè)控件:不透明部分(黑色),在這一部分源位圖是可見(jiàn)的和透明部分(黑色),在這一部分目標(biāo)文件保持未動(dòng)。因?yàn)椤懊婢摺敝挥蓛煞N顏色 組成,所以它可以很方便地由一種單色位圖代表,雖然它可以是一個(gè)黑白色位圖 。就象在下面的“真正的屏蔽方法”和“使源文件變黑的方法”中要討論的,數(shù)據(jù)塊傳遞屏蔽被用作多數(shù)數(shù)據(jù)塊傳遞處理的一部分,它的源位圖的最終透明數(shù)據(jù)塊傳遞設(shè)置目標(biāo)文件。TRANSBLT樣本應(yīng)用程序使用帶設(shè)置為1的透明色素和設(shè)置為0的非透明色素的單色屏蔽。如果需要應(yīng)用程序可以轉(zhuǎn)換這兩個(gè)值,并將在這一部分中的一些將要描述的單色到彩色的轉(zhuǎn)換中進(jìn)行補(bǔ)充。
除了為透明性提供方便以外,屏蔽在模仿復(fù)雜的剪裁操作時(shí)也很有用。這種剪裁操作不能被有效地在使用區(qū)域中處理。一個(gè)被屏蔽的數(shù)據(jù)塊傳遞的網(wǎng)狀效應(yīng)將裁減掉源位圖的一部分。比如:要只顯示位圖中的一個(gè)圓形區(qū)域,可創(chuàng)建一個(gè)象源文件一樣大小的屏蔽并且在相應(yīng)的區(qū)域畫(huà)一個(gè)透明位的圓形。執(zhí)行這一被屏蔽的數(shù)據(jù)塊傳遞的機(jī)制將在后面的“真正的屏蔽方法”和“使源文件變黑的方法”中描述。
單色到 彩色的轉(zhuǎn)換
透明仿真也牽涉到基于Windows的單色位圖到彩色位圖的轉(zhuǎn)換機(jī)制。反之亦然?;赪indows的文本前景色和背景色的概念被用于在兩種格式之間進(jìn)行轉(zhuǎn)換。在對(duì)一彩色目標(biāo)文件進(jìn)行位傳遞操作的時(shí)候,一個(gè)單色的源位圖(和/或當(dāng)可應(yīng)用時(shí)一個(gè)畫(huà)刷)在實(shí)際的光柵操作(ROP)中在這一位上實(shí)現(xiàn)之前被轉(zhuǎn)換成背景色。相反的,當(dāng)目標(biāo)文件是單色時(shí),Windows 把彩色源轉(zhuǎn)換成單色。在這種情況下,彩色位圖中所有和背景色一致的象素都變成1,其他的象素都被轉(zhuǎn)換成0。因?yàn)橄旅嫠婕暗降睦佣际褂脝紊帘?,所以?duì)一個(gè)應(yīng)用程序而言,在執(zhí)行塊操作之前正確地設(shè)置背景色和前景色是非常重要的。
性能和屏幕閃爍
增強(qiáng)的位圖操作變得比較慢的原因完全是因?yàn)楸挥绊懙奈坏臄?shù)目。而且當(dāng)直接對(duì)屏幕進(jìn)行操作導(dǎo)致閃爍的事實(shí)又使得這一點(diǎn)更為嚴(yán)重。當(dāng)被影響的區(qū)域增大尺寸時(shí),事情只會(huì)變得更加糟糕,雖然沒(méi)有什么辦法可以魔術(shù)般的提高速度,但是可以通過(guò)使用陰影位圖來(lái)刪除可見(jiàn)的閃爍 。首先,應(yīng)用程序把將要被影響的屏幕區(qū)域復(fù)制到存儲(chǔ)位圖,然后應(yīng)用程序在陰影位圖而不是在屏幕上實(shí)現(xiàn)位操作(例如,透明效果 )。最后,陰影又被復(fù)制到屏幕上。結(jié)果只有一個(gè)塊傳遞影響屏幕,所以閃爍沒(méi)有了。很明顯,兩個(gè)額外的塊操作引起了速度減慢(雖然在有些設(shè)備上存儲(chǔ)器塊傳遞能比已經(jīng)訪問(wèn)了屏幕的塊傳遞要塊一些),但是依靠位圖的尺寸和驚奇的操作,操作可能會(huì)因?yàn)殚W爍消失而讓人感覺(jué)到快了一些。事物也因?yàn)闆](méi)有讓人混亂的閃爍而變得更為清楚。陰影操作是否恰當(dāng)取決于應(yīng)用程序特定的需要。
真正的屏蔽塊傳遞并不需要對(duì)即將有用的源位圖的某部分做任何的修改。被屏蔽的塊傳遞涉及到了步操作并且屏蔽把所有透明的元素都設(shè)置為1,所以不透明的元素設(shè)置為0。下面是有關(guān)基本代碼:
// Set up destination for monochrome blt (only needed for monochrome
// mask). These are the default values and may not need to be
// changed. They should also be restored.
SetBkColor(hdcDest, RGB(255, 255, 255)); // 1s --> 0xFFFFFF
SetTextColor(hdcDest, RGB(0, 0, 0)); // 0s --> 0x000000
// Do the real work.
BitBlt(hdcDest, x, y, dx, dy, hdcSrc, x0, y0, SRCINVERT);
BitBlt(hdcDest, x, y, dx, dy, hdcMask, 0, 0, SRCAND);
BitBlt(hdcDest, x, y, dx, dy, hdcSrc, x0, y0, SRCINVERT);
被屏蔽的塊傳遞處理中的三步操作如下:
- 第一步(帶SRCINVERT的位塊傳遞)將源位圖異或到目標(biāo)文件。這看起來(lái)有點(diǎn)意思,但第二步異或有把目標(biāo)文件恢復(fù)成原始狀態(tài)的效果。
- 第二步(帶SRCAND的位塊傳遞)是一個(gè)屏蔽操作。當(dāng)屏蔽與目標(biāo)文件相與,所有的透明象素都不會(huì)改變目標(biāo)文件的象素,而不透明象素則直接把目標(biāo)文件變?yōu)楹凇,F(xiàn)在目標(biāo)文件中有了一個(gè)源文件的不透明部分給勾勒出來(lái)的圖象,而它自身在透明部分中的異或圖象。
- 第三步(帶SRCINVERT的位塊傳遞)與源文件異或送到目標(biāo)文件。透明象素被恢復(fù)成源狀態(tài)(兩步異或就能做到),不透明象素則季節(jié)從源文件上復(fù)制。
不幸的是,當(dāng)三個(gè)步驟執(zhí)行時(shí),目標(biāo)文件確實(shí)有一陣看起來(lái)相當(dāng)難看,而直接對(duì)屏幕執(zhí)行三次塊傳遞又會(huì)引起屏幕閃爍。
使源文件變黑的方法
只要在創(chuàng)建源位圖時(shí)稍稍計(jì)劃一下,透明性塊傳遞就可以減少到只有兩個(gè)調(diào)用。屏幕仍和上面的例子一樣保持不變,但是源文件則必須在屏蔽的塊傳遞代碼看起來(lái)象這樣:
// Set up destination for monochrome blt. These are the default
// values and may not need to be changed. They should also be
// restored.
SetBkColor(hdcDest, RGB(255, 255, 255)); // 1s --> 0xFFFFFF
SetTextColor(hdcDest, RGB(0, 0, 0)); // 0s --> 0x000000
// Do the real work.
BitBlt(hdcDest, x, y, dx, dy, hdcMask, 0, 0, SRCAND);
BitBlt(hdcDest, x, y, dx, dy, hdcSrc, x0, y0, SRCPAINT);
屏蔽第二次用來(lái)使不透明的象素變黑而保持剩下的不變。然后源文件再這個(gè)的向上與之相或,并在目標(biāo)文件現(xiàn)在為黑的部分上畫(huà)畫(huà)。因?yàn)樵?a target="_blank">文件在想要透明的地方只有黑象素,或操作使得目標(biāo)文件在那些透明區(qū)域保持不變。注意SRCINVERT POP可以在第二個(gè)塊傳遞調(diào)用時(shí)代替SRCPAINT并取得多樣的效果。源屏蔽設(shè)置刪除了的可能性,這是XOR不同于OR的唯一情形。
用這種方法屏幕閃爍變得不再那么引人注目,而且一旦源文件已經(jīng)再正確的位置被設(shè)置為黑,透明性看起來(lái)非常好。Windows也使用這種機(jī)制在屏幕上顯示圖象。圖標(biāo)被分成兩部分存儲(chǔ)在.ICO文件中,這兩部分“XOR MASK”和位圖本身。因?yàn)槲粓D和圖標(biāo)一樣小,所以實(shí)現(xiàn)透明性非常順利。
位圖透明性
位圖透明性通常指的是一種處理,這種處理取出一幅位圖,并使位圖中的一種顏色變?yōu)橥该?,從而?dāng)位圖被塊傳遞到屏幕時(shí),目標(biāo)文件可以通過(guò)位圖的透明色被看見(jiàn)。一個(gè)應(yīng)用程序可以通過(guò)構(gòu)造一個(gè)合適的屏蔽和使用在前面“真正的屏蔽方法”和“使源文件變黑的方法”中描述過(guò)的屏蔽技術(shù)來(lái)模仿這種操作。下面的章節(jié)描述了如何為不能執(zhí)行透明塊傳遞的顯示設(shè)備模仿位圖透明性構(gòu)造一個(gè)屏蔽。
從彩色位圖中構(gòu)造一個(gè)單色屏蔽相當(dāng)容易,因?yàn)槲粔K傳遞的內(nèi)置彩色單色轉(zhuǎn)換自動(dòng)完成所有這些工作。目標(biāo)是一個(gè)所有不透明元素都設(shè)置為0和所有透明元素都設(shè)置為1的屏蔽。把背景色設(shè)置為透明色恰好做到了這一點(diǎn)。沒(méi)有必要設(shè)置文本前景色因?yàn)樗荒苡糜诓噬珕紊D(zhuǎn)換(所以的非背景色素都被設(shè)置成0),下面的代碼完成了這項(xiàng)工作:
SetBkColor(hdcSrc, rgbTransparent);
BitBlt(hdcMask, 0, 0, dx, dy, hdcSrc, x0, y0, SRCCOPY);
代碼構(gòu)造了一個(gè)源文件相當(dāng)于透明色時(shí)為1和其他地方為0的屏蔽。這復(fù)制了上面使用過(guò)的屏蔽。
使用屏蔽
現(xiàn)在是使用上面描述過(guò)的屏蔽方法的時(shí)候了。真正的屏蔽方法不要求額外的工作:創(chuàng)建 屏蔽并且源文件不需要處理。三個(gè)位塊傳遞確實(shí)引起了屏幕閃爍,但現(xiàn)在只有三個(gè)的一個(gè)。
另一方面,使源文件變黑的方法要求對(duì)源位圖做一些額外的工作來(lái)得到正確的輸入方案透明位需要變黑。當(dāng)然,如果透明色一開(kāi)始就是黑色,那位圖就已經(jīng)準(zhǔn)備好要運(yùn)行了。在源文件上把透明色素涂黑非常類似于在目標(biāo)文件上把不透明象素涂黑,并且在使用屏蔽時(shí)已經(jīng)這樣作了,如下所示:
SetBkColor(hdcSrc, RGB(0,0,0)); // 1s --> black (0x000000)
SetTextColor(hdcSrc, RGB(255,255,255)); // 0s --> white (0xFFFFFF)
BitBlt(hdcSrc, x0, y0, dx, dy, hdcMask, 0, 0, SRCAND);
現(xiàn)在有兩個(gè)位塊傳遞被用于透明的位塊傳遞。
一旦現(xiàn)實(shí)的透明位塊傳遞完成了,源位圖應(yīng)該被恢復(fù)到它的初始色:
SetBkColor(hdcSrc, rgbTransparent); // 1s --> transparent color
SetTextColor(hdcSrc, RGB(0,0,0)); // 0s --> black (0x000000)
BitBlt(hdcSrc, x0, y0, dx, dy, hdcMask, 0, 0, SRCPAINT);
因?yàn)樵次粓D必須被更改,然后再恢復(fù),相關(guān)的位塊傳遞的總數(shù)是4。這使得處理變慢,但因?yàn)槲粔K傳遞中的兩個(gè)是在存儲(chǔ)位圖中進(jìn)行而不是在屏幕上作的,相比較于真正的屏幕方法屏幕閃爍減少了。如果源位圖能保持透明位被設(shè)置為黑色,則兩個(gè)轉(zhuǎn)換塊傳遞可以一起被避免而且也只需要兩個(gè)塊傳遞用于操作;這事實(shí)上是動(dòng)畫(huà)制作必要的。
簡(jiǎn)易的位圖透明性
有些設(shè)備驅(qū)動(dòng)器直接支持塊傳遞。一個(gè)驅(qū)動(dòng)器指示使用CAPSI能力的CI-TRANSPARENT的能力由GetDevicePaps函數(shù)返回。一個(gè)特殊的背景模式,NEWTRANSPARENT指示后來(lái)的塊傳遞是透明的塊傳遞。目標(biāo)文件的現(xiàn)在的背景色是透明色。當(dāng)這個(gè)能力適用于驅(qū)動(dòng)器時(shí),基本的透明塊傳遞可以按下面的代碼執(zhí)行:
// Only attempt this if device supports functionality.
if(GetDeviceCaps(hdcDest, CAPS1) & C1_TRANSPARENT)
{
// Special transparency background mode
oldMode = SetBkMode(hdcDest, NEWTRANSPARENT);
rgbBk = SetBkColor(hdcDest, rgbTransparent);
// Actual blt is a simple source copy; transparency is automatic.
BitBlt(hdcDest, x, y, dx, dy, hdcSrc, x0, y0, SRCCOPY);
SetBkColor(hdcDest, rgbBk);
SetBkMode(hdcDest, oldMode);
}
這確實(shí)使得事情變得簡(jiǎn)單了。不幸的是,目前沒(méi)有多少設(shè)備驅(qū)動(dòng)器支持透明的塊傳遞那些用Windows3.1版本安裝的塊傳遞沒(méi)有這個(gè)功能。這應(yīng)該在不遠(yuǎn)的將來(lái)有所改變。
并且目前,WINDOWS。H并不包含對(duì)任意這些新變量的定義。取而代之的是MMSYSTEM。H文件提供定義,這個(gè)文件能Windows3.1版本的軟件開(kāi)發(fā)包(SDK)中找到。
帶有設(shè)備獨(dú)立位圖(DIB)的透明性
如果源位圖是設(shè)備獨(dú)立位圖(DIB)格式,那么,整個(gè)屏蔽處理可以通過(guò)用一個(gè)設(shè)備獨(dú)立位圖(DIB)當(dāng)作源文件。屏蔽和對(duì)色彩表的簡(jiǎn)單操作就可以大大地獲得簡(jiǎn)化了。處理過(guò)程與上面討論的相同,處理應(yīng)用程序可通過(guò)改變色彩表執(zhí)行所有的彩色單色和單色彩色的轉(zhuǎn)換,如下:
save a copy of the color table;
// Build the mask.
for (every color in the color table)
{
if (color == rgbTransparent)
color = white;
else
color = black;
}
// Prepare destination by blting the mask.
StretchDIBits(hdcDest, lpDIB, SRCAND); // (Yes, there are more
// parameters.)
// Now prepare "blacked out" source for the mask blt.
for (every color in the color table)
{
if (color == white) // (white from above change)
color = black;
else
color = original color from color table;
}
// Transparently blt the source.
StretchDIBits(hdcDest, lpDIB, SRCPAINT); // (Yes, there are more
// parameters.)
// To restore DIB to original state, restore original color table.
這種方法需要注意的關(guān)鍵之處在于只需要位圖的一個(gè)備份因?yàn)樗揽可时磙D(zhuǎn)換即充當(dāng)屏幕又充當(dāng)源文件。不管,把設(shè)備獨(dú)立位圖(DIB)格式轉(zhuǎn)換成設(shè)備依賴格式的額外不便仍然存在。
from: http://www.chinaitpower.com/A/2001-10-04/680.html
posted on 2006-07-24 23:54 weidagang2046 閱讀(333) 評(píng)論(0) 編輯 收藏 所屬分類: Windows