How to Create a Gdiplus::Bitmap from an HBITMAP, retaining the alpha channel information?
14,165
Solution 1
It turns out that GDI+ never brings across the alpha channel when creating a Bitmap from an HBITMAP.
The answer is to:
- Use GetObject passing in a BITMAP and the HBITMAP, to get the width and height (and if the input bitmap is a DIB, the pixel data) of the input HBITMAP.
- Create a Bitmap of the correct size with 32 bit PARGB pixel format.
- Use LockBits to get hold of the pixelData memory of your new Bitmap.
- If you got the pixels from GetObject, copy the ARGB values across using memcpy.
- Call UnlockBits on the new Bitmap.
In my case, the format of the input HBITMAP is correct for doing a straight memcpy from input bitmap pixel data to the new Bitmap pixel data.
If you didnt get the input pixel data from GetObject, use GetDIBits to get a copy in the correct format.
Solution 2
I think working code is more useful than instructions, so:
#include <GdiPlus.h>
#include <memory>
Gdiplus::Status HBitmapToBitmap( HBITMAP source, Gdiplus::PixelFormat pixel_format, Gdiplus::Bitmap** result_out )
{
BITMAP source_info = { 0 };
if( !::GetObject( source, sizeof( source_info ), &source_info ) )
return Gdiplus::GenericError;
Gdiplus::Status s;
std::auto_ptr< Gdiplus::Bitmap > target( new Gdiplus::Bitmap( source_info.bmWidth, source_info.bmHeight, pixel_format ) );
if( !target.get() )
return Gdiplus::OutOfMemory;
if( ( s = target->GetLastStatus() ) != Gdiplus::Ok )
return s;
Gdiplus::BitmapData target_info;
Gdiplus::Rect rect( 0, 0, source_info.bmWidth, source_info.bmHeight );
s = target->LockBits( &rect, Gdiplus::ImageLockModeWrite, pixel_format, &target_info );
if( s != Gdiplus::Ok )
return s;
if( target_info.Stride != source_info.bmWidthBytes )
return Gdiplus::InvalidParameter; // pixel_format is wrong!
CopyMemory( target_info.Scan0, source_info.bmBits, source_info.bmWidthBytes * source_info.bmHeight );
s = target->UnlockBits( &target_info );
if( s != Gdiplus::Ok )
return s;
*result_out = target.release();
return Gdiplus::Ok;
}
Author by
mackenir
Updated on July 08, 2022Comments
-
mackenir almost 2 years
When I create a new Gdiplus::Bitmap using the Bitmap::FromHBITMAP function, the resulting Bitmap is opaque - none of the partial transparency from the original HBITMAP is preserved.
Is there a way to create a Gdiplus::Bitmap from an HBITMAP which brings across the alpha channel data?
-
Johann Gerell over 15 yearsDo you know for sure that the HBITMAP has an alpha channel to start with? For instance, did you create it as a DIB with BI_RGB and 32bpp or some other way?
-
mackenir over 15 yearsYes, it does have transparency. It draws nicely using the ::AlphaBlend() function.
-
mackenir over 15 years(I created it in C# using new Bitmap(string filename) and the calling GetHBitmap() on the .NET bitmap object)
-
-
foraidt over 9 yearsI had similar problem with a
CBitmap
, from which I retrieved theHBITMAP
. In that case::GetObject()
leftsource_info.bmBits
set to NULL. I could solve this by callingmyCBitmapPtr->GetBitmapBits(source_info.bmWidthBytes * source_info.bmHeight, target_info.Scan0)
-
foraidt over 9 yearsBTW, the return values are nicely checked in your example but if
source_info.bmBits
is NULL, there's an access violation in the call toCopyMemory()
.