Friday, February 18, 2005

Non-Rectangular Windows Forms Part 2

I figured nobody could answer my question. Therefore, I solved it myself.

Basically what I did was to use the form's Region property. This Region property selects the region in which it is to display.

Problem, how am I gonna find out the region of an irregular shape? Simple. Parse each pixel bit by bit to check for your transparency key. If it is not transparent, create a graphics path of 1 pixel to create your region. I got the idea from here.

http://weblogs.asp.net/cumpsd/articles/79878.aspx

The code is as follows.

public static Region ConvertFromTransparentBitmap(Bitmap imageRegion, Color transparentColor) {
// First we get the dimensions of our image
GraphicsUnit aPixel = GraphicsUnit.Pixel;
RectangleF imageBoundsF = imageRegion.GetBounds(ref aPixel);
int imageWidth = Convert.ToInt32(imageBoundsF.Width);
int imageHeight = Convert.ToInt32(imageBoundsF.Height);

// This will be the path for our Region
GraphicsPath regionPath = new GraphicsPath();

// We loop over every line in our image, and every pixel per line
for (int intY = 0; intY < imageHeight; intY++) {
for (int intX = 0; intX < imageWidth; intX++) {
if (imageRegion.GetPixel(intX, intY) != transparentColor) {
// We have to see this pixel!
regionPath.AddRectangle(new Rectangle(intX, intY, 1, 1));
}
}
}


Region formRegion = new Region(regionPath);
regionPath.Dispose();
return formRegion;
} /* ConvertFromTransparentBitmap */


But that took 12 secs to load up. So I did it the opposite way, since I have more visible part than transparent. If the pixel matches your transparency key, create the graphics path then. And I did an exclude. Like this.

public static Region ConvertFromTransparentBitmap(Bitmap imageRegion, Color transparentColor) {
// First we get the dimensions of our image
GraphicsUnit aPixel = GraphicsUnit.Pixel;
RectangleF imageBoundsF = imageRegion.GetBounds(ref aPixel);
int imageWidth = Convert.ToInt32(imageBoundsF.Width);
int imageHeight = Convert.ToInt32(imageBoundsF.Height);

// Create an arraylist to store the rectangles
ArrayList rectArray = new ArrayList();

// This will be the path for our Region
GraphicsPath regionPath = new GraphicsPath();

// We loop over every line in our image, and every pixel per line
for (int intY = 0; intY < imageHeight; intY++) {
for (int intX = 0; intX < imageWidth; intX++) {
//Color c = imageRegion.GetPixel(intX, intY);
if (imageRegion.GetPixel(intX, intY) == transparentColor) {
// We have to see this pixel!
//regionPath.AddRectangle(new Rectangle(intX, intY, 1, 1));
// Add a rectangle to the array
rectArray.Add(new Rectangle(intX, intY, 1, 1));
}
}
}

Rectangle[] rectList = new Rectangle[rectArray.Count];

// Now we convert the ArrayList into an array of Rectangles
for(int i=0; i < rectArray.Count; i++) {
rectList = (Rectangle)rectArray;
}

regionPath.AddRectangles(rectList);
Region formRegion = new Region(regionPath);

// we need to create a region which encompasses the entire form
Region fullRegion = new Region(new Rectangle(0, 0, imageRegion.Width, imageRegion.Height));

// we do a simple exclude those parts that intersect
fullRegion.Exclude(formRegion);

formRegion.Dispose();
regionPath.Dispose();
return fullRegion;
} /* ConvertFromTransparentBitmap */

Basically this alternate way speed up the entire loading process down to 4 secs. Of course together with DoubleBuffering on, and using the 32bit-enabled transparency code up there, I was able to create one ultra fast and smooth irregular window, without any flicker to the controls within.

A faster way to speed up the loading process, which I haven't tried yet, is to create the array of points to the graphics path. Meaning you'll have to pre-generate the points first, put it in an array, and use it in a GraphicsPath object. This will speed everything up to the second, theoretically speaking.

This method is called inline masking. If those of you have done low-level graphics and trying to accelerate graphics and transparency, this is how the old school does it.

If anyone has any suggestions whereby how to make it work another way, please post here.

No comments: