BMP文件学习笔记(一): C/C++语言实现.bmp文件读写
2017-06-26 19:27
741 查看
1 认识BMP文件
bmp文件结构:第一部分:位图文件头BITMAPFILEHEADER,是一个结构。这个结构的长度是固定的,为14个字节(WORD为无符号16位整数,DWORD为无符号32位整数)。
其定义如下:
typedef struct tagBITMAPFILEHEADER {
WORD bfType;//位图文件类型,必须是0x4D42,即字符串“BM”,也就是说所有.bmp文件的头两个字节都是“BM”
DWORD bfSize;//位图文件大小,包括这14个字节
WORD bfReserved1;//保留字,设为0
WORD bfReserved2;//保留字,设为0
DWORD bfOffBits;//从文件头到实际的位图数据的偏移字节数,单位:字节
} BITMAPFILEHEADER;
第二部分:位图信息头BITMAPINFOHEADER,是一个结构。这个结构的长度也是固定的,为40个字节(LONG为32位整数)。
其定义如下:
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;//本结构所占用字节数,大小为40字节
LONG biWidth;//位图宽度,单位:字节
LONG biHeight;//位图高度,单位:字节
WORD biPlanes;//目标设备级别,必须为1
WORD biBitCount;//表示颜色时每个像素要用到的位数,常用的值为1(黑白二色图), 4(16色图), 8(256色), 24(真彩色图)
DWORD biCompression;// 位图是否压缩,其类型是 0(BI_RGB不压缩), 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)
DWORD biSizeImage;//实际的位图数据占用的字节数
LONG biXPelsPerMeter;//位图水平分辨率,每米像素数
LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数
DWORD biClrUsed;//指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2的biBitCount次幂个
DWORD biClrImportant;//指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的
} BITMAPINFOHEADER;
第三部分:调色板Palette。这里是对那些需要调色板的位图文件而言的,有些位图,如真彩色图,是不需要调色板的,BITMAPINFOHEADER后直接是位图数据。
调色板实际上是一个数组,共有biClrUsed个元素(如果该值为零,则有2的biBitCount次幂个元素)。数组中每个元素的类型是一个RGBQUAD结构,占4个字节,其定义如下:
typedef struct tagRGBQUAD {
BYTE rgbBlue; //该颜色的蓝色分量(值范围为0-255)
BYTE rgbGreen; //该颜色的绿色分量(值范围为0-255)
BYTE rgbRed; //该颜色的红色分量(值范围为0-255)
BYTE rgbReserved; //保留值,设为0
} RGBQUAD;
第四部分:实际的图象数据。对于用到调色板的位图,图象数据就是该象素颜在调色板中的索引值。对于真彩色图,图象数据就是实际的R、G、B值。
对于2色位图,用1位就可以表示该象素的颜色(一般0表示黑,1表示白),所以一个字节可以表示8个象素。
对于16色位图,用4位可以表示一个象素的颜色,所以一个字节可以表示2个象素。
对于256色位图,一个字节刚好可以表示1个象素。
对于真彩色图,三个字节表示1个象素。
注意:
(1) 每一行的字节数必须是4的整倍数,如果不是,则需要补齐。这在前面介绍biSizeImage时已经提到了。
(2) 一般来说,.bMP文件的数据从下到上,从左到右的。也就是说,从文件中最先读到的是图象最下面一行的左边第一个象素,然后是左边第二个象素……接下来是倒数第二行左边第一个象素,左边第二个象素……依次类推 ,最后得到的是最上面一行的最右一个象素。
2 编程
代码如下:#include <iostream> #include <Windows.h> using std::cout; using std::endl; unsigned char * pData; int width; int height; int bitCount; RGBQUAD * pRGBQUAD; bool readBmpFile(char * filename) { FILE * pf; pf = fopen(filename, "rb"); if (NULL == pf) { cout << "文件打开失败!" << endl; fclose(pf); return false; } BITMAPFILEHEADER bitMapFileHeader; BITMAPINFOHEADER bitMapInfoHeader; fread(&bitMapFileHeader, sizeof(BITMAPFILEHEADER), 1, pf); if (0x4D42 != bitMapFileHeader.bfType) { cout << "此文件不是BMP文件!" << endl; return false; } fread(&bitMapInfoHeader, sizeof(BITMAPINFOHEADER), 1, pf); cout << "位图文件头:" << endl; cout << "位图文件类型: " << bitMapFileHeader.bfType << endl; cout << "位图文件大小: " << bitMapFileHeader.bfSize << endl; cout << "偏移的字节数: " << bitMapFileHeader.bfOffBits << endl; width = bitMapInfoHeader.biWidth; height = bitMapInfoHeader.biHeight; bitCount = bitMapInfoHeader.biBitCount; cout << "\n位图信息头:" << endl; cout << "信息头占用字节数:" << bitMapInfoHeader.biSize << endl; cout << "位图宽度: " << bitMapInfoHeader.biWidth << endl; cout << "位图高度: " << bitMapInfoHeader.biHeight << endl; cout << "位图压缩类型: " << bitMapInfoHeader.biCompression << endl; cout << "位图每像素占用位数: " << bitMapInfoHeader.biBitCount << endl; cout << "位图数据占用字节数: " << bitMapInfoHeader.biSizeImage << endl; if (8 == bitMapInfoHeader.biBitCount) { pRGBQUAD = new RGBQUAD[256]; fread(pRGBQUAD, sizeof(RGBQUAD), 256, pf); } //数据每行字节数为4的倍数 int lineByte = (bitMapInfoHeader.biWidth * bitMapInfoHeader.biBitCount / 8 + 3) / 4 * 4; pData = new unsigned char[bitMapInfoHeader.biHeight * lineByte]; fread(pData, sizeof(unsigned char), bitMapInfoHeader.biHeight * lineByte, pf); fclose(pf); return true; } bool writeBmpFile(char * filename, unsigned char * pData, int biWidth, int biHeight, int biBitCount) { FILE * pf; pf = fopen(filename, "wb"); if (NULL == pf) { cout << "文件打开失败!" << endl; fclose(pf); return false; } int colorTablesize = 0; if (biBitCount == 8) colorTablesize = 1024; //待存储图像数据每行字节数为4的倍数 int lineByte = (biWidth * biBitCount / 8 + 3) / 4 * 4; //申请位图文件头结构变量,填写文件头信息 BITMAPFILEHEADER bitMapFileHeader; bitMapFileHeader.bfType = 0x4D42;//bmp类型 bitMapFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTablesize + lineByte*biHeight; bitMapFileHeader.bfReserved1 = 0; bitMapFileHeader.bfReserved2 = 0; bitMapFileHeader.bfOffBits = 54 + colorTablesize; //申请位图信息头结构变量,填写信息头信息 BITMAPINFOHEADER bitMapInfoHeader; bitMapInfoHeader.biBitCount = biBitCount; bitMapInfoHeader.biClrImportant = 0; bitMapInfoHeader.biClrUsed = 0; bitMapInfoHeader.biCompression = 0; bitMapInfoHeader.biHeight = biHeight; bitMapInfoHeader.biPlanes = 1; bitMapInfoHeader.biSize = 40; bitMapInfoHeader.biSizeImage = lineByte * biHeight; bitMapInfoHeader.biWidth = biWidth; bitMapInfoHeader.biXPelsPerMeter = 0; bitMapInfoHeader.biYPelsPerMeter = 0; //写文件头进文件 fwrite(&bitMapFileHeader, sizeof(BITMAPFILEHEADER), 1, pf); //写位图信息头进内存 fwrite(&bitMapInfoHeader, sizeof(BITMAPINFOHEADER), 1, pf); //如果灰度图像,有颜色表,写入文件 if (biBitCount == 8) { fwrite(pRGBQUAD, sizeof(RGBQUAD), 256, pf); } fwrite(pData, sizeof(unsigned char), biHeight * lineByte, pf); fclose(pf); return true; } int main() { char * toReadfilename = "read.bmp"; bool ret = readBmpFile(toReadfilename); if (!ret) { cout << "读BMP文件失败!" << endl; return -1; } else { cout << "读BMP文件成功!" << endl; } cout << endl; char * toWritefilename = "write.bmp"; ret = writeBmpFile(toWritefilename, pData, width, height, bitCount); if (!ret) { cout << "写BMP文件失败!" << endl; return -1; } else { cout << "写BMP文件成功!" << endl; } delete[] pRGBQUAD; pRGBQUAD = NULL; delete[] pData; pData = NULL; return 0; }
9fa9
参考文献:
1. http://www.cnblogs.com/lyy289065406/archive/2011/08/25/2153030.html
相关文章推荐
- C/C++实现bmp文件读写
- 学习笔记之C++为什么将函数声明或者类的定义放在.h文件中,而将其实现放在原文件中
- c++实现bmp文件的读写
- PE文件学习系列笔记四-C++实现PE文件的分析
- Java学习笔记---实现文件随机读写-RandomAccessFile
- Android基础学习笔记之-基本文件读写实现
- c++学习笔记之 文件的读写操作
- 一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记 用C++/lua/python/bash的四重实现(1) f(x) = 2f(x-1) + x^2
- 一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记 用C++/lua/python/bash的四重实现(2) IntCell类
- C++程序设计语言特别版 学习笔记(0)
- 一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记 用C++/lua/python/bash的四重实现(3) 最大子序列和问题
- MonoRail学习笔记二十:资源文件的使用和多语言支持
- 《面向对象基础:C++实现》学习笔记之六
- 《面向对象基础:C++实现》学习笔记之三
- 一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记 用C++/lua/python/bash的四重实现(4)二分搜索算法
- 一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记 习题2.8 随机数组的三种生成算法(补) 将bash的实现翻译成比较纯正的bash风格
- struts2学习笔记之转换器实现语言切换
- 孙鑫VC学习笔记:第十二讲 用C语言函数读写文件
- [学习笔记]Java 中对文件的读写操作之比较
- Java IO 实现文件复制 -Java 学习笔记 (25)