几何尺寸与公差论坛

 找回密码
 注册
查看: 1314|回复: 1

关于位图的一点疑问

[复制链接]
发表于 2009-9-25 13:50:51 | 显示全部楼层 |阅读模式
关于位图的一点疑问


void CBmpDemoDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
if (!m_dcMem.GetSafeHdc())
{
m_dcMem.CreateCompatibleDC(&dc);
}
if (!m_bmpMem.GetSafeHandle())
{
if (m_dcMem.GetSafeHdc())
{
m_bmpMem.CreateCompatibleBitmap(&dc,m_rcClient.Width(),m_rcClient.Height());
m_dcMem.SelectObject(&m_bmpMem);
}
}
ASSERT(m_dcMem.GetSafeHdc()!=NULL);
ASSERT(m_bmpMem.GetSafeHandle()!=NULL);
m_dcMem.FillSolidRect(m_rcClient,RGB(129,0,0));

CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
/*CBitmap *pBmpTemp=m_dcMem.GetCurrentBitmap();*/
CBitmap bmpMem;
bmpMem.CreateCompatibleBitmap(&dc,m_rcClient.Width(),m_rcClient.Height());

m_dcMem.SelectObject(&bmpMem); //1
dcMem.SelectObject(&m_bmpMem); //3

dcMem.FillSolidRect(m_rcClient,RGB(50,200,0)); //2

dc.BitBlt(m_rcClient.left,m_rcClient.top,m_rcClient.Width(),m_rcClient.Height(),&dcMem,0,0,SRCCOPY);
}
上述代码仅仅是为了测试,
现在的疑问是1处的代码如果不调用,那么dcMem接下来的绘图操作2失效了?

疑问2 是关于GetCurrentBitmap,MSDN上面说这个函数会返回当前DC中选入的Bitmap,
如果我把3改成dcMem.SelectObject(dcMem.GetCurrentBitmap),为什么会没有效果呢?


////////////////////////////////////

BOOL CBmpDemoDlg::SaveBitmapFile(HDC p_hDC, LPCTSTR p_pchFileName)
{

HBITMAP hBmp = (HBITMAP)GetCurrentObject( p_hDC, OBJ_BITMAP );

BITMAPINFO stBmpInfo;
stBmpInfo.bmiHeader.biSize = sizeof( stBmpInfo.bmiHeader );
stBmpInfo.bmiHeader.biBitCount = 0;
GetDIBits( p_hDC, hBmp, 0, 0, NULL, &stBmpInfo, DIB_RGB_COLORS );

ULONG iBmpInfoSize;
switch( stBmpInfo.bmiHeader.biBitCount )
{
case 24:
iBmpInfoSize = sizeof(BITMAPINFOHEADER);
break;
case 16:
case 32:
iBmpInfoSize = sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3;
break;
default:
iBmpInfoSize = sizeof(BITMAPINFOHEADER)
+ sizeof(RGBQUAD)
* ( 1 << stBmpInfo.bmiHeader.biBitCount );
break;
}

PBITMAPINFO pstBmpInfo;
if( iBmpInfoSize != sizeof(BITMAPINFOHEADER) )
{
pstBmpInfo = (PBITMAPINFO)GlobalAlloc
( GMEM_FIXED | GMEM_ZEROINIT, iBmpInfoSize );
PBYTE pbtBmpInfoDest
= (PBYTE)pstBmpInfo;
PBYTE pbtBmpInfoSrc
= (PBYTE)&stBmpInfo;
ULONG iSizeTmp
= sizeof( BITMAPINFOHEADER );

while( iSizeTmp-- )
{
*( ( pbtBmpInfoDest )++ ) = *( ( pbtBmpInfoSrc )++ );
}
}

HANDLE hFile
= CreateFile
( p_pchFileName, GENERIC_WRITE, 0
, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE
, NULL );

BITMAPFILEHEADER stBmpFileHder;
stBmpFileHder.bfType = 0x4D42; // 'BM'
stBmpFileHder.bfSize
= sizeof(BITMAPFILEHEADER)
+ sizeof(BITMAPINFOHEADER)
+ iBmpInfoSize
+ pstBmpInfo->bmiHeader.biSizeImage;
stBmpFileHder.bfReserved1 = 0;
stBmpFileHder.bfReserved2 = 0;
stBmpFileHder.bfOffBits = sizeof(BITMAPFILEHEADER) + iBmpInfoSize;

DWORD dRet;
WriteFile
( hFile, (LPCVOID)&stBmpFileHder
, sizeof(BITMAPFILEHEADER), &dRet, NULL );

PBYTE pBits
= (PBYTE)GlobalAlloc
( GMEM_FIXED | GMEM_ZEROINIT
, stBmpInfo.bmiHeader.biSizeImage );

HBITMAP hBmpOld;
HBITMAP hTmpBmp
= CreateCompatibleBitmap
( p_hDC
, pstBmpInfo->bmiHeader.biWidth
, pstBmpInfo->bmiHeader.biHeight );
hBmpOld = (HBITMAP)SelectObject( p_hDC, hTmpBmp );
GetDIBits
( p_hDC, hBmp, 0, pstBmpInfo->bmiHeader.biHeight
, (LPSTR)pBits, pstBmpInfo, DIB_RGB_COLORS );

WriteFile
( hFile, (LPCVOID)pstBmpInfo
, iBmpInfoSize, &dRet, NULL );

WriteFile( hFile, (LPCVOID)pBits
, pstBmpInfo->bmiHeader.biSizeImage
, &dRet, NULL );

SelectObject( p_hDC, hBmpOld );
DeleteObject( hTmpBmp );
CloseHandle( hFile );
GlobalFree( pstBmpInfo );
GlobalFree( pBits );
return TRUE;
}
这段代码本意是将当前DC内容保存到文件,但是如果调用时传入的DC是任何窗口dc,其结果是保持的是整个屏幕的位图,这里用到了GetCurrentObject,是不是这个函数返回的HBITMAP,只能取其数据,将其选入dc作为兼容位图就不行?
/////////////////////////////////////
void CBmpDemoDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
if (!m_dcMem.GetSafeHdc())
{
m_dcMem.CreateCompatibleDC(&dc);
}
if (!m_bmpMem.GetSafeHandle())
{
if (m_dcMem.GetSafeHdc())
{
m_bmpMem.CreateCompatibleBitmap(&dc,m_rcClient.Width(),m_rcClient.Height());
m_dcMem.SelectObject(&m_bmpMem);
}
}
ASSERT(m_dcMem.GetSafeHdc()!=NULL);
ASSERT(m_bmpMem.GetSafeHandle()!=NULL);
m_dcMem.FillSolidRect(m_rcClient,RGB(129,0,0));

CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
/*CBitmap *pBmpTemp=m_dcMem.GetCurrentBitmap();*/
CBitmap bmpMem;
bmpMem.CreateCompatibleBitmap(&dc,m_rcClient.Width(),m_rcClient.Height());

//类成员内存DC在上面已经选择了类成员位图对象了,
//下面这句执行后,就会将临时创建的位图对象选进了DC,而将类成员位图对象选出来了,
//之后的绘图操作均会被绘制到临时的位图对象中,而类成员位图对象则不会被绘制到。
m_dcMem.SelectObject(&bmpMem); //1


//这句是将类成员位图对象选进了临时的DC中,如果函数退出时没有进行选出,
//则会导致该位图对象的损坏。
dcMem.SelectObject(&m_bmpMem); //3

dcMem.FillSolidRect(m_rcClient,RGB(50,200,0)); //2

dc.BitBlt(m_rcClient.left,m_rcClient.top,m_rcClient.Width(),m_rcClient.Height(),&dcMem,0,0,SRCCOPY);

//这边应该要将类成员位图对象选出,否则会导致下次OnPaint没有效果。
//因为m_bmpMem != NULL,而它的数据却已经损坏,不再可以被选进DC中了。
//所以第二次的OnPaint的运行效果就没有了。
//应该在这里将dcMem的OldBMP选进来!!!

}
上述代码仅仅是为了测试,
现在的疑问是1处的代码如果不调用,那么dcMem接下来的绘图操作2失效了?

疑问2 是关于GetCurrentBitmap,MSDN上面说这个函数会返回当前DC中选入的Bitmap,
如果我把3改成dcMem.SelectObject(dcMem.GetCurrentBitmap),为什么会没有效果呢?

关于你的上面2点疑问,我觉得首先需要重新调整你的代码写法,然后再来回答。
我们感觉你的这个函数代码在后面部分是乱的,所以调整后,问题自然就不存在了。
GetCurrentBitmap的意思是返回DC的当前选中的HBitmap,而你在后面又选进来了,
这不是逻辑上有问题吗?选进一个已经选中的对象,是不是一个逻辑错误呢?


////////////////////////////////////////////////////////////////
BOOL CBmpDemoDlg::SaveBitmapFile(HDC p_hDC, LPCTSTR p_pchFileName)
{
//你这样获得的位图有一个问题,就是你这个位图目前还在DC中,
//如果你要使用那个位图,必须将该位图选出DC,这样可以保证该位图不会被DC临时
//修改。一般不要这样使用GetCurrentObject,最好选进一个Bitmap,返回值就是我们想要的那个位图了。

HBITMAP hBmp = (HBITMAP)GetCurrentObject( p_hDC, OBJ_BITMAP );

BITMAPINFO stBmpInfo;
stBmpInfo.bmiHeader.biSize = sizeof( stBmpInfo.bmiHeader );
stBmpInfo.bmiHeader.biBitCount = 0;
GetDIBits( p_hDC, hBmp, 0, 0, NULL, &stBmpInfo, DIB_RGB_COLORS );

ULONG iBmpInfoSize;
switch( stBmpInfo.bmiHeader.biBitCount )
{
case 24:
iBmpInfoSize = sizeof(BITMAPINFOHEADER);
break;
case 16:
case 32:
iBmpInfoSize = sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3;
break;
default:
iBmpInfoSize = sizeof(BITMAPINFOHEADER)
+ sizeof(RGBQUAD)
* ( 1 << stBmpInfo.bmiHeader.biBitCount );
break;
}

PBITMAPINFO pstBmpInfo;
if( iBmpInfoSize != sizeof(BITMAPINFOHEADER) )
{
pstBmpInfo = (PBITMAPINFO)GlobalAlloc
( GMEM_FIXED | GMEM_ZEROINIT, iBmpInfoSize );
PBYTE pbtBmpInfoDest
= (PBYTE)pstBmpInfo;
PBYTE pbtBmpInfoSrc
= (PBYTE)&stBmpInfo;
ULONG iSizeTmp
= sizeof( BITMAPINFOHEADER );

while( iSizeTmp-- )
{
*( ( pbtBmpInfoDest )++ ) = *( ( pbtBmpInfoSrc )++ );
}
}

HANDLE hFile
= CreateFile
( p_pchFileName, GENERIC_WRITE, 0
, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE
, NULL );

BITMAPFILEHEADER stBmpFileHder;
stBmpFileHder.bfType = 0x4D42; // 'BM'
stBmpFileHder.bfSize
= sizeof(BITMAPFILEHEADER)
+ sizeof(BITMAPINFOHEADER)
+ iBmpInfoSize
+ pstBmpInfo->bmiHeader.biSizeImage;
stBmpFileHder.bfReserved1 = 0;
stBmpFileHder.bfReserved2 = 0;
stBmpFileHder.bfOffBits = sizeof(BITMAPFILEHEADER) + iBmpInfoSize;

DWORD dRet;
WriteFile
( hFile, (LPCVOID)&stBmpFileHder
, sizeof(BITMAPFILEHEADER), &dRet, NULL );

PBYTE pBits
= (PBYTE)GlobalAlloc
( GMEM_FIXED | GMEM_ZEROINIT
, stBmpInfo.bmiHeader.biSizeImage );

HBITMAP hBmpOld;
HBITMAP hTmpBmp
= CreateCompatibleBitmap
( p_hDC
, pstBmpInfo->bmiHeader.biWidth
, pstBmpInfo->bmiHeader.biHeight );
hBmpOld = (HBITMAP)SelectObject( p_hDC, hTmpBmp );
GetDIBits
( p_hDC, hBmp, 0, pstBmpInfo->bmiHeader.biHeight
, (LPSTR)pBits, pstBmpInfo, DIB_RGB_COLORS );

WriteFile
( hFile, (LPCVOID)pstBmpInfo
, iBmpInfoSize, &dRet, NULL );

WriteFile( hFile, (LPCVOID)pBits
, pstBmpInfo->bmiHeader.biSizeImage
, &dRet, NULL );

SelectObject( p_hDC, hBmpOld );
DeleteObject( hTmpBmp );
CloseHandle( hFile );
GlobalFree( pstBmpInfo );
GlobalFree( pBits );
return TRUE;
}
///////////////////////////////////
不好意思,上面的代码是有点乱,主要是做测试用,“dcMem.SelectObject(dcMem.GetCurrentBitmap)”实属笔误,本意是dcMem.SelectObject(m_dcMem.GetCurrentBitmap),看下面代码,我稍微做了下注释:
void CBmpDemoDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
if (!m_dcMem.GetSafeHdc())
{
m_dcMem.CreateCompatibleDC(&dc);
}
if (!m_bmpMem.GetSafeHandle())
{
if (m_dcMem.GetSafeHdc())
{
m_bmpMem.CreateCompatibleBitmap(&dc,m_rcClient.Width(),m_rcClient.Height());
m_dcMem.SelectObject(&m_bmpMem);
}
}
ASSERT(m_dcMem.GetSafeHdc()!=NULL);
ASSERT(m_bmpMem.GetSafeHandle()!=NULL);
//先将图绘制到成员m_dcMem中
m_dcMem.FillSolidRect(m_rcClient,RGB(129,200,0));

CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
//再将已经绘制了内容的成员m_bmpMem选到临时dcMem中
CBitmap *pOldBmp=(CBitmap*)dcMem.SelectObject(&m_bmpMem);
//将临时dcMem拷贝到屏幕
dc.BitBlt(m_rcClient.left,m_rcClient.top,m_rcClient.Width(),m_rcClient.Height(),&dcMem,0,0,SRCCOPY);
//将成员bmpMem选出
dcMem.SelectObject(pOldBmp);
}

我的本意是:类成员m_dcMem和m_bmpMem只创建一次,先将内容画在m_bmpMem上,然后将这个位图选入到另一个dcMem上,我的理解是此时dcMem的绘制表面就是m_bmpMem了,然后在dcMem上面的绘图都是绘制到m_bmpMem上,不知道这样对不对?主要是测试下,没有什么其他实际意义。

///////////////////////////////////////////////
这样写,dc上面还是什么都没有,不知道为什么,但是我将m_dcMem.SelectObject(&bmpMem); //1
放在CBitmap *pOldBmp=(CBitmap*)dcMem.SelectObject(&m_bmpMem);之前就可以了,难道是一个位图不能被选到两个dc中吗?


GetCurrentObject这个函数一般应该怎么用,如果我要获取当前DC位图,感觉这个函数是最直接的
/////////////////////////////////////
还有一点就是上面的SaveBitmapFile函数传入任何窗口dc,哪怕是对话框上面的一个button的dc,最后保存文件都是整个屏幕,但是如果我传入m_dcMem,它就是获取的该dc的区域,不知道是不是窗口的dc通过GetCurrentObject都是整个屏幕?
////////////////////////////////////////////////////////
一个被选到DC中的位图再被选出之前当然不能再次被选入另外一个DC中!
画不出来是当然的。
/////////////////////////////////////////////////////////
An application can use the GetCurrentObject and GetObject functions to retrieve descriptions of the graphic objects currently selected into the given device context.

我觉得你应该整理清楚思路。
第一、确保你获得窗口DC是正确的,你是如何获得DC的;
第二、请不要再使用GetCurrentObject,你问为什么,我只能告诉你在这个地方使用不合适;
第三、确保可以正确选取位图,通过使用老位图来将位图选出。
////////////////////////////////////////////////////
CButton* pButton=(CButton*)GetDlgItem(IDC_BUTTON1);
CClientDC dc(pButton);
SaveBitmapFile((HDC)dc,"Test1.bmp");

上面是你的写法。

其实很简单,只要将CClientDC 拷贝到一个新的内存DC就OK

/////////////////////////////////////////////////////////
嗯,现在理解了,GetCurrentObject只能获得兼容位图
 楼主| 发表于 2009-9-25 13:52:05 | 显示全部楼层

回复: 关于位图的一点疑问

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|Archiver|小黑屋|几何尺寸与公差论坛

GMT+8, 2024-12-22 12:37 , Processed in 0.039000 second(s), 19 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表