Some time ago, I concluded a series of articles on how to convert a WIC bitmap to a Windows Runtime SoftwareBitmap. What about the reverse?
As I hinted in the Bonus Chatter, you can use the ISoftwareBitmapNative interface to reach inside the SoftwareBitmap and access the IWICBitmap hiding inside, if it has one.
#include <windows.graphics.imaging.interop.h>
#include <winrt/Windows.Graphics.Imaging.h>
winrt::com_ptr<IWICBitmap>
GetWICBitmap(winrt::Windows::Graphics::Imaging::SoftwareBitmap const& bitmap)
{
winrt::com_ptr wicBitmap;
bitmap.as<ISoftwareBitmapNative>()->GetData(IID_PPV_ARGS(wicBitmap.put()));
return wicBitmap;
}
Alternatively, if you like one-liners:
winrt::com_ptr<IWICBitmap>
GetWICBitmap(winrt::Windows::Graphics::Imaging::SoftwareBitmap const& bitmap)
{
return winrt::try_capture<IWICBitmap>(
bitmap.as<ISoftwareBitmapNative>(),
&ISoftwareBitmapNative::GetData);
}
The problem is that if the SoftwareBitmap is in a video format, the wrapped bitmap will be a IMF2DBuffer, not a IWICBitmap, and the call to GetData fails. In that case, we can convert the SoftwareBitmap to a WIC format and try again.
namespace winrt
{
using namespace winrt::Windows::Graphics::Imaging;
}
winrt::com_ptr<IWICBitmap>
GetWICBitmap(winrt::SoftwareBitmap const& bitmap)
{
auto result = winrt::try_capture<IWICBitmap>(
bitmap.as<ISoftwareBitmapNative>(),
&ISoftwareBitmapNative::GetData);
if (!result) {
auto converted = winrt::SoftwareBitmap::
Convert(bitmap, BitmapPixelFormat::Rgba8);
result = winrt::try_capture<IWICBitmap>(
converted.as<ISoftwareBitmapNative>(),
&ISoftwareBitmapNative::GetData);
}
return result;
}
Note that in this second case, we are returning a copy of the pixels, since we did a conversion from the original format.
Now, maybe you want the IWICBitmap to have a specific format. In that case, you could force a conversion if the original SoftwareBitmap is not to your liking.
winrt::com_ptr<IWICBitmap>
GetWICBitmapInFormat(
winrt::SoftwareBitmap const& bitmap,
winrt::BitmapPixelFormat format,
winrt::BitmapAlphaMode alphaMode)
{
winrt::SoftwareBitmap converted{ nullptr };
if (bitmap.BitmapPixelFormat() == format &&
bitmap.BitmapAlphaMode() == alphaMode) {
converted = bitmap;
} else {
converted = winrt::SoftwareBitmap::Convert(bitmap, format, alphaMode);
}
return winrt::capture<IWICBitmap>(
converted.as<ISoftwareBitmapNative>(),
&ISoftwareBitmapNative::GetData);
}
If the SoftwareBitmap is already in the desired format, then you will get an IWICBitmap that shares pixels with the source. Otherwise, you get a private IWICBitmap whose pixels you are free to modify. If you’d rather get a private one all the time, you could force the conversion unconditionally.
winrt::com_ptr<IWICBitmap>
GetCopyAsWICBitmap(
winrt::SoftwareBitmap const& bitmap,
winrt::BitmapPixelFormat format,
winrt::BitmapAlphaMode alphaMode)
{
auto converted = winrt::SoftwareBitmap::Convert(bitmap, format, alphaMode);
return winrt::capture<IWICBitmap>(
converted.as<ISoftwareBitmapNative>(),
&ISoftwareBitmapNative::GetData);
}
Good God, this blog is great. If I were to save one Microsoft blog from a fire and let the rest burn, it would be this blog! 👍