Skinned Forms Using Images
An alternate method to when TransparencyKey is not working .
Introduction
You may have seen the TransparencyKey
property of Windows Forms. It is for setting a color which will appear transparent in the output. But, unfortunately, this property will not work when we set a background image for the form and pick a color from the image and set it as the TransparencyKey
. This is a bug in the framework.
- BUG: The
TransparencyKey
property is not effective for Windows Forms if the color depth of the monitor is set to a value that is greater than 24-bit.
But unfortunately, the resolution provided also will not work sometimes: check out this link.
Background
You may have already read my previous post about speeding up Windows Forms drawing when a background image is used. That technique is also used here (see how the form background property is overridden).
Using the Code
See the following function that is used to extract a region from a Bitmap
object:
Region ^ DrawingHelper::ExtractRegion(Bitmap ^bitmap, Color transparencyKey)
{
if (bitmap == nullptr)
return nullptr;
GraphicsUnit unit = GraphicsUnit::Pixel;
System::Drawing::RectangleF boundsF = bitmap->GetBounds(unit);
System::Drawing::Rectangle bounds =
System::Drawing::Rectangle(safe_cast<int>(boundsF.Left),
safe_cast<int>(boundsF.Top), safe_cast<int>(boundsF.Width),
safe_cast<int>(boundsF.Height));
//Prepare the trasperant color key
System::UInt32 key =
safe_cast<System::UInt32>((transparencyKey.A << 24) |
(transparencyKey.R << 16) | (transparencyKey.G << 8) |
(transparencyKey.B << 0));
//access to the raw bits of the image
BitmapData ^bitmapData = bitmap->LockBits(bounds,
ImageLockMode::ReadOnly, PixelFormat::Format32bppArgb);
System::UInt32* pixelPtr = (System::UInt32*)bitmapData->Scan0.ToPointer();
//avoid property accessors in the for better perforance
int yMax = safe_cast<int>(boundsF.Height);
int xMax = safe_cast<int>(boundsF.Width);
//Graphics path to keep extracted area
GraphicsPath ^path = gcnew GraphicsPath();
for (int y = 0; y < yMax; y++)
{
//store the pointer so we can jump to next linr from it later
System::Byte* basePos = (System::Byte*)pixelPtr;
for (int x = 0; x < xMax; x++, pixelPtr++)
{
//is transparent? if yes, just continue the loop
if (*pixelPtr == key)
continue;
//store where the starting position
int x0 = x;
//if not transparent - scan until the next transparent byte
while (x < xMax && *pixelPtr != key)
{
++x;
pixelPtr++;
}
//add the area we have found to the path
path->AddRectangle(System::Drawing::Rectangle(x0, y, x - x0, 1));
}
//jump to the next line
pixelPtr = (System::UInt32*)(basePos + bitmapData->Stride);
}
//now create the region from the graphic path
Region ^region = gcnew Region(path);
//clean up
delete path;
bitmap->UnlockBits(bitmapData);
return region;
}
The extracted region is set as the region of the form so that we can make forms in any shape.
If you need the source code in any other language like C# or VB.NET, please drop me an email.
Happy coding :)