windows
上的窗口絕大多數(shù)是方形的,但是偶爾也會(huì)有一些其他形狀的窗口,如圓形甚至不規(guī)則形狀。這些不常見(jiàn)的窗口是如何做出來(lái)的呢? 有兩種方法可以創(chuàng)建不規(guī)則窗口 1、windows
在很早的時(shí)代就支持不規(guī)則形狀的窗口了。windows
提供了SetWindowRgn
函數(shù)來(lái)設(shè)置窗口的區(qū)域,還提供了一些函數(shù)來(lái)創(chuàng)建橢圓、圓角矩形區(qū)域,還可以自己繪制區(qū)域,但是這種方法有個(gè)缺陷,曲線的邊緣有嚴(yán)重的鋸齒。 2、使用windows2000
以后提供的透明窗口方法來(lái)創(chuàng)建不規(guī)則形狀的窗口。當(dāng)透明窗口的透明部分的透明度為0的時(shí)候次部分就會(huì)完全透明,鼠標(biāo)也可以穿透。所以只要制造出有不規(guī)則的完全透明區(qū)域的窗口后,就可以形成不規(guī)則窗口。 而且可以通過(guò)邊緣抗鋸齒來(lái)實(shí)現(xiàn)平滑的窗口邊緣,消除鋸齒。 本文主要講的就是第二種方法。
之前已經(jīng)有一篇文章介紹了簡(jiǎn)單的透明窗口使用vc++
創(chuàng)建windows
透明窗口,一些基礎(chǔ)內(nèi)容就不在贅述,請(qǐng)自行閱讀。 此次主要用到上一篇文章中沒(méi)有詳細(xì)說(shuō)明的UpdateLayeredWindow
函數(shù),UpdateLayeredWindow
函數(shù)的透明和其他的透明方式不太一樣,一旦使用UpdateLayeredWindow
函數(shù)就不能再使用其他的透明函數(shù), 而且窗口的繪制也完全被UpdateLayeredWindow
接管,此時(shí)正常的繪圖都不再生效,必須通過(guò)UpdateLayeredWindow
來(lái)刷新窗口的界面。 要使用UpdateLayeredWindow
刷新窗口,首先要建立一個(gè)內(nèi)存dc
,然后在dc
上畫(huà)圖,在通過(guò)UpdateLayeredWindow
將dc
上的內(nèi)容根據(jù)參數(shù)做透明處理然后刷新界面。
UpdateLayeredWindow
有9個(gè)參數(shù)。 第一個(gè)參數(shù)是要刷新的窗口句柄,第二個(gè)是要刷新的窗口的 dc
,這個(gè)參數(shù)通常可以傳NULL
,會(huì)自動(dòng)從第一個(gè)參數(shù)的窗口句柄代表的窗口中去獲取。 第三個(gè)參數(shù)是你要刷新的區(qū)域的左上坐標(biāo)點(diǎn),如果是全屏也可以傳NULL
,第四個(gè)參數(shù)是刷新區(qū)域的長(zhǎng)寬,不能傳NULL
。 第五個(gè)參數(shù)就是畫(huà)好圖的內(nèi)存dc
了,第六個(gè)參數(shù)是內(nèi)存dc
上刷新過(guò)去的區(qū)域的左上坐標(biāo)點(diǎn)。 第七個(gè)參數(shù)和第八個(gè)參數(shù)就是控制透明處理的參數(shù),第七個(gè)參數(shù)設(shè)置透明色,也就是這種顏色的區(qū)域全部透明,第八個(gè)參數(shù)是設(shè)置整體透明度。 第九個(gè)參數(shù)控制是第七個(gè)參數(shù)還是第八個(gè)參數(shù)生效或者都生效。 此處要說(shuō)明一下第八個(gè)參數(shù)設(shè)置的整體透明度,他只是在原有的顏色上按照參數(shù)里的比例做透明處理,如果內(nèi)存dc
里的一塊區(qū)域顏色本身就是全透明的,那么無(wú)論怎么設(shè)置參數(shù)這里依然是全透明的。
使用透明窗口來(lái)制造不規(guī)則窗口的關(guān)鍵就在于怎樣制造出包含透明區(qū)域的內(nèi)存dc來(lái)。
眾所周知,png
是支持透明通道的一種圖片格式,所以只要提前做好png
透明圖片,然后運(yùn)行時(shí)加載到內(nèi)存dc上就行了,是不是簡(jiǎn)單爆了。
如果能用圖片那是最好啦,但是有時(shí)候如果窗口的形狀要經(jīng)常變化而且形態(tài)非常多的話,就不能通過(guò)外部圖片了。只能在內(nèi)存中畫(huà)出dc
的圖來(lái)。 gdi+
中的顏色支持了透明色,但是這種透明色指的是畫(huà)刷畫(huà)圖的時(shí)候和背景色做透明,最終畫(huà)在dc
上還是不透明的,并不能達(dá)到我們要的效果。那么怎么辦呢?只能使出終極辦法: 直接修改內(nèi)存中的顏色數(shù)據(jù)。也就是將要透明的區(qū)域先用一種特別的顏色填充好,然后遍歷內(nèi)存中的顏色數(shù)據(jù),將這個(gè)顏色的內(nèi)存數(shù)據(jù)強(qiáng)行修改為完全透明-——將ARGB
的代表A
通道的字節(jié)置零。 由于內(nèi)存dc
并不支持遍歷內(nèi)存數(shù)據(jù),所以一般是建立一個(gè)位圖,然后在這個(gè)位圖上畫(huà)圖標(biāo)記處需要透明的位置,最后遍歷位圖內(nèi)存,將需要透明的位置全部摳掉。托cpu進(jìn)步的福,我摳一副1080p的圖瞬間就可以完成。然后把位圖畫(huà)到dc
上去。 這里有一個(gè)小問(wèn)題,因?yàn)槲粓D中有透明顏色,上次在dc
畫(huà)圖的時(shí)候這部分會(huì)遺留下來(lái)干擾到下次繪圖,所以要每次重畫(huà)之前把dc
內(nèi)容用其他顏色給填掉。 最后就是鋸齒的問(wèn)題了。如果要抗鋸齒的話就要再做一個(gè)特殊處理,首先得明白抗鋸齒的原理,可以Jan之前我寫(xiě)過(guò)的gdi+的畫(huà)圖抗鋸齒原理。 用gdi+
畫(huà)出抗鋸齒的圖后,然后在遍歷的時(shí)候?qū)ふ夷切┯蓛煞N顏色混合成的顏色,然后根據(jù)混合的比例相應(yīng)的轉(zhuǎn)換成半透明度設(shè)置到這個(gè)像素點(diǎn)。通常我用白色和黑色做前景色和背景色,這樣混合變成灰色,灰色的rgb
值除以白色rgb
值的比例就是半透明度。 最后將這樣一幅位圖畫(huà)到dc
上,用UpdateLayeredWindow
函數(shù)刷新到窗口上就會(huì)形成不規(guī)則的窗口了。
畫(huà)出要被扣掉的關(guān)鍵色區(qū)域位圖的代碼:
void SpotlightWindow::DrawMaskImage(Bitmap * pImage){ Graphics graphics(pImage); graphics.FillRectangle(m_pBlackBrush, 0, 0, m_windowRect.Width(), m_windowRect.Height()); graphics.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeAntiAlias); graphics.FillEllipse(m_pKeyBrush, m_ellipseRect.left, m_ellipseRect.top, m_ellipseRect.Width(), m_ellipseRect.Height()); graphics.Flush(Gdiplus::FlushIntentionSync);}遍歷摳掉透明區(qū)域并對(duì)邊緣做半透明處理的代碼:
void SpotlightWindow::MakeTransparentImage(Bitmap * pImage){ Gdiplus::Rect rect(0, 0, pImage->GetWidth(), pImage->GetHeight()); Gdiplus::BitmapData bmpData; pImage->LockBits(&rect, Gdiplus::ImageLockModeWrite | Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, &bmpData); int width = pImage->GetWidth(); int height = pImage->GetHeight(); int stride = bmpData.Stride; unsigned int *pData = reinterPRet_cast<unsigned int *>(bmpData.Scan0); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { unsigned int color = *pData; if (color == WHITE_COLOR) { *pData = TRANSPARENCY_WHITE_COLOR;//設(shè)為透明色 } else if (color != BLACK_COLOR) { *pData = TransportTransparentColor(color);//根據(jù)計(jì)算結(jié)果設(shè)為把半透明顏色 } ++pData; } } pImage->UnlockBits(&bmpData);}計(jì)算半透明的代碼:
unsigned int SpotlightWindow::TransportTransparentColor(unsigned int color){ //像素顏色數(shù)據(jù) unsigned char * pArgb = reinterpret_cast<unsigned char *>(&color); //換算出的透明度 unsigned char transparency = MAX_COLOR-*pArgb; //新的像素顏色 unsigned int transparencyColor = BLACK_COLOR; pArgb = reinterpret_cast<unsigned char *>(&transparencyColor); //設(shè)置新的像素顏色的透明度 pArgb[TRANSPARENCY_INDEX] = transparency; return transparencyColor;}將透明圖片畫(huà)到dc
上并刷新到窗口上的代碼:
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注