|
关于位图的一点疑问
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只能获得兼容位图 |
|