一、前言
隨這計算機技術、多媒體技術的飛速發展,圖形圖像已經廣泛應用到現實生活中的各個領域,如游戲開發、視頻監控、媒體傳播等等。如何高速有效地處理這些信息量巨大的圖像數據,是我們不得不面對的現實問題,解決問題的一個有效途徑就是繞過Windows操作系統提供的API,直接操作計算機的顯卡,以此來充分利用硬件的加速效果。開發人員不必為此擔心程序的兼容性問題,因為自從微軟推出高效的DirectX技術后,事實上它已經成為了多媒體開發的標準之一,不同的硬件廠家都是根據DirectX的標準來編寫驅動程序,所以開發人員只要同單一的DirectX庫打交道而不用考慮具體的硬件,這樣就大大的提高了編程的效率。由于DirectX編程技術博大精深,它的內容足夠寫幾本大部頭的書,所以本文只能涉及到它的一小部分,既主要探討VB開發環境下基于DirectDraw技術的程序開發,處理二維平面圖像。
最初的DirectX多媒體開發僅僅適用于C/C++的編譯環境,但是微軟自從DirectX7.0版本(目前DirectX的最高版本9.0測試版已經公布)推出以后,就開始提供VB開發庫,支持在VB環境下開發基于DirectX的多媒體應用程序。如果要進行DirectX開發,系統必須安裝DirectX SDK庫并且要在VB項目中引入它。
為了進行DirectDraw的編程,需要讀者特別注意的一點是,在點擊VB菜單中的 Project | References 項引入DirectX 為VB提供的SDK庫時,在Object Library 列表中要選擇DirectX7.0 For Visual Basic Type Library 這個列表項。為什么不選擇DirectX8.0或更高版本提供的類庫呢?因為只有DirectX7.0提供了DirectDraw對象,在后續的DirectX版本中,DirectDraw 已被融入Direct3D對象中了,而Direct3D對象主要用于三維立體圖像的處理,它的使用比較復雜,不適用初學者,所以本文主要講述如何使用DirectX7.0類庫中的DirectDraw對象,今后在此基礎上,我們再對Direct3D等復雜的編程技術詳細介紹,由淺如深,逐漸進入DirectX編程的奇妙世界。
二、DirectX7.0技術簡介
為了更好的介紹DirectDraw編程,我們首先簡單介紹一下DirectX7.0技術,使讀者對它有一個初步的印象,至于DirectX的后續版本,只是在7.0版本的基礎上添加了一些新的功能和作了一些結構調整罷了,相關內容讀者可以自己參閱MSDN。DirectX7.0提供的開發庫是基于對象的,其中最基本的對象是DirectX7對象,只有在程序中首先建立了DirectX7對象之后才可以在該對象的基礎上建立其它對象。DirectX基本對象包含以下幾個常用的類:
1)DirectDraw類,獨立于設備的表面繪圖對象,支持以全屏方式或者窗口方式繪圖,同時DirectDraw實現了對顯卡內存的直接操作(也可以實現對系統的內存的操作,如果在顯卡內存不夠或者開發人員指定使用系統內存的情況下,DirectDraw將操作系統內存),一旦定義DirectDraw對象并鎖定,就可以象操作一個數組一樣操作內存中的圖像數據了。
2)獨立于設備的Direct3D類,包括立即模式和保留模式,它是DirectX的三維繪圖接口、開發三維DirectX游戲的基礎。
3)DirectSound類, DirectX中獨立于設備的Wave音頻對象,支持混音、硬件加速、直接設備訪問、聲音捕捉和回放。
4)DirectMusic類,音樂數據文件操作對象,支持DLS(downloadable sounds)標準和運行時編輯。
5)DirectInput類,獨立于設備的游戲控制器對象,支持操作映射,過操作映射能夠在輸入操作和輸入設備之間建立連接。您只需輸入設備處理,而不必再依賴特定的設備對象。用于支持鼠標、鍵盤、游戲桿。
6)DirectPlay類,為支持多任務的網絡游戲而設計的對象,可以協調速度不同的計算機之間的信息傳遞以支持不同的計算機用戶通過網絡進行互連。
DirectX7對象包含了諸如建立DirectDraw、 Direct3D、 DirectSound、 DirectInput等對象的方法,例如,當建立成功一個DirectX7對象之后,就可以使用該對象的DirectDrawCreate、Direct3DRMCreate等方法建立DirectDraw、Direct3D對象了。
三、DirectDraw編程
正如上節介紹的那樣,DirectDraw提供了與硬件無關性的同時允許直接操作顯存,它是一個與Windows 圖形系統接口(GDI)相兼容的直接操作顯示設備的軟件接口,是DirectX技術中進行圖像處理的基礎,是DirectX中的關于視頻輸入輸出的基本部分,只要使用的硬件支持DirectDraw,開發人員就可以使用DirectDraw可以方便地編制出高效的視頻處理、圖像處理的程序來。
DirectDraw類包含DirectDraw7、DirectDrawClipper 、DirectDrawEnumModes、 DirectDrawPalette、DirectDrawSurface7等對象。其中,DirectDraw7對象是DirectX7中的DirectDraw對象,你需要首先建立一個DirectX7對象,然后使用該對象的DirectDrawCreate方法來建立DirectDraw7對象。
DirectDraw7的GetDisplayModesEnum方法可以建立一個DirectDrawEnumModes對象,通過它可以獲得當前系統支持的顯示模式;DirectDraw7的CreatePalette方法可以建立一個DirectDrawPalette對象,DirectDrawPalette對象是DirectDraw下的繪圖調色板對象,它很類似于Windows下的調色板。
利用DirectDraw7的CreateClipper方法可以建立一個DirectDraw7下的DirectDrawClipper對象,DirectDrawClipper對象的作用是建立一個屏幕的剪裁區域,這樣DirectDraw就知道繪圖時那些部分是可見的而那些部分應該被剪裁掉。
DirectDrawSurface7是DirectDraw下的最重要的對象,這個對象描述了一塊線型的顯存區域,我們所進行的所有圖形、文本操作都是在這個對象上進行的,你可以通過DirectDrawSurface7對象直接操作這片區域。
通過DirectDraw7的CreateSurface方法,可以建立一個單一繪圖表面、復合繪圖表面或者三維繪圖表面對象。而利用DirectDraw7的CreateSurfaceFromFile方法、CreateSurfaceFromResource方法可以分別根據位圖文件和位圖資源建立一個包含圖像數據的 DirectDrawSurface7對象。
僅僅知道如何創建DirectDraw編程需要使用到的一些對象是遠遠不夠的,還需要使用靈活的使用這些對象提供的方法,下面按照DirectDraw處理圖像的步驟來說明這些方法的使用。
1、建立協作層
當建立了一個DirectDraw對象之后,首先要設定DirectDraw的協作層。實現的方法是調用DirectDraw對象的SetCooperativeLevel函數。該函數的定義是:
SetCooperativeLevel( hdl As Long, flags As CONST_DDSCLFLAGS)
其中參數hdl指定程序的窗口句柄,參數flag決定程序運行的方式,它的值可以使用Or組合起來。具體值的含義如下:DDSCL_FULLSCREEN表示使用全屏幕(必須和DDSCL_EXCLUSIVE一起用)、DDSCL_ALLOWMODEX使用Modex模式、DDSCL_EXCLUSIVE 使用獨占模式、DSCL_NORMAL 使用Windows的窗口、DDSCL_NOWINDOWCHANGES 禁止改變窗體大小。例如函數調用SetCooperativeLevel Me.hWnd, DDSCL_NORMAL將使程序運行于普通的協作層既窗口模式之下,在這種協作層你無法改變主表面調色板或進行頁交換。而函數調用SetCooperativeLevel MainForm.hWnd, DDSCL_EXCLUSIVE Or _DDSCL_FULLSCREEN將使程序運行于全屏幕模式之下。在全屏幕協作模式之下你可以完全使用硬件的一切,你可以設置自己的調色板,改變顯示分辨率及進行頁交換。
2、設置顯示模式
設置系統的顯示模式是使用DirectDraw的SetDispalyMode函數實現的,函數的定義如下:
SetDisplayMode( w As Long, h As Long, bpp As Long, ref As Long, mode As CONST_DDSDMFLAGS)
其中參數w、h分別指定屏幕的寬度和高度,bpp指定屏幕顯示的顏色位數,參數ref指定屏幕的刷新頻率,設置為0使用顯示驅動的缺省刷新頻率,mode指定附加的參數。為了保證程序的健壯性,需要獲得系統支持的顯示模式,此時可以使用DirectDraw對象的GetDisplayModesEnum函數來遍歷所有支持的顯示模式,然后根據返回的結果進行設置。
3、建立DirectDrawSurface表面對象顯示圖像
DirectDrawSurface對象是DirectDraw用來進行圖像顯示和繪制的對象,開發人員不僅可以在DirectDrawSurface上貼位圖、繪制圖形,還可以直接操作DirectDrawSurface對象來處理顯存或內存里的圖像數據。一般來說它分成以下幾種類型:primarysruf surface(主表面), 屏幕上可以看到的顯示記憶體,圖像畫在上面就能顯示在屏幕上;backsurf surface (后備表面),這是一個能跟primarysruf surface作快速切換的表面;offscreen surface (離屏表面),這是一個屏幕上看不見的Surface,主要用來存放圖像數據。
利用DirectDraw對象的CreateSurface方法可以建立一個DirectDrawSurface7對象,設置DDSURFACEDESC2結構對象的不同標志可以實現不同的表面對象。為了改善圖像顯示效果,在DirectDraw編程時,一般設立一個主表面、后備表面和若干個離屏表面,主表面就是用戶看到的顯示圖像的表面,也就是當前顯示的表面,后備表面是主表面的附帶緩沖表面,用來存儲、處理即將顯示的圖像,離屏表面用來裝載最初的圖像數據,可以利用DirectDraw對象的CreateSurfaceFromFile函數或者CreateSurfaceFromResource函數建立一個DirectDrawSurface7對象,同時將圖象文件或者資源文件中的圖像裝入建立的 DirectDrawSurface中,該函數調用成功后返回一個DirectDrawSurface對象。
處理后使用Blt函數將數據拷貝到后備表面,此時DirectDraw使用filp方法交換主表面和后備表面,后備表面變為主表面顯示圖像,先前的主表面變為后備表面,這樣作既提高的圖像數據的處理速度,又有效的避免圖像閃爍,筆者認為這是DirectDraw編程中最重要的思想。
上面給出了DirectDraw的主要編程步驟,為了使讀者更清楚的了解一些編程細節,下面給出了一個具體的實現代碼,該程序在Windows98、Visual Basic 6.0編譯環境下編譯通過,運行正常,它實現了全屏幕顯示一個圖像,同時在圖像的左上方疊加了一行文字說明。讀者可以在此基礎上實現動畫效果,比如讓說明文字在背景圖像上動態顯示等。
| ////////////////////////////////////// Option Explicitf '聲名全局變量; Dim dx As New DirectX7 'DirectX對象; Dim dd As DirectDraw7 'DirectDraw對象; Dim picturesurf As DirectDrawSurface7 '離屏表面對象; Dim primarysruf As DirectDrawSurface7 '主表面對象; Dim backsurf As DirectDrawSurface7 '后備表面對象; Dim ddsd1 As DDSURFACEDESC2 'DDSURFACEDSC2對象; Dim ddsd2 As DDSURFACEDESC2 Dim ddsd3 As DDSURFACEDESC2 Dim brunning As Boolean Dim binit As Boolean ///////////////////////////////////////////// Private Sub Form_Load() On Local Error GoTo errOut Set dd = dx.DirectDrawCreate("") '生成DirectDraw對象; Me.Show '設置協作模式 Call dd.SetCooperativeLevel(Me.hWnd, DDSCL_FULLSCREEN Or DDSCL_ALLOWMODEX Or DDSCL_EXCLUSIVE) '設置"1024*768*24"的顯示模式 dd.SetDisplayMode 1024, 768, 24, 0, DDSDM_DEFAULT '設置主繪圖平面的屬性 ddsd1.lFlags = DDSD_CAPS Or DDSD_BACKSURFCOUNT ddsd1.ddsCaps.lCaps = DDSCAPS_PRIMARYSRUFSURFACE Or DDSCAPS_FLIP Or DDSCAPS_COMPLEX ddsd1.lBacksurfCount = 1 '創建主表面和后備表面; Set primarysruf = dd.CreateSurface(ddsd1) Dim caps As DDSURFACEDESC2 '設置緩沖繪圖平面的屬性 caps.lCaps = DDSCAPS_BACKSURF Set backsurf = primarysruf.GetAttachedSurface(caps) backsurf.GetSurfaceDesc ddsd3 '獲取后備表面的尺寸; backsurf.SetForeColor vbBlue '設置后備表面中的字體顏色為藍色; backsurf.SetFontTransparency True '設置后備表面的字體為透明效果; Set picturesurf = Nothing ddsd2.lFlags = DDSD_CAPS Or DDSD_HEIGHT Or DDSD_WIDTH ddsd2.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN ddsd2.lWidth = ddsd3.lWidth ddsd2.lHeight = ddsd3.lHeight Set picturesurf = dd.CreateSurfaceFromFile("c:Waterskier.bmp", ddsd2) '離屏表面以全屏幕的尺寸來裝載圖像; binit = True brunning = True Do While brunning blt DoEvents Loop errOut: '顯示出現錯誤的情況下恢復系統的正常顯示模式; Call dd.RestoreDisplayMode Call dd.SetCooperativeLevel(Me.hWnd, DDSCL_NORMAL) End End Sub ////////////////////////////////////////// Private Sub Form_Paint() '窗口重畫函數; blt End Sub /////////////////////////////////////////////////////// Sub blt() '"畫圖"函數; On Local Error GoTo errOut If binit = False Then Exit Sub Dim rPicture As RECT, rback As RECT rPicture.Bottom = ddsd2.lHeight rPicture.Right = ddsd2.lWidth rback.Bottom = ddsd3.lHeight rback.Right = ddsd3.lWidth Call backsurf.BltFast(0, 0, picturesurf, rPicture, DDBLTFAST_WAIT) '在表面上書寫文字; Call backsurf.DrawText(20, 20, "DirectDarw 編程示范", False) Call backsurf.DrawText(20, 40, "單擊鼠標后退出!", False) primarysruf.Flip Nothing, DDFLIP_WAIT errOut: Call dd.RestoreDisplayMode Call dd.SetCooperativeLevel(Me.hWnd, DDSCL_NORMAL) End Sub ///////////////////////////////////////////// Private Sub Form_Click() '單擊鼠標后程序結束程序運行; Call dd.RestoreDisplayMode Call dd.SetCooperativeLevel(Me.hWnd, DDSCL_NORMAL) End End Sub |
上面的程序雖然比較簡單,但是它包含了DirectDraw的基本應用。首先通過DirectX7的DirectDrawCreate方法建立一個DirectDraw對象,然后通過調用DirectDraw對象的SetCooperativeLevel 方法將程序設置為獨占、全屏的模式,SetDisplayMode方法設置系統的顯示模式。
接下來是設置繪圖平面,DDSURFACEDESC2結構的dwFlags成員用來決定DDSURFACEDESC2結構中哪些成員將被填充有效的信息,例如程序中在創建主表面時,將DDSURFACEDESC2結構的lFlags設為想要用DDSCAPS結構(DDSD_CAPS)和想創建后備表面(back buffer)(DDSD_BACKBUFFERCOUNT),ddCaps成員在例子中標示一個將要在DDSCAPS結構中使用的標志位,程序中指定了一個主表面(DDSCAPS_PRIMARYSURFACE)、一個交換頁(DDSCAPS_FLIP)、一個合成表面(DDSCAPS_COMPLEX);DDSURFACEDESC2結構中lBacksurfCount成員為"1"說明當前要在主表面下建立一個后備表面。
創建主表面后,程序通過調用主表面的GetAttachedSurface方法獲取了后備表面。獲取了表面對象后,調用表面對象的SetForeColor、 DrawText等方法就可以在相應的表面上添加特定效果的文本了。至于程序中繪制圖像我就不多解釋了,讀者可以仔細閱讀上面的代碼,其實說白了,DirectDraw顯示圖像時無非就是在不同表面之間Blt圖像數據罷了。