一、加载BMP文件
#include <Windows.h>
#include <fstream>
#include <iostream>
unsigned char* LoadDataFromDisk(const char* path)
{
unsigned char* content = nullptr;
FILE * file= fopen(path,"rb");
if (file)
{
int ret = fseek(file, 0, SEEK_END); //fseek 函数只返回执行的结果是否成功,并不返回文件的读写位置
int len = ftell(file); //ftell 取得当前文件的读写位置
if (len > 0)
{
rewind(file); //rewind 函数用于将文件内部的位置指针重新指向一个流(数据流或者文件)的起始位置
content = new unsigned char[len + 1];
fread(content, sizeof(unsigned char), len, file);
content[len] = '\0';
}
fclose(file);
}
return content;
}
1.笔记:
(1)当创建一个windows桌面应用程序以后,想要看控制台输出,可在“生成后事件”命令行中加入以下设置“editbin /subsystem:console $(OutDir)$(ProjectName).exe”:
(2)当给unsigned char* 赋值后,运行时发现字符串中多了别的“?”等意外字符时,可以使用NotePad++修改编码格式来解决这个问题。
(3)源文件首行添加#define _CRT_SECURE_NO_WARNINGS与在“属性”->“C/C++”->“命令行”中添加/D_CRT_SECURE_NO_WARNINGS都可解决不安全问题。
#define _CRT_SECURE_NO_WARNINGS
二、解码BMP图片
1.运用Notepad++查看BMP文件
当我们用Notepad++打开bmp文件时,可能看到的是类似下方的一堆乱码。别急,用一个插件可以解决这个问题。
HexEditor下载
HexEditor是Notepad++的一个插件,支持以16进制形式查看bmp图片数据。根据需要(我用x86版本)在https://download.csdn.net/download/weixin_39766005/87570339下载,下载后可得到一个HexEditor.dll文件。
HexEditor安装
在Notepad++安装目录下的plugins目录下新建一个HexEditor文件夹,并将下载好的HexEditor.dll文件拖入其中。
打开Notepad++,找到菜单栏下的设置->导入->导入插件,定位到HexEditor.dll文件。重启Notepad++。
打开一个bmp文件(别急,此时肯定还是乱码),在Notepad++界面中,选择插件 -> 选HexEditor -> View in Hex即可看到类似下图的数据。
2.BMP格式详解
BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序,RGB数据也是倒着念的,原始数据是按B、G、R的顺序排列的。BMP格式的文件从头到尾依次是如下信息:bmp文件头(bmp file header)、位图信息头(bitmap information header)、调色板(color palette)、位图数据。接下来详细说明一下bmp文件头与位图信息头。
bmp文件头(bmp file header)
位图文件头包含有关于文件类型、文件大小、存放位置等信息,共14字节。在Windows 3.0以上版本的位图文件中用BITMAPFILEHEADER结构来定义:
typedef struct tagBITMAPFILEHEADER {
UINT bfType;
DWORD bfSize;
UINT bfReserved1;
UINT bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
bfType:2字节,文件类型,BMP格式的文件这两个字节是0x4D42,10进制就是19778,字符显示就是‘BM’。
bfSize:4字节,文件大小。
bfReserved1:2字节,保留,必须设置为0。
bfReserved2:2字节,保留,必须设置为0。
bfOffBits:4字节,从头到位图数据的偏移。
位图信息头(bitmap information)
位图信息头包含有位图文件的大小、压缩类型和颜色格式,共40字节。其结构定义为:
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
biSize:4字节,信息头的大小,即40;
biWidth:4字节,以像素为单位说明图像的宽度;
biHeight:4字节,以像素为单位说明图像的高度,同时如果为正,说明位图倒立(即数据表示从图像的左下角到右上角),如果为负说明正向;
biPlanes:2字节,为目标设备说明颜色平面数,总被设置为1;
biBitCount:2字节,说明比特数/像素数,值有1、2、4、8、16、24、32;
biCompression:4字节,说明图像的压缩类型,最常用的就是0(BI_RGB),表示不压缩;
biSizeImages:4字节,说明位图数据的大小,当用BI_RGB格式时,可以设置为0;
biXPelsPerMeter:4字节,表示水平分辨率,单位是像素/米,有符号整数;
biYPelsPerMeter:4字节,表示垂直分辨率,单位是像素/米,有符号整数;
biClrUsed:4字节,说明位图使用的调色板中的颜色索引数,为0说明使用所有;
biClrImportant:4字节,说明对图像显示有重要影响的颜色索引数,为0说明都重要;
3.解码BMP图片
unsigned char* DecodeTexture(unsigned char * data, int& width, int& height)
{
if (0x4D42 == *((unsigned short*)data))
{
int pixelDataOffset = *((int*)(data + 10));
width = *((int*)(data + 18));
height = *((int*)(data + 22));
unsigned char* pixelData = data + pixelDataOffset; //位图数据
//bgr->rgb
for (size_t i = 0; i < width*height*3; i+=3)
{
unsigned char temp = pixelData[i];
pixelData[i] = pixelData[i + 2];
pixelData[i + 2] = temp;
}
return pixelData;
}
else {
return nullptr;
}
}
三、生成纹理
GLuint textureId;
void Init(const char * imagePath)
{
//加载纹理图片
unsigned char* data = LoadDataFromDisk(imagePath);
//解码图片
int width = 0, height = 0;
unsigned char* pixelData= DecodeTexture(data, width, height);
//生成openGl纹理:textureId
glGenTextures(1, &textureId);
//操作该纹理
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
//将纹理从内存传到显存
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixelData);
glBindTexture(GL_TEXTURE_2D, 0);
delete data;
}
四、进行纹理贴图
void Draw()
{
glClearColor(1, 1, 0, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
Init("C:\\Users\\fengchujun\\Downloads\\272.bmp");
glEnable(GL_TEXTURE_2D);//开启纹理
glBindTexture(GL_TEXTURE_2D,textureId);
glBegin(GL_TRIANGLES); //注意是逆时针绘制(CCW),可以通过 glFrontFace(GL_CW); 改成顺时针绘制
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-0.2f, -0.2f, -1.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(0.2f, -0.2f, -1.0f);
glTexCoord2f(0.5f, 1.0f);
glVertex3f(0.0f, 0.2f, -1.0f);
glEnd();
}
五、绘制效果
(在跟随课程学习时,发现有的bmp文件贴图不正确)