深入GDI图像显示
| 深入GDI圖像顯示 |
| ? |
| 摘? 要:本文首先給出了一種結(jié)合了DIB和DDB兩種位圖優(yōu)點(diǎn)的圖像顯示方法,其次對GDI函數(shù)的高級應(yīng)用,如透明位圖顯示、圖像旋轉(zhuǎn)顯示、圖像鏡像顯示進(jìn)行了研究。 關(guān)鍵詞:GDI圖像顯示,特殊GDI函數(shù)的應(yīng)用,透明位圖顯示,圖像幾何變換顯示 ? ??? 圖像信息是人類認(rèn)識世界的重要知識來源,人類獲得的70%以上的信息來自于眼睛攝取的豐富和真切的圖像。圖像與計(jì)算機(jī)相結(jié)合帶給人們近乎神奇的圖像藝術(shù)。對于程序開發(fā)者來說,實(shí)現(xiàn)高速的繪圖是設(shè)計(jì)漂亮友好的用戶界面的基礎(chǔ)和關(guān)鍵所在。 ??? 在Win32圖像程序設(shè)計(jì)中,圖像顯示的方法主要有:GDI,DirectDraw,OpenGL等技術(shù),本文僅討論利用GDI函數(shù)實(shí)現(xiàn)位圖的顯示。Windows使用的位圖有兩種:設(shè)備無關(guān)位圖DIB(Device IndependentBitmap)和設(shè)備相關(guān)位圖DDB(Device Dependent Bitmap)。DDB位圖一般用HBITMAP句柄或CBitmap對象保存,Windows提供的創(chuàng)建DDB位圖的函數(shù)主要有CreateCompatibleBitmap()、CreateBitmap()和CreateBitmapIndirect()。出于效率的關(guān)系,一般創(chuàng)建彩色位圖用CreateCompatibleBitmap()函數(shù),因?yàn)樗鼊?chuàng)建的位圖格式與顯示DC的格式一致,用SelectObject()選入DC和顯示的速度要快,另外兩個(gè)函數(shù)多用來創(chuàng)建單色位圖。也可以創(chuàng)建與顯示DC不同格式的位圖,但這樣灰使得顯示速度略為變慢。 ??? 由于DDB位圖的設(shè)備相關(guān)性,因此它在顯示速度方面有優(yōu)勢,但是同時(shí)也限制了DDB位圖只能在相關(guān)DC上才能有效顯示。而這時(shí)DIB位圖的優(yōu)勢就體現(xiàn)出來了,DIB位圖具有良好的設(shè)備無關(guān)性,可以包含諸如調(diào)色板、分辨率等信息。可以使得應(yīng)用程序獨(dú)立于操作系統(tǒng),因此得到了廣泛的應(yīng)用。在Win32中DIB位圖的顯示技術(shù)有了較大的提高,與DDB位圖相比,兩者差別并不是很明顯。但是可以對DDB位圖操作的GDI函數(shù)遠(yuǎn)遠(yuǎn)多于DIB位圖。另外獲取和操作DDB位圖數(shù)據(jù)比較麻煩,而DIB位圖可以采用直接分配內(nèi)存來保存,因此在位圖數(shù)據(jù)獲取和操作上具有一定的優(yōu)勢。本文給出一種充分結(jié)合了兩者優(yōu)點(diǎn)的圖像顯示技術(shù),并且深入研究了諸如透明位圖顯示,圖像旋轉(zhuǎn)顯示、鏡像顯示、梯度填充等GDI高級功能應(yīng)用。 ? 一、位圖顯示新方法 ? ??? 用于操作DIB圖像的應(yīng)用類有許多,筆者在"電腦編程與技巧"雜志99年第10期, 介紹了一個(gè)封裝的通用圖像基類(CImage),可完成DIB圖像數(shù)據(jù)的管理和一些基本處理功能。其中大多的DIB類都采用直接分配內(nèi)存的方式,然后用DIB操作函數(shù)來實(shí)現(xiàn)圖像的顯示。這種方式在Win98中,DIB操作函數(shù)將DIB內(nèi)存直接寫入顯示內(nèi)存中,但是在NT中,操作系統(tǒng)首先將DIB拷貝到服務(wù)器端創(chuàng)建一個(gè)DDB,然后再將DDB內(nèi)存寫入顯存中,因此圖像顯示速度就會變慢。具體的原理有興趣的讀者可以參閱MSDN種的技術(shù)文獻(xiàn)中關(guān)于GDI操作的文章"Win32 動畫原理"。 ??? 在Win98/NT中提供了一個(gè)新的函數(shù)CreateDIBSection(),可以在客戶與服務(wù)器之間創(chuàng)建一個(gè)存儲DIB位圖的公共內(nèi)存區(qū)給GDI。可以在該內(nèi)存上執(zhí)行各種GDI操作(包括利用BitBlt()函數(shù)直接輸出到顯存);另外可以直接訪問該內(nèi)存。這樣就可以提高DIB位圖的顯示速度。但是可以看到極少數(shù)介紹這種方法的書籍上采用的方法是:先分配DIB內(nèi)存讀入位圖,然后用獲得位圖信息再利用CreateDIBSection()函數(shù)分配內(nèi)存,將數(shù)據(jù)拷貝到其中,最后將先分配DIB內(nèi)存刪除。這種方法過程繁瑣就不說了,如果遇到調(diào)入的位圖有幾十兆,那速度將慢的驚人。 ??? 本文采用的顯示方法是:分配DIB內(nèi)存就用CreateDIBSection()函數(shù),然后用該函數(shù)返回的HBITMAP結(jié)構(gòu)變量將位圖連接到一個(gè)CBitmap變量中。這樣在圖像顯示的時(shí)候,建立一個(gè)與當(dāng)前DC兼容的DC,然后將位圖選入用BitBlt()或StretchBlt()函數(shù)來顯示。這種方法不僅顯示圖像的速度快,而且可以直接獲得圖像數(shù)據(jù),對于圖像處理應(yīng)用程序,可以提高圖像顯示和處理的性能。該方法是基于通用圖像基類(CImage)來實(shí)現(xiàn)的, ? 關(guān)于該基類的介紹,可參見“一個(gè)通用圖像基類”一文。具體的實(shí)現(xiàn)代碼如下: ??? 在類的定義中加入下面的變量 ??? HBITMAP hBitmap;? // 位圖句柄 ??? CBitmap m_Bitmap; // DDB位圖變量 ??? CDC *BMP_DC;? // 兼容的DC ??? CBitmap *m_lpOldBmp; // 存放舊圖像的指針 ??? 在類的構(gòu)造函數(shù)中加入如下代碼,建立兼容的DC ??? BMP_DC = new CDC; ??? BMP_DC->CreateCompatibleDC( NULL ); ??? 在分配內(nèi)存的函數(shù)中加入下面的代碼,位圖信息頭m_lpDibInfo要首先獲得。 ??? // 用CreateDIBSection()分配圖像數(shù)據(jù)內(nèi)存 ??? hBitmap = CreateDIBSection( BMP_DC->m_hDC,// 兼容DC句柄 ??????????????? m_lpDibInfo,// 位圖信息頭?????????? ??????????????? DIB_RGB_COLORS, // 色彩類型 ??????????????? (void **)&m_lpDibArray, // 數(shù)據(jù)內(nèi)存指針 ??????????????? NULL, 0 ); ??? // 如果內(nèi)存分配成功,將它連接到一個(gè)CBitmap變量中 ??? if( hBitmap != NULL ) ??? { ??????? m_Bitmap.Attach( hBitmap ); ??????? BMP_DC->SelectObject( m_Bitmap ); ??? } ??? // 如果內(nèi)存分配失敗,用new來分配內(nèi)存 ??? else ??? { ??????? // 分配圖像數(shù)據(jù)內(nèi)存 ??????? m_lpDibArray = new BYTE[ m_ImageSize ]; ??????? // 初始化圖像數(shù)據(jù)內(nèi)存 ??????? memset( m_lpDibArray, 0, m_ImageSize ); ??????? // 如果分配失敗,報(bào)錯(cuò) ??????? if( m_lpDibArray == NULL )? AfxThrowMemoryException(); ??? } ? ??? 其中CreateDIBSection()函數(shù)第三個(gè)參數(shù)指明色彩類型,一般有兩種DIB_RGB_COLORS和DIB_PAL_COLORS。在Win98操作系統(tǒng)下,對于壓縮格式為BI_BITFIELDS類型的BMP位圖(一般為16位或32位有色彩掩碼位圖)應(yīng)用參數(shù)DIB_PAL_COLORS,而對于壓縮格式為BI_RGB類型的BMP位圖用的參數(shù)應(yīng)為DIB_RGB_COLORS。但是在NT或Win2000操作系統(tǒng)下,該參數(shù)只能為DIB_RGB_COLORS,否者在為16位或32位有色彩掩碼位圖分配內(nèi)存時(shí)會出錯(cuò)。由于筆者開發(fā)程序基于Win2000操作系統(tǒng),因此該參數(shù)就直接用DIB_RGB_COLORS。 ? ??? 獲取圖像的數(shù)據(jù)可用指針m_lpDibArray,顯示圖像可用BitBlt()或StretchBlt()函數(shù)來完成。 下面給出的顯示函數(shù),可以實(shí)現(xiàn)圖像放大和大型位圖的顯示,在視類的OnDraw()函數(shù)中調(diào)用,參數(shù)僅為當(dāng)前DC指針和客戶區(qū)的大小,其余計(jì)算滾動位置和源圖范圍的工作均由該函數(shù)完成。 BOOL CImage::BestBlt(CDC *pMpDc, CRect ClientRect, BOOL IsFull) { ??? // 將設(shè)備坐標(biāo)轉(zhuǎn)換位邏輯坐標(biāo) ??? pMpDc->DPtoLP( &ClientRect ); ??? // 設(shè)置Blt的方式 ??? pMpDc->SetStretchBltMode( COLORONCOLOR ); ? ??? // 計(jì)算對應(yīng)客戶區(qū)的圖像區(qū)大小 ??? int ClientW = ClientRect.Width(); ??? int ClientH = ClientRect.Height(); ? ??? CPoint BMPLUP, BMPRBP; ? ??? if( IsFull ) ??? { ??????? BMPLUP.x = BMPLUP.y = 0; ??????? BMPRBP.x = m_ImageWidth; ??????? BMPRBP.y = m_ImageHeight; ??? } ??? else ??? { ??????? BMPLUP.x = (int) (ClientRect.left?? / m_fScale + 0.5); ??????? BMPLUP.y = (int) (ClientRect.top??? / m_fScale + 0.5); ??????? BMPRBP.x = (int) (ClientRect.right? / m_fScale + 0.5); ??????? BMPRBP.y = (int) (ClientRect.bottom / m_fScale + 0.5); ??????? ??????? if( BMPRBP.x > m_ImageWidth ) ??????? { ??????????? BMPRBP.x? = m_ImageWidth; ??????? } ??????? if( BMPRBP.y > m_ImageHeight ) ??????? { ??????????? BMPRBP.y? = m_ImageHeight; ??????? } ??? } ??? ??? // 顯示圖像 ??? return pMpDc->StretchBlt(ClientRect.left, ??????????????? ClientRect.top, ??????????????? ClientRect.Width(), ??????????????? ClientRect.Height(), ??????????????? BMP_DC, ??????????????? BMPLUP.x, ??????????????? BMPLUP.y, ??????????????? BMPRBP.x - BMPLUP.x, ??????????????? BMPRBP.y - BMPLUP.y, ??????????????? SRCCOPY); } ??? 上面的位圖顯示方法不僅可以直接獲得圖像數(shù)據(jù),而且圖像又可以作為一個(gè)CBitmap變量,CBitmap變量作為GDI對象可以靈活地被各種GDI函數(shù)調(diào)用,因此次方法結(jié)合了DIB和DDB兩種位圖優(yōu)點(diǎn),實(shí)現(xiàn)了位圖靈活、快速的顯示。 ? 二、透明位圖的顯示方法 ? ??? 在許多動畫程序中,精靈(sprite)的出現(xiàn)給程序增色許多。精靈的顯示就涉及到透明位圖的顯示問題,其實(shí)精靈實(shí)際上視一幅矩形位圖,只是背景是固定的顏色,類似于電影拍攝中的藍(lán)幕技術(shù)。下面就給出幾種能實(shí)現(xiàn)透明位圖的顯示方法。 ? ??? 1.直接修改位圖數(shù)據(jù) ? ??? 許多程序是用編制的DIB圖像類來實(shí)現(xiàn)圖像的顯示,如果直接改變圖像內(nèi)存的數(shù)據(jù),如果源圖像上點(diǎn)的顏色等于背景色,就不修改目標(biāo)圖像,否則用源圖像點(diǎn)替代目標(biāo)圖像點(diǎn)。這樣就可以實(shí)現(xiàn)精靈圖像的顯示。 ??? 這種方法有兩個(gè)缺點(diǎn):一是要求精靈圖像較小,二是對于源圖像和目標(biāo)圖像格式不同的情況處理起來比較困難,而且要求程序員對程序進(jìn)行必要的優(yōu)化以加快圖像更新速度,因此這種方法一般不被采用。 ? ??? 2.用TransparentBlt()函數(shù)實(shí)現(xiàn) ? ??? 對于Win98或NT5.0操作系統(tǒng),提供了一個(gè)函數(shù)TransparentBlt(),可以實(shí)現(xiàn)透明位圖的顯示。該函數(shù)類似于StretchBlt()函數(shù),支持縮放操作,但不支持鏡像操作。具體用法如下: ??? TransparentBlt( pDC->m_hDC, // 目標(biāo)設(shè)備環(huán)境句柄 ??????????? 10, // 目標(biāo)矩形區(qū)域的左上角x坐標(biāo) ??????????? 70, // 目標(biāo)矩形區(qū)域的左上角y坐標(biāo) ??????????? w,? // 目標(biāo)矩形區(qū)域?qū)挾?/span> ??????????? h,? // 目標(biāo)矩形區(qū)域高度 ??????????? BackDC.m_hDC, // 源圖像的設(shè)備環(huán)境句柄 ??????????? 0, // 源圖像的左上角x坐標(biāo) ??????????? 0, // 源圖像的左上角y坐標(biāo) ??????????? w, // 源圖像的寬度 ??????????? h, // 源圖像的高度 ??????????? BackColor );// 透明區(qū)域的顏色值 ? ??? 注意要使用該函數(shù)必須要將MSIMG32.LIB庫連接到工程中,可以通過在相應(yīng)CPP文件中加入下面的代碼來將該庫連接進(jìn)工程: ? ??? #pragma comment( lib, "MSIMG32.LIB") ? ??? 3.用MaskBlt()函數(shù)實(shí)現(xiàn) ? ??? MaskBlt()函數(shù)通過一個(gè)單色模板位圖和相應(yīng)的光柵操作碼來實(shí)現(xiàn)透明位圖得顯示。對于模板圖像上顏色值為“1”的點(diǎn),采用前景光柵碼來操作,而對于顏色值為“0”的點(diǎn),采用背景光柵碼來操作。如果前景光柵操作碼光柵操作碼為0x00AA0029(顯示目標(biāo)圖像點(diǎn),具體含義可參見MSDN),只要制作好單色模板位圖就可以用MaskBlt()函數(shù)來實(shí)現(xiàn)透明位圖的顯示。實(shí)現(xiàn)的代碼如下: ??? ??? // 設(shè)置位圖兼容DC的背景顏色為透明色 ??? BackDC.SetBkColor( BackColor ); ??? // 生成模板位圖 ??? MaskDC.BitBlt( 0, 0, w, h, &BackDC, 0, 0, NOTSRCCOPY ); ??? // 顯示模板位圖 ??? pDC->BitBlt(70, 10, 48, 48, &MaskDC, 0, 0, SRCCOPY); ??? // 生成MaskBlt透明顯示所需的光柵操作碼 ??? DWORD dwFore = SRCCOPY;???? // 前景光柵碼 ??? DWORD dwBack = 0x00AA0029;? // 背景光柵碼 ??? DWORD dwRop4 = MAKEROP4( dwFore, dwBack ); ??? // 顯示透明位圖 ??? pDC->MaskBlt(10, 70, w, h, &BackDC, 0, 0, MaskBmp, 0, 0, dwRop4); ? ??? 生成單色模板位圖的方法比較簡單,首先將兼容DC的背景色設(shè)為透明色,然后用BitBlt()函數(shù)將透明圖像寫入模板圖像兼容DC中,就可以得到所需的單色模板位圖。注意選用不同的光柵操作碼得到的結(jié)果是不同的。如果選用SRCCOPY如果源圖像的顏色與目標(biāo)的背景色相同,則相應(yīng)單色圖的輸出為白色1,否則為黑色0,這里要選用NOTSRCCOPY光柵操作碼才能得到所需的模板。 ? ??? 4.用CImageList類實(shí)現(xiàn) ? ??? CImageList類是用來管理相同大小的圖標(biāo)和位圖的類,可以實(shí)現(xiàn)圖標(biāo)和位圖的透明顯示,因此可以用它來實(shí)現(xiàn)位圖的透明顯示,方法如下: ? ??? CImageList? m_ImageList; // 定義CImageList類的對象 ??? // 將位圖裝入,設(shè)置透明色 ??? m_ImageList.Create( IDB_BITMAP_TRANS, 48, 1, BackColor ); ??? // 顯示透明位圖 ??? m_ImageList.Draw( pDC, 0, CPoint(10, 70), ILD_TRANSPARENT ); ? ??? 5.用光柵操作碼來實(shí)現(xiàn) ? ?? 用于圖像顯示的三元光柵碼有兩百多個(gè),三元光柵碼指明了源圖像、畫刷和目標(biāo)圖像的顏色的組合操作方式,如果巧妙地利用光柵操作碼可以組合出許多的顯示透明位圖的方法,這里給出一種。 ? ??? // 生成模板位圖 ??? MaskDC.BitBlt( 0, 0, w, h,? &BackDC, 0, 0, SRCCOPY ); ??? // 顯示模板位圖 ??? pDC->BitBlt(70, 10, 48, 48, &MaskDC, 0, 0, SRCCOPY); ? ??? // 將源圖與目標(biāo)圖像進(jìn)行"異或(xor)"運(yùn)算 ??? // d xor s = d|s ??? pDC->BitBlt(10, 70, 48, 48, &BackDC, 0, 0, SRCINVERT); ??? // 將模板與目標(biāo)圖像進(jìn)行 "and" 運(yùn)算 ??? // 透明的部分保持不變,不透明的部分為 0 ??? pDC->BitBlt(10, 70, 48, 48, &MaskDC, 0, 0, SRCAND); ??? // 將源圖與目標(biāo)圖像進(jìn)行"異或(xor)"運(yùn)算 ??? // 0?? xor G = G,? 因此不透明的地方被源圖覆蓋 ??? // d|s xor s = d, 因此透明的地方恢復(fù) ??? pDC->BitBlt(10, 70, 48, 48, &BackDC, 0, 0, SRCINVERT); ? ??? 6.用PlgBlt()函數(shù)實(shí)現(xiàn) ? ??? PlgBlt()函數(shù)可以將源DC中的圖像傳送到目標(biāo)DC上的一個(gè)平行四邊形區(qū)域中,而且可以根據(jù)傳入的單色模板位圖來實(shí)現(xiàn)透明位圖的顯示。該函數(shù)的第一個(gè)參數(shù)是三個(gè)點(diǎn)結(jié)構(gòu)的指針,第四個(gè)點(diǎn)的計(jì)算由函數(shù)內(nèi)部實(shí)現(xiàn),該點(diǎn)的位置如下圖示意。只要計(jì)算好平行四邊形區(qū)域,也可以實(shí)現(xiàn)位圖的旋轉(zhuǎn)顯示,本文將在下面討論。 ? 實(shí)現(xiàn)代碼如下: ? ??????????????? ┏━━━━━━━━━┓ ??????????????? ┃? Pic01.jpg 文件? ┃ ??????????????? ┗━━━━━━━━━┛ ??????????????? 圖1 ?平行四邊形構(gòu)成示意圖 ? ??? // 生成模板位圖 ??? MaskDC.BitBlt(0, 0, w, h, &BackDC, 0, 0, NOTSRCCOPY); ??? // 顯示模板位圖 ??? pDC->BitBlt(70, 10, 48, 48, &MaskDC, 0, 0, SRCCOPY); ? ??? // 計(jì)算顯示的位置 ??? CPoint Pt[3]; ??? Pt[0].x = 10; ??? Pt[0].y = 70; ??? Pt[1].x = Pt[0].x + w; ??? Pt[1].y = Pt[0].y; ??? Pt[2].x = Pt[0].x; ??? Pt[2].y = Pt[0].y + h; ??? ??? // 顯示透明位圖 ??? pDC->PlgBlt(Pt, &BackDC, 0, 0, w, h, MaskBmp, 0, 0); ? 三、圖像的幾何變換顯示 ? ??? 1. 用StretchBlt()函數(shù)實(shí)現(xiàn)圖像鏡像顯示 ? ??? StretchBlt()函數(shù)支持圖像的鏡像顯示,如果將目標(biāo)區(qū)域的高度或?qū)挾热樨?fù)值,就可以實(shí)現(xiàn)圖像的鏡像顯示。 ? ??? // 顯示正常圖像 ??? pDC->StretchBlt(100, 100,? 48,? 48, &BackDC, 0, 0, 48, 48, SRCCOPY); ??? // 顯示水平對稱圖像 ??? pDC->StretchBlt(100, 100, -48,? 48, &BackDC, 0, 0, 48, 48, SRCCOPY); ??? // 顯示垂直對稱圖像 ??? pDC->StretchBlt(100, 100,? 48, -48, &BackDC, 0, 0, 48, 48, SRCCOPY); ??? // 顯示中心對稱圖像 ??? pDC->StretchBlt(100, 100, -48, -48, &BackDC, 0, 0, 48, 48, SRCCOPY); ? ??? 2.用SetWorldTransform()函數(shù)實(shí)現(xiàn)幾何變換顯示??? SetWorldTransform()函數(shù)設(shè)置世界坐標(biāo)與目標(biāo)DC坐標(biāo)之間的二維坐標(biāo)變換,可以實(shí)現(xiàn)圖像的旋轉(zhuǎn)、鏡像、縮放、平移、剪切以及上述各種變換的組合變換。該函數(shù)的第二個(gè)參數(shù)為XFORM結(jié)構(gòu)變量,具體定義讀者可參見MSDN,它的變換方程如下: ? ??????? x' = x * eM11 + y * eM21 + eDx, ??????? y' = x * eM12 + y * eM22 + eDy, ? ??? 可以用CombineTransform()函數(shù)將兩個(gè)變換組合為一個(gè)變換。下面的例子實(shí)現(xiàn)了位圖的旋轉(zhuǎn)顯示,要實(shí)現(xiàn)其它的變換,只需給XFORM結(jié)構(gòu)變量賦予不同的值就可以實(shí)現(xiàn)。另外注意一點(diǎn):只有首先用SetGraphicsMode()函數(shù)將DC的屬性設(shè)為GM_ADVANCED類型,SetWorldTransform()函數(shù)才有效,并且注意文字顯示坐標(biāo)也要隨之變化。 ? ??? // 計(jì)算旋轉(zhuǎn)的參數(shù) ??? double? Angle = 40.0/ 180* 3.1415926; ??? float?? cosAngle = (float)cos( Angle ); ??? float?? sinAngle = (float)sin( Angle ); ? ??? XFORM xform; ??? xform.eM11 = cosAngle; ??? xform.eM12 =-sinAngle; ??? xform.eM21 = sinAngle; ??? xform.eM22 = cosAngle; ? ??? xform.eDx = 0; ??? xform.eDy = 0; ? ??? // 設(shè)置DC的屬性,使得SetWorldTransform()執(zhí)行有效 ??? SetGraphicsMode( pDC->m_hDC, GM_ADVANCED ); ??? // 設(shè)置坐標(biāo)轉(zhuǎn)換方式 ??? SetWorldTransform( pDC->m_hDC, &xform ); ??? // 顯示位圖 ??? pDC->StretchBlt(10, 100, 90, 90, &BackDC, 0, 0, 48, 48, SRCCOPY); ? ??? 3.用PlgBlt()函數(shù)實(shí)現(xiàn)圖像旋轉(zhuǎn)、剪切顯示 ? ??? 在介紹圖像透明已經(jīng)介紹了PlgBlt()函數(shù)的用法,只要按所需計(jì)算好顯示區(qū)域的A、B、C點(diǎn)的坐標(biāo)就可以實(shí)現(xiàn)圖像旋轉(zhuǎn)、剪切顯示。這里要注意如果要顯示全部圖像可以將模板位圖像素值用下面的方法設(shè)為“1”。 ? ??? MaskDC.BitBlt(0, 0, w, h, &BackDC, 0, 0, WHITENESS); ? ??? 4.直接修改位圖數(shù)據(jù) ? ??? 在介紹圖像透明已經(jīng)介紹了這種方法,只要設(shè)計(jì)好算法,這種方法可以實(shí)現(xiàn)更多的變換。比如類似于Photoshop中的各種變換濾鏡,限于篇幅,本文對這種方法就不進(jìn)行討論了。 ? 四、圖像的梯度填充效果實(shí)現(xiàn) ? ??? 編過OpengGL程序的讀者一定知道,在對場景內(nèi)的物體進(jìn)行光滑明暗處理時(shí),如果多邊形的各個(gè)頂點(diǎn)顏色不同,則多邊形內(nèi)部的點(diǎn)就會用Gouraud方法來進(jìn)行平滑,融合處理。以往用GDI函數(shù)來實(shí)現(xiàn)采用的方法一般為:將區(qū)域分為許多小份,用漸變顏色的畫刷進(jìn)行填充。其實(shí)可以用GradientFill()函數(shù)實(shí)現(xiàn)三角形片、三角形扇和矩形區(qū)域的梯度填充,如果與SelectClipRgn()函數(shù)結(jié)合就可以實(shí)現(xiàn)特殊區(qū)域的梯度填充顯示。下面的例子實(shí)現(xiàn)了三角形片和矩形梯度填充。 ??? ??? // 定義三角形的邊長 ??? int w = 100; ? ??? // 定義三角形片的頂點(diǎn)結(jié)構(gòu)變量 ??? TRIVERTEX??????? vert[4]; ??? // 給頂點(diǎn)賦坐標(biāo)和顏色值 ??? vert[0].x?????? =? 10; ??? vert[0].y?????? =? 10; ??? vert[0].Red???? =? 0x0000; ??? vert[0].Green?? =? 0x0000; ??? vert[0].Blue??? =? 0x0000; ??? vert[0].Alpha?? =? 0x0000; ? ??? vert[1].x?????? =? vert[0].x + w; ??? vert[1].y?????? =? vert[0].y; ??? vert[1].Red???? =? 0x0000; ??? vert[1].Green?? =? 0x0000; ??? vert[1].Blue??? =? 0xff00; ??? vert[1].Alpha?? =? 0x0000; ? ??? vert[2].x?????? =? vert[0].x + w; ??? vert[2].y?????? =? vert[0].y + w; ??? vert[2].Red???? =? 0x0000; ??? vert[2].Green?? =? 0x0000; ??? vert[2].Blue??? =? 0xff00; ??? vert[2].Alpha?? =? 0x0000; ? ??? vert[3].x?????? =? vert[0].x; ??? vert[3].y?????? =? vert[0].y + w; ??? vert[3].Red???? =? 0xff00; ??? vert[3].Green?? =? 0xff00; ??? vert[3].Blue??? =? 0xff00; ??? vert[3].Alpha?? =? 0x0000; ? ??? // 指定三角形片的順序 ??? GRADIENT_TRIANGLE ???gTRi[2]; ??? gTRi[0].Vertex1?? = 0; ??? gTRi[0].Vertex2?? = 1; ??? gTRi[0].Vertex3?? = 2; ? ??? gTRi[1].Vertex1?? = 0; ??? gTRi[1].Vertex2?? = 2; ??? gTRi[1].Vertex3?? = 3; ? ??? // 填充三角形片 ??? GradientFill( hdc, vert, 4, &gTRi, 2, GRADIENT_FILL_TRIANGLE ); ? ??? // 定義矩形區(qū)域的左上和右下頂點(diǎn)結(jié)構(gòu)變量 ??? TRIVERTEX??????? Rectvert[2]; ??? // 給頂點(diǎn)賦坐標(biāo)和顏色值 ??? Rectvert[0].x?????? =? vert[0].x + w + 10; ??? Rectvert[0].y?????? =? 10; ??? Rectvert[0].Red???? =? 0x0000; ??? Rectvert[0].Green?? =? 0x0000; ??? Rectvert[0].Blue??? =? 0x0000; ??? Rectvert[0].Alpha?? =? 0x0000; ? ??? Rectvert[1].x?????? =? Rectvert[0].x + w; ??? Rectvert[1].y?????? =? Rectvert[0].y + w; ??? Rectvert[1].Red???? =? 0xFF00; ??? Rectvert[1].Green?? =? 0x0000; ??? Rectvert[1].Blue??? =? 0x0000; ??? Rectvert[1].Alpha?? =? 0x0000; ? ??? // 指定矩形區(qū)域的左上和右下頂點(diǎn)順序 ??? GRADIENT_RECT gRect; ??? gRect.UpperLeft? = 0; ??? gRect.LowerRight = 1; ? ??? // 水平方向梯度填充矩形 ??? GradientFill( hdc, Rectvert, 2, &gRect, 1, GRADIENT_FILL_RECT_H ); ? ??? Rectvert[0].x +=? 10 + w; ??? Rectvert[1].x +=? 10 + w; ??? ??? // 垂直方向梯度填充矩形 ??? GradientFill( hdc, Rectvert, 2, &gRect, 1, GRADIENT_FILL_RECT_V ); ? 五、獲取任何DC中圖像 ? ??? 從DC中獲取圖像有許多的用處,比如將DDB圖像轉(zhuǎn)化位DIB圖像保存,實(shí)現(xiàn)屏幕圖像抓取等。 為了擴(kuò)大CImage類的功能,添加了一個(gè)GetIMGFromDC()函數(shù),可以將傳入的DC中指定區(qū)域的圖像保存到CImage類中。具體的代碼如下: BOOL CImage::GetIMGFromDC(? CDC *pDC, // 傳入的DC指針 ??????????????? int ox,?? // 區(qū)域的左上點(diǎn)x坐標(biāo) ??????????????? int oy,?? // 區(qū)域的左上點(diǎn)y坐標(biāo) ??????????????? int cw,?? // 區(qū)域的寬度 ??????????????? int ch)?? // 區(qū)域的高度 { ??? CBitmap???? bitmap; // 定義位圖 ??? CDC???? memDC;? // 定義兼容DC ? ??? // 創(chuàng)建定義兼容DC ??? memDC.CreateCompatibleDC(pDC); ??? // 創(chuàng)建DDB兼容位圖 ??? bitmap.CreateCompatibleBitmap(pDC, m_ImageWidth, m_ImageHeight); ? ??? // 將兼容位圖選入兼容DC ??? CBitmap* pOldBitmap = memDC.SelectObject( &bitmap ); ??? // 設(shè)置Blt模式 ??? memDC.SetStretchBltMode( COLORONCOLOR ); ??? // 將當(dāng)前DC的圖像寫入兼容DC中 ??? memDC.StretchBlt( 0, 0, m_ImageWidth, m_ImageHeight, pDC, ox, oy, cw, ch, SRCCOPY ); ? ??? // 獲得色彩類型 ??? if( m_lpDibInfo->bmiHeader.biCompression == BI_RGB ) ??????? m_uBltUsage? = DIB_RGB_COLORS; ??? else if( m_lpDibInfo->bmiHeader.biCompression == BI_BITFIELDS ) ??????? m_uBltUsage? = DIB_PAL_COLORS; ? ??? // 從位圖中獲得圖像數(shù)據(jù) ??? GetDIBits ( memDC.m_hDC, HBITMAP( bitmap ), ??????????????? 0, m_ImageHeight, m_lpDibArray, ??????????????? m_lpDibInfo, m_uBltUsage ); ? ??? // 恢復(fù)兼容DC ??? memDC.SelectObject( pOldBitmap ); ??? // 刪除兼容DC ??? memDC.DeleteDC(); ??? // 刪除位圖 ??? bitmap.DeleteObject(); ??? return true; } ??? 如果要將獲取當(dāng)前屏幕的圖像,使用的方法為: ? ??? // 獲得屏幕的尺寸 ??? int ScreenX = ::GetSystemMetrics(SM_CXSCREEN); ??? int ScreenY = ::GetSystemMetrics(SM_CYSCREEN); ? ??? // 定義CImage對象 ??? CImage lhw(ScreenX/ 2, ScreenY/ 2, 16, 0); ??? // 定義一個(gè)當(dāng)前屏幕的DC ??? CClientDC dc( NULL ); ? ??? // 抓取屏幕圖像 ??? lhw.GetIMGFromDC(&dc, 0, 0, ScreenX, ScreenY); ??? 創(chuàng)建的CImage圖像對象的尺寸為屏幕尺寸的一半,該函數(shù)內(nèi)部實(shí)現(xiàn)圖像的縮放。 ??? 以上就是筆者在對GDI函數(shù)在圖像顯示應(yīng)用深入研究后的一點(diǎn)體會,希望對廣大讀者有所幫助。 ? |
總結(jié)
- 上一篇: iOS 平台上常见的安装包有三种,deb
- 下一篇: beam-search及其torch实现