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

Icons are losing alpha layer when loaded using GDI+

2,161 views
Skip to first unread message

Ben

unread,
Jul 14, 2005, 2:01:03 PM7/14/05
to
I am trying to write a function that creates a grayscale icon based on an
original.

The process is easily performed with GDI+. I construct a bitmap with an
icon handle, convert the bitmap to grayscale and then get a new Icon handle
from the grayed bitmap.

My problem is that when the bitmap is created from the icon, the alpha layer
is flattened to either opaque (255) or transparent (0) with nothing in
between.
Below is a modified version of my function that demonstrates what is
happening. It looks at the alpha layer of every pixel, if you step through
the loop you will see that it is always 255 or 0.

HICON CreateGrayscaleIcon(HICON hIcon)
{
//GDIplus must be initialized

//create a bitmap from the icon
Bitmap bmpIcon(hIcon);

//we are only using 32 bit icons with an alpha layer
ASSERT(bmpIcon.GetPixelFormat() == PixelFormat32bppARGB);

// Look at the alpha layer for every pixel
Color c;
BYTE bAlpha;
for(int w = 0; w < bmpIcon.GetWidth(); w++)
{
for(int h = 0; h < bmpIcon.GetHeight(); h++)
{
bmpIcon.GetPixel(w,h, &c);
bAlpha = c.GetAlpha();
//bAlpha should be 0-255
// it is always 0 or 255 never in-between
// THIS IS THE PROBLEM!!!!!!!!!!!!!!!!!!!!!!!!
}
}

//
// Code to convert bmpIcon to grayscale
//

//create an icon from our grayed bitmap
HICON hIconGray = NULL;
Status s;
s = bmpIcon.GetHICON(&hIconGray);
ASSERT(s == Ok);


return hIconGray;
}


So all this function is actually doing is returning a copy of the original
icon, however the result is not an identical copy as the alpha blending has
been reduced to opaque or transparent.

To rule out other possible problems I have tried this with an icon that
only has one image with a 32bit color depth and the problem persisted. I am
positive that the original icon does in fact have alpha vales. I can display
the original on the toolbar and it looks perfect with smooth edges while the
icon that is generated from the above function has a black outline with
jagged edges.

Using:
C++, MFC
WindowsXP SP2
Visual Studio 2003

Am I missing something? Any help would be greatly appreciated.

Thanks,
Ben Corneau


TC

unread,
Jul 15, 2005, 12:50:18 AM7/15/05
to
Repost in microsoft.public.dotnet.framework.windowsforms if you do not
get an answer here.

HTH,
TC

TC

unread,
Jul 15, 2005, 12:51:53 AM7/15/05
to
Oops, sorry, I thought we were in another newsgroup. Advice still
holds, though.

TC

Michael Phillips, Jr.

unread,
Jul 18, 2005, 11:33:25 AM7/18/05
to
This MSDN article may assist you with your problem:

http://support.microsoft.com/default.aspx?scid=kb;en-us;318876

"Ben" <B...@discussions.microsoft.com> wrote in message
news:8992661B-255E-43B8...@microsoft.com...

TC

unread,
Jul 20, 2005, 12:24:59 AM7/20/05
to
Um, that is GDI. He is using GDI+, an entriely different beast :-)

TC

Michael Phillips, Jr.

unread,
Jul 20, 2005, 10:05:50 AM7/20/05
to
Below are the steps to solve your problem:

1) Create a bitmap from the icon.
Gdiplus::Bitmap *pIcon =
Gdiplus::Bitmap::FromHICON(static_cast<HICON>(hico));
2) Create a PixelFormat32bppARGB bitmap with the same width and height of
your icon.
Gdiplus::Bitmap *pBitmap = new Gdiplus::Bitmap( pIcon->GetWidth(),
pIcon->GetHeight(), PixelFormat32bppARGB );
3) Create a Graphics object from the bitmap ( not the icon )
Gdiplus::Graphics *g = Gdiplus::Graphics::FromImage( pBitmap );
4) Draw the icon image into your bitmap.
g->SetInterpolationMode(Gdiplus::InterpolationModeHighQuality);
g->SetCompositingQuality(Gdiplus::CompositingQualityHighQuality);
g->SetCompositingMode(Gdiplus::CompositingModeSourceOver);
g->DrawImage(pIcon, 0, 0, pIcon->GetWidth(), pIcon->GetHeight());
delete g;
5) Convert your bitmap to grayscale.
6) Get your icon back.
HICON hIcon;
pBitmap->GetHICON(&hIcon);

"Michael Phillips, Jr." <mphil...@nospam.jun0.c0m> wrote in message
news:eQVXG46...@TK2MSFTNGP15.phx.gbl...

Ben

unread,
Jul 21, 2005, 11:37:44 AM7/21/05
to
Michael, Thank you for your response.
Unfortunately the code you provided has the same effect as my original code.
It creates the icon, but without the alpha blending (only fully transparent
or fully opaque pixels).
Any other ideas?

And again, thank you for your time, it is appreciated.

TC

unread,
Jul 21, 2005, 10:50:50 PM7/21/05
to
As I suggested before: repost in
microsoft.public.dotnet.framew­ork.windowsforms. You might get an
answer from Bob Powell, the author of the GDI+ FAQ.

HTH,
TC

Michael Phillips, Jr.

unread,
Jul 22, 2005, 10:25:12 AM7/22/05
to
Are you sure!

I tried using the code in my post and using the technique
in the MSDN article for creating alpha icons.

In both cases, the alpha channel was preserved and I was
able to display the icon correctly.

I used "PP0N6A08.png" image from the png test image suite.
That image clearly had alpha channel. When I converted it to
an icon using my code and the code in the MSDN article,
the alpha channel was preserved.

First I used DrawImage to display the .png with transparency.
Then I converted it to an icon and then used DrawIcon to display
it. I compared the results displayed side by side.

There was slight degradation around the edge of the icon.
I don't know if it is due to the monobitmap created for the
icon or that I should have played around with the alpha
channel as described in the article.


"Ben" <B...@discussions.microsoft.com> wrote in message

news:B32BED12-221E-4A87...@microsoft.com...

Ben

unread,
Jul 22, 2005, 12:22:03 PM7/22/05
to
Very Sure.
Duplicating your steps I was able to load a PNG image and display it with
the alpha channel preserved, however when I use an Icon(*.ico) I lose the
alpha channel.

Ben

unread,
Jul 22, 2005, 12:24:03 PM7/22/05
to
I will try posting there.

Thanks,
Ben

Ben

unread,
Jul 22, 2005, 2:01:01 PM7/22/05
to
I found the solution thanks to a link from a similar post
The other post:
http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg=microsoft.public.dotnet.framework.drawing&tid=7bc72019-0cda-46f9-92ae-46232f51d177&cat=en_US_d02fc761-3f6b-402c-82f6-ba1a8875c1a7&lang=en&cr=&sloc=en-us&m=1&p=1

The helpful link:
http://dotnetrix.co.uk/misc.html (Get Alpha Bitmap from 32 bit Icon)

The Solution was to Get the ICONINFO from the icon, Create a bitmap using
the ICONINFO’s bitmap handle, get the BitmapData from it, and use the
BitmapData to create another bitmap which has the alpha channel.

//get the icon info
ICONINFO ii;
GetIconInfo(hIcon, &ii);

//create a bitmap from the ICONINFO so we can access the bitmapData
Bitmap bmpIcon(ii.hbmColor, NULL);
Rect rectBounds(0, 0, bmpIcon.GetWidth(), bmpIcon.GetHeight() );

//get the BitmapData
BitmapData bmData;
bmpIcon.LockBits(&rectBounds, ImageLockModeRead,
bmpIcon.GetPixelFormat(), &bmData);

// create a new 32 bit bitmap using the bitmapData
Bitmap bmpAlpha(bmData.Width, bmData.Height, bmData.Stride,
PixelFormat32bppARGB, (BYTE*)bmData.Scan0);
bmpIcon.UnlockBits(&bmData);

Now we have the icon with the alpha channel loaded into bmpAlpha!
Note: my code only works for icons with an alpha channel.

This solves my problem but it seems strange to me that directly creating a
Bitmap from an HICON cusses the Alpha channel to be lost. Am I not
understanding something, or is it possibly a bug?. If anyone can explain
this I would love to know the reason.

Thanks for the help,
Ben

Michael Phillips, Jr.

unread,
Jul 22, 2005, 2:31:36 PM7/22/05
to
Since it is your goal to produce a grayscale alpha channel icon
from the original, this is the code that I used:

I tested this code with the following image:
"stefan_full_rgba.png" which I pulled from
http://www.libpng.org/pub/png/pngs-img.html

I coverted this .png to a PixelFormat32bppARGB bitmap
and then used the function below to create the icon.

I get a grayscaled alpha channel icon which
displays without any artifacts. I even get the shadows!


HICON CreateAlphaIcon(Gdiplus::Bitmap *pImg)
{
HDC hMemDC = NULL;
DWORD dwWidth, dwHeight;
BITMAPV5HEADER bi;
HBITMAP hBitmap, hOldBitmap;
void *lpBits;
DWORD x,y;
HICON hAlphaIcon = NULL;

dwWidth = 32; // width of cursor
dwHeight = 32; // height of cursor

ZeroMemory(&bi,sizeof(BITMAPV5HEADER));
bi.bV5Size = sizeof(BITMAPV5HEADER);
bi.bV5Width = dwWidth;
bi.bV5Height = dwHeight;
bi.bV5Planes = 1;
bi.bV5BitCount = 32;
bi.bV5Compression = BI_BITFIELDS;
// The following mask specification specifies a supported 32 BPP
// alpha format for Windows XP.
bi.bV5RedMask = 0x00FF0000;
bi.bV5GreenMask = 0x0000FF00;
bi.bV5BlueMask = 0x000000FF;
bi.bV5AlphaMask = 0xFF000000;

bi.bV5CSType = LCS_WINDOWS_COLOR_SPACE;
bi.bV5Endpoints.ciexyzRed.ciexyzX =
bi.bV5Endpoints.ciexyzGreen.ciexyzX =
bi.bV5Endpoints.ciexyzBlue.ciexyzX = 0;
bi.bV5Endpoints.ciexyzRed.ciexyzY =
bi.bV5Endpoints.ciexyzGreen.ciexyzY =
bi.bV5Endpoints.ciexyzBlue.ciexyzY = 0;
bi.bV5Endpoints.ciexyzRed.ciexyzZ =
bi.bV5Endpoints.ciexyzGreen.ciexyzZ =
bi.bV5Endpoints.ciexyzBlue.ciexyzZ = 0;
bi.bV5GammaRed = 0;
bi.bV5GammaGreen = 0;
bi.bV5GammaBlue = 0;
bi.bV5Intent = LCS_GM_IMAGES;
bi.bV5ProfileData = 0;
bi.bV5ProfileSize = 0;
bi.bV5Reserved = 0;

HDC hdc;
hdc = ::GetDC(NULL);

// Create the DIB section with an alpha channel.
hBitmap = ::CreateDIBSection( hdc, reinterpret_cast<BITMAPINFO *>(&bi),
DIB_RGB_COLORS,
&lpBits, NULL, DWORD(0) );

hMemDC = ::CreateCompatibleDC(hdc);
::ReleaseDC(NULL,hdc);

// Draw something on the DIB section.
hOldBitmap = static_cast<HBITMAP>(SelectObject(hMemDC, hBitmap));

PatBlt(hMemDC,0,0,dwWidth,dwHeight,WHITENESS);

if ( pImg )
{
Gdiplus::Graphics * g = Gdiplus::Graphics::FromHDC(hMemDC);

if ( g )
{
Gdiplus::Status stat = g->GetLastStatus();


g->SetInterpolationMode(Gdiplus::InterpolationModeHighQuality);
g->SetCompositingQuality(Gdiplus::CompositingQualityHighQuality);
g->SetCompositingMode(Gdiplus::CompositingModeSourceOver);

#if 1
// Grayscale the icon
// Create an ImageAttributes object, and set its color key.
Gdiplus::ImageAttributes imAtt;
Gdiplus::ColorMatrix gscale = {
0.30f, 0.30f, 0.30f, 0.00f, 0.00f,
0.59f, 0.59f, 0.59f, 0.00f, 0.00f,
0.11f, 0.11f, 0.11f, 0.00f, 0.00f,
0.00f, 0.00f, 0.00f, 1.00f, 0.00f,
0.00f, 0.00f, 0.00f, 0.00f, 1.00f };
imAtt.SetColorMatrix(
&gscale,
Gdiplus::ColorMatrixFlagsDefault,
Gdiplus::ColorAdjustTypeBitmap);

g->DrawImage( pImg,
Gdiplus::Rect( 0, 0, 32, 32), // dest rect
0, 0, pImg->GetWidth(), pImg->GetHeight(), // source rect
Gdiplus::UnitPixel,
&imAtt);
#else
g->DrawImage( pImg, Gdiplus::Rect(0, 0, 32, 32),
0, 0,
pImg->GetWidth(), pImg->GetHeight(),
Gdiplus::UnitPixel);
#endif
delete g;
}
}

::SelectObject(hMemDC, hOldBitmap);
::DeleteDC(hMemDC);

// Create an empty mask bitmap.
HBITMAP hMonoBitmap = ::CreateBitmap(dwWidth,dwHeight,1,1,NULL);


ICONINFO ii;
ii.fIcon = TRUE;
ii.xHotspot = 0;
ii.yHotspot = 0;
ii.hbmMask = hMonoBitmap;
ii.hbmColor = hBitmap;

// Create the alpha cursor with the alpha DIB section.
hAlphaIcon = ::CreateIconIndirect(&ii);

DeleteObject(hBitmap);
DeleteObject(hMonoBitmap);

return hAlphaIcon;


}
"Ben" <B...@discussions.microsoft.com> wrote in message

news:502F4881-384B-48FC...@microsoft.com...

Ben

unread,
Jul 29, 2005, 5:33:02 PM7/29/05
to
Maybe I wasn't clear, but my goal is to produce a grayscale alpha channel
icon from a full color alpha channel icon. I need the source to be an icon;
in your function the source is a bitmap.

I was able to solve my problem (solution in my previous post) however I
don't understand why I can't just create a bitmap with an alpha channel using
the following code:
Bitmap bmpIcon(hIcon);

It seems that this should work, however the alpha channel is lost when it is
done this way.

Michael Phillips, Jr.

unread,
Jul 30, 2005, 12:43:16 PM7/30/05
to
If hIcon is an alpha channel icon, then
Bitmap bmpIcon(hIcon);
produces a PixelFormat32bppARGB bitmap.

If you use the code in
http://support.microsoft.com/default.aspx?scid=kb;en-us;318876
and then use Bitmap bmpIcon(hIcon); on the icon produced
by the code you clearly get an alpha channel bitmap!
You don't lose the alpha channel!

However the alpha channel is not in the expected format
for further image manipulation from Microsoft's GDI functions.

The images returned in Bitmap::GetHBITMAP and Bitmap::FromHICON are
PixelFormat32bppARGB not PixelFormat32bppPARGB.
The GDI functions work with premultiplied alpha channels!

If you convert to premultiplied alpha everything is OK. If you read the
docs for AlphaBlend,
you will see that Microsoft expects the images to be in a premultiplied
alpha format
for the internal GDI functions to work properly.

You can easily deconstruct the hIcon yourself using GetDIBits and
GetIconInfo.
Once you have the bits, you can premultiply the alpha channel against the
colors
and create an alpha channel bitmap. The premultiplied alpha channel bitmap
will
work properly with any GDI function. The resulting image will always
display properly.
In addition, you can save the image once you grayscaled it to an alpha icon
or alpha
bitmap. You must use the BITMAPV5HEADER as this in the only bitmap format
that was extended to use alpha channels.


"Ben" <B...@discussions.microsoft.com> wrote in message

news:48570542-5BEE-473B...@microsoft.com...

0 new messages