Win32 API∶繪圖函數

二、創建GDI繪圖對象

     今天我們要討論的是Win32 API中最有有趣的部分───用繪圖函數完成圖形輸出。可以說,所有前面講的內容都是本課程的前期準備。當時,我們在一些試例程序中偶爾用了一些繪圖函數,可能當時您有些不太好理解。沒有關係,只要您已經來到了這裏,並且對前期的各內容有一點點的蒙朧記憶,那已經是足夠了。因爲,前期的各內容,必須與本課堂中內容相結合才能形成一個完整的理解。看完了本期教程以後,再回頭看過去的幾個教程,對您來說問題會變得更加清晰,透明。

      我們已經講過,Windows中的繪圖是在設備場景中進行的。設備場景有兩種,一是關聯設備場景,之所以說它是關聯設備場景,只是因爲設備場景是同某一窗口關聯在一起的。關聯的意識就是,只要你在這個設備場景中繪圖,那麼繪圖內容自動反映(顯現)在窗體中,使得您能夠看到。那麼另一種設備場景是不關聯的設備場景啦。當然,正如你已經猜到的那樣,這種設備場景,就算你在其中繪了圖,它也不會顯示在窗口中的。
那麼,這兩種設備場景在其內部結構上有什麼區別呢?其實沒有區別,大概同已婚和未婚的關係一樣,一個有老婆,一個還沒有老婆,只不過就是這樣。

      既然,與窗口不關聯的設備場景中的繪圖內容是看不到的,那麼它又有什麼用途呢?嗨,別小看它,很多圖象是需要在這種設備場景中加工的,目的是爲了讓您看不到圖形的加工過程。等到圖形加工完了以後,可以一次性地把完成的圖象傳送到其他已經與窗口關聯的設備場景,讓它顯示出來。當然效果是更好的。
      那麼,在真正的繪圖以前,我們必須學會如何進行選筆,配色。是不是?繪圖總得去選擇一個筆或者畫刷吧,而且得考慮用什麼顏色來繪圖。
      畫筆和畫刷是最常用的GDI繪圖對象。其中,畫筆是定義如何畫線的GDI對象。WIN32的標準畫筆具有三個屬性,分別是顏色、寬度和線型。畫筆顏色用來定義線條的RGB顏色,實際使用的顏色與設備有關。而GDI能自動選擇與設備最接近的顏色。寬度屬性的單位是邏輯單位。標準畫筆可畫出的線型有∶實線、不可見線和幾種虛線、點線。注意,只有實線與不可見線的寬度能大於1。WIN32還提供了擴展畫筆,準備以後接觸的時候再講。

      刷子的用途是填充區域。它定義一塊小區域(一般是8×8像素),然後和WINDOWS95的桌面背景圖案平埔操作一樣,把這個小塊中定義的圖案複製到整個填充區域中。
      刷子主要有三種類型。其一是,實體刷。塊圖爲單一色的固定顏色,可用RGB來確定顏色。其二是,圖樣刷。這時塊圖就是一個用戶指定的小位圖,當然不能大於8×8像素點。最後一種是陰影刷。說是陰影刷,實際上是由一些各種類型的交叉的網格線來構成。這些究竟採用哪種網格線,就得由一些BS_爲前綴的參數來指定。
那麼如何去獲得這些畫筆或花刷呢?可以採用以下列出的API函數。(有關其函數說明均可在APIBROW中找到)

函   數                                           說   明
CreateBrushIndirect           在一個LOGBRUSH數據結構的基礎上創建一個刷子
CreateDIBPatternBrush      用一幅與設備無關的位圖創建一個刷子,以便指定刷子樣式(圖案)
CreateDIBPatternBrushPt   用一幅與設備無關的位圖創建一個刷子,以便指定刷子樣式(圖案)
CreateHatchBrush               創建帶有陰影圖案的一個刷子(陰影圖案見註解)
CreatePatternBrush            用指定了刷子圖案的一幅位圖創建一個刷子
CreatePen                           用指定的樣式、寬度和顏色創建一個畫筆
CreatePenIndirect               根據指定的LOGPEN結構創建一個畫筆
CreateSolidBrush                 用純色創建一個刷子
ExtCreatePen                      創建一個擴展畫筆(裝飾或幾何)
GetStockObject                    取得一個固有對象(Stock)。這是可由任何應用程序使用的windows標準對象之一


例1∶創建一個紅色實線畫筆,畫筆寬度爲3個像素點
Dim NewPen As Long
Private Const PS_SOLID = 0
NewPen&=CreatePen (PS_SOLID,3,RGB(255,0,0))
注∶其中PS_SOLID常數代表實線
例2∶創建陰影刷子
LOGBRUSH結構的定義如下∶
Private Type LOGBRUSH
        lbStyle As Long
        lbColor As Long
        lbHatch As Long
End Type
以下代碼饜了創建一個 刷子樣式爲 陰影(BS_HATCHED) ,陰影類型爲十字交叉(HS_CROSS)的紅色畫筆。
Dim BrushInfo As LOGBRUSH
BrushInfo.lbStyle = BS_HATCHED

BrushInfo.lbColor = RGB(255,0,0)
BrushInfo.lbHatch = HS_CROSS
NewBrush = CreateBrushIndirect(BrushInfo)

例3∶用純色創建刷子(這個例子中是紅色)
    NewBrush =CreateSolidBrush(vbRed)


同樣,用其他幾個函數,按照其用法可以創建相應的GDI繪圖對象。現在,您大概瞭解有以上這些函數和,理解給出的幾個例子就可以了。稍後,我們結合實際例子,更深入地探討這些函數的用法。

 

三、拿起和放下畫筆(GDI對象)

      現在我感覺好象向一羣繪畫系的學生講課,儘管自己不怎麼會繪畫。首先是練基本功,怎樣拿起畫筆和放下畫筆,這可能是繪畫專業學生首先要學習的吧?當然,更廣義地講應當是怎樣選擇和刪除GDI繪圖對象。
      在通過前述方法來創建一個GDI對象句柄(上例中的NewBrush,NewPen等)以後,爲了使用它們,我們必須用SelectObjecth  API函數把它們選入相應的設備場景。一個設備場景在某一時刻、在每一種類型中只能擁有一個對象,如一個畫筆和一個刷子一個位圖等。
      SelectObjecth函數的用法非常簡單,需要記住的是,此被調用後,如果成功將返回舊的對象句柄。你需要把它保存起來。當然,這一過程只需要把返回值附值於某一Long型變量就可以了。如∶

OldPen&=SelectObject(Picture1.hDC , NewPen&)

     接下來該做什麼呢?對對,這位同學說的對∶繪圖。該怎麼繪圖呢?不,不,不要着急,這個問題,我們留在下一節中討論,現在你只需記住,這裏可以畫些圓呀、矩形呀、添充多邊型呀的操作。那麼,繪圖操作結束以後該怎麼辦呢?答案是∶應當把舊的繪圖對象回設到設備場景中去。如下∶

    SelectObject  Picture1.hDC,OldPen&

      這樣,設備場景將恢復到我們爲其選入繪圖對象以前的狀態。因爲,我們不能斷定其他繪圖函數會使用什麼樣的繪圖對象。因此把原來的繪圖對象放回去乃是一個上策。但,如果你接着要爲設備場景選擇另一個對象,這個步驟可以留在後面進行。那麼在這次的選入過程中就沒有必要保存舊的對象句柄了,這是因爲SelectObject函數返回的舊對象的句柄就是剛纔我們爲其選擇的句柄。
     繪圖也完了,設備場景也恢復了原始狀況,那麼就操作告一段落了嗎?不,還有一點。您最好把您自己創建的GDI對象刪除掉,釋放掉剛纔使用過的資源。操作如下∶

DeleteObject  NewPen
又如∶
DeleteObject  NewBrush
      其實,您不刪除這些對象資源,應用程序退出時會自動釋放的。這是因爲在Win32中,資源爲每個應用程序私有的。由於這種原因,應用程序之間也不能共享一個GDI對象。但是,刪除你所創建的GDI對象仍是一個好的編程習慣。既然不用,留着它做什麼呢,何必佔用資源空間呢?
     外,您千萬千萬切記,切記,千萬∶不要刪除已經選入設備場景的系統GDI對象。
     還有一點,GetStockObject函數返回的對象是系統對象,請不要用DeleteObject函數刪除它,否則會出現非常非常可怕的事情───你的硬盤將被永遠用不了啦。@@~ 呵呵,嚇唬你一把,其實沒那麼嚴重。不過,我想您大概不是明知整了壞,偏向壞裏整的人吧?
      如果是的話,隨便整好了。

      OK,以下展示了使用GDI對象的API函數。

函   數                                       說   明
DeleteObject           用這個函數刪除GDI對象,比如畫筆、刷子、字體、位圖、區域以及調色板等等。對象使用的所有系統資源都會被釋放
EnumObjects           枚舉可隨同指定設備場景使用的畫筆和刷子
GetCurrentObject    用於獲得指定類型的當前選定對象
GetObjectAPI           取得對指定對象進行說明的一個結構。windows手冊建議用GetObject 這個名字來引用該函數。GetObjectAPI在vb中用於避免與GetObject關鍵字混淆
GetObjectType        判斷由指定句柄引用的GDI對象的類型
SelectObject            每個設備場景都可能有選入其中的圖形對象。其中包括位圖、刷子、字體、畫筆以及區域等等。一次選入設備場景的只能有一個對象。選定的對象會在設備場景的繪圖操作中使用。例如,當前選定的畫筆決定了在設備場景中描繪的線段顏色及樣式
       除了DeleteObject和SelectObject以外的其他函數用於從系統或指定設備場景中獲取有關GDI對象的信息,一般不十分常用。
      這樣,假如我們要用畫筆和刷子來做一些繪圖朝着的話,編寫代碼的大概步驟是這樣的。
Dim NewPen As Long
Dim NewBrush As Long
Dim OldPen As Long
Dim OldBrush As Long

NewPen& = CreatePen(PS_SOLID, 3, RGB(255, 0, 0)) * 創建畫筆
NewBrush& = CreateSolidBrush(vbRed) * 創建畫刷
OldPen& = SelectObject(Picture1.hDC,NewPen&)
      '添加繪圖操作代碼

OldBrush& = SelectObject(Picture1.hDC,NewBrush)
      '添加填充操作代碼
SelectObject Picture1.hDC,OldPen&
SelectObject Picture1.hDC,OldBrush&
DeleteObject NewPen&
DeleteObject NewBrush&
      注意一點,要用API繪圖函數,並非一定要創建畫筆和刷子。完全可以使用現有的GDI對象,直接調用函數來繪圖。記住,設備場景中總有個默認的畫筆和刷子,問題是它符不符合您的要求了。但我覺得,在繪圖之前選擇畫筆是個好習慣。有些VB功能也可以結合使用,比如你想用紅色畫筆,你可以設置Forcolor屬性爲紅色、想加寬畫筆的寬度,可以設置DrawWidth屬性等。

 

四、繪圖屬性與繪圖函數

      到目前爲止,我們已經學會了繪圖所需要的一切準備工作。上一節中最後給出的代碼就說明這一點。代碼中,現在只缺少具體的繪圖代碼。本節就討論關於如何繪圖的問題。
      在接觸繪圖函數之前,首先需要了解繪圖屬性。設備場景定義了一系列繪圖屬性。這些繪圖屬性定義了刷子和畫筆與窗口或設備表面當前內容相互作用的方法。比如,當前畫筆的位置、當前背景顏色、圓弧和矩形的繪製方向、光柵操作模式等等。雖然後面給出了很多屬性控制函數,但用VB自身的函數和方法屬性,更容易實現。比如,設置背景模式,只要設置控件的BackColor屬性就可以很輕鬆、帶愉快地完成。但是,如果是要在一個不與窗口關聯的自建設備場景中繪圖的話,想必依靠這些函數是不可逃避的。

      線光柵操作∶我們已經知道,光柵操作是一種位操作。通常你想用畫筆進行繪圖時,都假定畫筆色彩只是簡單的繪製到顯示器或設備上。實際上,WINDOWS支持16種不同的線繪圖模式,它們定義了一條線如何與顯示器上已有的信息組合。這些模式就叫做線光柵操作(有時叫ROP2模式)。並且它們被作爲繪圖模式引入到了VisualBasic。ROP2光柵操作相當於設置VB的DrawMode屬性。
      背景模式∶陰影刷子、虛線畫筆和文本都有一個背景。對於陰影刷,它是指陰影線之間的區域,對於虛線畫筆,則指點和虛線之間的區域。而對於文本,它是指每個字符單元的背景。背景模式決定了WINDOWS如何處理這些背景區。它可以是不透明的,也可以是透明的。若是不透明的,則背景區設置爲背景色;否則如果是透明的,則背景區域保持原狀。

      當前位置∶在VB中,要畫一條直線其實非常簡單,採用Line方法就可以,而且能夠在一個語句中表達完成。如Line (5,5)-(10,10)
但在API中並不這樣簡單了(但也不是太麻煩)。要畫直線,需要首先設定直線的起點。一般用MoveToEx函數來完成。然後在下一行代碼中繪製直線,如LineTo 10,10。MoveToEx函數是經常使用的函數之一,用來確定繪圖前的起始位置。。

      繪圖屬性控制函數

函   數                                          說   明
GetArcDirection              畫圓弧的時候,判斷當前採用的繪圖方向
GetBkColor                    取得指定設備場景當前的背景顏色
GetBkMode                    針對指定的設備場景,取得當前的背景填充模式
GetCurrentPositionEx    在指定的設備場景中取得當前的畫筆位置
GetMiterLimit                 取得設備場景的斜率限制(Miter)設置——斜率限制是指斜角長度與線寬間的比率
GetNearestColor           根據設備的顯示能力,取得與指定顏色最接近的一種純色
GetPolyFillMode             針對指定的設備場景,獲得多邊形填充模式。
GetROP2                        針對指定的設備場景,取得當前的繪圖模式。這樣可定義繪圖操作如何與正在顯示的圖象合併起來
MoveToEx                      爲指定的設備場景指定一個新的當前畫筆位置。
SetArcDirection              設置圓弧的描繪方向
SetBkColor                     爲指定的設備場景設置背景顏色。背景顏色用於填充陰影刷子、虛線畫筆以及字符(如背景模式爲OPAQUE)中的空隙。也在位圖顏色轉換期間使用。
SetBkMode                     指定陰影刷子、虛線畫筆以及字符中的空隙的填充方式
SetMiterLimit                  設置設備場景當前的斜率限制
SetPolyFillMode              設置多邊形的填充模式。
SetROP2                         設置指定設備場景的繪圖模式。與vb的DrawMode屬性完全一致。

      同VisualBasic相比較,API提供了功能更強大的繪圖函數。大部分繪圖函數的用法都非常簡單明瞭,只要按其說明使用就可以,覺得沒有必要我多加說明。

     WindoesAPI繪圖函數

函   數                                                    說   明
AngleArc                          用一個連接弧畫一條線,參考註解
Arc                                    畫一個圓弧
ArcTo                                畫一個圓弧,並更新當前位置
CancelDC                         取消另一個線程裏的長時間繪圖操作
Chord                               畫一條絃線(橢圓的平分線)
Ellipse                              描繪一個橢圓,由指定的矩形圍繞。橢圓用當前選擇的畫筆描繪,並用當前選擇的刷子填充
FillRect                             用指定的刷子填充一個矩形
FloodFill                            用當前選定的刷子在指定的設備場景中填充一個區域。區域是由顏色crColor定義的
FrameRect                        用指定的刷子圍繞一個矩形畫一個邊框(組成一個幀),邊框的寬度是一個邏輯單位
GetPixel                            在指定的設備場景中取得一個指定像素的當前RGB值
InvertRect                         通過反轉每個像素的值,從而反轉一個設備場景中指定的矩形
LineDDA                            枚舉指定線段中的所有點
Pie                                    畫一個扇形
PolyBezier                        繪一條或多條貝塞爾(Bezier)曲線。
PolyBezierTo                     繪一條或多條貝塞爾(Bezier)曲線,並將當前畫筆位置設爲前一條曲線的終點
PolyDraw                          描繪一條複雜的曲線,由線段及貝塞爾曲線組成
Polygon                            描繪一個多邊形,由兩點或三點的任意系列構成。windows會將最後一個點與第一個點連接起來,從而封閉多邊形。多邊形的邊框用當前選定的畫筆描繪,多邊形用當前選定的刷子填充
Polyline                            用當前畫筆描繪一系列線段。使用PolylineTo函數時,當前位置會設爲最後一條線段的終點。它不會由Polyline函數改動
PolylineTo                        同上,並設置當前畫筆位置用當前選定畫筆描繪兩個或多個多邊形。根據由SetPolyFillMode函數指定的多邊形填充模式,用當前選定的刷子填充它們。每個多邊形都必須是封閉的
PolyPolygon                     用當前選定畫筆描繪兩個或多個多邊形。根據由SetPolyFillMode函數指定的多邊形填充模式,用當前選定的刷子填充它們。每個多邊形都必須是封閉的
PolyPolyline                     用當前選定畫筆描繪兩個或多個多邊形
Rectangle                        用當前選定的畫筆描繪矩形,並用當前選定的刷子進行填充
RoundRect                       用當前選定的畫筆畫一個圓角矩形,並用當前選定的刷子在其中填充。X3和Y3定義了用於生成圓角的橢圓
SetPixel                           在指定的設備場景中設置一個像素的RGB值,並返回該點的顏色
SetPixelV                         在指定的設備場景中設置一個像素的RGB值

      我把上表中大部分的函數的用法例舉到了本教程附帶的program1.vbp中。另外,
Bezier曲線的用法比較有趣。如果你用過3D Studio三維動畫製作軟件就知道,其中的很多繪圖工作,尤其是二維平面繪圖,就是採用Bezier曲線技術。本教程附帶的program3.vbp
      程序簡單展示了這種技術的應用。《前線》網站源碼解析中的第24號(濾波器演示程序)、第26號(如何用指定顏色填充不規則封閉線框區域)等程序,也是這裏部分函數的好例程,可以下載看看。

       Windows還提供了一些更特殊的繪圖函數,你可以在Windows的內部用它們來繪製控件外框、標題欄、3D控件和桌面等系統對象。

    Win32 API其他繪圖函數

函   數                                              說   明
DrawEdge                    用指定的樣式(包括3D效果)描繪一個矩形的邊框
DrawEscape                 換碼(Escape)函數將數據直接發至顯示設備驅動程序(在vb裏使用:能夠使用。但由於Escape對設備有較強的依賴性,所以除非萬不得以,儘量不要用它)
DrawFocusRect            畫一個焦點矩形。這個矩形是在標誌焦點的樣式中通過異或運算完成的(焦點通常用一個點線表示)。如用同樣的參數再次調用這個函數,就表示刪除焦點矩形
DrawFrameControl       這個函數用於描繪一個標準控件。例如,可描繪一個按鈕或滾動條的幀
DrawState                    這個函數可爲一幅圖象或繪圖操作應用各式各樣的效果
GdiFlush                        在繪圖操作前注意隊列。  執行任何未決的繪圖操作。註釋
GdiGetBatchLimit           判斷有多少個GDI繪圖命令位於隊列中
GdiSetBatchLimit           指定有多少個GDI繪圖命令能夠進入隊列
PaintDesktop                在指定的設備場景中描繪桌面牆紙圖案

      這裏有幾個函數很有趣,比如DrawEdge、DrawFrameControl。使用他們可以非常輕鬆地繪出按鈕控件、編輯框控件等的外觀。我已經把常用的函數的用法包含到了附帶程序program2.vbp。

 

五、路  徑

     應當說,路徑是較爲高級的話題,儘管它不是難於理解的。我學到的有關路徑的知識,來自於Dan的《Visual Basic 5.0 WIN32開發人員指南》一書中的不到兩頁的內容中,在其他的書中尚未看到。
     糟糕的是路徑沒有句柄,所以說它不是GDI對象的成員。不過,千萬要記住一點,任何一個設備場景只有一個路徑。從這一點來看,就算爲路徑設置了句柄也是多餘的。從我的感覺來看,路徑像是在一個設備場景中繪出的任意形狀的多邊形區域(儘管它不是區域)。

      我在路徑中體驗出的一個好處就是,創建一個路徑後,可以把它轉換爲區域。這一點可以用PathToRegion函數來完成。一旦這一步成功了就好辦了,得到區域句柄以後就可以和其他區域對象一樣處理了。總而言之,通過路徑我們可以很輕鬆地創建複雜的圖形區域。
     創建一個路徑非常簡單。具體形式如下∶

        dl& = BeginPath&(Out.hdc)
               (繪圖)
        dl& = EndPath&(Out.hdc)
     在(繪圖)的位置上編寫代碼來繪出什麼圖形,就能形成什麼樣的路徑了。不過,並非任何繪圖函數都可以產生路徑的。可以用來產生路徑的函數如下所列。

函數             Windows NT      Windows 95
  AngleArc         Yes                     No
  Arc                  Yes                     No
  ArcTo              Yes                     No
  Chord             Yes                     No
  Ellipse             Yes                     No
  ExtTextOut      Yes                    Yes
  LineTo             Yes                    Yes
  MoveToEx        Yes                    Yes
  Pie                   Yes                     No
  PolyBezier       Yes                    Yes
  PolyBezierTo    Yes                    Yes
  PolyDraw         Yes                    No

  Polygon            Yes                   Yes
  Polyline             Yes                   Yes
  PolylineTo         Yes                   Yes
  PolyPolygon      Yes                   Yes
  PolyPolyline       Yes                   Yes
  Rectangle          Yes                   No
  RoundRect        Yes                   No
  TextOut             Yes                  Yes

      看完這個表,我想Windows95的用戶就可能有點心痛∶這麼多函數用不了!嗨,我也沒辦法,只好責怪微軟了。以下是有關路徑的API函數∶

    API 路徑函數

函   數                                               說   明
AbortPath                 拋棄選入指定設備場景中的所有路徑。也取消目前正在進行的任何路徑的創建工作
BeginPath                 啓動一個路徑分支。在這個命令後執行的GDI繪圖命令會自動成爲路徑的一部分。對線段的連接會結合到一起。設備場景中任何現成的路徑都會被清除。參考下表,其中列出的函數都可記錄到路徑中
CloseFigure               描繪到一個路徑時,關閉當前打開的圖形(將當前路徑段轉爲閉圖)
EndPath                    停止定義一個路徑。如執行成功,BeginPath函數調用和這個函數之間發生的所有繪圖操作都會正式成爲指定設備場景的路徑
FillPath                      關閉路徑中任何打開的圖形,並用當前刷子填充
FlattenPath               將一個路徑中的所有曲線都轉換成線段
GetPath                    取得對當前路徑進行定義的一系列數據
PathToRegion           將當前選定的路徑轉換到一個區域裏
SelectClipPath           將設備場景當前的路徑合併到剪切區域裏
StrokeAndFillPath      針對指定的設備場景,關閉路徑上打開的所有區域。用當前畫筆描繪路徑的一個輪廓,並用當前刷子填充路徑
StrokePath                用當前畫筆描繪一個路徑的輪廓。打開的圖形不會被這個函數關閉
     好了,其實也沒啥,很簡單的,請閱讀program4.vbp演示程序吧,你會觸目驚心!哇!這就是路徑?!

課後練習

A、製作畫筆和畫刷觀察器。

提示∶

    ①創建一個圖片框控件,用當前畫筆來畫一個矩形(使用Rectangle,巨型的內部將自動被當前刷子填充)。我們要準備依此來觀察當前畫筆和當前刷子。

    ②設置5個滾動條,用來分別調整畫筆的顏色,畫筆的寬度,畫筆的樣式(實線、虛線實體畫刷顏色,陰影畫刷樣式。畫筆的創建可以用函數CreatePen,實體畫刷的創建可以用函數CreateSolidBrush,陰影畫刷的創建可以用函數CreateHatchBrush。(圖樣刷子的創建留在下一課再討論,暫時可以不練)

    通過以上思路,當某個滾動條移動的時候,圖片框中的圖象相應地進行變化,如寬度加厚,  顏色變暗等。

B、創建一個類模塊來模仿Check控件的最基本功能。

提示∶

        ①需要添加的屬性有Wight,Height,Value屬性

        ②需要添加的事件有Click事件。可以用Timer控件來作事件源。

        ③控件外觀可以用DrawFrameControl函數來繪製。

       說真的,還真的想不起來好例子。如果學完了位圖就好了。請等待,下期就是位圖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章