www.pudn.com > ImageProcessing.rar > cdib.cpp
// cdib.cpp
#include "stdafx.h"
#include "math.h"
#include "process.h"
#include "cdib.h"
#include "GlobalApi.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// 声明串行化过程
IMPLEMENT_SERIAL(CDib, CObject, 0);
/*************************************************************************
*
* \函数名称:
* CDib()
*
* \输入参数:
* 无
*
* \返回值:
* 无
*
* \说明:
* 构造函数
*
************************************************************************
*/
CDib::CDib()
{
m_hFile = NULL;
m_hBitmap = NULL;
m_hPalette = NULL;
m_nBmihAlloc = m_nImageAlloc = noAlloc;
Empty();
}
/*************************************************************************
*
* \函数名称:
* CDib()
*
* \输入参数:
* CSize size - 位图尺寸
* int nBitCount - 象素位数
*
* \返回值:
* 无
*
* \说明:
* 构造函数
* 根据给定的位图尺寸和象素位数构造CDib对象,并对信息头和调色板分配内存
* 但并没有给位图数据分配内存
*
************************************************************************
*/
CDib::CDib(CSize size, int nBitCount)
{
m_hFile = NULL;
m_hBitmap = NULL;
m_hPalette = NULL;
m_nBmihAlloc = m_nImageAlloc = noAlloc;
Empty();
// 根据象素位数计算调色板尺寸
ComputePaletteSize(nBitCount);
// 分配DIB信息头和调色板的内存
m_lpBMIH = (LPBITMAPINFOHEADER) new
char[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries];
// 设置信息头内存分配状态
m_nBmihAlloc = crtAlloc;
// 设置信息头中的信息
m_lpBMIH->biSize = sizeof(BITMAPINFOHEADER);
m_lpBMIH->biWidth = size.cx;
m_lpBMIH->biHeight = size.cy;
m_lpBMIH->biPlanes = 1;
m_lpBMIH->biBitCount = nBitCount;
m_lpBMIH->biCompression = BI_RGB;
m_lpBMIH->biSizeImage = 0;
m_lpBMIH->biXPelsPerMeter = 0;
m_lpBMIH->biYPelsPerMeter = 0;
m_lpBMIH->biClrUsed = m_nColorTableEntries;
m_lpBMIH->biClrImportant= m_nColorTableEntries;
// 计算图象数据内存的大小,并设置此DIB的调色板的指针
ComputeMetrics();
// 将此DIB的调色板初始化为0
memset(m_lpvColorTable, 0, sizeof(RGBQUAD) * m_nColorTableEntries);
// 暂时不分配图象数据内存
m_lpImage = NULL;
}
/*************************************************************************
*
* \函数名称:
* ~CDib()
*
* \输入参数:
* 无
*
* \返回值:
* 无
*
* \说明:
* 析构函数,并释放所有分配的DIB内存
*
************************************************************************
*/
CDib::~CDib()
{
Empty();
}
/*************************************************************************
*
* \函数名称:
* GetDimensions()
*
* \输入参数:
* 无
*
* \返回值:
* CSize - DIB的宽度和高度
*
* \说明:
* 返回以象素表示的DIB的宽度和高度
*
************************************************************************
*/
CSize CDib::GetDimensions()
{
if(m_lpBMIH == NULL) return CSize(0, 0);
return CSize((int) m_lpBMIH->biWidth, (int) m_lpBMIH->biHeight);
}
/*************************************************************************
*
* \函数名称:
* AttachMapFile()
*
* \输入参数:
* const char* strPathname - 映射文件的路径名
* BOOL bShare - 如果文件以共享形式打开,设置为TRUE
* - 默认值为FALSE
*
* \返回值:
* BOOL - 如果成功,则为TRUE
*
* \说明:
* 以读模式打开内存映射文件,并将其与CDib对象进行关联。因为在文件使用之前并没有读入内存,
* 所以它立即返回。不过,当访问这个DIB的时候,可能会有一些延迟,因为文件是分页的。
* DetachMapFile函数可以释放现有的已分配的内存,并关闭以前关联的任何内存映射文件。
* 用内存中的DIB与已有的CDib对象关联。此内存可能是程序的资源,或者是可能是剪贴板
* 或者OLE数据对象内存。内存可能已经由CRT堆栈用new运算符分配了,或者可能已经由
* Windows堆栈用GlobalAlloc分配了。
* 如果打开相同的文件两次,则Windows以另一个文件来对待
*
************************************************************************
*/
BOOL CDib::AttachMapFile(const char* strPathname, BOOL bShare) // for reading
{
// 获取文件句柄,并设置打开模式为共享
HANDLE hFile = ::CreateFile(strPathname, GENERIC_WRITE | GENERIC_READ,
bShare ? FILE_SHARE_READ : 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
ASSERT(hFile != INVALID_HANDLE_VALUE);
// 获取文件的尺寸
DWORD dwFileSize = ::GetFileSize(hFile, NULL);
// 创建文件映射对象,并设置文件映射的模式为读写
HANDLE hMap = ::CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
DWORD dwErr = ::GetLastError();
if(hMap == NULL) {
AfxMessageBox("Empty bitmap file");
return FALSE;
}
// 映射整个文件,注意FILE_MAP_WRITE为读写模式
LPVOID lpvFile = ::MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0); // map whole file
ASSERT(lpvFile != NULL);
// 确认为bmp格式文件
if(((LPBITMAPFILEHEADER) lpvFile)->bfType != 0x4d42) {
AfxMessageBox("Invalid bitmap file");
DetachMapFile();
return FALSE;
}
// 将内存中的DIB与已有的CDib对象关联
AttachMemory((LPBYTE) lpvFile + sizeof(BITMAPFILEHEADER));
// 将这些有用的句柄设置为类数据成员
m_lpvFile = lpvFile;
m_hFile = hFile;
m_hMap = hMap;
// 返回
return TRUE;
}
/*************************************************************************
*
* \函数名称:
* CopyToMapFile()
*
* \输入参数:
* const char* strPathname - 映射文件的路径名
*
* \返回值:
* BOOL - 如果成功,则为TRUE
*
* \说明:
* 该函数可以创建一个新的内存映射文件,并将现有的CDib数据复制到该文件的内存
* 释放以前的所有内存。并关闭现有的所有内存映射文件。实际上,直到新文件
* 关闭的时候,才将这个数据写到磁盘,但是,当CDib对象被重复使用或被破坏
* 时,也会发生写磁盘操作
*
************************************************************************
*/
BOOL CDib::CopyToMapFile(const char* strPathname)
{
BITMAPFILEHEADER bmfh;
// 设置文件头信息
bmfh.bfType = 0x4d42;
bmfh.bfSize = m_dwSizeImage + sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * m_nColorTableEntries + sizeof(BITMAPFILEHEADER);
bmfh.bfReserved1= bmfh.bfReserved2 = 0;
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * m_nColorTableEntries;
// 创建接收数据的文件
HANDLE hFile = ::CreateFile(strPathname, GENERIC_WRITE | GENERIC_READ, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
ASSERT(hFile != INVALID_HANDLE_VALUE);
// 计算文件的大小尺寸
int nSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * m_nColorTableEntries + m_dwSizeImage;
// 创建内存映射文件对象
HANDLE hMap = ::CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, nSize, NULL);
DWORD dwErr = ::GetLastError();
ASSERT(hMap != NULL);
// 映射整个文件
LPVOID lpvFile = ::MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
ASSERT(lpvFile != NULL);
// 临时文件指针
LPBYTE lpbCurrent = (LPBYTE) lpvFile;
// 拷贝文件头信息到内存映射文件中
memcpy(lpbCurrent, &bmfh, sizeof(BITMAPFILEHEADER));
// 计算信息头在文件中的地址,并拷贝信息头信息
lpbCurrent += sizeof(BITMAPFILEHEADER);
LPBITMAPINFOHEADER lpBMIH = (LPBITMAPINFOHEADER) lpbCurrent;
memcpy(lpbCurrent, m_lpBMIH,
sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries);
// 计算调色板在文件中的地址,并拷贝调色板
lpbCurrent += sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries;
memcpy(lpbCurrent, m_lpImage, m_dwSizeImage);
// 暂时存放图象数据尺寸变量
DWORD dwSizeImage = m_dwSizeImage;
// 释放一起分配的所有内存
Empty();
// 设置图象数据尺寸并设置内存分配状态
m_dwSizeImage = dwSizeImage;
m_nBmihAlloc = m_nImageAlloc = noAlloc;
// 信息头指针重新指向文件中的位置
m_lpBMIH = lpBMIH;
// 图象数据指针重新指向文件中的数据地址
m_lpImage = lpbCurrent;
// 设置文件句柄
m_hFile = hFile;
// 设置映射对象句柄
m_hMap = hMap;
// 设置映射文件指针
m_lpvFile = lpvFile;
// 重新计算得到调色板尺寸
ComputePaletteSize(m_lpBMIH->biBitCount);
// 重新计算图象数据块大小,并设置调色板指针
ComputeMetrics();
// 如果调色板存在的话,读取并创建一个Windows调色板
MakePalette();
// 返回
return TRUE;
}
/*************************************************************************
*
* \函数名称:
* AttachMemory()
*
* \输入参数:
* LPVOID lpvMem - 要关联的内存地址
* BOOL bMustDelete - 如果CDib类负责删除这个内存,标记为TRUE
* - 默认值为FALSE
* HGLOBAL hGlobal - 如果内存是通过Win32 GlobalAlloc得到的,
* - 则CDib对象必须保存该句柄,这样,以后
* - 可以释放句柄。这里假设bMustDelete设置为TRUE
*
* \返回值:
* BOOL - 如果成功,则为TRUE
*
* \说明:
* 用内存中的DIB与已有的CDib对象关联。此内存可能是程序的资源,或者是可能是剪贴板
* 或者OLE数据对象内存。内存可能已经由CRT堆栈用new运算符分配了,或者可能已经由
* Windows堆栈用GlobalAlloc分配了。
*
************************************************************************
*/
BOOL CDib::AttachMemory(LPVOID lpvMem, BOOL bMustDelete, HGLOBAL hGlobal)
{
// 首先释放已经分配的内存
Empty();
m_hGlobal = hGlobal;
// bMustDelete为TRUE表示此CDib类分配的内存,负责删除
// 否则的设置信息头分配状态为noAlloc
if(bMustDelete == FALSE) {
m_nBmihAlloc = noAlloc;
}
else {
m_nBmihAlloc = ((hGlobal == NULL) ? crtAlloc : heapAlloc);
}
try {
// 设置信息头指针
m_lpBMIH = (LPBITMAPINFOHEADER) lpvMem;
// 重新计算得到图象数据块的大小,并设置调色板的指针
ComputeMetrics();
// 计算调色板的尺寸
ComputePaletteSize(m_lpBMIH->biBitCount);
// 设置图象数据指针
m_lpImage = (LPBYTE) m_lpvColorTable + sizeof(RGBQUAD) * m_nColorTableEntries;
// 如果调色板存在的话,读取它,并创建一个Windows调色板,
// 并将调色板的句柄存放在数据成员中
MakePalette();
}
// 错误处理
catch(CException* pe) {
AfxMessageBox("AttachMemory error");
pe->Delete();
return FALSE;
}
// 返回
return TRUE;
}
/*************************************************************************
*
* \函数名称:
* UsePalette()
*
* \输入参数:
* CDC* pDC - 要实现调色板的设备上下文指针
* BOOL bBackground - 如果标记为FALSE(默认值),并且应用
* - 程序正在前台运行,则Windows将把该调
* - 色板作为前台调色板来实现(向系统调色
* - 板中复制尽可能多的颜色)。如果标记为
* - TURE,则Windows将把该调色板作为后台
* - 调色板来实现(尽可能相系统调色板映射
* - 逻辑调色板)
*
* \返回值:
* UINT - 如果成功,则返回映射到系统调色板的逻
* - 辑调色板的表项数,否则返回GDI_ERROR
*
* \说明:
* 该函数将CDib对象的逻辑调色板选入设备上下文,然后实现该调色板。Draw成员函
* 数在绘制DIB之前调用UsePalette。
*
************************************************************************
*/
UINT CDib::UsePalette(CDC* pDC, BOOL bBackground /* = FALSE */)
{
// 判断是否存在调色板
if(m_hPalette == NULL) return 0;
// 得到设备上下文句柄
HDC hdc = pDC->GetSafeHdc();
// 选择调色板到设备上下文
::SelectPalette(hdc, m_hPalette, bBackground);
// 实现该调色板
return ::RealizePalette(hdc);
}
/*************************************************************************
*
* \函数名称:
* Draw()
*
* \输入参数:
* CDC* pDC - 指向将要接收DIB图象的设备上下文指针
* CPoint origin - 显示DIB的逻辑坐标
* CSize size - 显示矩形的宽度和高度
*
* \返回值:
* BOOL - 如果成功,则为TRUE,
*
* \说明:
* 通过调用Win32 SDK的StretchDIBits函数将CDib对象输出到显示器(或者打印机)。
* 为了适合指定的矩形,位图可以进行必要的拉伸
*
************************************************************************
*/
BOOL CDib::Draw(CDC* pDC, CPoint origin, CSize size)
{
// 如果信息头为空,表示尚未有数据,返回FALSE
if(m_lpBMIH == NULL) return FALSE;
// 如果调色板不为空,则将调色板选入设备上下文
if(m_hPalette != NULL) {
::SelectPalette(pDC->GetSafeHdc(), m_hPalette, TRUE);
}
// 设置显示模式
pDC->SetStretchBltMode(COLORONCOLOR);
// 在设备的origin位置上画出大小为size的图象
::StretchDIBits(pDC->GetSafeHdc(), origin.x, origin.y,size.cx,size.cy,
0, 0, m_lpBMIH->biWidth, m_lpBMIH->biHeight,
m_lpImage, (LPBITMAPINFO) m_lpBMIH, DIB_RGB_COLORS, SRCCOPY);
// 返回
return TRUE;
}
/*************************************************************************
*
* \函数名称:
* CreateSection()
*
* \输入参数:
* CDC* pDC - 设备上下文指针
*
* \返回值:
* HBITMAP - 到GDI位图的句柄。如果不成功,则为NULL。
* - 该句柄也是作为公共数据成员存储的
*
* \说明:
* 通过调用Win32 SDK的CreateDIBSection函数创建一个DIB段。图象内存将不被初始化
*
************************************************************************
*/
HBITMAP CDib::CreateSection(CDC* pDC /* = NULL */)
{
// 如果信息头为空,不作任何处理
if(m_lpBMIH == NULL) return NULL;
// 如果图象数据不存在,不作任何处理
if(m_lpImage != NULL) return NULL;
// 创建一个DIB段
m_hBitmap = ::CreateDIBSection(pDC->GetSafeHdc(), (LPBITMAPINFO) m_lpBMIH,
DIB_RGB_COLORS, (LPVOID*) &m_lpImage, NULL, 0);
ASSERT(m_lpImage != NULL);
// 返回HBIMAP句柄
return m_hBitmap;
}
/*************************************************************************
*
* \函数名称:
* MakePalette()
*
* \输入参数:
* 无
*
* \返回值:
* BOOL - 如果成功,则为TRUE
*
* \说明:
* 如果颜色表存在的话,该函数将读取它,并创建一个Windows调色板。
* HPALETTE存储在一个数据成员中。
*
************************************************************************
*/
BOOL CDib::MakePalette()
{
// 如果不存在调色板,则返回FALSE
if(m_nColorTableEntries == 0) return FALSE;
if(m_hPalette != NULL) ::DeleteObject(m_hPalette);
TRACE("CDib::MakePalette -- m_nColorTableEntries = %d\n", m_nColorTableEntries);
// 给逻辑调色板分配内存
LPLOGPALETTE pLogPal = (LPLOGPALETTE) new char[2 * sizeof(WORD) +
m_nColorTableEntries * sizeof(PALETTEENTRY)];
// 设置逻辑调色板的信息
pLogPal->palVersion = 0x300;
pLogPal->palNumEntries = m_nColorTableEntries;
// 拷贝DIB中的颜色表到逻辑调色板
LPRGBQUAD pDibQuad = (LPRGBQUAD) m_lpvColorTable;
for(int i = 0; i < m_nColorTableEntries; i++) {
pLogPal->palPalEntry[i].peRed = pDibQuad->rgbRed;
pLogPal->palPalEntry[i].peGreen = pDibQuad->rgbGreen;
pLogPal->palPalEntry[i].peBlue = pDibQuad->rgbBlue;
pLogPal->palPalEntry[i].peFlags = 0;
pDibQuad++;
}
// 创建逻辑调色板
m_hPalette = ::CreatePalette(pLogPal);
// 删除临时变量并返回TRUE
delete pLogPal;
return TRUE;
}
/*************************************************************************
*
* \函数名称:
* SetSystemPalette()
*
* \输入参数:
* CDC* pDC - 设备上下文指针
*
* \返回值:
* BOOL - 如果成功,则为TRUE,
*
* \说明:
* 如果16bpp、24bpp或32bppDIB不具备调色板,则该函数可以为CDib对象创建一个逻辑调色板,
* 它与由CreatehalftonePalette函数返回的调色板相匹配。如果程序在256色调色板显示器上
* 运行,而你又没有调用SetSystemPalette,那么,你将不具有任何调色板,只有20中标准的
* Windows颜色出现在DIB中
*
************************************************************************
*/
BOOL CDib::SetSystemPalette(CDC* pDC)
{
// 如果DIB不具备调色板,则需要利用系统的调色板
if(m_nColorTableEntries != 0) return FALSE;
// 为设备上下文创建中间调色板,并将其与CPalette对象连接
m_hPalette = ::CreateHalftonePalette(pDC->GetSafeHdc());
// 返回
return TRUE;
}
/*************************************************************************
*
* \函数名称:
* CreateBitmap()
*
* \输入参数:
* CDC* pDC - 设备上下文指针
*
* \返回值:
* HBITMAP - 到GDI位图的句柄;如果不成功,则为NULL
* - 该句柄不是作为公共数据成员存储的
*
* \说明:
* 从已有的DIB中创建DDB位图。不要将这个函数与CreateSection
* 弄混了,后者的作用是生成DIB并保存句柄
*
************************************************************************
*/
HBITMAP CDib::CreateBitmap(CDC* pDC)
{
// 如果不存在图象数据,则返回NULL
if (m_dwSizeImage == 0) return NULL;
// 用指定的DIB来创建DDB,并用DIB信息初始化位图的图象位
HBITMAP hBitmap = ::CreateDIBitmap(pDC->GetSafeHdc(), m_lpBMIH,
CBM_INIT, m_lpImage, (LPBITMAPINFO) m_lpBMIH, DIB_RGB_COLORS);
ASSERT(hBitmap != NULL);
// 返回DDB位图句柄
return hBitmap;
}
/*************************************************************************
*
* \函数名称:
* ConvertDDBToDIB()
*
* \输入参数:
* HBITMAP hBitmap - 指向源数据的BITMAP句柄
* CDib* pDibDst - 指向转换目标的CDib对象指针
*
* \返回值:
* BOOL - 如果操作成功,则返回TRUE
*
* \说明:
* 该函数将源BITMAP类pDibSrc中的数据拷贝到pDibDst中,并对相应的数据成员赋值
*
*************************************************************************
*/
BOOL CDib::ConvertFromDDB(HBITMAP hBitmap, HPALETTE hPal)
{
// 声明一个BITMAP结构
BITMAP bm;
// 设备上下文
HDC hDC;
// 象素位数
WORD biBitCount;
// 调色板表项数
int nColorTableEntries;
// 如果hBitmap句柄无效,则返回
if(!hBitmap){
return FALSE;
}
// 释放已分配的内存
Empty();
// 填充图象数据到bm中,其中最后一个参数表示接收这个指定的对象的指针
if(!GetObject(hBitmap,sizeof(BITMAP),(LPBYTE)&bm)){
return FALSE;
}
// 计算象素位数
biBitCount=bm.bmPlanes*bm.bmBitsPixel;
if(biBitCount<=1)
biBitCount=1;
else if(biBitCount<=4)
biBitCount=4;
else if(biBitCount<=8)
biBitCount=8;
else
biBitCount=24;
// 计算调色板的尺寸
// 如果biClrUsed为零,则用到的颜色数为2的biBitCount次方
switch(biBitCount) {
case 1:
nColorTableEntries = 2;
break;
case 4:
nColorTableEntries = 16;
break;
case 8:
nColorTableEntries = 256;
break;
case 16:
case 24:
case 32:
nColorTableEntries = 0;
break;
default:
ASSERT(FALSE);
}
ASSERT((nColorTableEntries >= 0) && (nColorTableEntries <= 256));
m_nColorTableEntries = nColorTableEntries;
// 分配DIB信息头和调色板的内存
m_lpBMIH = (LPBITMAPINFOHEADER) new char
[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorTableEntries];
m_nBmihAlloc = m_nImageAlloc = crtAlloc;
m_lpBMIH->biSize = sizeof(BITMAPINFOHEADER);
m_lpBMIH->biWidth = bm.bmWidth;
m_lpBMIH->biHeight = bm.bmHeight;
m_lpBMIH->biPlanes = 1;
m_lpBMIH->biBitCount = biBitCount;
m_lpBMIH->biCompression = BI_RGB;
m_lpBMIH->biSizeImage = 0;
m_lpBMIH->biXPelsPerMeter = 0;
m_lpBMIH->biYPelsPerMeter = 0;
m_lpBMIH->biClrUsed = nColorTableEntries;
m_lpBMIH->biClrImportant = nColorTableEntries;
// 获得设备上下文句柄
hDC=GetDC(NULL);
// 如果没有指定调色板,则从系统中获得当前的系统调色板
if(hPal==NULL){
hPal = GetSystemPalette();
}
hPal = SelectPalette(hDC, hPal, FALSE);
RealizePalette(hDC);
// 调用GetDIBits填充信息头,并获得图象数据的尺寸。注意这里图象数据指针为NULL
GetDIBits( hDC, hBitmap, 0, (UINT)m_lpBMIH->biHeight, NULL, (LPBITMAPINFO)m_lpBMIH, DIB_RGB_COLORS);
// 如果没有正确的获得图象数据尺寸,则重新计算
if( m_lpBMIH->biSizeImage == 0 ){
m_lpBMIH->biSizeImage=(((bm.bmWidth*biBitCount) + 31) / 32 * 4)*bm.bmHeight;
}
// 分配存放图象数据的内存
m_lpImage = (LPBYTE) new char[m_lpBMIH->biSizeImage];
// 调用GetDIBits加载图象数据,注意这里给出了图象数据指针
// 如果加载图象数据不成功,则释放已经分配的内存,并返回FALSE
if( GetDIBits( hDC, hBitmap, 0, (UINT)m_lpBMIH->biHeight, (LPBYTE)m_lpImage,
(LPBITMAPINFO)m_lpBMIH, DIB_RGB_COLORS) == 0 ){
//clean up and return NULL
Empty();
SelectPalette( hDC, hPal, TRUE );
RealizePalette( hDC );
ReleaseDC( NULL, hDC );
return FALSE;
}
// 删除临时变量
SelectPalette(hDC, hPal, TRUE);
RealizePalette(hDC);
ReleaseDC(NULL, hDC);
return TRUE;
}
/*************************************************************************
*
* \函数名称:
* Compress()
*
* \输入参数:
* CDC* pDC - 设备上下文指针
* BOOL bCompress - TRUE对应于压缩的DIB,FALSE对应于不压缩的DIB
*
* \返回值:
* BOOL - 如果成功,则返回TRUE
*
* \说明:
* 该函数将DIB重新生成为压缩或者不压缩的DIB。在内部,它转换已有的DIB为DDB位图
* 然后生成一个新的压缩或者不压缩的DIB。压缩仅为4bpp和8bpp的DIB所支持。不能
* 压缩DIB段
*
************************************************************************
*/
BOOL CDib::Compress(CDC* pDC, BOOL bCompress /* = TRUE */)
{
// 判断是否为4bpp或者8bpp位图,否则,不进行压缩,返回FALSE
if((m_lpBMIH->biBitCount != 4) && (m_lpBMIH->biBitCount != 8)) return FALSE;
// 如果为DIB段,也不能支持压缩,返回FALSE
if(m_hBitmap) return FALSE;
TRACE("Compress: original palette size = %d\n", m_nColorTableEntries);
// 获得设备上下文句柄
HDC hdc = pDC->GetSafeHdc();
// 将此DIB的调色板选入设备上下文,并保存以前的调色板句柄
HPALETTE hOldPalette = ::SelectPalette(hdc, m_hPalette, FALSE);
HBITMAP hBitmap;
// 创建一个DDB位图,如果不成功,则返回FALSE
if((hBitmap = CreateBitmap(pDC)) == NULL) return FALSE;
// 计算信息头加上调色板的大小尺寸,并给它们分配内存
int nSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries;
LPBITMAPINFOHEADER lpBMIH = (LPBITMAPINFOHEADER) new char[nSize];
// 将信息头和调色板拷贝到内存中
memcpy(lpBMIH, m_lpBMIH, nSize); // new header
// 如果需要进行压缩,设置相应的信息,并创建压缩格式的DIB
if(bCompress) {
switch (lpBMIH->biBitCount) {
case 4:
lpBMIH->biCompression = BI_RLE4;
break;
case 8:
lpBMIH->biCompression = BI_RLE8;
break;
default:
ASSERT(FALSE);
}
// 设置位图数据指针为NULL,调用GetDIBits来得到压缩格式的DIB的尺寸
// 如果不能创建DIB,则进行相应的错误处理。
if(!::GetDIBits(pDC->GetSafeHdc(), hBitmap, 0, (UINT) lpBMIH->biHeight,
NULL, (LPBITMAPINFO) lpBMIH, DIB_RGB_COLORS)) {
AfxMessageBox("Unable to compress this DIB");
// 删除临时变量,并释放已分配内存
::DeleteObject(hBitmap);
delete [] lpBMIH;
// 重新将以前的调色板选入,并返回FALSE
::SelectPalette(hdc, hOldPalette, FALSE);
return FALSE;
}
// 如果位图数据为空,则进行相应的错误处理
if (lpBMIH->biSizeImage == 0) {
AfxMessageBox("Driver can't do compression");
// 删除临时变量,并释放已分配内存
::DeleteObject(hBitmap);
delete [] lpBMIH;
// 重新将以前的调色板选入,并返回FALSE
::SelectPalette(hdc, hOldPalette, FALSE);
return FALSE;
}
// 将位图数据尺寸赋值给类的成员变量
else {
m_dwSizeImage = lpBMIH->biSizeImage;
}
}
// 如果是解压缩,进行相应的处理
else {
// 设置压缩格式为不压缩
lpBMIH->biCompression = BI_RGB;
// 根据位图的宽度和高度计算位图数据内存的大小
DWORD dwBytes = ((DWORD) lpBMIH->biWidth * lpBMIH->biBitCount) / 32;
if(((DWORD) lpBMIH->biWidth * lpBMIH->biBitCount) % 32) {
dwBytes++;
}
dwBytes *= 4;
// 将得到位图数据的大小尺寸保存在类的成员变量中
m_dwSizeImage = dwBytes * lpBMIH->biHeight;
// 将位图数据内存的大小赋值给临时的信息头中的相应的变量
lpBMIH->biSizeImage = m_dwSizeImage;
}
// 再次调用GetDIBits来生成DIB数据
// 分配临时存放位图数据
LPBYTE lpImage = (LPBYTE) new char[m_dwSizeImage];
// 再次调用GetDIBits来生成DIB数据,注意此时位图数据指针不为空
VERIFY(::GetDIBits(pDC->GetSafeHdc(), hBitmap, 0, (UINT) lpBMIH->biHeight,
lpImage, (LPBITMAPINFO) lpBMIH, DIB_RGB_COLORS));
TRACE("dib successfully created - height = %d\n", lpBMIH->biHeight);
// 压缩转换完毕,进行相应的其他处理
// 删除临时的DDB位图
::DeleteObject(hBitmap);
// 释放原来的DIB分配的内存
Empty();
// 重新设置图象信息头和图象数据内存分配状态
m_nBmihAlloc = m_nImageAlloc = crtAlloc;
// 重新定位信息头和图象数据指针
m_lpBMIH = lpBMIH;
m_lpImage = lpImage;
// 计算图象数据尺寸,并设置DIB中调色板的指针
ComputeMetrics();
// 计算DIB中调色板的尺寸
ComputePaletteSize(m_lpBMIH->biBitCount);
// 如果DIB中调色板存在的话,读取并创建一个Windows调色板
MakePalette();
// 恢复以前的调色板
::SelectPalette(hdc, hOldPalette, FALSE);
TRACE("Compress: new palette size = %d\n", m_nColorTableEntries);
// 返回
return TRUE;
}
/*************************************************************************
*
* \函数名称:
* Read()
*
* \输入参数:
* CFile* pFile - 指向CFile对象的指针
*
* \返回值:
* BOOL - 如果成功,则返回TRUE
*
* \说明:
* 该函数DIB从一个文件读入CDib对象。该文件必须成功打开。如果该文件是BMP文件
* 读取工作从文件头开始。如果该文件是一个文档,读取工作则从当前文件指针处开始
*
************************************************************************
*/
BOOL CDib::Read(CFile* pFile)
{
// 释放已经分配的内存
Empty();
// 临时存放信息的变量
int nCount, nSize;
BITMAPFILEHEADER bmfh;
// 进行读操作
try
{
// 读取文件头
nCount = pFile->Read((LPVOID) &bmfh, sizeof(BITMAPFILEHEADER));
if(nCount != sizeof(BITMAPFILEHEADER)) {
throw new CException;
}
// 如果文件类型部位"BM",则返回并进行相应错误处理
if(bmfh.bfType != 0x4d42) {
throw new CException;
}
// 计算信息头加上调色板的大小,并分配相应的内存
nSize = bmfh.bfOffBits - sizeof(BITMAPFILEHEADER);
m_lpBMIH = (LPBITMAPINFOHEADER) new char[nSize];
m_nBmihAlloc = m_nImageAlloc = crtAlloc;
// 读取信息头和调色板
nCount = pFile->Read(m_lpBMIH, nSize);
// 计算图象数据大小并设置调色板指针
ComputeMetrics();
// 计算调色板的表项数
ComputePaletteSize(m_lpBMIH->biBitCount);
// 如果DIB中存在调色板,则创建一个Windows调色板
MakePalette();
// 分配图象数据内存,并从文件中读取图象数据
m_lpImage = (LPBYTE) new char[m_dwSizeImage];
nCount = pFile->Read(m_lpImage, m_dwSizeImage);
}
// 错误处理
catch(CException* pe)
{
AfxMessageBox("Read error");
pe->Delete();
return FALSE;
}
// 返回
return TRUE;
}
/*************************************************************************
*
* \函数名称:
* ReadSection()
*
* \输入参数:
* CFile* pFile - 指向CFile对象的指针;对应的磁盘
* - 文件中包含DIB
* CDC* pDC - 设备上下文指针
*
* \返回值:
* BOOL - 如果成功,则返回TRUE
*
* \说明:
* 该函数从BMP文件中读取信息头,调用CreateDIBSection来分配图象内存,然后将
* 图象从该文件读入刚才分配的内存。如果你想从磁盘读取一个DIB,然后通过调用
* GDI函数编辑它的话,可以使用该函数。你可以用Write或CopyToMapFile将DIB写
* 回到磁盘
*
************************************************************************
*/
BOOL CDib::ReadSection(CFile* pFile, CDC* pDC /* = NULL */)
{
// 释放已经分配的内存
Empty();
// 临时变量
int nCount, nSize;
BITMAPFILEHEADER bmfh;
// 从文件中读取数据
try {
// 读取文件头
nCount = pFile->Read((LPVOID) &bmfh, sizeof(BITMAPFILEHEADER));
if(nCount != sizeof(BITMAPFILEHEADER)) {
throw new CException;
}
// 如果文件类型部位"BM",则返回并进行相应错误处理
if(bmfh.bfType != 0x4d42) {
throw new CException;
}
// 计算信息头加上调色板的大小,并分配相应的内存
nSize = bmfh.bfOffBits - sizeof(BITMAPFILEHEADER);
m_lpBMIH = (LPBITMAPINFOHEADER) new char[nSize];
m_nBmihAlloc = crtAlloc;
m_nImageAlloc = noAlloc;
// 读取信息头和调色板
nCount = pFile->Read(m_lpBMIH, nSize);
// 如果图象为压缩格式,则不进行后续处理
if(m_lpBMIH->biCompression != BI_RGB) {
throw new CException;
}
// 计算图象数据大小并设置调色板指针
ComputeMetrics();
// 计算调色板的表项数
ComputePaletteSize(m_lpBMIH->biBitCount);
// 如果DIB中存在调色板,则创建一个Windows调色板
MakePalette();
// 将CDib对象的逻辑调色板选入设备上下文
UsePalette(pDC);
// 创建一个DIB段,并分配图象内存
m_hBitmap = ::CreateDIBSection(pDC->GetSafeHdc(), (LPBITMAPINFO) m_lpBMIH,
DIB_RGB_COLORS, (LPVOID*) &m_lpImage, NULL, 0);
ASSERT(m_lpImage != NULL);
// 从文件中读取图象数据
nCount = pFile->Read(m_lpImage, m_dwSizeImage);
}
// 错误处理
catch(CException* pe) {
AfxMessageBox("ReadSection error");
pe->Delete();
return FALSE;
}
return TRUE;
}
/*************************************************************************
*
* \函数名称:
* Write()
*
* \输入参数:
* CFile* pFile - 指向CFile对象的指针
*
* \返回值:
* BOOL - 如果成功,则返回TRUE
*
* \说明:
* 该函数把DIB从CDib对象写进文件中。该文件必须成功打开或者创建
*
************************************************************************
*/
BOOL CDib::Write(CFile* pFile)
{
BITMAPFILEHEADER bmfh;
// 设置文件头中文件类型为"BM"
bmfh.bfType = 0x4d42;
// 计算信息头和调色板的大小尺寸
int nSizeHdr = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries;
// 设置文件头信息
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + nSizeHdr + m_dwSizeImage;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * m_nColorTableEntries;
// 进行写操作
try {
pFile->Write((LPVOID) &bmfh, sizeof(BITMAPFILEHEADER));
pFile->Write((LPVOID) m_lpBMIH, nSizeHdr);
pFile->Write((LPVOID) m_lpImage, m_dwSizeImage);
}
// 错误处理
catch(CException* pe) {
pe->Delete();
AfxMessageBox("write error");
return FALSE;
}
// 返回
return TRUE;
}
/*************************************************************************
*
* \函数名称:
* Serialize()
*
* \输入参数:
* CArchive& ar - 指向应用程序归档对象
*
* \返回值:
* 无
*
* \说明:
* 该函数进行串行化过程,将CDib数据进行读入或者写出
*
************************************************************************
*/
void CDib::Serialize(CArchive& ar)
{
DWORD dwPos;
// 获得此归档文件的CFile对象指针
dwPos = ar.GetFile()->GetPosition();
TRACE("CDib::Serialize -- pos = %d\n", dwPos);
// 从归档文件缓冲区中冲掉未写入数据
ar.Flush();
// 重新获得此归档文件的CFile对象指针
dwPos = ar.GetFile()->GetPosition();
TRACE("CDib::Serialize -- pos = %d\n", dwPos);
// 确定归档文件是否被存储,是则进行存储
if(ar.IsStoring()) {
Write(ar.GetFile());
}
// 否则进行加载
else {
Read(ar.GetFile());
}
}
/*************************************************************************
*
* \函数名称:
* ComputePaletteSize()
*
* \输入参数:
* int nBitCount - 指向CFile对象的指针
*
* \返回值:
* 无
*
* \说明:
* 该函数根据位图象素位数计算调色板的尺寸
*
************************************************************************
*/
void CDib::ComputePaletteSize(int nBitCount)
{
// 如果biClrUsed为零,则用到的颜色数为2的biBitCount次方
if((m_lpBMIH == NULL) || (m_lpBMIH->biClrUsed == 0)) {
switch(nBitCount) {
case 1:
m_nColorTableEntries = 2;
break;
case 4:
m_nColorTableEntries = 16;
break;
case 8:
m_nColorTableEntries = 256;
break;
case 16:
case 24:
case 32:
m_nColorTableEntries = 0;
break;
default:
ASSERT(FALSE);
}
}
// 否则调色板的表项数就是用到的颜色数目
else {
m_nColorTableEntries = m_lpBMIH->biClrUsed;
}
ASSERT((m_nColorTableEntries >= 0) && (m_nColorTableEntries <= 256));
}
/*************************************************************************
*
* \函数名称:
* ComputeMetrics()
*
* \输入参数:
* 无
*
* \返回值:
* 无
*
* \说明:
* 该函数计算图象位图的尺寸,并对DIB中的调色板的指针进行赋值
*
************************************************************************
*/
void CDib::ComputeMetrics()
{
// 如果结构的长度不对,则进行错误处理
if(m_lpBMIH->biSize != sizeof(BITMAPINFOHEADER)) {
TRACE("Not a valid Windows bitmap -- probably an OS/2 bitmap\n");
throw new CException;
}
// 保存图象数据内存大小到CDib对象的数据成员中
m_dwSizeImage = m_lpBMIH->biSizeImage;
// 如果图象数据内存大小为0,则重新计算
if(m_dwSizeImage == 0) {
DWORD dwBytes = ((DWORD) m_lpBMIH->biWidth * m_lpBMIH->biBitCount) / 32;
if(((DWORD) m_lpBMIH->biWidth * m_lpBMIH->biBitCount) % 32) {
dwBytes++;
}
dwBytes *= 4;
m_dwSizeImage = dwBytes * m_lpBMIH->biHeight;
}
// 设置DIB中的调色板指针
m_lpvColorTable = (LPBYTE) m_lpBMIH + sizeof(BITMAPINFOHEADER);
}
/*************************************************************************
*
* \函数名称:
* Empty()
*
* \输入参数:
* 无
*
* \返回值:
* 无
*
* \说明:
* 该函数清空DIB,释放已分配的内存,并且必要的时候关闭映射文件
*
************************************************************************
*/
void CDib::Empty()
{
// 关闭内存映射文件的连接
DetachMapFile();
// 根据内存分配的状态,调用相应的函数释放信息头
if(m_nBmihAlloc == crtAlloc) {
delete [] m_lpBMIH;
}
else if(m_nBmihAlloc == heapAlloc) {
::GlobalUnlock(m_hGlobal);
::GlobalFree(m_hGlobal);
}
// 释放图象数据内存
if(m_nImageAlloc == crtAlloc) delete [] m_lpImage;
// 释放调色板句柄
if(m_hPalette != NULL) ::DeleteObject(m_hPalette);
// 如果创建了BITMAP,则进行释放
if(m_hBitmap != NULL) ::DeleteObject(m_hBitmap);
// 重新设置内存分配状态
m_nBmihAlloc = m_nImageAlloc = noAlloc;
// 释放内存后,还需要将指针设置为NULL并将相应的数据设置为0
m_hGlobal = NULL;
m_lpBMIH = NULL;
m_lpImage = NULL;
m_lpvColorTable = NULL;
m_nColorTableEntries = 0;
m_dwSizeImage = 0;
m_lpvFile = NULL;
m_hMap = NULL;
m_hFile = NULL;
m_hBitmap = NULL;
m_hPalette = NULL;
}
/*************************************************************************
*
* \函数名称:
* DetachMapFile()
*
* \输入参数:
* 无
*
* \返回值:
* 无
*
* \说明:
* 函数可以释放现有的已分配的内存,并关闭以前关联的任何内存映射文件。
*
************************************************************************
*/
void CDib::DetachMapFile()
{
// 如果没有进行内存映射,则不进行处理
if(m_hFile == NULL) return;
// 关闭内存映射
::UnmapViewOfFile(m_lpvFile);
// 关闭内存映射对象和文件
::CloseHandle(m_hMap);
::CloseHandle(m_hFile);
m_hFile = NULL;
}
/*************************************************************************
*
* \函数名称:
* PaletteSize()
*
* \输入参数:
* 无
*
* \返回值:
* DWORD - 返回调色板的尺寸
*
* \说明:
* 该函数计算机调色板所需的尺寸
*
************************************************************************
*/
WORD CDib::PaletteSize()
{
// 临时变量
WORD NumColors;
LPBITMAPINFOHEADER lpbi=m_lpBMIH;
// 如果biClrUsed为零,且图象象素位数小于8,则计算调色板用到的表项数
NumColors = ((lpbi)->biClrUsed == 0 && (lpbi)->biBitCount <= 8 \
? (int)(1 << (int)(lpbi)->biBitCount) \
: (int)(lpbi)->biClrUsed);
// 根据颜色表示的字节数计算调色板的尺寸
if (lpbi->biSize == sizeof(BITMAPCOREHEADER))
return NumColors * sizeof(RGBTRIPLE);
else
return NumColors * sizeof(RGBQUAD);
}
/*************************************************************************
*
* \函数名称:
* IsEmpty()
*
* \输入参数:
* 无
*
* \返回值:
* BOOL - 如果信息头和图象数据为空,则返回TRUE
*
* \说明:
* 判断信息头和图象数据是否为空
*
************************************************************************
*/
BOOL CDib::IsEmpty()
{
if( m_lpBMIH == NULL&&m_lpImage == NULL)
return TRUE;
else
return FALSE;
}
/*************************************************************************
*
* \函数名称:
* GetDibSaveDim()
*
* \输入参数:
* 无
*
* \返回值:
* CSize - DIB实际存储的高度和宽度
*
* \说明:
* 该函数函数用来得到dib的实际存储宽度(DWORD对齐)
*
************************************************************************
*/
CSize CDib::GetDibSaveDim()
{
CSize sizeSaveDim;
sizeSaveDim.cx = ( m_lpBMIH->biWidth * m_lpBMIH->biBitCount + 31)/32 * 4;
sizeSaveDim.cy = m_lpBMIH->biHeight;
return sizeSaveDim;
}
/*************************************************************************
*
* \函数名称:
* GetPixelOffset()
*
* \输入参数:
* int x - 象素在X轴的坐标
* int y - 象素在Y轴的坐标
*
* \返回值:
* int - 返回象素在图象数据块中的真实地址
*
* \说明:
* 该函数得到坐标为(x,y)的象素点的真实地址。由于DIB结构中对图象数据排列的
* 方式为从下到上,从左到右的,所以需要进行转换。
*
************************************************************************
*/
LONG CDib::GetPixelOffset(int x, int y)
{
CSize sizeSaveDim;
sizeSaveDim = GetDibSaveDim();
LONG lOffset = (LONG) (sizeSaveDim.cy - y - 1) * sizeSaveDim.cx +
x / (8 / m_lpBMIH->biBitCount);
return lOffset;
}
/*************************************************************************
*
* \函数名称:
* GetPixel()
*
* \输入参数:
* int x - 象素在X轴的坐标
* int y - 象素在Y轴的坐标
*
* \返回值:
* RGBQUAD - 返回DIB在该点真实的颜色
*
* \说明:
* 该函数得到DIB图象在该点真是的颜色。
*
************************************************************************
*/
RGBQUAD CDib::GetPixel(int x, int y)
{
// 颜色结构
RGBQUAD cColor;
// 根据每象素比特数得到此点的象素值
switch (m_lpBMIH->biBitCount)
{
case 1 :
if (1<<(7-x%8) & *(LPBYTE)(m_lpImage+GetPixelOffset(x, y)))
{
cColor.rgbBlue = 255;
cColor.rgbGreen = 255;
cColor.rgbRed = 255;
cColor.rgbReserved =0;
}
else
{
cColor.rgbBlue = 0;
cColor.rgbGreen = 0;
cColor.rgbRed = 0;
cColor.rgbReserved =0;
}
break;
case 4 :
{
int nIndex = (*(LPBYTE)(m_lpImage+GetPixelOffset(x, y)) &
(x%2 ? 0x0f : 0xf0)) >> (x%2 ? 0 : 4);
LPRGBQUAD pDibQuad = (LPRGBQUAD) (m_lpvColorTable) + nIndex;
cColor.rgbBlue = pDibQuad->rgbBlue;
cColor.rgbGreen = pDibQuad->rgbGreen;
cColor.rgbRed = pDibQuad->rgbRed;
cColor.rgbReserved =0;
}
break;
case 8 :
{
int nIndex = *(BYTE*)(m_lpImage+GetPixelOffset(x, y));
LPRGBQUAD pDibQuad = (LPRGBQUAD) (m_lpvColorTable) + nIndex;
cColor.rgbBlue = pDibQuad->rgbBlue;
cColor.rgbGreen = pDibQuad->rgbGreen;
cColor.rgbRed = pDibQuad->rgbRed;
cColor.rgbReserved =0;
}
break;
default:
int nIndex = *(BYTE*)(m_lpImage+GetPixelOffset(x, y));
cColor.rgbRed = m_lpImage[nIndex];
cColor.rgbGreen = m_lpImage[nIndex + 1];
cColor.rgbBlue = m_lpImage[nIndex + 2];
cColor.rgbReserved =0;
break;
}
// 返回颜色结构
return cColor;
}