Last time, we converted a WIC bitmap to a Windows Runtime SoftwareBitmap by encoding the WIC bitmap to a stream, then decoding it back into a SoftwareBitmap. But you don’t have to pass the pixels through a stream. The SoftwareBitmap lets you pass the pixels directly in the form of an IBuffer.
winrt::SoftwareBitmap ToSoftwareBitmap(IWICBitmapSource* wicBitmap)
{
// Look up the Windows Runtime pixel format and alpha mode.
WICPixelFormatGUID format;
winrt::check_hresult(wicBitmap->GetPixelFormat(&format));
static struct Mapping
{
WICPixelFormatGUID const& format;
int bytesPerPixel;
winrt::BitmapPixelFormat pixelFormat;
winrt::BitmapAlphaMode alphaMode;
} const mappings[] = {
{
GUID_WICPixelFormat32bppPRGBA,
4,
winrt::BitmapPixelFormat::Rgba8,
winrt::BitmapAlphaMode::Premultiplied
},
{ ... etc ... },
};
auto it = std::find_if(std::begin(mappings),
std::end(mappings), [&](auto&& mapping)
{ return mapping.format == format; });
if (it == std::end(mappings)) {
throw winrt::hresult_error(
WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT);
}
// Create a buffer that can hold the pixels.
UINT width, height;
winrt::check_hresult(wicBitmap->GetSize(&width, &height));
// Avoid zero-sized or oversized bitmaps (integer overflow)
if (width == 0 || height == 0 ||
width > ~0U / it->bytesPerPixel / height) {
throw winrt::hresult_error(
WINCODEC_ERR_IMAGESIZEOUTOFRANGE);
}
auto size = width * height * it->bytesPerPixel;
winrt::Buffer buffer(size);
buffer.Length(size);
// Copy the pixels into the buffer.
winrt::check_hresult(wicBitmap->CopyPixels(
nullptr, width * it->bytesPerPixel, size,
buffer.data()));
winrt::SoftwareBitmap softwareBitmap(it->pixelFormat,
width, height, it->alphaMode);
// Create a SoftwareBitmap from the buffer.
return winrt::SoftwareBitmap::CreateCopyFromBuffer(
buffer, pixelFormat, width, height, alphaMode);
}
We no longer use the Windows Runtime BitmapDecoder, which means that we can do all of our work synchronously and return a SoftwareÂBitmap.
The idea here is that we peek at the IWICBitmap to see what its pixel format is, copy the pixels to a buffer, and then create a SoftwareÂBitmap of a matching format from that buffer. Unfortunately, there doesn’t appear to be an easy way to convert between WIC pixel formats and Windows Runtime pixel formats, so we had to create a huge lookup table.
If you would rather force the SoftwareÂBitmap into a specific pixel format, then you can get rid of the format-sniffing code and instead use WICConvertÂBitmapÂSource to convert the wicBitmap to a matching source, and then just hard-code all the pixel format nonsense.
It turns out that even this is working too hard. The pixels for the bitmap get copied twice: Once from the wicBitmap to the buffer, and then again from the buffer to the final SoftwareÂBitmap. Next time, we’ll reduce it to just one copy.
0 comments