您的位置:首页 > 其它

MFC图像处理-DIB位图之CDib类

2013-07-03 09:41 225 查看
位图处理是图像处理中很重要的一个知识点,本章是编写一个基本的DIB位图信息处理的类CDib类,在此之前必须了解DIB位图的文件结构,建议大家先找资料学一下。

首先在你要编写CDib类的工程的ClassView中右击工程名称,然后点击New Class,记住,我用的是MFC
AppWizard项目。

然后在弹出的New Class对话框中的Class type下拉列表中选中Generic Class,在Name中输入该类的名称,我这里是CDib。在Base
class中Derived From中输入CObject,As保持public,点击OK就成功在你的工程中添加了CDib类了。

创建好新类后我们就可以开始编写了。

首先必须明确我们编写这个类要实现哪些基本的功能,下面是我写的类中包含的功能,如果有需要,可以从这个CDib类中派生其他类以实现其他功能:

void LoadFile(const TCHAR * dibFileName);		// 载入BMP位图文件
BOOL SaveFile(const TCHAR * pszFileName);		// 保存BMP位图文件
TCHAR * GetFileName();							// 获取BMP位图文件名
DWORD GetSize();								// 获取位图文件的大小
UINT GetWidth();								// 获取位图宽度像素数
UINT GetHeight();								// 获取位图高度像素数
UINT GetNumberOfColors();						// 获取位图颜色数目
RGBQUAD * GetRGB();								// 获取颜色表指针
DIBINFO * GetDibInfo();							// 获取位图信息结构的指针
BYTE * GetDibData();							// 获取位图数据指针
BOOL IsValid();									// 判断是否载入了位图文件,对象是否可用
DWORD GetDibWidthBytes();						// 获取位图的宽度字节数
DWORD PaletteSize(LPBYTE lpDib);				// 获取位图指针指向的位图的调色板的字节大小
WORD DibNumberColors(LPBYTE lpDib);				// 获取位图指针指向的位图的颜色数目


在这个类中我加入了以下成员变量:

RGBQUAD * m_pRGB;							// 位图颜色表指针
BYTE * m_pData;								// 位图数据指针
UINT m_numberOfColors;						// 位图颜色数目
BOOL m_bValid;								// 记录是否载入了位图文件
DIBFILEHEADER m_dibFileHeader;				// 位图文件头
DIBINFOHEADER * m_pDibInfoHeader;			// 位图信息头指针
DIBINFO	* m_pDibInfo;						// 位图信息指针
UINT m_byBitCount;
DWORD m_dwWidthBytes;
BYTE * m_pDib;								// 文件中位图总数据指针
DWORD m_dwDib;								// 位图总数据的长度
TCHAR m_fileName[256];						// 记录文件名称


然后就是方法的实现部分:

CDib::CDib()
{
m_pDibInfo = NULL;
m_pDibInfoHeader = NULL;
m_pData = NULL;
m_pDib = NULL;
m_pRGB = NULL;
}

CDib::~CDib()
{
if(m_pDibInfo != NULL)
GlobalFreePtr(m_pDibInfo); // 释放在LoadFile中分配给m_pDib的内存资源
}

void CDib::LoadFile(const TCHAR * dibFileName)
{
// 判断是否传入文件名字符串,否则直接跳出函数
if(dibFileName == NULL)
return;

if(m_bValid)
{
GlobalFreePtr(m_pDibInfo);
m_pDibInfo = NULL;
m_pDibInfoHeader = NULL;
m_pData = NULL;
m_pDib = NULL;
m_pRGB = NULL;
}

_tcscpy(m_fileName, dibFileName);	// 复制文件名字符串
CFile dibFile(m_fileName, CFile::modeRead); // 只读打开文件
dibFile.Read((void *) &m_dibFileHeader, sizeof(DIBFILEHEADER));	// 读取文件信息头数据

// 判断文件是否为位图文件
if(m_dibFileHeader.bfType == 0x4d42)
{
m_bValid = TRUE;

DWORD fileLength = dibFile.GetLength(); // 获取文件数据总长度
m_dwDib = fileLength - sizeof(DIBFILEHEADER); // 计算位图文件位图总数据长度
BYTE * m_pDib =	(BYTE *) GlobalAllocPtr(GMEM_MOVEABLE, m_dwDib); // 分配相应的内存资源

dibFile.Read((void *) m_pDib, m_dwDib); // 读取文件中位图总数据
dibFile.Close(); // 关闭位图文件

m_pDibInfo = (DIBINFO *) m_pDib; // 指向位图信息,位图信息包括信息头和颜色表
m_pDibInfoHeader = (DIBINFOHEADER *) m_pDib; // 指向位图信息头
m_pRGB = (RGBQUAD *)(m_pDib + m_pDibInfoHeader->biSize); // 指向位图颜色表,指针沿数据向后移动m_pDibInfoHeader->biSize个字节

int m_numberOfColors = GetNumberOfColors(); // 获取颜色数目
if(m_pDibInfoHeader->biClrUsed == 0)
m_pDibInfoHeader->biClrUsed = m_numberOfColors; // 如果信息头没有记录使用的颜色数目,重新设置

DWORD colorTableSize = m_numberOfColors * sizeof(RGBQUAD); // 计算颜色表大小

if(colorTableSize == 0) // 没有颜色表
m_pRGB = NULL;

m_pData = m_pDib + m_pDibInfoHeader->biSize + colorTableSize; // 指向位图数据

m_pDibInfoHeader->biSizeImage = GetSize(); // 在信息头中设置文件大小值
}
else
{// 打开的文件不是位图文件
m_bValid = FALSE;
AfxMessageBox("这不是一个位图文件!");
}
}

BOOL CDib::IsValid()
{// 判断是否打开了文件
return m_bValid;
}

TCHAR * CDib::GetFileName()
{// 文件已打开就可以获取文件名
if(IsValid())
return m_fileName;

return NULL;
}

UINT CDib::GetWidth()
{// 如果信息头不为NULL,可以获取位图宽度像素数
if(m_pDibInfoHeader != NULL)
return (UINT) m_pDibInfoHeader->biWidth;

return NULL;
}

UINT CDib::GetHeight()
{// 如果信息头不为NULL,可以获取位图高度像素数
if(m_pDibInfoHeader != NULL)
return (UINT) m_pDibInfoHeader->biHeight;

return NULL;
}

DWORD CDib::GetSize()
{
if(IsValid()) // 位图文件没有打开,返回0
return 0;
else if(m_pDibInfoHeader->biSizeImage != 0)
{// 位图文件打开了,并且信息头的位图数据大小不为0就直接返回biSizeImage
return m_pDibInfoHeader->biSizeImage;
}
else
{// 文件已打开,但是信息头记录的位图大小为0,重新计算并返回位图数据大小
DWORD height = (DWORD)GetHeight();
DWORD width = (DWORD)GetWidth();
return height * width;
}
}

DWORD CDib::GetDibWidthBytes()
{// 返回位图的宽度字节数
if(!m_bValid)
return 0; // 位图对象无效直接返回0

// 获取位图像素位数和位图宽度像素数
m_byBitCount = m_pDibInfoHeader->biBitCount;
LONG nWidth = m_pDibInfoHeader->biWidth;

m_dwWidthBytes = (DWORD)m_pDibInfoHeader->biWidth; // 如果位图的每个像素的位数是8,那么位图的字节数等于像素数
if(m_byBitCount == 1) m_dwWidthBytes = (nWidth + 7) / 8; // 如果位图的每个像素的位数是1,那么一个字节包含8个像素
else if(m_byBitCount == 4) m_dwWidthBytes = (nWidth + 1) / 2; // 如果位图的每个像素的位数是4,那么一个字节包含2个像素
else if(m_byBitCount == 24) m_dwWidthBytes = 3 * nWidth; // 如果位图的每个像素的位数是24,那么3个字节为一个像素

while((m_dwWidthBytes & 3) != 0) m_dwWidthBytes++; //  位图的宽度字节数总是4的倍数

return m_dwWidthBytes;
}

UINT CDib::GetNumberOfColors()
{
if(!m_bValid)
return 0; // 位图对象无效直接返回0

int numberOfColors;

if((m_pDibInfoHeader->biClrUsed == 0) &&
(m_pDibInfoHeader->biBitCount < 9))
{// 位图信息头的颜色数目为零,并且颜色位数小于9,
// 从颜色位数判断颜色数目
switch(m_pDibInfoHeader->biBitCount)
{
case 1: numberOfColors = 2; break;
case 4: numberOfColors = 16; break;
case 8: numberOfColors = 256;
}
}
else
{// 位图信息头的颜色数目不为零,直接获取颜色数目
numberOfColors = (int)m_pDibInfoHeader->biClrUsed;
}
return numberOfColors;
}

BYTE * CDib::GetDibData()
{// 返回位图数据
return m_pData;
}

RGBQUAD * CDib::GetRGB()
{// 返回位图颜色表
return m_pRGB;
}

DIBINFO * CDib::GetDibInfo()
{// 返回位图信息
return m_pDibInfo;
}

BOOL CDib::SaveFile(const TCHAR *pszFileName)
{
if(pszFileName == NULL)
return FALSE; // 如果文件名为空,直接返回FALSE

DIBFILEHEADER dibFileHeader; // 位图文件头
LPDIBINFOHEADER lpDibInfoHeader; // 位图信息头指针
DWORD dwDibSize; // 位图数据大小

dibFileHeader.bfType = 0x4d42; // 0x4d42编码指定文件是位图格式
lpDibInfoHeader = (LPDIBINFOHEADER) m_pDibInfoHeader; // 取得位图信息头指针
dwDibSize = *(LPWORD) lpDibInfoHeader + PaletteSize((LPBYTE) lpDibInfoHeader); // 计算位图信息头数据的大小

if((lpDibInfoHeader->biCompression == BI_RLE8) || (lpDibInfoHeader->biCompression == BI_RLE4))
{// 若位图是经过RLE压缩的,直接加上biSizeImage储存的位图空间量
dwDibSize += lpDibInfoHeader->biSizeImage;
}
else
{// 若位图不是经过RLE压缩的,通过算法(biWidth*biBitCount+31) / 32 * 4 * biHeight算出biSizeImage的值
DWORD dwDibBitsSize;
dwDibBitsSize = WIDTHBYTES((lpDibInfoHeader->biWidth) *
((DWORD) lpDibInfoHeader->biBitCount)) * lpDibInfoHeader->biHeight;
dwDibSize += dwDibBitsSize;
}

dibFileHeader.bfSize = dwDibSize + sizeof(DIBFILEHEADER); // 加上文件头大小就为位图文件的大小
dibFileHeader.bfReserved1 = 0;
dibFileHeader.bfReserved2 = 0;
dibFileHeader.bfOffBits = (DWORD)sizeof(DIBFILEHEADER) +			// 计算位图数据距文件头的偏移量
lpDibInfoHeader->biSize + PaletteSize((LPBYTE) lpDibInfoHeader);

// 创建新位图文件,并写入位图文件信息和数据
CFile dibFile(pszFileName, CFile::modeWrite | CFile::modeCreate);
dibFile.Write(&dibFileHeader, sizeof(DIBFILEHEADER));
dibFile.WriteHuge(lpDibInfoHeader, dwDibSize);
dibFile.Close();

return TRUE;
}

DWORD CDib::PaletteSize(LPBYTE lpDib)
{// 如果位图指针有效,返回位图调色板的字节大小,
// 否则返回0
if(lpDib != NULL)
return (DibNumberColors(lpDib) * sizeof(COLORREF)); // 这里书中是用sizeof(RGBTRIPLE),但是好像用sizeof(COLORREF)才是准确的,何解???

return 0;
}

WORD CDib::DibNumberColors(LPBYTE lpDib)
{// 如果位图指针有效,返回根据bcBitCount的值返回位图的颜色数目
if(lpDib == NULL)
return 0;

WORD wBitCount;
wBitCount = ((LPDIBINFOHEADER)lpDib)->biBitCount; // 书中这里是用LPBITMAPCOREHEADER指针,但经过我的验证,好像不行,原本lpDib传进来的就是LPBITMAPINFOHEADER指针,直接用LPBITMAPINFOHEADER不是更准确吗?

switch(wBitCount)
{
case 1:
return 2;
case 4:
return 16;
case 8:
return 256;
default:
return 0;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: