Search code examples
c++direct2d

Converting Direct2D's ID2D1HwndRenderTarget to ID2D1Bitmap


I need to convert a Direct2D ID2D1HwndRenderTarget into an ID2D1Bitmap. However, I'm encountering an error with CopyFromRenderTarget. ”Error: E_INVALIDARG One or more arguments are invalid.”

What could be the issue?

using Direct2D 1.0 (include d2d1.h)

 HRESULT hr;
 ID2D1Factory* factory;
 hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);

 ID2D1HwndRenderTarget* target;
  D2D1_RENDER_TARGET_PROPERTIES prop{ D2D1::RenderTargetProperties() };
 factory->CreateHwndRenderTarget(prop,D2D1::HwndRenderTargetProperties(hwnd, 
D2D1::SizeU(static_cast<UINT>(width), static_cast<UINT>(height))), target);


  // ID2D1HwndRenderTarget* target 

    HRESULT hr;
    D2D1_SIZE_F size = target->GetSize();
    UINT width = static_cast< UINT >( size.width );
    UINT height = static_cast< UINT >( size.height );

    D2D1_SIZE_U px_size = target->GetPixelSize();
    
    D2D1_BITMAP_PROPERTIES props;
    bitmapProperties.pixelFormat = D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED );
    bitmapProperties.dpiX = 96.0f;
    bitmapProperties.dpiY = 96.0f;

    ID2D1Bitmap* src_bitmap;
    hr = target->CreateBitmap( px_size, NULL, 0, &props, &src_bitmap );

    // Copy the contents of the current drawing target to a bitmap
    D2D1_POINT_2U destPoint = D2D1::Point2U( 0, 0 );
    D2D1_RECT_U srcRect = D2D1::RectU( 0, 0, px_size.width, px_size.height );

    // Error: E_INVALIDARG One or more arguments are invalid.
    hr = src_bitmap->CopyFromRenderTarget( &destPoint, target, &srcRect );
   

Solution

  • An HWND Direct2D Render Target supports various pixel formats. When you create it like you do (passing D2D1::RenderTargetProperties()) you're implicitly using the DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_IGNORE parameters.

    So you can either change how you create the render target (you can use D2D1_ALPHA_MODE_PREMULTIPLIED) and don't change the bitmap's pixel format, or change the bitmap's pixel format to D2D1_ALPHA_MODE_IGNORE.

    But the best is to just copy the render target's pixel format like this:

    D2D1_BITMAP_PROPERTIES bitmapProperties{}; // use 0 for dpi means auto-adapt
    bitmapProperties.pixelFormat = target->GetPixelFormat();