G
Guest
The GDI+ function GdipCreateHBITMAPFromBitmap seems to be broken. When given
an image with an alpha channel, it produces an HBITMAP with its colours oddly
skewed. Only translucent pixels are skewed. This is evident, for instance,
when adding 32-bit RGBA System.Drawing.Bitmap objects to
System.Windows.Forms.ImageList controls in .NET. The
ControlPaint.CreateHBitmapColorMask function uses Bitmap.GetHBitmap which
calls the underlying GdipCreateHBITMAPFromBitmap function.
To test that it was in fact GdipCreateHBITMAPFromBitmap producing the blue
color skew clearly visible when ImageList is used with images with
translucency, I made a simple test case. It can be downloaded at:
http://israel.logiclrd.cx/GDIPlusBug.zip
The odd thing is, my test case produces images with a green tint instead of
blue. In both the case of ImageList and of my C++ test case which directly
calls into GDI+, the "background" parameter passed in is achromatic (all 3
channels have the same intensity -- gray), so I don't know where the colour
is coming from.
The only thing I can conclude based on the symptoms is that
GdipCreateHBITMAPFromBitmap is assuming that HBITMAPs can't properly handle
32-bit data and perhaps is trying to convert the data to an alpha-free form.
However, using a freeware DLL hooking library (madCHook), I was able to
attach my own implementation of GdipCreateHBITMAPFromBitmap:
GpStatus WINGDIPAPI
FixedGdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
HBITMAP* hbmReturn,
ARGB background)
{
GpStatus status;
UINT uWidth, uHeight;
status = GdipGetImageWidth(bitmap, &uWidth);
if (status != Ok)
return status;
status = GdipGetImageHeight(bitmap, &uHeight);
if (status != Ok)
return status;
GpRect rect;
rect.X = 0;
rect.Y = 0;
rect.Width = uWidth;
rect.Height = uHeight;
PixelFormat format;
status = GdipGetImagePixelFormat(bitmap, &format);
if (status != Ok)
return status;
int bits_per_pixel;
switch (format)
{
case PixelFormat1bppIndexed: bits_per_pixel = 1; break;
case PixelFormat4bppIndexed: bits_per_pixel = 4; break;
case PixelFormat8bppIndexed: bits_per_pixel = 8; break;
case PixelFormat16bppARGB1555:
case PixelFormat16bppGrayScale:
case PixelFormat16bppRGB555:
case PixelFormat16bppRGB565: bits_per_pixel = 16; break;
case PixelFormat24bppRGB: bits_per_pixel = 24; break;
case PixelFormat32bppARGB:
case PixelFormat32bppPARGB:
case PixelFormat32bppRGB: bits_per_pixel = 32; break;
case PixelFormat48bppRGB: bits_per_pixel = 48; break;
case PixelFormat64bppARGB:
case PixelFormat64bppPARGB: bits_per_pixel = 64; break;
default:
return InvalidParameter;
}
BitmapData data;
status = GdipBitmapLockBits(bitmap, &rect, ImageLockModeRead, format,
&data);
if (status != Ok)
return status;
HBITMAP ret = CreateBitmap(uWidth, uHeight, 1, bits_per_pixel, data.Scan0);
status = GdipBitmapUnlockBits(bitmap, &data);
if (ret == NULL)
return Win32Error;
if (status != Ok)
{
DeleteObject(ret);
return status;
}
*hbmReturn = ret;
return Ok;
}
As you can see, it simply dumps the bits it gets from locking the GDI+
Bitmap object into the GDI CreateBitmap function. In my limited testing, it
worked fine, and it eliminated the ImageList problem when used within a .NET
context
Any chance of getting this GDI+ bug fixed, perhaps through a Windows Update
patch?
an image with an alpha channel, it produces an HBITMAP with its colours oddly
skewed. Only translucent pixels are skewed. This is evident, for instance,
when adding 32-bit RGBA System.Drawing.Bitmap objects to
System.Windows.Forms.ImageList controls in .NET. The
ControlPaint.CreateHBitmapColorMask function uses Bitmap.GetHBitmap which
calls the underlying GdipCreateHBITMAPFromBitmap function.
To test that it was in fact GdipCreateHBITMAPFromBitmap producing the blue
color skew clearly visible when ImageList is used with images with
translucency, I made a simple test case. It can be downloaded at:
http://israel.logiclrd.cx/GDIPlusBug.zip
The odd thing is, my test case produces images with a green tint instead of
blue. In both the case of ImageList and of my C++ test case which directly
calls into GDI+, the "background" parameter passed in is achromatic (all 3
channels have the same intensity -- gray), so I don't know where the colour
is coming from.
The only thing I can conclude based on the symptoms is that
GdipCreateHBITMAPFromBitmap is assuming that HBITMAPs can't properly handle
32-bit data and perhaps is trying to convert the data to an alpha-free form.
However, using a freeware DLL hooking library (madCHook), I was able to
attach my own implementation of GdipCreateHBITMAPFromBitmap:
GpStatus WINGDIPAPI
FixedGdipCreateHBITMAPFromBitmap(GpBitmap* bitmap,
HBITMAP* hbmReturn,
ARGB background)
{
GpStatus status;
UINT uWidth, uHeight;
status = GdipGetImageWidth(bitmap, &uWidth);
if (status != Ok)
return status;
status = GdipGetImageHeight(bitmap, &uHeight);
if (status != Ok)
return status;
GpRect rect;
rect.X = 0;
rect.Y = 0;
rect.Width = uWidth;
rect.Height = uHeight;
PixelFormat format;
status = GdipGetImagePixelFormat(bitmap, &format);
if (status != Ok)
return status;
int bits_per_pixel;
switch (format)
{
case PixelFormat1bppIndexed: bits_per_pixel = 1; break;
case PixelFormat4bppIndexed: bits_per_pixel = 4; break;
case PixelFormat8bppIndexed: bits_per_pixel = 8; break;
case PixelFormat16bppARGB1555:
case PixelFormat16bppGrayScale:
case PixelFormat16bppRGB555:
case PixelFormat16bppRGB565: bits_per_pixel = 16; break;
case PixelFormat24bppRGB: bits_per_pixel = 24; break;
case PixelFormat32bppARGB:
case PixelFormat32bppPARGB:
case PixelFormat32bppRGB: bits_per_pixel = 32; break;
case PixelFormat48bppRGB: bits_per_pixel = 48; break;
case PixelFormat64bppARGB:
case PixelFormat64bppPARGB: bits_per_pixel = 64; break;
default:
return InvalidParameter;
}
BitmapData data;
status = GdipBitmapLockBits(bitmap, &rect, ImageLockModeRead, format,
&data);
if (status != Ok)
return status;
HBITMAP ret = CreateBitmap(uWidth, uHeight, 1, bits_per_pixel, data.Scan0);
status = GdipBitmapUnlockBits(bitmap, &data);
if (ret == NULL)
return Win32Error;
if (status != Ok)
{
DeleteObject(ret);
return status;
}
*hbmReturn = ret;
return Ok;
}
As you can see, it simply dumps the bits it gets from locking the GDI+
Bitmap object into the GDI CreateBitmap function. In my limited testing, it
worked fine, and it eliminated the ImageList problem when used within a .NET
context
Any chance of getting this GDI+ bug fixed, perhaps through a Windows Update
patch?