Introduction to WIC: How to use WIC to load an image, and draw it with tranparency using only GDI?
A while ago I wrote an article: “Introduction to WIC: How to use WIC to load an image, and draw it with GDI?”
The code in that article didn’t handle transparency.
It’s actually trivial to implement transparency in that sample code.
First thing you have to do is modify CNuonImg::Open() and specify GUID_WICPixelFormat32bppPBGRA, which is a pre-multiplied alpha format, instead of GUID_WICPixelFormat32bppBGR.
Second, modify CNuonImg::Render() as follows (changes are highlighted):
void CNuonImg::Render(HDC hDC, UINT x, UINT y, UINT cx, UINT cy) { // Make sure an image has been loaded if (!IsLoaded()) throw WINCODEC_ERR_WRONGSTATE; // Get the WIC factory from the singleton wrapper class IWICImagingFactory* pFactory = CWICImagingFactory::GetInstance().GetFactory(); if (!pFactory) throw WINCODEC_ERR_NOTINITIALIZED; // Create a WIC image scaler to scale the image to the requested size CComPtr<IWICBitmapScaler> pScaler = nullptr; IfFailedThrowHR(pFactory->CreateBitmapScaler(&pScaler)); IfFailedThrowHR(pScaler->Initialize(m_pConvertedFrame, cx, cy, WICBitmapInterpolationModeFant)); // Render the image to a GDI device context HBITMAP hDIBBitmap = NULL; HDC memDC = NULL; try { // Get a DC for the full screen HDC hdcScreen = GetDC(NULL); if (!hdcScreen) throw 1; HDC memDC = CreateCompatibleDC(hdcScreen); if (!memDC) throw 2; BITMAPINFO bminfo; ZeroMemory(&bminfo, sizeof(bminfo)); bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bminfo.bmiHeader.biWidth = cx; bminfo.bmiHeader.biHeight = -(LONG)cy; bminfo.bmiHeader.biPlanes = 1; bminfo.bmiHeader.biBitCount = 32; bminfo.bmiHeader.biCompression = BI_RGB; void* pvImageBits = nullptr; // Freed with DeleteObject(hDIBBitmap) hDIBBitmap = CreateDIBSection(hdcScreen, &bminfo, DIB_RGB_COLORS, &pvImageBits, NULL, 0); if (!hDIBBitmap) throw 3; ReleaseDC(NULL, hdcScreen); HBITMAP hOldBitmap = (HBITMAP)::SelectObject(memDC, hDIBBitmap); // Calculate the number of bytes in 1 scanline UINT nStride = DIB_WIDTHBYTES(cx * 32); // Calculate the total size of the image UINT nImage = nStride * cy; // Copy the pixels to the DIB section IfFailedThrowHR(pScaler->CopyPixels(nullptr, nStride, nImage, reinterpret_cast<BYTE*>(pvImageBits))); // Copy the bitmap to the target device context BLENDFUNCTION bf; bf.AlphaFormat = AC_SRC_ALPHA; bf.BlendFlags = 0; bf.BlendOp = AC_SRC_OVER; bf.SourceConstantAlpha = 255; ::AlphaBlend(hDC, x, y, cx, cy, memDC, 0, 0, cx, cy, bf); ::SelectObject(memDC, hOldBitmap); DeleteDC(memDC); DeleteObject(hDIBBitmap); } catch (...) { if (hDIBBitmap) DeleteObject(hDIBBitmap); if (memDC) DeleteDC(memDC); // Rethrow the exception, so the client code can handle it throw; } }
Tony Teveris said,
Wrote on February 26, 2014 @ 1:10 pm
Marc, Thanks a bunch
T
Steve Valliere said,
Wrote on August 13, 2020 @ 2:07 pm
This looks promising, but before I begin the struggle to change it into something I can use from a native 64-bit C (NOT C++ or MFC or .NET or any of that) project, I am curious how the code determines that the loaded image contains and requires transparency? Also, to further expose my ignorance of image “stuff” I have been asked to choose a specific color to make the “transparent” color by some image editors, while others just “delete” the background. Are those the same and magically (to me) handled by this example, or are they different and only one is handled here?
Marc Gregoire said,
Wrote on August 14, 2020 @ 9:13 am
How the image is loaded is explained in the earlier blog post: http://www.nuonsoft.com/blog/2011/10/17/introduction-to-wic-how-to-use-wic-to-load-an-image-and-draw-it-with-gdi/
Look at the CNuonImg::Open() method.
It loads the image using WIC, and then converts it to 32bppBGR. If there is transparency in the image, WIC will do the right thing and provide a properly filled in alpha channel in the 32bppBGR result.
If you want to make a specific color transparent, WIC doesn’t know that, so you’ll have to do that yourself as far as I know.