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?
Resizing HBITMAP while keeping transparent background
2.4k Views Asked by 0x400921FB54442D18 AtThere are 2 best solutions below
On
I tried looking for WinAPI function that would provide either functionality, but I found none.
Although others already gave a few suggestions, to my knowledge none of the currently (12/2017) available native OS APIs provide high-quality image resampling.
There are many third-party libraries available for resampling. Many (most?) of them are quite complicated and propably overkill if image processing is not the main job of your application.
In the following example I am using the public-domain, single-header file, no-external-dependencies "stb_image_resize.h" library. Just #include it in your project and be done with it. The library is not the fastest, but I wouldn't call it particularly slow. Where it shines is ease-of-use and versatility. It is also easy to extend, if you'd like to have additional filters like Lanczos (for which I can provide code if there is interest). Though the built-in filters are already much better than the ones of the OS APIs.
The following sample program expects to find a 32 bpp bitmap with an alpha-channel (non-premultiplied) in the file "flower.bmp" of the current directory.
The image is loaded using the OS API LoadImageW(), resampled to one third of the original dimensions using stbir_resize_uint8() and stored as "flower_resized.bmp" in the current directory using GDI+.
#include <Windows.h>
#include <iostream>
#include <gdiplus.h>
#pragma comment( lib, "gdiplus" )
namespace gp = Gdiplus;
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize.h"
int main()
{
// Using LR_CREATEDIBSECTION flag to get direct access to the bitmap's pixel data.
HBITMAP hBmpIn = reinterpret_cast<HBITMAP>(
LoadImageW( NULL, L"flower.bmp", IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION ) );
if( !hBmpIn )
{
std::cout << "Failed to load bitmap.\n";
return 1;
}
// Getting bitmap information including a pointer to the bitmap's pixel data
// in infoIn.dsBm.bmBits.
// This will fail if hBmpIn is not a DIB. In this case you may call GetDIBits()
// to get a copy of the bitmap's pixel data instead.
DIBSECTION infoIn{};
if( !GetObject( hBmpIn, sizeof( infoIn ), &infoIn ) )
{
std::cout << "Bitmap is not a DIB.\n";
return 1;
}
// Some sanity checks of the input image.
if( infoIn.dsBm.bmBitsPixel != 32 || infoIn.dsBm.bmPlanes != 1 ||
infoIn.dsBmih.biCompression != BI_RGB )
{
std::cout << "Bitmap is not 32 bpp uncompressed.\n";
return 1;
}
// Create a DIB for the output. We receive a HBITMAP aswell as a writable
// pointer to the bitmap pixel data.
int out_w = infoIn.dsBm.bmWidth / 3, out_h = infoIn.dsBm.bmHeight / 3;
BITMAPINFO infoOut{};
auto& hdr = infoOut.bmiHeader;
hdr.biSize = sizeof(hdr);
hdr.biBitCount = 32;
hdr.biCompression = BI_RGB;
hdr.biWidth = out_w;
hdr.biHeight = out_h; // negate the value to create top-down bitmap
hdr.biPlanes = 1;
unsigned char* pOutPixels = nullptr;
HBITMAP hBmpOut = CreateDIBSection( NULL, &infoOut, DIB_RGB_COLORS,
reinterpret_cast<void**>( &pOutPixels ), NULL, 0 );
if( !hBmpOut )
{
std::cout << "Could not create output bitmap.\n";
return 1;
}
// Resample the input bitmap using the simplest API.
// These functions use a "default" resampling filter defined at compile time
// (currently "Mitchell" for downsampling and "Catmull-Rom" for upsampling).
// To change the filter, you can change the compile-time defaults
// by #defining STBIR_DEFAULT_FILTER_UPSAMPLE and STBIR_DEFAULT_FILTER_DOWNSAMPLE,
// or you can use the medium-complexity API.
// Consult "stb_image_resize.h" which contains the documentation.
stbir_resize_uint8(
reinterpret_cast< unsigned char const* >( infoIn.dsBm.bmBits ),
infoIn.dsBm.bmWidth,
infoIn.dsBm.bmHeight,
0, // input_stride_in_bytes, 0 = packed continously in memory
pOutPixels,
out_w,
out_h,
0, // output_stride_in_bytes, 0 = packed continously in memory
4 // num_channels
);
// Use GDI+ for saving the resized image to disk.
gp::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdipToken = 0;
gp::GdiplusStartup( &gdipToken, &gdiplusStartupInput, nullptr );
{
gp::Bitmap bmpOut( hBmpOut, nullptr );
// I'm taking a shortcut here by hardcoding the encoder CLSID. Check MSDN to do it by-the-book:
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms533843(v=vs.85).aspx
class __declspec(uuid("{557cf400-1a04-11d3-9a73-0000f81ef32e}")) BmpEncoderId;
bmpOut.Save( L"flower_resized.bmp", &__uuidof(BmpEncoderId) );
}
// Cleanup
gp::GdiplusShutdown( gdipToken );
DeleteObject( hBmpIn );
DeleteObject( hBmpOut );
std::cout << "All done.\n";
return 0;
}
Notes:
When resampling transparent images, it is generally advisable to use a premultiplied alpha channel. Otherwise the resampled image can have artifacts, generally noticable along the edges of shapes. STBIR will use "alpha-weighted resampling" (effectively premultiplying, resampling, then unpremultiplying) unless you specify the STBIR_FLAG_ALPHA_PREMULTIPLIED flag. So you will gain performance benefits when manually premultiplying once after loading an image. Most Windows APIs (like AlphaBlend) that can show transparent images, expect the alpha channel to be premultiplied anyway.
First,
BitBlt,StretchBltandTransparentBltdo NOT support the alpha channel..TransparentBltworks by making whatever specified colour you want, transparent.If you want Alpha channel and blending support, you need:
AlphaBlend.You can do the following:
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: