遊戲開發新手入門之點陣圖化圖形(轉)

post0發表於2007-08-12
遊戲開發新手入門之點陣圖化圖形(轉)[@more@]

  簡介

  

  終於,你已經掌握了製作一個完整遊戲的基礎知識了,只不過你現在還只能使用GDI。今天,我們就學習使用DirectX來執行每一件你以前用GDI完成的工作,以及一些關於DirectX其它的東東。具體內容是:裝載(呼叫)點陣圖,使用位塊傳輸,填充表面,使用剪裁板、顏色鍵等複製點陣圖。

  

  你可以在不瞭解前一章內容的基礎上學習本章,但象素格式是很重要的,我將經常直接或間接的提到它,所以你至少應該看看上一章關於象素格式的部分!另外,我假設你已經本系列的第一、二、三、四章,並且擁有一個DirectX SDK遊戲開發平臺。準備好了嗎?發動引擎吧,女士們、先生們!

  

  裝載點陣圖

  

  不管你信不信,你的確已經知道了把點陣圖裝載到DirectDraw表面的大部分知識。怎麼會這樣呢?Well,在Windows GDI下裝載點陣圖同在DirectDraw下極其相似,只是有一點點不同。輕輕的回憶一下,我們曾經使用LoadImage()函式得到點陣圖的控制程式碼,然後把點陣圖選入到記憶體裝置上下文中,最後利用BitBlt()函式把圖形從記憶體裝置上下文中複製到顯示裝置上下文中,裝置上下文可以用GetDC()函式得到。如果這個承擔顯示任務的就是DirectDraw表面(現在我們就是要用它),我們就可以針對性的得到DirectDraw表面的裝置上下文!感謝上帝,IDirectDrawSurface7介面提供了一個極其簡單的函式來得到這個裝置上下文:

  

  HRESULT GetDC(HDC FAR *lphDC);

  

  該函式的返回型別同所有DirectDraw函式的返回型別相同。如果函式呼叫成功,引數就是一個HDC型別的裝置上下文的指標,很簡單吧!本章就是從把一個點陣圖裝載到DirectDraw表面講起的。千萬要記住使用完了表面裝置上下文後,你一定要釋放它哦!你可能已經想到了,用表面介面函式ReleaseDC()完成:

  

  HRESULT ReleaseDC(HDC hDC);

  

  你不用回頭去看關於GDI部分的點陣圖呼叫,我將把適合於DirectDraw的點陣圖呼叫展現給你。唯一不同的是:不是直接把裝置上下文作為一個引數,而是用一個DirectDraw表面指標取代了它,然後函式從表面得到裝置上下文,用它來複製圖形,最終釋放裝置上下文。(可能這裡我說的有些混亂,但你看一下下面的程式程式碼就都明白了):

  

  int LoadBitmapResource(LPDIRECTDRAWSURFACE7 lpdds, int xDest, int yDest, int nResID)

  {

  HDC hSrcDC; // source DC - memory device context

  HDC hDestDC; // destination DC - surface device context

  HBITMAP hbitmap; // handle to the bitmap resource

  BITMAP bmp; // structure for bitmap info

  int nHeight, nWidth; // bitmap dimensions

  

  // first load the bitmap resource

  if ((hbitmap = (HBITMAP)LoadImage(hinstance, MAKEINTRESOURCE(nResID), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION)) == NULL)

  return(FALSE);

  

  // create a DC for the bitmap to use

  if ((hSrcDC = CreateCompatibleDC(NULL)) == NULL)

  return(FALSE);

  

  // select the bitmap into the DC

  if (SelectObject(hSrcDC, hbitmap) == NULL)

  {

  DeleteDC(hSrcDC);

  return(FALSE);

  }

  

  // get image dimensions

  if (GetObject(hbitmap, sizeof(BITMAP), &bmp) == 0)

  {

  DeleteDC(hSrcDC);

  return(FALSE);

  }

  

  nWidth = bmp.bmWidth;

  nHeight = bmp.bmHeight;

  

  // retrieve surface DC

  if (FAILED(lpdds->GetDC(&hDestDC)))

  {

  DeleteDC(hSrcDC);

  return(FALSE);

  }

  

  // copy image from one DC to the other

  if (BitBlt(hDestDC, xDest, yDest, nWidth, nHeight, hSrcDC, 0, 0, SRCCOPY) == NULL)

  {

  lpdds->ReleaseDC(hDestDC);

  DeleteDC(hSrcDC);

  return(FALSE);

  }

  

  // kill the device contexts

  lpdds->ReleaseDC(hDestDC);

  DeleteDC(hSrcDC);

  

  // return success

  return(TRUE);

  }

  

  上面這段程式碼被設計成從資源呼叫點陣圖,但你可以很容易就把它修改成從外部檔案呼叫點陣圖,或者更理想的是,首先你從資源呼叫點陣圖,如果失敗,再試圖從外部檔案呼叫點陣圖。從外部呼叫,需要記住的是呼叫LoadImage()函式時加上LR_LOADFROMFILE標誌。最美妙的事情是,函式BitBlt()自動完成象素格式的轉換。舉例說,當我們把24-bit的點陣圖放入記憶體裝置上下文,再把它傳送(複製)到16-bit色彩深度的表面,所有的顏色將得到正確的顯示,不用顧忌象素格式是555還是565,很方便吧,哦?

  

  如果你要控制點陣圖傳遞的實際過程,而不是使用BitBlt()這樣簡單的函式,你有兩個選擇。第一個,你可以修改這個函式,需要利用BITMAP結構的bmBits成員,它是一個組成圖象的位的LPVOID指標變數。第二種方法,如果你真的想控制圖象的呼叫過程,你可以自己編寫函式,思路是使用標準的I/O函式來開啟圖象檔案,然後讀取它。要這樣做,你需要了解點陣圖檔案的結構。我們將不涉及這種函式的編寫,因為目前的對我們來說已經足夠了,但我還是要為你將來的大展鴻圖做一點點鋪墊。

  

  點陣圖格式

  

  令人高興的是,要自己寫一個呼叫點陣圖的函式,有一個Win32結構的點陣圖標頭檔案可以利用。讀取這個標頭檔案的資訊,用fread()這樣簡單的函式就可以了。所有的點陣圖檔案都有這樣一個標頭檔案,它包含了點陣圖的全部資訊。BITMAPFILEHEADER就是這個標頭檔案結構的名字,下面是它的原形:

  

  typedef struct tagBITMAPFILEHEADER { // bmfh

  WORD bfType; // file type - must be "BM" for bitmap

  DWORD bfSize; // size in bytes of the bitmap file

  WORD bfReserved1; // must be zero

  WORD bfReserved2; // must be zero

  DWORD bfOffBits; // offset in bytes from the BITMAPFILEHEADER

  // structure to the bitmap bits

  } BITMAPFILEHEADER;

  

  我就不詳細介紹這些成員了,因為註釋裡已經說得很清楚了,只要使用fread()讀取它們就可以了。注意要檢測bfType成員是否等於字元“BM”,若是,說明你正在處理一個有效的點陣圖。在此之後,有另一個標頭檔案需要讀取,它包含點陣圖的尺寸、壓縮型別等圖象資訊。以下是它的結構:

  

  typedef struct tagBITMAPINFOHEADER{ // bmih

  DWORD biSize; // number of bytes required by the structure

  LONG biWidth; // width of the image in pixels

  LONG biHeight; // height of the image in pixels

  WORD biPlanes; // number of planes for target device - must be 1

  WORD biBitCount; // bits per pixel - 1, 4, 8, 16, 24, or 32

  DWORD biCompression; // type of compression - BI_RGB for uncompressed

  DWORD biSizeImage; // size in bytes of the image

  LONG biXPelsPerMeter; // horizontal resolution in pixels per meter

  LONG biYPelsPerMeter; // vertical resolution in pixels per meter

  DWORD biClrUsed; // number of colors used

  DWORD biClrImportant; // number of colors that are important

  } BITMAPINFOHEADER;

  

  只有幾個成員需要解說一下。第一個,注意壓縮格式。大多數的點陣圖你都需要做解壓縮的操作。最普通的點陣圖壓縮格式是run-length編碼(RLE),但只能應用於4-bit或8-bit圖象,在此情況時,成員biCompression將分別是BI_RLE4和BI_RLE8,我們就不討論這種壓縮格式了,但它真的很簡單,很容易理解,你如果要了解它是不會有任何麻煩的。

  

  第二個,對於高色彩的點陣圖,biClrUsed和biClrImportant這兩個成員通常設定為0,所以不用太在意它們。對於BI_RGB這種未壓縮格式的點陣圖,成員biSizeImage也將被設定為0。最後,針對我們的目的,其它的結構成員都不是很重要的,我們只需要注意點陣圖的長、寬和色彩的深度(biWidth、biHeight、biBitCount)。

  

  讀取完了這些標頭檔案的資訊後,如果點陣圖是8-bit或者以下色彩深度的(也就是調色盤模式),調色盤的資訊會緊跟在這些資訊之後。也許出乎你的意料,調色盤的資訊不是儲存在PALETTEENTRY結構中,而是在RGBQUAD結構中。RGBQUAD結構如下:

  

  typedef struct tagRGBQUAD { // rgbq

  BYTE rgbBlue;

  BYTE rgbGreen;

  BYTE rgbRed;

  BYTE rgbReserved;

  } RGBQUAD;

  

  不要問我為什麼紅、綠、藍以倒序方式排列,事實就是這樣!讀取RGBQUAD中的資料,把資料傳遞給DirectDraw調色盤的陣列。記得要把每個PALETTEENTRY的peFlag設定成PC_NOCOLLAPSE。

  

  之後呢(調色盤資訊不一定存在,因為高彩模式下就沒有),你將發現圖象位(image bits),你可能會想到建立一個指標,在記憶體中分配足夠的空間來控制這些圖象位資料,然後讀取它們。對極了,我正要這樣幹。假設把儲存在BITMAPINFOHEADER結構中的資訊標頭檔案稱作info,你的圖象位指標稱作fptr,實施的程式碼如下:

  

  UCHAR* buffer = (UCHAR*)malloc(info.biSizeImage);

  fread(buffer, sizeof(UCHAR), info.biSizeImage, fptr);

  

  要記住,在一些情況下,biSizeImage的值可能為0,所以有必要在上面的程式碼執行前檢測它一下。如果它被設定為0,你將不得不計算圖象由多少個象素構成,每個象素需要多少個位元組。

  

  寫你自己的點陣圖呼叫函式,

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-952084/,如需轉載,請註明出處,否則將追究法律責任。

相關文章