从 CDC 创建 CBitmap?
Posted
技术标签:
【中文标题】从 CDC 创建 CBitmap?【英文标题】:Create CBitmap from CDC? 【发布时间】:2021-06-07 16:52:39 【问题描述】:使用 C++/MFC 和 GDI(不是 GDI+),总体目标是创建一个有图案的HBRUSH
,将在OnCtlColor
中使用以红色勾勒编辑控件,并能够打开轮廓和关闭。为此,您使用CreatePatternBrush
将位图附加到HBRUSH
。这是使用存储的位图资源执行此操作的代码:
CDialog::OnInitDialog();
BOOL ok = redBoxBitmap.LoadBitmap(MAKEINTRESOURCE(IDB_mespe_EditBox_Red));
ok = redBoxBrush.CreatePatternBrush(&redBoxBitmap);
在OnCtlColor
HBRUSH CModelEditorSpecies::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
HBRUSH hbr;
int ctrlID=pWnd->GetDlgCtrlID();
if(ctrlID==IDC_MyEditControl)
hbr=(HBRUSH) redBoxBrush;
else
hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
return hbr;
上面的代码都可以按需要工作。但是,这取决于位图的大小调整到编辑控件。我现在需要的是在 C++ 程序中创建位图的能力,其大小与控件的客户区一致,这取决于控件的设计大小(在对话框编辑器中)和用户在Windows 10 设置。
我找不到一种直接的方法来构建位图,或者更好的是,创建一个适当大小的空位图(可以做),将其选择为CDC
(可以做),将红色框画入其中(可以做),然后从CDC
中提取更新位图(怎么做?)。
任何人都可以建议如何以编程方式创建位图,或者建议一种更好的方法,在程序需要时让编辑控件以红色框起来?
添加以回应@Constantine Georgiou 3/9 的回答:
新代码:
CBitmap redBoxBitmap; // member variables of class CModelEditorSpecies
CBrush redBoxBrush;
BOOL CModelEditorSpecies::OnInitDialog()
CDialog::OnInitDialog();
BOOL ok;
CRect r; defaultSpecies1Ctrl.GetClientRect(&r);
xx(r.Width(), r.Height()/*, redBoxBrush*/);
ok = redBoxBrush.CreatePatternBrush(&redBoxBitmap);
//...
void CModelEditorSpecies::xx(const int w, const int h)
CDC *pDC=GetDC();
redBoxBitmap.CreateCompatibleBitmap(pDC, w, h);
// Create a red pen
CPen redPen;
redPen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
// Draw the bitmap - red pen & default background brush
CBitmap *pOldBitmap=pDC->SelectObject(&redBoxBitmap);
pDC->SelectObject(&redPen);
CBrush editBoxBrush;
editBoxBrush.CreateSysColorBrush(COLOR_WINDOW);
pDC->SelectObject(&editBoxBrush);
pDC->Rectangle(0, 0, w, h);
pDC->SelectObject(pOldBitmap);
// Create the edit-control custom brush
redBoxBrush.CreatePatternBrush(&redBoxBitmap);
return;
此代码生成一个全黑的编辑控件,就好像正在使用的位图是单色的。如果在 dc 中绘制不会影响位图,或者在与 dc 兼容的位图中绘制不使用@IInspectable 建议的redPen
和editBoxBrush
中的颜色,这是可以预期的。
【问题讨论】:
使用WM_CTLCOLOR
(或OnCtlColor()
)事件会影响编辑控件的背景,而不是轮廓。我猜你到目前为止所做的是创建一个大小等于控件客户区的位图,边缘为红色,并用它创建一个图案画笔。您可以使用CreateCompatibleBitmap()
简单地创建位图,用背景画笔填充它并调用MoveTo()/LineTo()
来绘制轮廓。画笔可以“缓存”(创建一次,只有在对话框类实例被销毁时才被销毁)。
要考虑的替代实现: 1. 处理WM_NCPAINT
消息,并改为对控件的边框执行自定义绘制。 2. 处理WM_PAINT
消息用于对话框 (CModelEditorSpecies::OnPaint()
) 以在控件周围 绘制一个框架(FrameRect()
函数)。
@Constantine Georgiou:您的第一条评论建议创建位图,但该位图中有什么?背景颜色为白色。 OnCtlColor()
使用画笔填充控件,但没有机会绘制红色边框线。正如您在现有代码中看到的那样,我已经在缓存画笔了。
您可以创建一个内存设备上下文 (CreateCompatibleDC
),在其中选择您的位图,执行您的渲染,然后在将其拆除之前将位图从其中选择回来。您的位图现在包含您在其上渲染的任何内容。不过,如果需要的话,我不相信您可以将 alpha 透明度与图案画笔一起使用。
在发布之前,当我尝试创建memDC
、选择位图、绘制和选择时,我得到了一个全黑的位图。我会再试一次。
【参考方案1】:
以下是创建画笔的方法 - 使用 Win32 函数而不是它们的 MFC 包装器,但您可以弄清楚。
BOOL CModelEditorSpecies::OnInitDialog()
CDialogEx::OnInitDialog();
// Get edit-control's size
RECT rc;
GetDlgItem(IDC_MyEditControl)->GetClientRect(&rc);
// Create the bitmap and a memory-DC
HDC hDC = ::GetDC(HWND_DESKTOP);
HDC mDC = CreateCompatibleDC(hDC);
HBITMAP hBmp = CreateCompatibleBitmap(hDC, rc.right, rc.bottom);
::ReleaseDC(HWND_DESKTOP, hDC);
// Create a red pen
HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
// Draw the bitmap - red pen & default background brush
HBITMAP hOldBmp = (HBITMAP) SelectObject(mDC, hBmp);
HPEN hOldPen = (HPEN) SelectObject(mDC, hPen);
HBRUSH hOldBr = (HBRUSH) SelectObject(mDC, GetSysColorBrush(COLOR_WINDOW));
Rectangle(mDC, rc.left, rc.top, rc.right, rc.bottom);
SelectObject(mDC, hOldBr);
SelectObject(mDC, hOldPen);
SelectObject(mDC, hOldBmp);
// Create the edit-control custom brush
redBoxBrush = CreatePatternBrush(hBmp);
// Clean-up - the SysColorBrush doesn't need to be deleted
DeleteObject(hPen);
DeleteObject(hBmp);
DeleteDC(mDC);
return TRUE;
HBRUSH CModelEditorSpecies::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
return (nCtlColor == CTLCOLOR_EDIT && pWnd->GetDlgCtrlID() == IDC_MyEditControl) ?
redBoxBrush : CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
还要检查我在 cmets 中建议的替代方案。这个将绘制到编辑控件的客户区。
编辑:
我在上面发布的代码确实有效,在它运行后剩下的就是HBRUSH
句柄(您的编辑框的模式画笔)。我不明白你为什么坚持使用 MFC 包装器。它们是“更高级别”的瘦包装器,所以......“瘦”实际上,您仍然必须执行几乎完全相同的操作(创建、选择到 DC、执行某些绘图操作、从 DC 中选择) GDI 的那些。您唯一不需要执行的操作是删除资源(对象的析构函数会为您调用DeleteObject()
)。
无论如何,如果您更喜欢 MFC 而不是 GDI,那么让我们看看您的代码有什么问题。它有很多问题:
首先,您需要memory DC 才能在位图上绘图,通过调用GetDC()
获得的窗口DC 将在窗口表面上绘图。
调用GetDC()
得到的DC必须返回给系统(ReleaseDC()
),因为pDC
只是一个指针,编译器不会调用析构函数。
您选择到 DC 中的对象必须在销毁之前将其选中,否则可能会发生内存泄漏。
所以你的代码应该如下所示进行更改:
void CModelEditorSpecies::xx()
CRect r;
GetDlgItem(IDC_MyEditControl)->GetClientRect(&r);
// Create the bitmap and a memory-DC
CBitmap redBoxBitmap;
CDC mDC, *pDC = GetDC();
redBoxBitmap.CreateCompatibleBitmap(pDC, r.Width(), r.Height());
mDC.CreateCompatibleDC(pDC);
ReleaseDC(pDC);
// Create a red pen and get the default background brush
CPen redPen;
redPen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
CBrush editBoxBrush;
editBoxBrush.CreateSysColorBrush(COLOR_WINDOW);
// Draw the bitmap - red pen & default background brush
CBitmap *pOldBitmap = mDC.SelectObject(&redBoxBitmap);
CPen *pOldPen = mDC.SelectObject(&redPen);
CBrush *pOldBrush =mDC.SelectObject(&editBoxBrush);
mDC.Rectangle(r);
mDC.SelectObject(pOldBrush);
mDC.SelectObject(pOldPen);
mDC.SelectObject(pOldBitmap);
// Create the edit-control custom brush
redBoxBrush.CreatePatternBrush(&redBoxBitmap);
与GDI版本基本相同。
【讨论】:
您的 MFC 代码运行良好。我错误地假设GetDC
返回了一个内存 DC。 Tnx 为您的耐心等待。顺便说一句,我为此使用 MFC,因为项目的其余部分使用它。以上是关于从 CDC 创建 CBitmap?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 CBitmap 上使用具有透明背景的 CDC 绘制文本?
如何使用 MFC 正确地将 ICON 转换为 BITMAP?