prewett
prewett

Reputation: 1647

What parameter do I have wrong for ID2D1DeviceContext::CreateBitmap?

I'm trying to draw to an offscreen bitmap using Direct2D. I need to use a ID2D1DeviceContext instead of ID2D1RenderTarget because I need to be able to set the blending mode. However, CreateBitmap is failing with error E_INVALIDARG ("The parameter is incorrect") and I can't figure out which parameter is incorrect. The src data is not required, the pixel format is what Direct2D needs, and is in other examples I have found. The docs say that the color context is optional. Actually, I suspect that some parameter to CreateBitmap is incompatible to how the Device or DeviceContext got created, but I can't see anything wrong. I would be grateful if someone could point me to where I'm going wrong, or at least give me some pointers on how to figure it out.

The following code works fine up until CreateBitmap, where it fails with E_INVALIDARG.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <comdef.h>
#include <d2d1.h>
#include <d2d1_1.h>
#include <d3d11.h>

#include <iostream>
#include <string>

void printError(const std::string& msg, HRESULT err)
{
    std::cerr << "[ERROR] " << msg << ": " << _com_error(err).ErrorMessage() << std::endl;
    assert(false);
}

int main(int argc, char *argv[])
{
    ID2D1Factory1 *mD2DFactory = nullptr;
    ID3D11Device *mD3DDevice = nullptr;
    ID3D11DeviceContext* mD3DDeviceContext = nullptr;
    IDXGIDevice *mDXGIDevice = nullptr;
    ID2D1Device *mD2DDevice = nullptr;

    HRESULT err;

    // Create factories first; they don't depend on anything.
    err = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
                            __uuidof(ID2D1Factory1),  // get a Direct2D 1.1 factory,
                            (void**)&mD2DFactory);
    if (err != S_OK) {
        printError("fatal: Could not create Direct2D factory!", err);
        return 1;
    }

    // Initialize DirectX 11.1
    D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1 };
    err = D3D11CreateDevice(NULL,                                 // first adapter
                            D3D_DRIVER_TYPE_HARDWARE,
                            NULL,                                 // software rasterizer DLL handle
                            D3D11_CREATE_DEVICE_SINGLETHREADED    // better performance
                              | D3D11_CREATE_DEVICE_BGRA_SUPPORT, // required for Direct2D
                            featureLevels,
                            sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL),
                            D3D11_SDK_VERSION,                    // docs say use this
                            &mD3DDevice,
                            NULL,                                 // don't care what feature level we got
                            &mD3DDeviceContext);
    if (err != S_OK) {
        printError("fatal: could not create a Direct3D 11.1 device", err);
        return 1;
    }

    err = mD3DDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&mDXGIDevice);
    if (err != S_OK) {
        printError("fatal: Direct3D device is not IDXGIDevice", err);
        return 1;
    }

    // Initialize Direct2D
    err = mD2DFactory->CreateDevice(mDXGIDevice, &mD2DDevice);
    if (err != S_OK) {
        printError("fatal: could not create Direct2D device", err);
        return 1;
    }

    //---------------- Create the bitmap --------------------
    ID2D1DeviceContext *mDC = nullptr;
    ID2D1Bitmap1 *mBitmap = nullptr;
    int width = 13, height = 13;
    float dpi = 76.0f;

    err = mD2DDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &mDC);
    if (err) {
        printError("Could not create device context", err);
        return 1;
    }

    D2D1_BITMAP_PROPERTIES1 bitmapProps;
    bitmapProps.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
    bitmapProps.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
    bitmapProps.dpiX = dpi;
    bitmapProps.dpiY = dpi;
    bitmapProps.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET |  // can use for SetTarget()
                                D2D1_BITMAP_OPTIONS_CPU_READ;
    bitmapProps.colorContext = nullptr;

    err = mDC->CreateBitmap({ UINT32(width), UINT32(height) },
                            nullptr, 0,
                            bitmapProps, &mBitmap);
    if (err == S_OK) {
        mDC->SetTarget(mBitmap);
    } else {
        printError("Could not create bitmap (" + std::to_string(width) + "x" + std::to_string(height) + ")", err);
        return 1;
    }

    std::cout << "Success!" << std::endl;
    return 0;
}

Upvotes: 0

Views: 776

Answers (1)

Simon Mourier
Simon Mourier

Reputation: 138776

As Official D2D1_BITMAP_OPTIONS enumeration says:

D2D1_BITMAP_OPTIONS_CPU_READ means that the bitmap can be mapped by using ID2D1Bitmap1::Map. This flag requires D2D1_BITMAP_OPTIONS_CANNOT_DRAW and cannot be combined with any other flags. The bitmap must be updated with the CopyFromBitmap or CopyFromRenderTarget methods.

Upvotes: 1

Related Questions