How to create a DIB after loading a 32-bit PNG image with alpha channel?

144 Views Asked by At

I want to use the BITMAPINFO structure to define the DIB of the 32-bit png image, so that I can then use StretchDIBits to send the image to the printer for printing. I tried the following code:

HDC memDC = CreateCompatibleDC(hdcPrinter);
BITMAPINFO bmi = { 0 };
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -static_cast<int>(height);
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; 
bmi.bmiHeader.biCompression = BI_RGB;
void* bits = NULL;
HBITMAP dib = CreateDIBSection(memDC, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);

//...

But I'm not sure if this will lose the alpha channel of the image.

1

There are 1 best solutions below

0
YangXiaoPo-MSFT On

Your BITMAPINFO structure is constructed correctly. As a sidenote, you can use Windows Imaging Component to convert.

References: https://learn.microsoft.com/en-us/answers/questions/1360203/how-use-32bit-bitmap-for-menus-in-win32-app

https://github.com/microsoft/Windows-classic-samples/blob/ac06e54a15e9a62443e400fffff190fb978ea586/Samples/Win7Samples/winui/shell/appshellintegration/RecipeThumbnailProvider/RecipeThumbnailProvider.cpp#L249

HRESULT ConvertBitmapSourceTo32BPPHBITMAP(IWICBitmapSource *pBitmapSource,
                                           IWICImagingFactory *pImagingFactory,
                                           HBITMAP *phbmp)
{
    *phbmp = NULL;

    IWICBitmapSource *pBitmapSourceConverted = NULL;
    WICPixelFormatGUID guidPixelFormatSource;
    HRESULT hr = pBitmapSource->GetPixelFormat(&guidPixelFormatSource);
    if (SUCCEEDED(hr) && (guidPixelFormatSource != GUID_WICPixelFormat32bppBGRA))
    {
        IWICFormatConverter *pFormatConverter;
        hr = pImagingFactory->CreateFormatConverter(&pFormatConverter);
        if (SUCCEEDED(hr))
        {
            // Create the appropriate pixel format converter
            hr = pFormatConverter->Initialize(pBitmapSource, GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, NULL, 0, WICBitmapPaletteTypeCustom);
            if (SUCCEEDED(hr))
            {
                hr = pFormatConverter->QueryInterface(&pBitmapSourceConverted);
            }
            pFormatConverter->Release();
        }
    }
    else
    {
        hr = pBitmapSource->QueryInterface(&pBitmapSourceConverted);  // No conversion necessary
    }

    if (SUCCEEDED(hr))
    {
        UINT nWidth, nHeight;
        hr = pBitmapSourceConverted->GetSize(&nWidth, &nHeight);
        if (SUCCEEDED(hr))
        {
            BITMAPINFO bmi = {};
            bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
            bmi.bmiHeader.biWidth = nWidth;
            bmi.bmiHeader.biHeight = -static_cast<LONG>(nHeight);
            bmi.bmiHeader.biPlanes = 1;
            bmi.bmiHeader.biBitCount = 32;
            bmi.bmiHeader.biCompression = BI_RGB;

            BYTE *pBits;
            HBITMAP hbmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&pBits), NULL, 0);
            hr = hbmp ? S_OK : E_OUTOFMEMORY;
            if (SUCCEEDED(hr))
            {
                WICRect rect = {0, 0, nWidth, nHeight};

                // Convert the pixels and store them in the HBITMAP.  Note: the name of the function is a little
                // misleading - we're not doing any extraneous copying here.  CopyPixels is actually converting the
                // image into the given buffer.
                hr = pBitmapSourceConverted->CopyPixels(&rect, nWidth * 4, nWidth * nHeight * 4, pBits);
                if (SUCCEEDED(hr))
                {
                    *phbmp = hbmp;
                }
                else
                {
                    DeleteObject(hbmp);
                }
            }
        }
        pBitmapSourceConverted->Release();
    }
    return hr;
}