![]() |
关于位图的一点疑问
关于位图的一点疑问
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只能获得兼容位图 |
回复: 关于位图的一点疑问
|
所有的时间均为北京时间。 现在的时间是 02:33 AM. |