Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gdiplus DrawString draws transparent text over Remote Desktop

I'm drawing text into a DIB section off-screen bitmap, which is 32bit deep and uses the alpha channel (ARGB). I draw pixels directly to memory. Then, I create a Gdiplus Graphics object, pass my memory DC to it, and draw text using Graphics::DrawString. This works fine under normal circumstances. Over Remote Desktop, however, the text rendered is completely transparent, i.e. instead of drawing any colour pixels, you can see through where the text is. Does anyone know why this is so, and how to fix it??

Here's my drawString routine:

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;
  }

And that's how I create the 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...
   }

After drawing text into the DIB, I do

gfx.Flush (Gdiplus::FlushIntentionSync);

EDIT: It may also be of interest to you that the window where the DIB is finally drawn into is a WS_EX_LAYERED window. It's a splash screen that shows when the application starts, and is slowly faded in and out using a timer and the following method:

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);
  }
like image 503
digory doo Avatar asked Oct 06 '22 00:10

digory doo


1 Answers

I'm surprised this ever works. Drawing with gdi32 loses alpha information, and as I understand it any drawing on a 32-bit DIB with gdi32 leaves the alpha information in the DIB undefined. GDI+ has to go through gdi32 in this case because you gave it an HDC.

To draw to an RGBA DIB using GDI+, you need to create a GDI+ Bitmap object backed by your DIB's memory with the correct pixel format (using a constructor that takes a pixel data pointer, such as this one), then create a Graphics object from your Bitmap. This allows GDI+ to draw directly to the DIB's memory and handle the alpha channel correctly rather than go through gdi32.

Of course, if you don't need per-pixel alpha, you can simplify things by using an AlphaFormat of 0 in your blend function to ignore the alpha channel in your DIB.

like image 70
Esme Povirk Avatar answered Oct 10 '22 03:10

Esme Povirk