gdi+在分层窗口输出文字时,会出现透明的洞。

日前遇到一个问题,在分层窗口上使用gdi+ 绘制文本的时候,在绘制的文字区域,文字产生了透明的洞,鼠标会穿透。
通过现象分析,应该是文字区域的透明度直接干成了0,所以产生了一个洞。因为分层窗口透明度为0的地方直接会穿透,所以鼠标也会穿透。
解决方法:
1 将要绘制的文本先绘制在内存位图上
2 位图的句柄选入到内存dc上
3 使用AlphaBlend 将内存dc 上的图绘制到窗口上。

如果绘制结果在边缘出现了杂色,那么需要在调用AlphaBlend函数前进行预乘处理。
预乘大概算法如下:
for(int i = 0; i <位图宽; i++)
{
for(int j = 0; j < 位图高; j++)
{
unsigned char* pucColor;//待处理的像素,一个像素4个字节。
pucColor[0] = pucColor[0] * pucColor[3] / 255;//red = red * alpha / 255
pucColor[1] = pucColor[1] * pucColor[3] / 255; //green = green * alpha / 255
pucColor[2] = pucColor[2] * pucColor[3] / 255;//blue = blue * alpha / 255
}
}
意思就是当前的像素的每个颜色分量值 * 该像素值的透明比例。

如下是解决gdi+ drawstring 透明问题的伪代码:

HWND cur_wnd = layerwnd; //当前绘制的分层窗口句柄

  HFONT hFont = m_pManager->GetFont(GetFont()); //将要用于绘制文本的字体句柄
  Gdiplus::Font font(hDC, hFont); //hDC:当前窗口的句柄
  Gdiplus::RectF rc(10, 100, 300, 300);//位图中的绘制位置
  Gdiplus::Graphics* pGraphics = new Gdiplus::Graphics(hDC);
  Gdiplus::Color color(254, 255, 0, 0);
  Gdiplus::SolidBrush redBrush(color);
  Gdiplus::SolidBrush greenBrush(Gdiplus::Color(255, 0, 255, 0));
  //pGraphics->DrawString(L"1111111111111111111xxxxxxxxxxx", -1, &font, rc,NULL, &greenBrush); //这个会产生透明的洞

  Gdiplus::Bitmap bitmap(180, 100);//内存位图
  Gdiplus::Graphics bitmapGraphics(&bitmap);//内存画布
  bitmapGraphics.Clear(Gdiplus::Color(0, 255, 255, 255));//将改颜色作用于位图上

bitmapGraphics.SetCompositingMode(Gdiplus::CompositingModeSourceOver); //这句可以删除,主要是作用于绘制对象间的作用,譬如在同一个位置绘制一个圆和正方形,那么在此处到底是覆盖还是混合
  bitmapGraphics.FillEllipse(&greenBrush, 30, 30, 150, 70);
  Gdiplus::RectF rc2(30, 30, 150, 70);
  
  Gdiplus::TextRenderingHint trh = Gdiplus::TextRenderingHintSystemDefault;
 
  bitmapGraphics.SetTextRenderingHint(trh);
	bitmapGraphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality); 
	bitmapGraphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
  Gdiplus::StringFormat stringFormat = Gdiplus::StringFormat::GenericTypographic();

  bitmapGraphics.DrawString(L"1111111111111111111xxxxxxxxxxx", -1, &font, rc2,&stringFormat, &redBrush);//在位图上绘制字符串
  HBITMAP save_bitmap = NULL;
  bitmap.GetHBITMAP(NULL, &save_bitmap); //取出位图中的句柄
  HDC mem_dc = CreateCompatibleDC(NULL); //创建内存dc
  HBITMAP old_bitmap = (HBITMAP)SelectObject(mem_dc, save_bitmap);//将位图句柄选入内存dc
  POINT pt = { 0,0 };

  
  BLENDFUNCTION oBF = { 0 };
  oBF.BlendOp = AC_SRC_OVER;
  oBF.BlendFlags = 0;
  oBF.SourceConstantAlpha = 255;
  oBF.AlphaFormat = AC_SRC_ALPHA;

  AlphaBlend(hDC, 0, 0, 392, 182, mem_dc, 0, 0, 180, 100, oBF);//将当前窗口的hdc 和内存dc 混合。既将当前位图中的话绘制到窗口上。
  SelectObject(mem_dc, old_bitmap);
  DeleteObject(save_bitmap);
  DeleteDC(mem_dc);

参考链接:https://qa.1r1g.com/sf/ask/90636381/
https://www.orcode.com/question/1473876_k57fa4.html
https://www.cnblogs.com/xzy1210/p/3622818.html(DrawText 出现此类问题,问题的原因一样)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用GDI+绘制异形窗口的示例代码: ```c++ #include <windows.h> #include <gdiplus.h> using namespace Gdiplus; LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // 初始化GDI+ GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); // 创建窗口 HWND hwnd; WNDCLASS wc = {}; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = L"MyWindowClass"; RegisterClass(&wc); hwnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, L"MyWindowClass", L"异形窗口", WS_POPUP, 100, 100, 400, 300, NULL, NULL, hInstance, NULL); // 设置窗口透明度 SetLayeredWindowAttributes(hwnd, 0, 128, LWA_ALPHA); // 显示窗口 ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); // 消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // 清理GDI+ GdiplusShutdown(gdiplusToken); return (int)msg.wParam; } LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); // 创建Graphics对象 Graphics graphics(hdc); // 绘制背景 SolidBrush brush(Color(255, 255, 0, 0)); // 红色 graphics.FillRectangle(&brush, 0, 0, 400, 300); // 绘制不规则形状 GraphicsPath path; path.AddEllipse(50, 50, 200, 100); Region region(&path); graphics.SetClip(&region, CombineModeReplace); // 绘制内部内容 SolidBrush innerBrush(Color(255, 0, 255, 0)); // 绿色 graphics.FillRectangle(&innerBrush, 0, 0, 400, 300); EndPaint(hwnd, &ps); } return 0; case WM_ERASEBKGND: return 1; // 留空,避免默认的擦除背景操作 case WM_NCHITTEST: { POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; RECT rc; GetWindowRect(hwnd, &rc); if (PtInRegion(CreateEllipticRgn(50, 50, 250, 150), pt.x - rc.left, pt.y - rc.top)) return HTCLIENT; // 鼠标在不规则形状的区域,返回HTCLIENT表示在客户区 else return HTCAPTION; // 鼠标在其他区域,返回HTCAPTION表示在标题栏 } case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, uMsg, wParam, lParam); } ``` 上述代码示例创建了一个异形窗口窗口形状为一个椭圆。在WM_PAINT消息处理函数中,使用Graphics对象进行绘制操作,首先绘制了一个红色的背景矩形,然后使用GraphicsPath对象创建了一个椭圆形状的区域,并将其应用到绘图上下文中。最后,绘制了一个绿色的矩形作为内部内容。在WM_NCHITTEST消息处理函数中,根据鼠标坐标和窗口的形状判断鼠标在窗口的哪个区域,并返回相应的结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值