目录
<windows.h>系统头文件中包含BMP位图格式相关结构体类型定义。
BMP文件格式:
数据段名称 | <windows.h>中结构体定义 | 大小(Byte) | |
head | 1.位图文件头 | BITMAPFILEHEADER | 14 |
2.位图信息头 | BITMAPINFOHEADER | 40 | |
3.调色板 | RGBQUAD | 由像素所占位数决定(仅索引图像有) | |
Data | 图像数据 | 由图像尺寸与像素位数决定 |
BMP格式解析参考以下博客:
常见图片文件格式简析 - 走看看 (zoukankan.com)http://t.zoukankan.com/go2bed-p-4097758.html
实现步骤:
读:
1.读二进制的方式“rb"打开文件;
2.跳过位图文件头;
3.定义位图信息头结构体变量,从文件流中读取”位图信息头“至信息头结构体变量指针指向的内存块;
4.申请调色板所需空间,从文件流中读取”位图调色板“至调色板结构体指针变量指向的内存块;
5.申请图像数据所需空间,从文件流中读取”图像数据“至图像数据指针变量指向的内存块;
6.关闭文件。
写:
1.以二进制写的方式”wb"打开文件;
2.定义文件头结构体变量,填写文件头信息,写文件头进文件流;
3.定义信息头结构体变量,填写信息头信息,写信息头进文件流;
4.若为索引图象,将颜色表写入文件;
5.写位图数据进文件;
6.关闭文件.
PS:
1.读写图像位图数据时,每行数据的字节数必须为4的整数倍,缺位补齐。
2.索引图像的位图数据为颜色表的索引值。
涉及文件操作函数:
(在<cstdio>系统头文件中定义)
1. fopen()函数
FILE* fopen (const char* filename, const char* mode);
---------------------------------------------------------------
function : 以某种模式打开指定的文件.
parameter: 1. filename:指向包含要打开的文件名的字符串的指针.
2. mode:指向指定文件打开模式的字符串的指针.
return : 成功,fopen() 函数将返回一个指针,指向控制打开的文件流的 FILE 对象.
失败,fopen() 函数返回一个空指针.
参考链接:C++ fopen()用法及代码示例 - 纯净天空 (vimsky.com)
2. fseek()函数
int fseek(FILE* stream, long offset, int origin);
------------------------------------------------------------------------------------------
function:重定位流(数据流/文件)上的文件内部位置指针
paragram:1.stream :要修改的文件流.
2.offset :从原点位移的偏移量(字节数),正数表示正向偏移,负数表示负向偏移.
3.origin :用作添加到偏移量的参考的位置。它可以具有以下值:
0(SEEK_SET)--文件开头位置;
1(SEEK_CUR)--文件当前位置;
2(SEEK_END)--文件尾。
return :成功返回0,失败返回1.
参考:c++ fseek函数-天道酬勤-花开半夏 (zhangshilong.cn)
3. fread()函数
size_t fread(void * buffer, size_t size, size_t count, FILE * stream)
------------------------------------------------------------------------------------------
function : 从流中读取数据块.
parameter: 1. buffer:它以至少(size * count)个字节的大小指定指向内存块的指针以存储对象.
2. size:它指定每个对象的大小(以字节为单位)。 size_t是无符号整数类型.
3. count:它指定元素的数量,每个元素的大小为字节大小.
4. stream:它指定要从中读取数据的文件流.
return : 函数返回成功读取的对象数.
参考:C++ fread()用法及代码示例 - 纯净天空 (vimsky.com)
4.fwrite()函数
size_t fwrite(const void * buffer, size_t size, size_t count, FILE * stream);
-----------------------------------------------------------------------------
function: 将 count 个对象写入给定的输出流,每个对象的大小为 size 个字节
parameter:1.buffer:指向其内容被写入的内存块的指针;
2.size:每个对象的大小(以字节为单位);
3.count:要读取的对象数;
4.stream:要写入数据的文件流.
return:函数返回成功读取的对象数。如果发生错误,返回值可能小于 count
参考: C++ fwrite()实例讲解 - 码农教程 (manongjc.com)
参考代码实现:
读:
bool readBmp(char* bmpName)
{
//1.二进制读方式打开指定的图像文件
FILE* fp = fopen(bmpName, "rb");
if (fp == 0){
cout<<"The file "<<bmpName<<" was not opened"<<endl;
return 0;
}
//2.跳过位图文件头结构BITMAPFILEHEADER
if(fseek(fp, sizeof(BITMAPFILEHEADER), 0)){
cout<<"跳转失败"<<endl;
return 0;
}
//3.定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
BITMAPINFOHEADER head;
fread(&head, sizeof(BITMAPINFOHEADER), 1, fp);
//获取图像宽、高、每像素所占位数等信息
bmpWidth = head.biWidth;
bmpHeight = head.biHeight;
biBitCount = head.biBitCount;
//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)
int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;
//4.灰度图像有颜色表,且颜色表表项为256
if (biBitCount == 8) {
//申请颜色表所需要的空间,读颜色表进内存
pColorTable = new RGBQUAD[256]; //这里可以理解为:申请了256个元素的数组空间,数组元素是RGBQUARD结构体类型
fread(pColorTable, sizeof(RGBQUAD), 256, fp);//读取的每个对象大小为sizeof(RGBQUAD)=4byte,从fp文件流中读取了256个对象至pColorTable指向的内存块
cout<<"读颜色表成功"<<endl;
}
//5.申请位图数据所需要的空间,读位图数据进内存
pBmpBuf = new unsigned char[lineByte * bmpHeight];
fread(pBmpBuf, 1, lineByte * bmpHeight, fp);
//6.关闭文件
fclose(fp);
cout<<"图像读取成功"<<bmpName<<"的图像信息:"<<endl;
cout<<"width="<<bmpWidth<<" ,height="<<bmpHeight<<" ,biBitCount="<< biBitCount<<endl;
return 1;
}
写:
bool saveBmp(char* bmpName, unsigned char* imgBuf, int width, int height,
int biBitCount, RGBQUAD* pColorTable)
{
//如果位图数据指针为0,则没有数据传入,函数返回
if (!imgBuf)
return 0;
//颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
int colorTablesize = 0;
if (biBitCount == 8)
colorTablesize = 1024;
//待存储图像数据每行字节数为4的倍数
int lineByte = (width * biBitCount / 8 + 3) / 4 * 4;
//1.以二进制写的方式打开文件
FILE* fp = fopen(bmpName, "wb");
if (fp == 0) return 0;
//2.申请位图文件头结构变量,填写文件头信息
BITMAPFILEHEADER fileHead;
fileHead.bfType = 0x4D42;//bmp类型
//bfSize是图像文件4个组成部分之和
fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
+ colorTablesize + lineByte * height;
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;
//bfOffBits是图像文件前三个部分所需空间之和
fileHead.bfOffBits = 54 + colorTablesize;
//写文件头进文件
fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);
//cout<<"写文件头成功"<<endl;
//3.申请位图信息头结构变量,填写信息头信息
BITMAPINFOHEADER head;
head.biBitCount = biBitCount;
head.biClrImportant = 0;
head.biClrUsed = 0;
head.biCompression = 0;
head.biHeight = height;
head.biPlanes = 1;
head.biSize = 40;
head.biSizeImage = lineByte * height;
head.biWidth = width;
head.biXPelsPerMeter = 0;
head.biYPelsPerMeter = 0;
//写位图信息头进内存
fwrite(&head, sizeof(BITMAPINFOHEADER), 1, fp);
//cout<<"写信息头成功"<<endl;
//4.如果灰度图像,有颜色表,写入文件
if (biBitCount == 8)
fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
//cout<<"写颜色表成功"<<endl;
//5.写位图数据进文件
fwrite(imgBuf, height * lineByte, 1, fp);
//6.关闭文件
fclose(fp);
cout<<"图像保存成功,请查看文件"<<bmpName<<":"<<endl;
cout<<"width="<<width<<" ,height="<<height<<" ,biBitCount="<< biBitCount<<endl;
return 1;
}