Search code examples
c++winapidirect2d

ID2D1HwndRenderTarget::CreateBitmapFromWicBitmap()


I am trying to use WIC to load picture from the file and display it on screen with Direct2D. I follow the MSDN example but I encounter a problem with the function CreateBitmapFromWicBitmap().

No matter which combination of pixel formats I use during ID2D1HwndRenderTarget creation and IWICFormatConverter::Initialize() function calling, function CreateBitmapFromWicBitmap() returns 0x88982f80 error (WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT).

I call the function using the same ID2D1HwndRenderTarget which I use for drawing. Should I create another render target?

In the comment section in this link, someone wrote that CreateBitmapFromWicBitmap() should be called by DXGI surface render target. Does it mean that this function simply cannot be used with ID2D1HwndRenderTarget?

EDIT:

void LoadBitmapFromFile(ID2D1HwndRenderTarget* target, ID2D1Bitmap** ppBitmap)
{
    IWICImagingFactory* factory;
    IWICBitmapDecoder* decoder;
    IWICBitmapFrameDecode* frame;
    IWICFormatConverter* converter;

    CoInitializeEx(0, COINIT_MULTITHREADED);
    CoCreateInstance(CLSID_WICImagingFactory1,
                     NULL,
                     CLSCTX_INPROC_SERVER,
                     IID_IWICImagingFactory,
                     reinterpret_cast<void**>(&factory));

    factory->CreateDecoderFromFilename(L".png",
                                       NULL,
                                       GENERIC_READ,
                                       WICDecodeMetadataCacheOnLoad,
                                       &decoder);

    decoder->GetFrame(0, &frame);

    factory->CreateFormatConverter(&converter);

    converter->Initialize(frame,
                          GUID_WICPixelFormat32bppPBGRA,
                          WICBitmapDitherTypeNone,
                          NULL,
                          0.f,
                          WICBitmapPaletteTypeMedianCut);

    target->CreateBitmapFromWicBitmap(frame, 0, ppBitmap);
}

ID2D1HwndRenderTarget is created like that:

ID2D1Factory* factory;
ID2D1HwndRenderTarget* target;

D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);

factory->CreateHwndRenderTarget(
    D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_HARDWARE,
                                 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)), 
    D2D1::HwndRenderTargetProperties(hwnd, D2D1::SizeU(width, height)),
    &target);

Solution

  • CreateBitmapFromWicBitmap expects converter as first parameter, not frame.

    Use CLSID_WICImagingFactory instead of CLSID_WICImagingFactory1. The compiler will pick the right value. In my case it picks CLSID_WICImagingFactory2

    The variable name for IWICImagingFactory* factory is hiding a global variable with the same name. This was probably not causing an error, but it's better to change it...

    The handles need to be released.

    HRESULT LoadBitmapFromFile(const wchar_t *filename, ID2D1HwndRenderTarget* target, ID2D1Bitmap** pBitmap)
    {
        HRESULT hr = S_FALSE;
        IWICImagingFactory* wic_factory = NULL;
        IWICBitmapDecoder* decoder = NULL;
        IWICBitmapFrameDecode* frame = NULL;
        IWICFormatConverter* converter = NULL;
    
        hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, reinterpret_cast<void**>(&wic_factory));
        if FAILED(hr) goto clenaup;
    
        hr = wic_factory->CreateDecoderFromFilename(filename, NULL, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &decoder);
        if FAILED(hr) goto clenaup;
    
        hr = decoder->GetFrame(0, &frame);
        if FAILED(hr) goto clenaup;
    
        hr = wic_factory->CreateFormatConverter(&converter);
        if FAILED(hr) goto clenaup;
    
        hr = converter->Initialize(frame, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeMedianCut);
        if FAILED(hr) goto clenaup;
    
        hr = target->CreateBitmapFromWicBitmap(converter, 0, pBitmap);
        if FAILED(hr) goto clenaup;
    
    clenaup:
        safe_release(decoder);
        safe_release(converter);
        safe_release(frame);
        safe_release(wic_factory);
        return hr;
    }
    

    CoInitializeEx(0, COINIT_MULTITHREADED) can be called once in initialization.

    ID2D1Factory* factory;
    ID2D1HwndRenderTarget* target;
    
    void initialize(HWND hwnd)
    {
        CoInitializeEx(0, COINIT_MULTITHREADED);
    
        RECT rc;
        GetClientRect(hwnd, &rc);
    
        D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
        factory->CreateHwndRenderTarget(
            D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_HARDWARE,
            D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)),
            D2D1::HwndRenderTargetProperties(hwnd, D2D1::SizeU(rc.right, rc.bottom)),
            &target);
    }
    
    void on_render()
    {
        target->BeginDraw();
        target->Clear(D2D1::ColorF(D2D1::ColorF::White));
    
        ID2D1Bitmap* pBitmap = NULL;
        if (SUCCEEDED(LoadBitmapFromFile(L"filename.png", target, &pBitmap)))
        {
            D2D1_SIZE_F size = pBitmap->GetSize();
            target->DrawBitmap(pBitmap, D2D1::RectF(0, 0, size.width, size.height));
            safe_release(pBitmap);
        }
    
        target->EndDraw();
    }