Here is a quick and dirty example of showing how to create an ID2D1Bitmap from an IWICBitmapSource using CopyPixels. This method will work for IWICBitmap because the IWICBitmap interface inherits from IWICBitmapSource. However, it shouldn't be that hard to change the code loading the D2D bitmap from IWICBitmalLock.
winrt::com_ptr<ID2D1Bitmap> create_from_wic_buffer(IWICBitmapSource *source, IWICImagingFactory *factory, ID2D1DeviceContext *device_context)
{
UINT width{};
UINT height{};
//Since we are aiming to create a D2D bitmap with the format DXGI_FORMAT_B8G8R8A8_UNORM
//then we know in advance that this is a 32 bits per pixel/4 bytes per pixel format.
const uint32_t bytes_per_pixel = 4;
uint32_t stride{};
uint32_t buffer_size{};
WICPixelFormatGUID format{};
winrt::check_hresult(source->GetSize(&width, &height));
winrt::check_hresult(source->GetPixelFormat(&format));
winrt::com_ptr<IWICFormatConverter> format_converter;
winrt::check_hresult(factory->CreateFormatConverter(format_converter.put()));
//This the image needs to be in a format that corresponds to one of the DXGI_FORMAT_* BGR formats.
//Just blindly convert here, but an optimisation could be to check the format to see if it needs
//converting.
BOOL convertable{};
winrt::check_hresult(format_converter->CanConvert(format, GUID_WICPixelFormat32bppBGRA, &convertable));
if (!convertable)
{
throw winrt::hresult_invalid_argument(L"Image format not convertable to B8G8R8A8");
}
winrt::check_hresult(format_converter->Initialize(source, GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, nullptr, 0.f, WICBitmapPaletteTypeCustom));
//Since this is going into memory and there is no special requirements, just set the stride to width * bytes_per_pixel.
stride = width * bytes_per_pixel;
buffer_size = width * height * bytes_per_pixel;
std::unique_ptr<BYTE[]> pixel_buffer = std::make_unique<BYTE[]>(buffer_size);
winrt::check_hresult(format_converter->CopyPixels(nullptr, stride, buffer_size, pixel_buffer.get()));
winrt::com_ptr<ID2D1Bitmap> d2d_bitmap;
winrt::check_hresult(device_context->CreateBitmap(D2D1::SizeU(width, height), pixel_buffer.get(), stride, D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)), d2d_bitmap.put()));
return d2d_bitmap;
}
This is using C++/WinRT for the COM utilities since the smart pointers and error checking are convenient.
As a side note, if the question was more of a going backwards from an ID2D1Bitmap to an IWICBitmap, then using ID2D1Bitmap1 will make things easier.
winrt::com_ptr<IWICBitmap> create_from_d2d1_bitmap(IWICBitmapSource *source, IWICImagingFactory *factory, ID2D1DeviceContext *device_context)
{
WICPixelFormatGUID format{};
winrt::check_hresult(source->GetPixelFormat(&format));
winrt::com_ptr<IWICFormatConverter> format_converter;
winrt::check_hresult(factory->CreateFormatConverter(format_converter.put()));
//This the image needs to be in a format that corresponds to one of the DXGI_FORMAT_* BGR formats.
//Just blindly convert here, but an optimisation could be to check the format to see if it needs
//converting.
BOOL convertable{};
winrt::check_hresult(format_converter->CanConvert(format, GUID_WICPixelFormat32bppBGRA, &convertable));
if (!convertable)
{
throw winrt::hresult_invalid_argument(L"Image format not convertable to B8G8R8A8");
}
winrt::check_hresult(format_converter->Initialize(source, GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, nullptr, 0.f, WICBitmapPaletteTypeCustom));
winrt::com_ptr<ID2D1Bitmap1> bitmap;
winrt::check_hresult(device_context->CreateBitmapFromWicBitmap(source, D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_CPU_READ | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)), bitmap.put()));
auto bitmap_size = bitmap->GetSize();
D2D1_MAPPED_RECT mapped_rect{};
uint32_t uwidth = static_cast<uint32_t>(bitmap_size.width);
uint32_t uheight = static_cast<uint32_t>(bitmap_size.height);
winrt::com_ptr<IWICBitmap> memory_bitmap;
winrt::check_hresult(bitmap->Map(D2D1_MAP_OPTIONS_READ, &mapped_rect));
//The stride/pitch is (bitmap width * bytes per pixel) + additional space.
//Since we don't control this, use the bitmap supplied stride. Since
//the stride is width * bytes per pixel, we just need to multiply it
//by height.
uint32_t buffer_size = uheight * mapped_rect.pitch;
winrt::check_hresult(factory->CreateBitmapFromMemory(uwidth, uheight, GUID_WICPixelFormat32bppBGRA, mapped_rect.pitch, buffer_size, mapped_rect.bits, memory_bitmap.put()));
winrt::check_hresult(bitmap->Unmap());
return memory_bitmap;
}
The biggest thing to remember is that the bitmap must be ID2D1Bitmap1 and it must be created as ID2D1Bitmap1 since the D2D1_BITMAP_OPTIONS_CPU_READ flag must be used. However, ID2D1Bitmap1 inherits from the ID2D1Bitmap interface, so it is possible to copy from an ID2D1Bitmap using CopyFromBitmap.This is just one example, since there are other options available, like ID2D1Factory::CreateWicBitmapRenderTarget. In the end, it is usually the pixel format or stride which causes issues.