Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Skinned Forms Using Images

0.00/5 (No votes)
26 Jul 2008 1  
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 :)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here