Search code examples
c++bitmapmfcpngtransparency

Converting a bitmap (bmp) to png with transparency (Windows c++)


I need to convert bmp files to png files while treating one of the colors in the bmps as transparent. My app uses MFC, but I am not tied to using MFC in the solution. I am doing this while porting from gdi to Direct2D, so I do have access to Direct2D (although I'm a true novice with Direct2D). I would like to avoid gdi+ and external source code (e.g., libpng), but I would reconsider if it offers an easy-to-implement solution. Performance is not an issue.

As shown here, MFC makes it easy to create a png file from a bmp file, but I don't know how to convert the "transparent color" in the bmp to an alpha mask in the png. (It's less desirable, but I'm okay with creating the png file, then reading it, changing the image's "transparent color" to transparency, and writing it.)

// Convert bmp to png.
CImage image;
if(image.Load(bmpFilename) == S_OK)
{
    image.Save(pngFilename, Gdiplus::ImageFormatPNG) == S_OK);
}

I have seen this thread which asks a very similar question, but its answers are high-level (e.g., "use libpng", "use gdi+", "use BITMAPINFOHEADER::biCompression == BI_PNG", etc). I am looking for the specifics and (relative) ease of implementation.

Thanks!


Solution

  • This should do:

    void CImageCopy(CImage& dest, CImage& src)
    {
        // rescale user image
        dest.Destroy();
        dest.Create(src.GetWidth(), src.GetHeight(), 32, CImage::createAlphaChannel);
        HDC hdc = dest.GetDC();
    
        src.AlphaBlend(hdc, 0,0,src.GetWidth(), src.GetHeight(),  0,0,src.GetWidth(),src.GetHeight());
        dest.ReleaseDC();
    }
    
    
    
    void Fix24BitTransparency(CImage& img)
    {
        if(img.GetBPP()<32 )
        {
            // alpha bits
            CImage imgout;
            imgout.Create(img.GetWidth(), img.GetHeight(), 32, CImage::createAlphaChannel);
    
            for(int x=0; x<img.GetWidth(); ++x)
            for(int y=0; y<img.GetHeight(); ++y)
            {
                COLORREF c1;
                c1 = img.GetPixel(x,y);  // user image
                imgout.SetPixel(x,y,c1);
                if( c1 == RGB(255,0,128) ) // or whatever you decide transparent...
                {
                    imgout.SetPixel(x,y, RGB(255,255,255) );
                    BYTE* pAlpha = (BYTE*)imgout.GetPixelAddress(x, y)+3;
                    *pAlpha = 0;
                }
                else
                {
                    BYTE* pAlpha = (BYTE*)imgout.GetPixelAddress(x, y)+3;
                    *pAlpha = 255;
                }
            }
            CImageCopy(img, imgout);
        }
    }