这几天一直都在忙关于 BMP 的操作问题,以前从来不知道它是个什么东东,所以感觉很吃力。到现在为止很多东西还是不明白,期间也怀疑自己能否胜任这样的学习,但是想想做什么容易那,做任何事情都是需要付出努力的啊。所以就把这些天一些微小的收获拿出来和大家共享。
之前听祝勇讲过一次关于BMP 的课,但是那时云里雾里的不知所云,只知道是图象的一种格式包含几个部分:文件头/信息头/颜色表/位图数据。
下面把我找到的一些资料归结一下:
1. BMP文件组成
BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。
2. BMP文件头
BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。
其结构定义如下:
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; // 位图文件的类型,必须为BM
DWORD bfSize; // 位图文件的大小,以字节为单位
WORD bfReserved1; // 位图文件保留字,必须为0
WORD bfReserved2; // 位图文件保留字,必须为0
DWORD bfOffBits; // 位图数据的起始位置,以相对于位图
// 文件头的偏移量表示,以字节为单位
} BITMAPFILEHEADER;
3. 位图信息头
BMP位图信息头数据用于说明位图的尺寸等信息。
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // 本结构所占用字节数
LONG biWidth; // 位图的宽度,以像素为单位
LONG biHeight; // 位图的高度,以像素为单位
WORD biPlanes; // 目标设备的级别,必须为1
WORD biBitCount// 每个像素所需的位数,必须是1(双色),
// 4(16色),8(256色)或24(真彩色)之一
DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),
// 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
DWORD biSizeImage; // 位图的大小,以字节为单位
LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数
LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数
DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数
DWORD biClrImportant;// 位图显示过程中重要的颜色数
} BITMAPINFOHEADER;
4. 颜色表
颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
typedef struct tagRGBQUAD {
BYTE rgbBlue;// 蓝色的亮度(值范围为0-255)
BYTE rgbGreen; // 绿色的亮度(值范围为0-255)
BYTE rgbRed; // 红色的亮度(值范围为0-255)
BYTE rgbReserved;// 保留,必须为0
} RGBQUAD;
颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
当biBitCount=1,4,8时,分别有2,16,256个表项;
当biBitCount=24时,没有颜色表项。
位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader; // 位图信息头
RGBQUAD bmiColors[1]; // 颜色表
} BITMAPINFO;
5. 位图数据
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;
Windows规定一个扫描行所占的字节数必须是
4的倍数(即以long为单位),不足的以0填充,
一个扫描行所占的字节数计算方法:
DataSizePerLine= (biWidth* biBitCount+31)/8;
// 一个扫描行所占的字节数
DataSizePerLine= DataSizePerLine/4*4; // 字节数必须是4的倍数
位图数据的大小(不压缩情况下):
DataSize= DataSizePerLine* biHeight;
看了这些以后又对位图有了一些更加深入的理解,也算是收获把。
关于位图的打开与显示问题有下面这些了解:
显示一张位图,基本的有以下三种形式:
一、位图资源的显示(设资源 ID 为 IDB——BITMAP1 程序如下)
{
CBitmap bitmap;
CBitmap* pOldBitmap;
CDC MemDC;
MemDC.CreateCompatibleDC(&dc); /// 建立与显示设备兼容的内存设备场境
bitmap.LoadBitmap(IDB_BITMAP1); /// 取出位图资源
pOldBitmap=MemDC.SelectObject(&bitmap); /// 将位图选入内存场境
dc.BitBlt(50,50,48,48,&MemDC,0,0,SRCCOPY); /// 显示它
MemDC.SelectObject(pOldBitmap);
}
二、创建位图,然后在里面画上图形,再显示它
{
CBitmap bitmap;
CBitmap* pOldBitmap;
CDC MemDC;
int i;
CPen pen;
CPen* oldpen;
MemDC.CreateCompatibleDC(&dc); /// 建立与显示设备兼容的内存设备场境
bitmap.CreateCompatibleBitmap(&dc,100,100);/// 建立与显示设备兼容的位图
pOldBitmap=MemDC.SelectObject(&bitmap); /// 将位图选入内存场境
for(i=0;i<100;i++)
{ /// 画 100 根线
pen.CreatePen(PS_SOLID,1, RGB(0,0,i*2+55));
oldpen=MemDC.SelectObject(&pen);
MemDC.MoveTo(0,i);
MemDC.LineTo(100,i);
MemDC.SelectObject(oldpen);
pen.DeleteObject();
}
dc.BitBlt(50,50,100,100,&MemDC,0,0,SRCCOPY); /// 显示它
MemDC.SelectObject(pOldBitmap);
}
三、显示磁盘上的外部 BMP 文件
{
HBITMAP bitmap;
/ 读取位图文件 SAMPLE.BMP
bitmap=(HBITMAP)LoadImage(AfxGetInstanceHandle(),"SAMPLE.BMP",IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);
ASSERT(bitmap);
HBITMAP OldBitmap;
CDC MemDC;CRect rect;
MemDC.CreateCompatibleDC(&dc);
GetClientRect(rect);
OldBitmap=(HBITMAP)MemDC.SelectObject(bitmap);
/// 显示它
dc.BitBlt(20,20,rect.Width()-20,rect.Height()-20,&MemDC,0,0,SRCCOPY);
MemDC.SelectObject(OldBitmap);
}
但是又查了一些资料说打开位图用的多的方法还有建立一个 cdib类,类里面封装了所需要的函数:
#if !defined(AFX_FG_DIB_H__873E62A0_D6E8_4151_9F0F_815768446735__INCLUDED_)
#define AFX_FG_DIB_H__873E62A0_D6E8_4151_9F0F_815768446735__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CFG_DIB : public CObject
{
public:
//默认构造函数
CFG_DIB();
//构造函数,根据图象宽和高,以及记录每个象素所需字节数来初始化
CFG_DIB(int width, int height, int nBitCounts);
virtual ~CFG_DIB();
public:
HBITMAP m_hBitmap;
LPBYTE m_lpDIBits; //DIB位的起始位置
LPBITMAPINFOHEADER m_lpBMPHdr; //BITMAPINFOHEADER信息
LPVOID m_lpvColorTable; //颜色表信息
HPALETTE m_hPalette; //调色板
int m_nMode; //内存模式,0为普通模式,1为内存映射模式
private:
DWORD m_dwImageSize; //非BITMAPINFOHEADER或BITMAPFILEHEADER的位
int m_nColorEntries; //颜色表项的个数
HANDLE m_hFile;
HANDLE m_hMap;
LPVOID m_lpvFile;
//显示参数
public:
CPoint m_Dest; //目的矩形域的左上角坐标
CSize m_DestSize; //显示矩形的宽度和高度
CPoint m_Src; //原矩形左下角坐标
CSize m_SrcSize; //原矩形宽度和高度
public:
void InitDestroy(); //初始化变量
void ComputePaletteSize(int nBitCounts); //计算调色板大小
void ComputeImage(); //计算图象大小
//从BMP文件中读入DIB信息
BOOL ReadFile(CFile* pFile);
//从BMP文件中读入DIB信息,与ReadFile不同的是使用CreateSection创建位图位
BOOL ReadSection(CFile* pFile, CDC* pDC = NULL);
//将DIB写入文件,保存成BMP图片格式
BOOL WriteFile(CFile* pFile);
//创建新的位图文件,根据参数width,height,nBitCounts分配内存空间
BOOL NewFile(int width, int height, int nBitCounts);
//关闭位图文件
BOOL CloseFile();
//显示位图
BOOL Display(CDC* pDC);
HBITMAP CreateBitmap(CDC* pDC); //用DIB创建DDB
HBITMAP CreateSection(CDC* pDC = NULL); //创建位图位数据,即象素数据
//如果DIB没有颜色表,可以用逻辑调色板
BOOL SetLogPalette(CDC* pDC);
//如果DIB有颜色表,可以创建系统调色板
BOOL SetWinPalette();
//把DIB对象的逻辑调色板选进设备环境里,然后实现调色板
UINT UseLogPalette(CDC* pDC);
//得到BitmapInfoHeader的大小,包含颜色表数据
int GetHeaderSize()
{
return sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorEntries;
}
//得到图像的高度
int GetHeight()
{
if(m_lpBMPHdr == NULL) return 0;
return m_lpBMPHdr->biHeight;
}
//得到图像的宽度
int GetWidth()
{
if(m_lpBMPHdr == NULL) return 0;
return m_lpBMPHdr->biWidth;
}
//得到图像的大小
int GetImageSize()
{
return m_dwImageSize;
}
long GetLineBit(); //得到一行的象素数
void SetMode(int mode); //设置内存模式
void ReleaseMapFile(); //释放内存映射文件
};
#endif // !defined(AFX_FG_DIB_H__873E62A0_D6E8_4151_9F0F_815768446735__INCLUDED_)
心里很郁闷啊任务还没有完成,不说了 ,今天就写到这里!!希望大家多帮助!!在此先谢过了!!