{"id":108048,"date":"2023-04-13T07:00:00","date_gmt":"2023-04-13T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108048"},"modified":"2023-04-13T07:43:52","modified_gmt":"2023-04-13T14:43:52","slug":"20230413-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230413-00\/?p=108048","title":{"rendered":"How can I convert a WIC bitmap to a Windows Runtime SoftwareBitmap? part 3: Filling the SoftwareBitmap directly"},"content":{"rendered":"<p>Last time, <a title=\"How can I convert a WIC bitmap to a Windows Runtime SoftwareBitmap? part 2: Via a buffer\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230412-00\/?p=108044\"> we converted a WIC bitmap to a Windows Runtime SoftwareBitmap by copying the pixels of the WIC bitmap to a buffer, and then creating the SoftwareBitmap from that same buffer<\/a> But you don&#8217;t have to pass the pixels through a buffer. The <code>SoftwareBitmap<\/code> lets you access its pixel buffer directly.<\/p>\n<pre>winrt::SoftwareBitmap ToSoftwareBitmap(IWICBitmapSource* wicBitmap)\r\n{\r\n    \/\/ Look up the Windows Runtime pixel format and alpha mode.\r\n    WICPixelFormatGUID format;\r\n    winrt::check_hresult(wicBitmap-&gt;GetPixelFormat(&amp;format));\r\n\r\n    static struct Mapping\r\n    {\r\n        WICPixelFormatGUID const&amp; format;\r\n        int bytesPerPixel;\r\n        winrt::BitmapPixelFormat pixelFormat;\r\n        winrt::BitmapAlphaMode alphaMode;\r\n    } const mappings[] = {\r\n        {\r\n            GUID_WICPixelFormat32bppPRGBA,\r\n            4,\r\n            winrt::BitmapPixelFormat::Rgba8,\r\n            winrt::BitmapAlphaMode::Premultiplied\r\n        },\r\n        { ... etc ... },\r\n    };\r\n\r\n    auto it = std::find_if(std::begin(mappings),\r\n    std::end(mappings), [&amp;](auto&amp;&amp; mapping)\r\n        { return mapping.format == format; });\r\n    if (it == std::end(mappings)) {\r\n        throw winrt::hresult_error(\r\n            WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT);\r\n    }\r\n\r\n    \/\/ Create a SoftwareBitmap of the same size and format.\r\n    UINT width, height;\r\n    winrt::check_hresult(wicBitmap-&gt;GetSize(&amp;width, &amp;height));\r\n    \/\/ Avoid zero-sized or oversized bitmaps (integer overflow)\r\n    if (width == 0 || height == 0 ||\r\n        width &gt; ~0U \/ it-&gt;bytesPerPixel \/ height) {\r\n        throw winrt::hresult_error(\r\n            WINCODEC_ERR_IMAGESIZEOUTOFRANGE);\r\n    }\r\n    winrt::SoftwareBitmap bitmap(\r\n        pixelFormat, width, height, alphaMode);\r\n\r\n    \/\/ Copy the pixels into the SoftwareBitmap.\r\n    auto buffer = bitmap.LockBuffer(winrt::BitmapBufferAccessMode::Write);\r\n    if (buffer.GetPlaneCount() != 1) {\r\n        throw winrt::hresult_error(\r\n            WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT);\r\n    }\r\n    auto planeDescription = buffer.GetPlaneDescription(0);\r\n    auto reference = buffer.CreateReference();\r\n    winrt::check_hresult(wicBitmap-&gt;CopyPixels(\r\n        nullptr, planeDescription.Stride,\r\n        reference.Capacity(), reference.data()));\r\n\r\n    \/\/ \"buffer\" destructor releases the lock\r\n\r\n    return bitmap;\r\n}\r\n<\/pre>\n<p>This avoids the intermediate <code>Buffer<\/code>, which is definitely a good thing for large bitmaps, so we&#8217;re down to just two copies of the pixels, one in the original<code>wicBitmap<\/code> and one in the converted <code>SoftwareBitmap<\/code>.<\/p>\n<p>However, there is still a lot of hassle with having to parse out the original bitmap format and convert it to a Windows Runtime bitmap format.<\/p>\n<p>If we would rather coerce the bitmap to a specific format, we can hard-code that into the function and avoid having to do format sniffing:<\/p>\n<pre>winrt::SoftwareBitmap ToSoftwareBitmap(IWICBitmapSource* wicBitmap)\r\n{\r\n    \/\/ Coerce into premultiplied ARGB\r\n    winrt::com_ptr&lt;IWICBitmapSource&gt; prgbaBitmap;\r\n    winrt::check_hresult(WICConvertBitmapSource(\r\n        GUID_WICPixelFormat32bppPRGBA,\r\n        wicBitmap,\r\n        prgbaBitmap.put()));\r\n\r\n    \/\/ Create a SoftwareBitmap of the same size and format.\r\n    UINT width, height;\r\n    winrt::check_hresult(prgbaBitmap-&gt;GetSize(&amp;width, &amp;height));\r\n    \/\/ Avoid zero-sized or oversized bitmaps (integer overflow)\r\n    if (width == 0 || height == 0 ||\r\n        width &gt; ~0U \/ 4 \/ height) {\r\n        throw winrt::hresult_error(\r\n            WINCODEC_ERR_IMAGESIZEOUTOFRANGE);\r\n    }\r\n    winrt::SoftwareBitmap bitmap(\r\n        winrt::BitmapPixelFormat::Rgba8, width, height\r\n        winrt::BitmapAlphaMode::Premultiplied);\r\n\r\n    \/\/ Copy the pixels into the SoftwareBitmap.\r\n    auto buffer = bitmap.LockBuffer(winrt::BitmapBufferAccessMode::Write);\r\n    if (buffer.GetPlaneCount() != 1) {\r\n        throw winrt::hresult_error(\r\n            WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT);\r\n    }\r\n    auto planeDescription = buffer.GetPlaneDescription(0);\r\n    auto reference = buffer.CreateReference();\r\n    winrt::check_hresult(prgbaBitmap-&gt;CopyPixels(\r\n        nullptr, planeDescription.Stride,\r\n        reference.Capacity(), reference.data()));\r\n\r\n    \/\/ \"buffer\" destructor releases the lock\r\n\r\n    return bitmap;\r\n}\r\n<\/pre>\n<p>Copying the pixels directly from the <code>IWICBitmapSource<\/code> to the <code>Software\u00adBitmap<\/code> removes an intermediate copy.<\/p>\n<p>This is great, but it turns out we can do even better. We&#8217;ll look some more next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Avoiding the intermediate buffer.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-108048","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Avoiding the intermediate buffer.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108048","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=108048"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108048\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=108048"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108048"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108048"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}