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 纹理加载器愉快!

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