ID2D1HwndRenderTarget::CreateBitmapFromWicBitmap()

1

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);
c++
winapi
direct2d
asked on Stack Overflow Oct 3, 2016 by Avert • edited Oct 4, 2016 by Barmak Shemirani

1 Answer

2

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();
}
answered on Stack Overflow Oct 4, 2016 by Barmak Shemirani • edited Oct 4, 2016 by Barmak Shemirani

User contributions licensed under CC BY-SA 3.0