Search code examples
c++winapigdihbitmap

Resizing HBITMAP while keeping transparent background


I have an application that loads an image with a transparent background, then I use StretchBlt to resize it to the desired size, with HALFTONE set using SetStretchBltMode (I tried using other modes that, while keeping the transparency intact, also made the resized image look 'ugly').
However, StretchBlt replaces the transparent background with a color (black) that doesn't fit the background of the window that the image will be displayed on.

So I have two options:
1) Replace the transparent background of the image with the background color of the window, then resize it using StretchBlt
2) Resize it while keeping the background transparency (preferred option)

I tried looking for WinAPI function that would provide either functionality, but I found none.

How do I do any of those options (replace the transparency or resize it while keeping it) using plain WinAPI?


Solution

  • First, BitBlt, StretchBlt and TransparentBlt do NOT support the alpha channel..

    TransparentBlt works by making whatever specified colour you want, transparent.

    If you want Alpha channel and blending support, you need: AlphaBlend.

    You can do the following:

    BLENDFUNCTION fnc;
    fnc.BlendOp = AC_SRC_OVER;
    fnc.BlendFlags = 0;
    fnc.SourceConstantAlpha = 0xFF;
    fnc.AlphaFormat = AC_SRC_ALPHA;
    
    //You need to create a memDC.. and an HBITMAP..
    
    //Select the hBitmap into the memDC.
    HGDIOBJ obj = SelectObject(memDC, hBmp);
    
    //Render with alpha blending..
    AlphaBlend(DC, rect.X, rect.Y, rect.Width, rect.Height, memDC, 0, 0, Width, Height, fnc);
    
    //Restore the memDC to original state..
    SelectObject(memDC, obj);
    

    OR do your own pre-multiplied alpha rendering by calculating the channel colours yourself..

    Alternatively, you can try GDI+ and see how that works out:

    ULONG_PTR GdiImage::GDIToken = 0;
    Gdiplus::GdiplusStartupInput GdiImage::GDIStartInput = NULL;
    Gdiplus::GdiplusStartup(&GdiImage::GDIToken, &GdiImage::GDIStartInput, NULL);
    
    Gdiplus::Image* Img = Gdiplus::Image::FromFile(L"PathToImage.ext"); //where ext can be png, bmp, etc..
    
    Gdiplus::Graphics graphics(DC);
    //graphics.SetSmoothingMode(SmoothingModeHighSpeed);
    graphics.SetInterpolationMode(Gdiplus:: InterpolationModeBilinear); //InterpolationModeNearestNeighbor
    //graphics.SetPixelOffsetMode(Gdiplus::PixelOffsetModeHalf);
    graphics.DrawImage(Img, x, y, w, h);
    
    delete Img;
    Gdiplus::GdiplusShutdown(GdiImage::GDIToken);
    GdiImage::GDIStartInput = NULL;
    GdiImage::GDIToken = 0;