5
votes

I want to port my movie rendering software from DirectDraw to Direct2D. Because of compatibility issues, GDI rendering needs to be done on top of the image. To optimize performance I want to implement some kind of backbuffer mechanism, but there seems to be a problem with the alpha channel information, all GDI drawing appears somehow transparent.

I create a ID2D1HwndRenderTarget for my window handle and and a ID2D1Bitmap to copy the images to. The pixelformat of the bitmap is DXGI_FORMAT_B8G8R8A8_UNORM, the alpha mode is D2D1_ALPHA_MODE_IGNORE. The HwdRenderTarget is GDI compatible, so my render function looks somehow like this:

HwdRenderTarget.BeginDraw;
HwdRenderTarget.DrawBitMap(myBitMap);
HwdRenderTarget.GetDC(dc);

... do GDI drawing here ...

HwdRenderTarget.ReleaseDC();
HwdRenderTarget.EndDraw;

This version works perfectly fine: the GDI objects draw with solid colors, DrawTextEx-Text has a transparent background.

To optimize performance, I want to do the GDI drawing in a "backbuffer", so it only needs to be done when something changes. Otherwise I just can render the cached bitmap. The bitmap is empty and transparent, so only the objects drawn should be visible.

So I create a CompatibleRenderTarget ID2D1BitmapRenderTarget, alpha mode is D2D1_ALPHA_MODE_PREMULTIPLIED:

 HwdRenderTarget.CreateCompatibleRenderTarget(nil, nil, nil, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_GDI_COMPATIBLE, CompatibleRenderTarget);

Now I do the GDI rendering in the CompatibleRenderTarget:

CompatibleRenderTarget.BeginDraw;
if Supports(CompatibleRenderTarget, ID2D1GdiInteropRenderTarget, GdiInteropRenderTarget) then
begin
  GdiInteropRenderTarget.GetDC(D2D1_DC_INITIALIZE_MODE_CLEAR, dc);

  ... do GDI drawing here ...

  GdiInteropRenderTarget.ReleaseDC(fDstSize);
end;
GdiInteropRenderTarget := nil;
CompatibleRenderTarget.EndDraw;
CompatibleRenderTarget.GetBitmap(BackBuffer); // save the bitmap for drawing

and my render function now looks like this:

HwdRenderTarget.BeginDraw;
HwdRenderTarget.DrawBitMap(myBitMap);
HwdRenderTarget.DrawBitmap(BackBuffer);
HwdRenderTarget.EndDraw;

The problem now is, that all GDI drawing is somehow transparent and the degree of transparency depends on the values of the underlying image pixels. Dark text appears dark on a dark image background but becomes white on a white background. But alpha channel is not used in GDI and myBitmap also has no alpha information.

So where does the alpha information come from? Anyone has an idea? Thanks in advance!

Btw, using Direct2D drawing on the CompatibleRenderTarget also works fine.

2

2 Answers

2
votes

I had the same problem. The following worked for me (you need to create the render target using the D2D1_ALPHA_MODE_IGNORE, not D2D1_ALPHA_MODE_PREMULTIPLIED).

ID2D1HwndRenderTarget* pRenderTarget; // render target created sometime earlier

D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat(
    DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE);

ID2D1BitmapRenderTarget* pOffscreenRT = NULL;
pRenderTarget->CreateCompatibleRenderTarget(NULL, NULL, &pixelFormat,
    D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_GDI_COMPATIBLE, &pOffscreenRT);
1
votes

There's a dirty little secret about rendering with GDI: It will always clobber the alpha channel. Anything you draw with it will set the alpha values to zero in that area. I suspect it's because it was never designed to work with an alpha channel or any type of compositing. It was designed to render directly to the screen and to printers, where alpha channels don't exist.