Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Create Gdiplus::Bitmap from HBITMAP retaining alpha channel

3,020 views
Skip to first unread message

Innes MacKenzie

unread,
Dec 2, 2008, 12:54:08 PM12/2/08
to
When I create a new Gdiplus::Bitmap using the Bitmap::FromHBITMAP function,
the alpha channel of the source HBITMAP is lost.

Is there a way to create a Gdiplus::Bitmap from an HBITMAP without
destroying the alpha channel information?

Michael Phillips, Jr.

unread,
Dec 2, 2008, 7:17:29 PM12/2/08
to
> When I create a new Gdiplus::Bitmap using the Bitmap::FromHBITMAP
> function,
> the alpha channel of the source HBITMAP is lost.

Are you sure that the alpha channel is lost? Double check by using LockBits
to scan the alpha channel. The constructor will create a PixelFormat32bppRgb
by default as gdiplus never scans for an alpha channel when creating a
bitmap.

If the alpha channel is still there, then create an empty Bitmap with the
same dimensions with PixelFormat32bppArgb and use DrawImage to blit the bits
from the old to the new.

> Is there a way to create a Gdiplus::Bitmap from an HBITMAP without
> destroying the alpha channel information?

If all you have is a handle to a bitmap(i.e., HBITMAP), you can create an
empty Bitmap object with the same dimensions with PixelFormat32bppArgb, and
either use GDI to BitBlt the bits or use GetDIBits to get a pointer to the
bits and copy them to the new bitmap using LockBits.


innes

unread,
Dec 3, 2008, 6:23:39 AM12/3/08
to
On Dec 3, 12:17 am, "Michael Phillips, Jr."
<mphillip...@nospam.jun0.c0m> wrote:

Thanks for the response.

> > When I create a new Gdiplus::Bitmap using the Bitmap::FromHBITMAP
> > function,
> > the alpha channel of the source HBITMAP is lost.
>
> Are you sure that the alpha channel is lost?  Double check by using LockBits
> to scan the alpha channel. The constructor will create a PixelFormat32bppRgb
> by default as gdiplus never scans for an alpha channel when creating a
> bitmap.
>
> If the alpha channel is still there, then create an empty Bitmap with the
> same dimensions with PixelFormat32bppArgb and use DrawImage to blit the bits
> from the old to the new.
>

The alpha channel information is definitely gone in the Bitmap.
Bitmap::GetPixel confirms, as does using LockBits.


> > Is there a way to create a Gdiplus::Bitmap from an HBITMAP without
> > destroying the alpha channel information?
>
> If all you have is a handle to a bitmap(i.e., HBITMAP),  you can create an
> empty Bitmap object with the same dimensions with PixelFormat32bppArgb, and
> either use GDI to BitBlt the bits or use GetDIBits to get a pointer to the
> bits and copy them to the new bitmap using LockBits.

I tried this suggestion of creating an empty Bitmap and rendering the
HBITMAP into it.

Using BitBlt to draw the source HBITMAP into the Bitmap:
With BitBlt - the result was completely opaque - as if the original
bitmap was rendered onto a black background.

Is this because BitBlt is not alpha-aware?


Using AlphaBlend:
With AlphaBlend - the result was the same as BitBlt, apart from those
pixels which were completely transparent in the source HBITMAP. These
came through as completely transparent. Partially transparent pixels
became opaque, with the source ARGB effectively drawn against a black
background.

(NB When I use AlphaBlend to render the original HBITMAP to the
screen, the alpha channel works fine)

Here is my code:

Bitmap* CreateGdibitmapFromHBITMAP(HBITMAP hbmp, HDC hdc)
{
// Get width and height of source HBITMAP
BITMAP bm;
memset((void*)&bm, 0, sizeof(BITMAP));
GetObject(m_hbmp, sizeof(BITMAP), (void*)&bm);
int width = bm.bmWidth;
int height = bm.bmHeight;

// Create a GDI+ bitmap of the same dimensions, with alpha.
Bitmap* copy = new Bitmap(width, height, PixelFormat32bppARGB);

//Get an HDC for this new Bitmap
Graphics* g = Graphics::FromImage(copy);
HDC copyHdc = g->GetHDC();

HDC srcHdc = ::CreateCompatibleDC(hdc);
::SelectObject(srcHdc, hbmp);

//This loses all alpha:
//BOOL bbrv = ::BitBlt(copyHdc, 0, 0, width, height, srcHdc, 0,
0, SRCCOPY);

//This retains some alpha (only where A == 0)
BLENDFUNCTION bf1;
bf1.BlendOp = AC_SRC_OVER;
bf1.BlendFlags = 0;
bf1.SourceConstantAlpha = 0xff;
bf1.AlphaFormat = AC_SRC_ALPHA;
BOOL abrv = ::AlphaBlend(copyHdc, 0, 0, width, height, srcHdc,
0, 0, width, height, bf1);

::DeleteDC(srcHdc);

g->ReleaseHDC(copyHdc);

return copy;
}

Can you see any problems with this code?

Thanks,
Innes

Michael Phillips, Jr.

unread,
Dec 3, 2008, 9:38:33 AM12/3/08
to
The use of BitBlt should work if your HBITMAP represents a DIB. You can
check the HBITMAP by using GetObject with DIBSECTION.

AlphaBlend requires the original bitmap's alpha channel be premultiplied
against the RGB components.
You would also need to create a PixelFormat32bppPArgb bitmap.

You could end all uncertainty by using GetDIBits to obtain a pointer to the
bits and verify an intact alpha channel.
Premultiply the alpha channel against the RGB components and then copy the
bits to a Bitmap Object created with
PixelFormat32bppPArgb via LockBits.


innes

unread,
Dec 3, 2008, 11:17:48 AM12/3/08
to
On Dec 3, 2:38 pm, "Michael Phillips, Jr."

<mphillip...@nospam.jun0.c0m> wrote:
> The use of BitBlt should work if your HBITMAP represents a DIB.  You can
> check the HBITMAP by using GetObject with DIBSECTION.

I believe the HBITMAP is a DIB, because I can get the pixel data by
calling GetObject with a BITMAP.

The API help implies that this only works if the bitmap was created by
calling CreateDIBSection:

"If hgdiobj is a handle to a bitmap created by calling
CreateDIBSection, and the specified buffer is large enough, the
GetObject function returns a DIBSECTION structure. In addition, the
bmBits member of the BITMAP structure contained within the DIBSECTION
will contain a pointer to the bitmap's bit values."

> AlphaBlend requires the original bitmap's alpha channel be premultiplied
> against the RGB components.

::AlphaBlend works fine on the original HBITMAP. That HBITMAP is
obtained by calling GetHBitmap on a C# System.Drawing.Bitmap which was
loaded from a PNG file. So I would have guessed/hoped that drawing the
same HBITMAP into the DC for the Gdiplus::Bitmap would have worked as
well as drawing it to the screen.

> You would also need to create a PixelFormat32bppPArgb bitmap.

I'll try that.

> You could end all uncertainty by using GetDIBits to obtain a pointer to the
> bits and verify an intact alpha channel.

I've lost track. I do know that the HBITMAP has a working alpha
channel, since AlphaBlend works (just not when drawing into my
Gdiplus::Bitmap) if that helps.

> Premultiply the alpha channel against the RGB components and then copy the
> bits to a Bitmap Object created with
> PixelFormat32bppPArgb via LockBits.

I think this could be the way to go. Gonna try it first.

I dont understand the issue with alpha pre-multiplication. Why can't I
create a Gdiplus::Bitmap with just ARGB format and set the pixel
values accordingly.
Does Gdiplus not support properly rendering a bitmap with that format?

innes

unread,
Dec 3, 2008, 11:37:22 AM12/3/08
to
Hi,
I tried your suggestion of copying pixel data into the Gdiplus::Bitmap
using LockBits, and it worked :)

Thanks for the help, much appreciated.


Here's the code that worked for me (no doubt would fail for various
flavours of HBITMAP):

// Get the width height, and pixel data for an HBITMAP (needs to be
a DIB)
//
byte* GetBitmapSizeAndData(HBITMAP hbmp, int& width, int& height)


{
// Get width and height

BITMAP bm;
memset((void*)&bm, 0, sizeof(BITMAP));

int irv = GetObject(hbmp, sizeof(BITMAP), (void*)&bm);
width = bm.bmWidth;
height = bm.bmHeight;
return (byte*)bm.bmBits;
}

// Create a Gdiplus::Bitmap copy of an HBITMAP
//


Bitmap* CreateGdibitmapFromHBITMAP(HBITMAP hbmp, HDC hdc)
{

// Get width and height and pixels
int width, height;
byte* sourceBytes = GetBitmapSizeAndData(m_hbmp, width, height);

// Create an argb GDI+ bitmap of the same dimensions.
Bitmap* copy = new Bitmap(width, height, PixelFormat32bppPARGB);

// Get access to the Gdiplus::Bitmap's pixel data
BitmapData bmd;
Rect rect(0, 0, width, height);
copy->LockBits(&rect, ImageLockMode::ImageLockModeWrite,
PixelFormat32bppPARGB, &bmd);

// Copy source pixel data to destination Bitmap (one is 'upside
down' relative to the other)
int lineSize = width * 4;
byte* destBytes = (byte*)(bmd.Scan0);
for (int y = 0; y < height; y++)
{
memcpy(destBytes + (y * lineSize), sourceBytes + ((height - y -
1) * lineSize), lineSize);
}

copy->UnlockBits(&bmd);

return copy;
}

heckubiss

unread,
Feb 8, 2009, 3:41:01 PM2/8/09
to
0 new messages