函 數 說 明
BrushInfo.lbColor = RGB(255,0,0) 例3∶用純色創建刷子(這個例子中是紅色)
三、拿起和放下畫筆(GDI對象) 現在我感覺好象向一羣繪畫系的學生講課,儘管自己不怎麼會繪畫。首先是練基本功,怎樣拿起畫筆和放下畫筆,這可能是繪畫專業學生首先要學習的吧?當然,更廣義地講應當是怎樣選擇和刪除GDI繪圖對象。
在通過前述方法來創建一個GDI對象句柄(上例中的NewBrush,NewPen等)以後,爲了使用它們,我們必須用SelectObjecth
API函數把它們選入相應的設備場景。一個設備場景在某一時刻、在每一種類型中只能擁有一個對象,如一個畫筆和一個刷子一個位圖等。 OldPen&=SelectObject(Picture1.hDC , NewPen&) 接下來該做什麼呢?對對,這位同學說的對∶繪圖。該怎麼繪圖呢?不,不,不要着急,這個問題,我們留在下一節中討論,現在你只需記住,這裏可以畫些圓呀、矩形呀、添充多邊型呀的操作。那麼,繪圖操作結束以後該怎麼辦呢?答案是∶應當把舊的繪圖對象回設到設備場景中去。如下∶ SelectObject Picture1.hDC,OldPen& 這樣,設備場景將恢復到我們爲其選入繪圖對象以前的狀態。因爲,我們不能斷定其他繪圖函數會使用什麼樣的繪圖對象。因此把原來的繪圖對象放回去乃是一個上策。但,如果你接着要爲設備場景選擇另一個對象,這個步驟可以留在後面進行。那麼在這次的選入過程中就沒有必要保存舊的對象句柄了,這是因爲SelectObject函數返回的舊對象的句柄就是剛纔我們爲其選擇的句柄。 DeleteObject NewPen OK,以下展示了使用GDI對象的API函數。 函 數 說 明 NewPen& = CreatePen(PS_SOLID, 3, RGB(255, 0, 0)) * 創建畫筆
OldBrush& = SelectObject(Picture1.hDC,NewBrush) 四、繪圖屬性與繪圖函數 到目前爲止,我們已經學會了繪圖所需要的一切準備工作。上一節中最後給出的代碼就說明這一點。代碼中,現在只缺少具體的繪圖代碼。本節就討論關於如何繪圖的問題。 線光柵操作∶我們已經知道,光柵操作是一種位操作。通常你想用畫筆進行繪圖時,都假定畫筆色彩只是簡單的繪製到顯示器或設備上。實際上,WINDOWS支持16種不同的線繪圖模式,它們定義了一條線如何與顯示器上已有的信息組合。這些模式就叫做線光柵操作(有時叫ROP2模式)。並且它們被作爲繪圖模式引入到了VisualBasic。ROP2光柵操作相當於設置VB的DrawMode屬性。 當前位置∶在VB中,要畫一條直線其實非常簡單,採用Line方法就可以,而且能夠在一個語句中表達完成。如Line
(5,5)-(10,10) 繪圖屬性控制函數 函 數 說 明
SetBkColor 爲指定的設備場景設置背景顏色。背景顏色用於填充陰影刷子、虛線畫筆以及字符(如背景模式爲OPAQUE)中的空隙。也在位圖顏色轉換期間使用。 同VisualBasic相比較,API提供了功能更強大的繪圖函數。大部分繪圖函數的用法都非常簡單明瞭,只要按其說明使用就可以,覺得沒有必要我多加說明。 WindoesAPI繪圖函數 函 數 說 明
Windows還提供了一些更特殊的繪圖函數,你可以在Windows的內部用它們來繪製控件外框、標題欄、3D控件和桌面等系統對象。 Win32 API其他繪圖函數 函 數 說 明 這裏有幾個函數很有趣,比如DrawEdge、DrawFrameControl。使用他們可以非常輕鬆地繪出按鈕控件、編輯框控件等的外觀。我已經把常用的函數的用法包含到了附帶程序program2.vbp。 五、路 徑 應當說,路徑是較爲高級的話題,儘管它不是難於理解的。我學到的有關路徑的知識,來自於Dan的《Visual
Basic 5.0 WIN32開發人員指南》一書中的不到兩頁的內容中,在其他的書中尚未看到。 我在路徑中體驗出的一個好處就是,創建一個路徑後,可以把它轉換爲區域。這一點可以用PathToRegion函數來完成。一旦這一步成功了就好辦了,得到區域句柄以後就可以和其他區域對象一樣處理了。總而言之,通過路徑我們可以很輕鬆地創建複雜的圖形區域。 dl&
= BeginPath&(Out.hdc) 函數 Windows
NT Windows
95
PolyBezier Yes Yes Polygon Yes Yes 看完這個表,我想Windows95的用戶就可能有點心痛∶這麼多函數用不了!嗨,我也沒辦法,只好責怪微軟了。以下是有關路徑的API函數∶ API 路徑函數 函 數 說 明 |
CreateDIBitmap與CreateDIBSection
HBITMAP CreateDIBitmap(HDC hdc,
CONST BITMAPINFOHEADER *lpbmih,
DWORD fdwlnit,
CONST VOID *lpblnit,
CONST BITMAPINFO *lpbmi,
UINT fuUsage);
HBITMAP LoadBitmapEx( LPCTSTR lpszFile) { if (lpszFile
== NULL) return NULL; HBITMAP hBitmap; HANDLE hf; BITMAPFILEHEADER*
pbmfh; DWORD dwBytesRead,
dwFileSize, dwFileSizeHigh; BOOL bSuccess; //
打開一個bmp文件 hf
= CreateFile(lpszFile, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (
hf == INVALID_HANDLE_VALUE) { TRACE( "Open
file filed with error %d " ,
GetLastError()); return NULL; } //
得到這個文件大小 dwFileSize
= GetFileSize(hf, &dwFileSizeHigh); if (
dwFileSizeHigh ) { CloseHandle(hf); return NULL; } //
分配內存,大小爲該文件的大小 pbmfh
= (BITMAPFILEHEADER*) malloc (dwFileSize); if (
!pbmfh ) { CloseHandle(hf); return NULL; } //
讀取數據 bSuccess
= ReadFile(hf, pbmfh, dwFileSize, &dwBytesRead, NULL); CloseHandle(hf); //
效驗文件大小和文件格式 if (
!bSuccess || dwFileSize != dwBytesRead || pbmfh->bfType != 0x4D42 || pbmfh->bfSize != dwFileSize) { free (( void *)pbmfh); return NULL; } //
進行DIB轉換 hBitmap
= CreateDIBitmap(GetWindowDC(NULL), (BITMAPINFOHEADER*)(pbmfh
+ 1), CBM_INIT, ( BYTE *)pbmfh
+ pbmfh->bfOffBits, (BITMAPINFO*)(pbmfh
+ 1), DIB_RGB_COLORS); free (( void *)pbmfh); return hBitmap; } |
HBITMAP MakeBitmap( HDC hDc,
LPBYTE lpBits,
long lWidth,
long lHeight,
WORD wBitCount) { BITMAPINFO
bitinfo; memset (&bitinfo,
0, sizeof (BITMAPINFO)); bitinfo.bmiHeader.biSize
= sizeof (BITMAPINFOHEADER); bitinfo.bmiHeader.biWidth
= lWidth; bitinfo.bmiHeader.biHeight
= lHeight; bitinfo.bmiHeader.biPlanes
= 1; bitinfo.bmiHeader.biBitCount
= wBitCount; bitinfo.bmiHeader.biCompression
= BI_RGB; bitinfo.bmiHeader.biSizeImage
= lWidth*lHeight*(wBitCount/8); bitinfo.bmiHeader.biXPelsPerMeter
= 96; bitinfo.bmiHeader.biYPelsPerMeter
= 96; bitinfo.bmiHeader.biClrUsed
= 0; bitinfo.bmiHeader.biClrImportant
= 0; return CreateDIBitmap(hDc,
&bitinfo.bmiHeader, CBM_INIT, lpBits, &bitinfo, DIB_RGB_COLORS); } |
函數原型:
HBITMAP CreateDIBSection( HDC hdc, CONST
BITMAPINFO * pbmi, UINT iUsage, VOID *
ppvBits, HANDLE hSection, DWORD dwOffset ); |
參數:
hdc:設備環境句柄。如果iUsage的值是DIB_PAL_COLORS,那麼函數使用該設備環境的邏輯調色板對與設備無關位圖的顏色進行初始化。
pbmi:指向BITMAPINFO結構的指針,該結構指定了設備無關位圖的各種屬性,其中包括位圖的尺寸和顏色。
iUsage:指定由pbmi參數指定的BITMAPINFO結構中的成員bmiColors數組包含的數據類型(要麼是邏輯調色板索引值,要麼是原文的RGB值)。下列值是系統定義的,其含義爲:
值 |
描述 |
DIB_RGB_COLORS |
根據BITMAPINFOHEADER 的biCompression 成員決定BITMAPINFO 結構包含位掩碼還是調色板數組,在呈現位圖時使用該數組值。DIB_RGB_COLORS 可以在任何位數的位圖上使用。 |
DIB_PAL_COLORS |
BITMAPINFO.bmiColors 數組被取消,在呈現位圖時使用目標調色板。DIB_PAL_COLORS只能在8bpp位圖中指定。 |
ppvBits:指向一個變量的指針,該變量接收一個指向DIB位數據值的指針。
hSection:該參數設置爲NULL。
dwOffset:參數取消。
返回值:
成功,返回值是一個指向剛剛創建的設備無關位圖的句柄,並且*ppvBits指向該位圖的位數據值;失敗,那麼返回值爲NULL,並且*ppvBit也爲NULL,若想獲得更多錯誤信息,請調用GetLastError函數。
備註:
系統爲設備獨立位圖分配內存。如果在之後調用DeleteObject來刪除設備獨立位圖,系統自動關閉內句柄。
在Windows CE 2.0及其以後版本,如果圖像是調色板模式(通常是1,2,4和8格式)的,BITMAPINFO 結構中必須包含一個顏色表。對於16bpp或32bpp非調色板圖像,顏色表必須是3個入口的長度,這3個入口必須指定紅、綠、藍色掩碼。 而且,BITMAPINFOHEADER 結構的biCompression 成員應該被設置爲BI_BITFIELDS。 這些位深不支持BI_RGB。 GDI取消24bpp圖像的顏色表,他們的像素必須被存儲爲 藍-綠-紅 (BGR)格式。
Windows CE 不支持332位閾設備。
在Windows CE 1.0 和 1.01版本,pbmi指向的BITMAPINFO結構必須指定每個像素點爲1或2位。
實例:下面這段代碼實現由已存在的圖像裸數據得到CBitmap位圖:
CBitmap
bitmap ; BITMAPINFO
bmpInfo; //創建位圖
bmpInfo.bmiHeader.biSize
= sizeof (BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth
= 480; //寬度 bmpInfo.bmiHeader.biHeight
= 480; //高度 bmpInfo.bmiHeader.biPlanes
= 1; bmpInfo.bmiHeader.biBitCount
= 24; bmpInfo.bmiHeader.biCompression
= BI_RGB; UINT uiTotalBytes
= 480 * 480 * 3; void *pArray
= new BYTE (uiTotalBytes
); HBITMAP hbmp
= CreateDIBSection(NULL, &bmpInfo, DIB_RGB_COLORS, &pArray, NULL, 0); //創建DIB ASSERT(hbmp
!= NULL); //!
將裸數據複製到bitmap關聯的像素區域 memcpy (pArray,
pImageData, uiTotalBytes); bitmap.Attach(hbmp); |
注意:
這裏想說聲sorry,之前沒太深入理解CreateDIBSection函數的工作機理,在上面的例子中使用new爲pArray開闢了一塊空間,然後將pArray傳入CreateDIBSection函數,其實這種方式是錯誤的,在Debug下面調試之後發現,你使用new開闢的pArray,
在使用之後並不能正常delete[]掉。CreateDIBSection參數ppvBits正常的使用應該只要傳入一個指針即可(就像上面實例中那樣
,只要定義一個BYTE *pArray,不需要自己分配空間,直接傳給CreateDIBSection即可)。
其實通過ppvBits參數類型(二級指針)也就明白了CreateDIBSection函數的基本使用原理 - CreateDIBSection函數使用hDC及
BITMAPINFO結構信息創建一個指定大小等信息的位圖,系統自動爲其開闢所需的像素空間,開闢的像素空間地址就是用ppvBits參數
返回的。而且此位圖空間系統會自動釋放。
之所以需要返回此地址,是因爲CreateDIBSection調用時只是根據我們提供的一些信息創建合適大小、格式的位圖並開闢控件
,而並不會填充實際像素值(這也是爲什麼我們直接將指向真實像素值的指針傳入此函數中無效的原因),返回這個指針只是爲了
之後使用真實像素值填充此段空間(就像上面示例中的memcpy那樣)!
1、關於CreateDIBSection函數的使用需要注意一點就是參數ppvBits的使用:傳給CreateDIBSection函數的不是一個new
出來一塊的空間,也不是直接傳入真實像素指針pImageData,而只是傳入一個指針變量(如本例中的pArray)用以接收CreateDIBSection自動開闢的像素區域指針!如果你傳入了自己手動new出來的一塊區域,在Debug調試運行時會發現一個delete []錯誤,此錯誤指明你之前new出來的空間無法釋放,原因是堆損壞,這部分需要了解下new、delete(或內存檢測)原理,詳見《new/delete內存分配及釋放檢測》。
2、像素數據的複製必須在調用了CreateDIBSection函數之後進行,如本例所示,memcpy(pArray, pImageData, uiTotalBytes);是在HBITMAP hbmp = CreateDIBSection(NULL, &bmpInfo, DIB_RGB_COLORS, &pArray, NULL, 0);之後進行的。可能是由於CreateDIBSection函數會對傳入的像素空間進行“清除”工作,所以在調用此函數前就先進行像素複製的話,你會發現最終繪製的圖像時一片漆黑!
說明:CreateDIBSection函數具有內存映射文件功能,但是一般我們不會使用!所以此處忽略討論!
此外還需說明的是,有時候在使用CreateDIBSection函數時會發生內存泄露的問題。當初本人開發一個項目時曾經碰到過此問題,在一個窗口進入特定模式後會頻繁的調用此函數生成特定的圖像,並用此圖像去重繪窗口背景。最後調試正是此函數發生了內存泄露,但是代碼中確是對產生的HBITMAP及DC進行了釋放。當時是使用了另一種方式解決了此問題,但是對於CreateDIBSection函數發生的泄露問題卻一直沒有深入研究。
雖然如此,對於使用此函數之後善後處理這裏僅提幾點:
1、最後一次使用完CreateDIBSection函數創建位圖之後將其從DC中換出memdc.SelectObject(pOldBmp);
2、在最後一次使用完CreateDIBSection函數創建的位圖之後纔可delete[] pArray釋放掉像素空間。
3、注意釋放位圖資源bitmap.DeleteObject();
4、釋放內存memdc.ReleaseDC();