Gdiplus DrawString 在远程桌面上绘制透明文本

5
我正在将文本绘制到一个离屏位图的DIB节段中,该节段具有32位深度并使用Alpha通道(ARGB)。我直接在内存中绘制像素。然后,我创建了一个Gdiplus Graphics对象,将我的内存DC传递给它,并使用Graphics :: DrawString绘制文本。在正常情况下,这很好地工作。然而,在远程桌面上,渲染的文本完全是透明的,即你可以透过文本看到背景。有人知道为什么会这样,并且如何解决吗?
这是我的drawString例程:
void SplashScreen::drawString (MyString &ivText, Gdiplus::RectF &r, 
  Gdiplus::ARGB c, Gdiplus::StringAlignment align, Gdiplus::Font &fnt,
  Gdiplus::Graphics &gfx)
  {
  Gdiplus::StringFormat fmt;
  fmt.SetAlignment (align);
  Gdiplus::SolidBrush brush (c);
  wchar_t *wstr = new wchar_t [ivText.length()+1];
  std::mbstowcs (wstr, ivText.cstr(), ivText.length()+1);
  gfx.DrawString (wstr, ivText.length(), &fnt, r, &fmt, &brush);
  delete wstr;
  }

这就是我创建DIB的方式:

BITMAPV5HEADER bhd;
memset (&bhd, 0, sizeof (bhd));
bhd.bV5Size = sizeof (BITMAPV5HEADER);
bhd.bV5Width = nWidth;
bhd.bV5Height = -nHeight;  // negative height indicates top-down DIB
bhd.bV5Planes = 1;
bhd.bV5BitCount = 32;
bhd.bV5Compression = BI_BITFIELDS;
bhd.bV5RedMask   = 0x00FF0000;
bhd.bV5GreenMask = 0x0000FF00;
bhd.bV5BlueMask  = 0x000000FF;
bhd.bV5AlphaMask = 0xFF000000; 
m_pBuf = NULL;
m_hBmp = ::CreateDIBSection (m_hDC, (BITMAPINFO *) &bhd, DIB_RGB_COLORS,
   (void **) &m_pBuf, NULL, 0);
if (m_hBmp == NULL || m_pBuf == NULL)
   {
   // error...
   }
HGDIOBJ oldObj = ::SelectObject (m_hDC, m_hBmp);
if (oldObj == NULL)
   {
   // error...
   }

在将文本绘制到DIB中后,我进行了如下操作:
gfx.Flush (Gdiplus::FlushIntentionSync);

编辑:对于您可能还感兴趣的是,最终绘制DIB的窗口是一个WS_EX_LAYERED窗口。它是应用程序启动时显示的闪屏,使用定时器和以下方法缓慢淡入淡出:

void SplashScreen::setWindowTransparency (int nAlpha)
  // @param nAlpha: 255 is opaque, 0 is fully transparent.
  {
  HWND hwnd = getHwnd();
  BLENDFUNCTION blend;
  blend.BlendOp = AC_SRC_OVER;
  blend.BlendFlags = 0;
  blend.SourceConstantAlpha = nAlpha;
  blend.AlphaFormat = AC_SRC_ALPHA;
  BOOL bResult = ::UpdateLayeredWindow (hwnd, NULL, NULL, NULL, NULL,
     NULL, RGB (0, 0, 0), &blend, ULW_ALPHA);
  }

当您更改RDP会话的颜色深度设置时会发生什么? - Lightness Races in Orbit
我已经测试了15位、16位、24位和32位(在开始RDP会话之前在显示选项卡中设置)。所有这些设置都有同样的问题。(实际上,32位对我来说看起来并不像32位。也许RDP在欺骗?) - digory doo
禁用持久位图缓存怎么样? - Lightness Races in Orbit
我尝试禁用它,但问题仍然存在。我还尝试将真实显示深度(而不是RDP)设置为16位,并在本机运行应用程序 - 在这里一切正常。 - digory doo
好的,谢谢你尝试了那些方法。 - Lightness Races in Orbit
还没有弄清楚。没有其他人有任何线索吗? - digory doo
2个回答

4
我很惊讶这能正常工作。使用gdi32绘图会丢失alpha信息,并且据我所知,在32位DIB上使用gdi32进行任何绘制都会使DIB中的alpha信息未定义。在这种情况下,GDI+必须通过gdi32进行操作,因为你给了它一个HDC。
要使用GDI+绘制RGBA DIB,需要创建一个支持正确像素格式的GDI+ Bitmap对象(使用接受像素数据指针的构造函数,例如此构造函数),并将其后备到您的DIB的内存中,然后从Bitmap创建Graphics对象。这允许GDI+直接绘制到DIB的内存中,并正确处理alpha通道,而不是通过gdi32。
当然,如果您不需要每个像素的alpha,则可以通过在混合函数中使用AlphaFormat 0来忽略DIB中的alpha通道来简化事情。

这可能是答案,但由于我只是为应用程序启动时的闪屏做这个操作,所以我通过简单地检测应用程序是否在远程桌面下运行来“解决”了这个问题,如果是的话,就不显示闪屏...虽然有点取巧。 - digory doo

0

我有一个可能适用于您的解决方案。不确定您连接到的是哪个版本的Windows,但在远程计算机上,您可能需要启用终端服务的32位色彩模式。否则,您的客户端可能会受到16位模式的限制。

在服务器上,使用gpedit.msc,将“限制最大颜色深度”选项配置为32位。在Windows 2008/2012中,此选项位于管理模板 - Windows组件 - 远程桌面服务 - 远程会话环境中。

如果您正在连接到Windows XP / Vista / 7/8计算机,则不确定是否可用此gpedit设置。


当然我不能这么做,因为我们的应用程序不只是我一个人在使用。我不能期望用户在远程桌面中更改他们的设置。相反,应用程序必须在任何环境下都能正常工作。 - digory doo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接