您的位置:首页 > 其它

DIB位图文件的格式、读取、保存和显示

2011-05-25 15:39 337 查看
一、位图文件结构

位图文件由三部分组成:文件头 + 位图信息 + 位图像素数据

1、位图文件头。位图文件头主要用于识别位图文件。以下是位图文件头结构的定义:

typedef struct tagBITMAPFILEHEADER { // bmfh
WORD    bfType;
DWORD   bfSize;
WORD    bfReserved1;
WORD    bfReserved2;
DWORD   bfOffBits;
} BITMAPFILEHEADER;


其中的bfType值应该是“BM”(0x4d42),标志该文件是位图文件。bfSize的值是位图文件的大小。

2、位图信息中所记录的值用于分配内存,设置调色板信息,读取像素值等。
以下是位图信息结构的定义:

typedef struct tagBITMAPINFO {
BITMAPINFOHEADER    bmiHeader;
RGBQUAD             bmiColors[1];
} BITMAPINFO;


可见位图信息也是由两部分组成的:位图信息头 + 颜色表

2.1位图信息头。位图信息头包含了单个像素所用字节数以及描述颜色的格式,此外还包括位图的宽度、高度、目标设备的位平面数、图像的压缩格式。以下是位图信息头结构的定义:

typedef struct tagBITMAPINFOHEADER{ // bmih
DWORD  biSize;
LONG   biWidth;
LONG   biHeight;
WORD   biPlanes;
WORD   biBitCount
DWORD  biCompression;
DWORD  biSizeImage;
LONG   biXPelsPerMeter;
LONG   biYPelsPerMeter;
DWORD  biClrUsed;
DWORD  biClrImportant;
} BITMAPINFOHEADER;


下表是对结构体当中各个成员的说明:
结构成员
说 明
biSize 结构BITMAPINFOHEADER的字节数,即sizeof(BITMAPINFOHEADER)*
biWidth
以像素为单位的图像宽度*
biHeight
以像素为单位的图像长度*
biplanes
目标设备的位平面数
biBitCount
每个像素的位数*(1)
biCompression
图像的压缩格式(这个值几乎总是为0)
biSizeImage
以字节为单位的图像数据的大小(对BI_RGB压缩方式而言)
biXPelsPermeter
水平方向上的每米的像素个数
biYpelsPerMeter
垂直方向上的每米的像素个数
biClrused
调色板中实际使用的颜色数(2)
biClrImportant
现实位图时必须的颜色数(3)

说明:*是需要加以注意的部分,因为它们是我们在进行位图操作时经常参考的变量
(1)对于每个像素的字节数,分别有一下意义:
0,用在JPEG格式中
1,单色图,调色板中含有两种颜色,也就是我们通常说的黑白图片
4,16色图
8,256色图,通常说的灰度图
16,64K图,一般没有调色板,图像数据中每两个字节表示一个像素,5个或6个位表示一个RGB分量
24,16M真彩色图,一般没有调色板,图像数据中每3个字节表示一个像素,每个字节表示一个RGB分量
32,4G真彩色,一般没有调色板,每4个字节表示一个像素,相对24位真彩图而言,加入了一个透明度,即RGBA模式

(2)这个值通常为0,表示使用biBitCount确定的全部颜色,例外是使用的颜色数目小于制定的颜色深度的颜色数目的最大值。

(3)这个值通常为0,表示所有的颜色都是必需的

2.2颜色表。颜色表一般是针对16位一下的图像而设置的,对于16位和16位以上的图像,由于其位图像素数据中直接对对应像素的RGB(A)颜色进行描述,因而省却了调色板。而对于16位一下的

图像,由于其位图像素数据中记录的只是调色板索引值,因而需要根据这个索引到调色板去取得相应的RGB(A)颜色。颜色表的作用就是创建调色板。

下图是带调色板和不带调色板的位图的简单示意图

图1 带调色板和不带调色板位图之间的区别

颜色表是由颜色表项组成的,颜色表项结构的定义如下:

typedef struct tagRGBQUAD { // rgbq
BYTE    rgbBlue;
BYTE    rgbGreen;
BYTE    rgbRed;
BYTE    rgbReserved;
} RGBQUAD;


其中需要注意的问题是,RGBQUAD结构中的颜色顺序是BGR,而不是平常的RGB。

3、位图数据。最后,在位图文件头、位图信息头、位图颜色表之后,便是位图的主体部分:位图数据。根据不同的位图,位图数据所占据的字节数也是不同的,比如,对于8位位图,每个字

节代表了一个像素,对于16位位图,每两个字节代表了一个像素,对于24位位图,每三个字节代表了一个像素,对于32位位图,每四个字节代表了一个像素

二、位图文件的读取、保存和显示

头文件中定义

private:
DWORD m_dwDibSize;
CPalette m_Palette;
int m_nPaletteEntries;
RGBQUAD *m_pPalette;
unsigned char *m_pDib, *m_pDibBits;
BITMAPINFOHEADER *m_pBIH;


1、读取位图

//加载图片
void CImageDisposeDlg::OnBtnloadimage()
{
// TODO: Add your control notification handler code here

//文件路径名称
CString pszFilename;

//浏览文件对话框
CFileDialog hFileDlg(TRUE,"bmp",
NULL,
OFN_FILEMUSTEXIST|OFN_READONLY|OFN_PATHMUSTEXIST|OFN_NOCHANGEDIR,
TEXT("BMP (*.bmp)|*.bmp|所有文件(*.*)|*.*|"),
NULL);

if(hFileDlg.DoModal() == IDOK)
{
//获得文件路径名称
pszFilename=hFileDlg.GetPathName();
}

//文件类
CFile cf;

//文件打开失败,程序返回
if( !cf.Open( pszFilename, CFile::modeRead ) )
{
return;
}

//获得位图信息文件大小
DWORD dwDibSize;
dwDibSize = cf.GetLength() - sizeof( BITMAPFILEHEADER );

//申请一块内存存放位图信息
unsigned char *pDib;
pDib = new unsigned char [dwDibSize];
if( pDib == NULL )
{
return;
}

//位图文件头
BITMAPFILEHEADER BFH;

//从文件读取位图文件头和位图数据
try{
// 判断读取位图文件头是否成功
if( cf.Read( &BFH, sizeof( BITMAPFILEHEADER ) )
!= sizeof( BITMAPFILEHEADER ) ||

// 判断是否是位图类型
BFH.bfType != 'MB' ||

// 判断读取位图数据是否成功
cf.Read( pDib, dwDibSize ) != dwDibSize ){

//释放位图数据指针
delete [] pDib;

//读取失败,程序返回
return;
}
}
catch( CFileException *e ){
e->Delete();
delete [] pDib;
return;
}

//重置全局位图信息指针
if( m_pDib != NULL )
{
delete m_pDib;
}

//将位图信息指针和位图信息大小赋值给全局变量
m_pDib = pDib;
m_dwDibSize = dwDibSize;

//获取位图信息头指针
m_pBIH = (BITMAPINFOHEADER *) m_pDib;

//获取位图调色板指针
m_pPalette = (RGBQUAD *) &m_pDib[sizeof(BITMAPINFOHEADER)];

// 计算调色板中实际颜色数量
m_nPaletteEntries = 1 << m_pBIH->biBitCount;//1左移
if( m_pBIH->biBitCount > 8 )
{
m_nPaletteEntries = 0;
}
else if( m_pBIH->biClrUsed != 0 )
{
m_nPaletteEntries = m_pBIH->biClrUsed;
}

// 获取位图数据指针
m_pDibBits = &m_pDib[sizeof(BITMAPINFOHEADER)+m_nPaletteEntries*sizeof(RGBQUAD)];

// 重置全局调色板
if( m_Palette.GetSafeHandle() != NULL )
{
m_Palette.DeleteObject();
}

//如果调色板颜色数量不为零,则通过逻辑调色板创建调色板
if( m_nPaletteEntries != 0 ){

//为逻辑调色板分配内存
LOGPALETTE *pLogPal = (LOGPALETTE *) new char
[sizeof(LOGPALETTE)+
m_nPaletteEntries*sizeof(PALETTEENTRY)];

if( pLogPal != NULL ){

//设置逻辑调色板的版本
pLogPal->palVersion = 0x300;
//设置逻辑调色板的颜色数量
pLogPal->palNumEntries = m_nPaletteEntries;

//为每个颜色实体赋颜色值
for( int i=0; i<m_nPaletteEntries; i++ ){
pLogPal->palPalEntry[i].peRed =
m_pPalette[i].rgbRed;
pLogPal->palPalEntry[i].peGreen =
m_pPalette[i].rgbGreen;
pLogPal->palPalEntry[i].peBlue =
m_pPalette[i].rgbBlue;
}

//创建调色板
m_Palette.CreatePalette( pLogPal );
//释放内存
delete [] pLogPal;
}
}
//重绘
Invalidate();
}


2、保存位图

//保存图片
void CImageDisposeDlg::OnBtnsave()
{
// TODO: Add your control notification handler code here
//文件路径名称
CString pszFilename;

//浏览文件对话框
CFileDialog hFileDlg(FALSE,"bmp",
NULL,
OFN_FILEMUSTEXIST|OFN_READONLY|OFN_PATHMUSTEXIST|OFN_NOCHANGEDIR,
TEXT("BMP (*.bmp)|*.bmp|所有文件(*.*)|*.*|"),
NULL);

if(hFileDlg.DoModal() == IDOK)
{
//获得文件路径名称
pszFilename=hFileDlg.GetPathName();
}

// 如果位图信息为空则程序返回
if( m_pDib == NULL )
return;

//文件类
CFile cf;

// 创建文件
if( !cf.Open( pszFilename,
CFile::modeCreate | CFile::modeWrite ) )
return;

// 写入数据
try{

//创建位图文件头
BITMAPFILEHEADER BFH;
memset( &BFH, 0, sizeof( BITMAPFILEHEADER ) );
BFH.bfType = 'MB';
BFH.bfSize = sizeof( BITMAPFILEHEADER ) + m_dwDibSize;
BFH.bfOffBits = sizeof( BITMAPFILEHEADER ) +
sizeof( BITMAPINFOHEADER ) +
m_nPaletteEntries * sizeof( RGBQUAD );

//将数据写入文件
cf.Write( &BFH, sizeof( BITMAPFILEHEADER ) );
cf.Write( m_pDib, m_dwDibSize );
}
catch( CFileException *e ){
e->Delete();
return;
}

}


3、显示位图

void CImageDisposeDlg::OnPaint()
{

CPaintDC dc(this);

//如果位图信息为空,程序返回
if( m_pDib == NULL )
return;

//获得位图宽高
int nWidth,nHeight;
nWidth = m_pBIH->biWidth;
nHeight = m_pBIH->biHeight;

//如果有调色板则使用调色板
if( m_Palette.GetSafeHandle() != NULL )
{
CPalette *pOldPalette;
pOldPalette = dc.SelectPalette( &m_Palette, FALSE );//选择调色板
dc.RealizePalette();//实现调色板

//绘图
StretchDIBits( dc.m_hDC, 0, 0,
nWidth, nHeight,
0, 0,
m_pBIH->biWidth, m_pBIH->biHeight,
m_pDibBits,
(BITMAPINFO *) m_pBIH,
BI_RGB, SRCCOPY );

//恢复调色板
dc.SelectPalette( pOldPalette, FALSE );
}
else//没有调色板,直接绘制
{
StretchDIBits( dc.m_hDC, 0, 0,
nWidth, nHeight,
0, 0,
m_pBIH->biWidth, m_pBIH->biHeight,
m_pDibBits,
(BITMAPINFO *) m_pBIH,
BI_RGB, SRCCOPY );
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: