2013年9月8日 (最後更新:2013年9月8日)

BMP 載入器

評分:3.6/5 (312票)
*****
引言
如果您點選了這篇文章,您一定想在您的應用程式中載入 .bmp 檔案。

雖然 .bmp 檔案體積可能很大,但它們在許多應用程式中仍被廣泛使用,並且在網際網路上文件齊全。

現在網上有很多關於如何實現此功能的教程,但它們大多使用 C 語言的方式。這將是一個 C++ 教程,而不是 C 語言教程。

====================

需要了解的內容
本教程假定您正在使用 Windows,但它不使用特定於 Windows 的函式。您可以輕鬆編寫自己的結構。維基百科對點陣圖標頭檔案有相當好的描述。

本教程還使用了 OpenGL,但移植到 DirectX 應該不難。

此外,我們有一個新型別。Uint8 本質上是 unsigned char。為此,我們還需要包含 fstream。

====================

教程
好了,我們開始吧。我們將有一個函式,該函式返回一個 GLuint 作為我們的紋理。

該函式接受 2 個引數。
int LoadBMP(const char* location, GLuint &texture);
第一個是檔案的位置。第二個是對紋理無符號整數的引用,這是生成的紋理的 ID。

好了,現在進入實際程式碼。我們首先宣告四個指標,並將它們設定為 nullptr(或者您也可以使用 NULL)。
1
2
3
4
5
6
	Uint8* datBuff[2] = {nullptr, nullptr}; // Header buffers

	Uint8* pixels = nullptr; // Pixels

	BITMAPFILEHEADER* bmpHeader = nullptr; // Header
	BITMAPINFOHEADER* bmpInfo   = nullptr; // Info 


它們非常自明。

我們使用 fstream 開啟一個檔案,然後檢查它是否已開啟。
1
2
3
4
5
6
7
8
	// The file... We open it with it's constructor
	std::ifstream file(location, std::ios::binary);
	if(!file)
	{
		std::cout << "Failure to open bitmap file.\n";

		return 1;
	}


現在,我們為標頭檔案分配記憶體,透過資料緩衝區獲取值。
1
2
3
4
5
6
	// Allocate byte memory that will hold the two headers
	datBuff[0] = new Uint8[sizeof(BITMAPFILEHEADER)];
	datBuff[1] = new Uint8[sizeof(BITMAPINFOHEADER)];

	file.read((char*)datBuff[0], sizeof(BITMAPFILEHEADER));
	file.read((char*)datBuff[1], sizeof(BITMAPINFOHEADER));


一旦我們載入了資料,我們就將載入的資料構造到標頭檔案中。
1
2
3
	// Construct the values from the buffers
	bmpHeader = (BITMAPFILEHEADER*) datBuff[0];
	bmpInfo   = (BITMAPINFOHEADER*) datBuff[1];


由於我們已經載入了這些,所以我們檢查檔案是否是 BMP 檔案。
1
2
3
4
5
6
	// Check if the file is an actual BMP file
	if(bmpHeader->bfType != 0x4D42)
	{
		std::cout << "File \"" << location << "\" isn't a bitmap file\n";
		return 2;
	}


分配畫素記憶體,然後跳轉到畫素資料開始的位置並讀取。
1
2
3
4
5
6
	// First allocate pixel memory
	pixels = new Uint8[bmpInfo->biSizeImage];

	// Go to where image data starts, then read in image data
	file.seekg(bmpHeader->bfOffBits);
	file.read((char*)pixels, bmpInfo->biSizeImage);


由於點陣圖將畫素儲存為 BGR,因此我們將其轉換為 RGB。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
	// We're almost done. We have our image loaded, however it's not in the right format.
	// .bmp files store image data in the BGR format, and we have to convert it to RGB.
	// Since we have the value in bytes, this shouldn't be to hard to accomplish
	Uint8 tmpRGB = 0; // Swap buffer
	for (unsigned long i = 0; i < bmpInfo->biSizeImage; i += 3)
	{
		tmpRGB        = pixels[i];
		pixels[i]     = pixels[i + 2];
		pixels[i + 2] = tmpRGB;
	}

	// Set width and height to the values loaded from the file
	GLuint w = bmpInfo->biWidth;
	GLuint h = bmpInfo->biHeight;


現在我們用 OpenGL 生成紋理。如果您使用 DirectX,請忽略此步驟,並使用您建立紋理的任何方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
	/*******************GENERATING TEXTURES*******************/

	glGenTextures(1, texture);             // Generate a texture
	glBindTexture(GL_TEXTURE_2D, texture); // Bind that texture temporarily

	GLint mode = GL_RGB;                   // Set the mode

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	 
	// Create the texture. We get the offsets from the image, then we use it with the image's
	// pixel data to create it.
	glTexImage2D(GL_TEXTURE_2D, 0, mode, w, h, 0, mode, GL_UNSIGNED_BYTE, pixels);

	// Unbind the texture
	glBindTexture(GL_TEXTURE_2D, NULL);

	// Output a successful message
	std::cout << "Texture \"" << location << "\" successfully loaded.\n";

	// Delete the two buffers.
	delete[] datBuff[0];
	delete[] datBuff[1];
	delete[] pixels;

	return 0; // Return success code 


就是這樣。請評價這篇文章。

祝您使用新的 BMP 紋理載入器愉快!

====================