Go Back  Xtreme Visual Basic Talk > Visual Basic .NET (2002/2003/2005/2008, including Express editions) > .NET Interface and Graphics > Draw region with antialias


Reply
 
Thread Tools Display Modes
  #1  
Old 09-18-2007, 07:53 PM
PrOpHeT's Avatar
PrOpHeT PrOpHeT is offline
Hopelessly confused...

* Expert *
 
Join Date: Mar 2001
Location: Tyler, Tx.
Posts: 3,054
Default Draw region with antialias


Code:
        Dim Lbl As New Label
        With Lbl
            .AutoSize = False
            .BackColor = Color.Blue
            .Size = New Size(310, 40)
        End With
        Dim gPath As New System.Drawing.Drawing2D.GraphicsPath
        gPath.AddEllipse(10, 10, 300, 20)
        Lbl.Region = New System.Drawing.Region(gPath)
        gPath.Dispose()
        Me.Controls.Add(Lbl)
can this ellipse be smoothed or do I have to use a different method?
__________________
When you earnestly believe you can compensate for a lack of skill by doubling your efforts, there's no end to what you can't do ;)

For the love of Gold...

Last edited by DougT; 06-16-2008 at 02:15 PM. Reason: smelling pistake
Reply With Quote
  #2  
Old 09-20-2007, 05:43 AM
boops boops's Avatar
boops boops boops boops is offline
Centurion
 
Join Date: Dec 2006
Location: Holland and France
Posts: 132
Default

The region is drawn without using a Graphics object, so I suppose you can't apply the GDI+ Graphics smoothing methods to its boundary.

How about taking this approach instead: define the label WithEvents and give it background color = Colors.Transparent. Then draw your blue ellipse in the label's Paint method using e.Graphics.SmoothingMode = AntiAlias and e.Graphics.FillEllipse.

If you need the region for other purposes, e.g. hit testing, you could define it immediately before use and clear it again afterwards, before the label is redrawn.

all the best, BB
Reply With Quote
  #3  
Old 09-20-2007, 10:11 AM
AtmaWeapon's Avatar
AtmaWeapon AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Antialiasing the edges of a control is a difficult topic. The short answer is I'm pretty certain this won't work for technical reasons.

Antialiasing works by blending a foreground and background color across a short distance. For GDI+ to antialias the edges of a control that is bounded by a region, it would need to know the colors of whatever control is underneath that control. There is no reliable way to get this information. Of course, one thinks at first that you could just take the background color of the parent control, but what if your control overlaps another control? What if your control is over an image? What if instead this is a Form and it is over a dynamic background such as a video?

Of course, to address all but the last case, Microsoft could have created a framework to do per-pixel checking as you render, but I believe Microsoft was correct in omitting this functionality since it would represent a severe performance liability. There's no logical way to handle the last case without introducing ridiculous performance problems.

Two derived label classes are posted below; one uses a region to draw itself and properly overlaps with other controls, though it is not antialiased. The other has a background color of "Transparent" and draws itself antialiased and showcases why Color.Transparent doesn't really do what you think. Color.Transparent basically takes the color of the parent control, so you end up with a nice antialiased ellipse surrounded by whatever the background of its parent control is.

Note that the Form Designer insists that the default for AutoSize should be true, even though the Label class's default is false, so you'll have to manually set it to false. Additionally, I omitted any code to draw the label's text, as you'd have to take great care to try and make sure the text stays inside the region.

This label doesn't use a region but isn't really transparent:
Code:
Public Class SmoothLabel
    Inherits Label

    Public Sub New()
        MyBase.New()

        ' Note the designer automatically sets this so my efforts are in vain
        Me.AutoSize = False

        Me.BackColor = Color.Transparent

        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        Me.SetStyle(ControlStyles.UserPaint, True)
    End Sub


    Protected Overrides Sub OnSizeChanged(ByVal e As System.EventArgs)
        MyBase.OnSizeChanged(e)
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
        MessageBox.Show("I was clicked!")
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        Dim g As Graphics = e.Graphics
        g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
        g.FillEllipse(Brushes.Green, 0, 0, Me.Width, Me.Height)
    End Sub
End Class
This label uses a region and is transparent but can't really be antialiased:
Code:
Public Class RegionLabel
    Inherits Label

    Public Sub New()
        MyBase.New()

        ' Note the designer automatically sets this so my efforts are in vain
        Me.AutoSize = False

        Me.Region = GetRegion()

        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
        Me.SetStyle(ControlStyles.UserPaint, True)
    End Sub

    Private Function GetRegion() As Region
        Dim path As New Drawing2D.GraphicsPath()
        path.AddEllipse(Me.ClientRectangle)
        Return New Region(path)
    End Function

    Protected Overrides Sub OnSizeChanged(ByVal e As System.EventArgs)
        MyBase.OnSizeChanged(e)
        Me.Region = GetRegion()
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
        MessageBox.Show("I was clicked!")
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        Dim g As Graphics = e.Graphics
        g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
        g.FillEllipse(Brushes.Blue, 0, 0, Me.Width, Me.Height)
    End Sub
End Class
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #4  
Old 09-20-2007, 12:05 PM
PrOpHeT's Avatar
PrOpHeT PrOpHeT is offline
Hopelessly confused...

* Expert *
 
Join Date: Mar 2001
Location: Tyler, Tx.
Posts: 3,054
Default

Well thank you both for the information, the label was an example, there were actually several instances where having this function would have benefited the current thought process. The whole things boils down to smoothing I guess rather than anti alias (Bad choice of words on my part)

For instance if I use the same logic on a form, rather than getting a nice smooth ellipse I get something that looks like the UFO on Atari's space invaders (for the kids among us it was a video game )

So I guess I should rephrase the question, can controls and forms be clipped to regions and end up with smoother edges?
see attached image
Attached Images
File Type: jpg untitled.JPG (2.7 KB, 38 views)
__________________
When you earnestly believe you can compensate for a lack of skill by doubling your efforts, there's no end to what you can't do ;)

For the love of Gold...
Reply With Quote
  #5  
Old 09-20-2007, 01:19 PM
AtmaWeapon's Avatar
AtmaWeapon AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

I addressed this by stating my opinion is no, but if anyone can contradict me I'd be pleased to find out I'm wrong.

The only way that Windows can smooth the curves is by antialiasing; pixels are still more or less square and you cannot represent a curve perfectly unless you have infinite dot pitch. Think about drawing a line on graph paper by filling in the squares: straight lines are easy, but how do you do a 45 degree angle line? You have to fill in diagonal squares, but that makes the line a little inaccurate since half of some of the squares should be filled in across the diagonal (see attached picture). There's no way to partially fill a pixel on the screen, so an antialiased line approximates the same visual effect by choosing a color between the foreground and background colors. GDI has no way to tell what the colors of pixels underneath the control are when it is rendering, therefore antialiasing is not possible.
Attached Images
File Type: png line.png (6.5 KB, 24 views)
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.

Last edited by AtmaWeapon; 09-20-2007 at 01:46 PM.
Reply With Quote
  #6  
Old 09-20-2007, 03:03 PM
boops boops's Avatar
boops boops boops boops is offline
Centurion
 
Join Date: Dec 2006
Location: Holland and France
Posts: 132
Default

Well AtmaWeapon, I'm going to contradict you (with some trepidation since you are a guru, forum leader etc. and I am just a culinarily mediocre fish species who has wasted too much time in the last few years mucking around with VB.Net graphics.)

VB.Net uses GDI+ as its graphics engine, and GDI+ has an easy and efficient way of antialasing: Graphics.SmoothingMode (=AntiAlias, HighQuality etc.) If you set the smoothing mode and then draw image A and then draw image B, image B will be antialiased into image A. So what's the problem?

The problem is how do you get hold of image A to start with. If you want image A to be the screen behind the form, you could use the Graphics.CopyFromScreen method to capture it. If you want to use the Form but none of its controls, you don't need to do anything, but you set the background color to transparent. If you want to use the form and its controls as the background, then you could use the Form.DrawToBitmap method to get image A. (I have to admit here that I have not yet succeed in getting the parameters for DrawToBitmap sorted out, so excuse me if I don't give an example.) Finally, if you want to use some of the Form's controls but not others, you could Hide/Show them individually before using DrawToBitmap to get the effect you want.

To sum up, I think it ought to be possible to use these methods to draw a control in any required shape and to smooth its edges in relation to its background. Ok, performance... well I don't do performance.

Regards, BB
Reply With Quote
  #7  
Old 09-20-2007, 03:17 PM
PrOpHeT's Avatar
PrOpHeT PrOpHeT is offline
Hopelessly confused...

* Expert *
 
Join Date: Mar 2001
Location: Tyler, Tx.
Posts: 3,054
Default

Hmm, now that makes one question then how do the many applications that get very nicely rounded objects on the screen get them there. I would assume then this is a trick of some other sort of graphic object on an otherwise transparent form?

In the attached image (Media player in skin mode, granted there are still some slightly jagged edges, but nothing like what I am getting) when you drag you still get a bordered region as if there is a base form that everywhere the image is not is hidden.

So that would lead to the next question, be that the solution, how does one make the form transparent and not respond to user input where the image is not if not using the forms region?
Attached Images
File Type: jpg untitled.jpg (10.6 KB, 36 views)
__________________
When you earnestly believe you can compensate for a lack of skill by doubling your efforts, there's no end to what you can't do ;)

For the love of Gold...
Reply With Quote
  #8  
Old 09-20-2007, 03:41 PM
AtmaWeapon's Avatar
AtmaWeapon AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Quote:
Originally Posted by boops boops View Post
Well AtmaWeapon, I'm going to contradict you (with some trepidation since you are a guru, forum leader etc. and I am just a culinarily mediocre fish species who has wasted too much time in the last few years mucking around with VB.Net graphics.)

VB.Net uses GDI+ as its graphics engine, and GDI+ has an easy and efficient way of antialasing: Graphics.SmoothingMode (=AntiAlias, HighQuality etc.) If you set the smoothing mode and then draw image A and then draw image B, image B will be antialiased into image A. So what's the problem?

The problem is how do you get hold of image A to start with. If you want image A to be the screen behind the form, you could use the Graphics.CopyFromScreen method to capture it. If you want to use the Form but none of its controls, you don't need to do anything, but you set the background color to transparent. If you want to use the form and its controls as the background, then you could use the Form.DrawToBitmap method to get image A. (I have to admit here that I have not yet succeed in getting the parameters for DrawToBitmap sorted out, so excuse me if I don't give an example.) Finally, if you want to use some of the Form's controls but not others, you could Hide/Show them individually before using DrawToBitmap to get the effect you want.

To sum up, I think it ought to be possible to use these methods to draw a control in any required shape and to smooth its edges in relation to its background. Ok, performance... well I don't do performance.

Regards, BB
Don't feel any fear contradicting or disagreeing with me; the worst I'll ever do is disagree with you and explain why I disagree. My longest replies tend to be on topics where I'm not entirely sure of my answer myself so I'm trying to learn as well. If you know something I don't I'd be more angry if you don't post it, and if you disagree with something I say then it does us both a favor to figure out who is wrong.

You are probably right; there's nothing stopping you from acquiring the Bitmap of the Form yourself and using that to antialias; I did not demonstrate it or mention it in detail because I felt like the relative complexity of determining the specific region to use and the performance hit would be a little bit much for a control; I wouldn't want many controls with this kind of heavyweight paint code on my form at all.

PrOpHeT you make a pretty good point; there's quite a few skinned forms out there that seem to accomplish antialiasing SOME kind of way; it could be that Forms are rendered differently than controls, or it could be that the curves chosen by these programs are carefully crafted in such a way that they aren't as jagged as they could be. Either way, if you could provide a few screenshots or examples of programs that do it I kind of want to do some more research on this.

*edit* I'm kind of leaning towards "careful use of curves" here... I took a look at a WMP skin and as the image shows, they aren't doing antialiasing but the corner is done in such a way that it doesn't appear vary jagged. I'd like to test a few other programs though...

The image you posted seems to use antialiasing, but it's hard to say because the JPEG compression can make it unclear.
Attached Images
File Type: png corner.png (7.9 KB, 26 views)
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.

Last edited by AtmaWeapon; 09-20-2007 at 03:54 PM.
Reply With Quote
  #9  
Old 09-20-2007, 04:46 PM
boops boops's Avatar
boops boops boops boops is offline
Centurion
 
Join Date: Dec 2006
Location: Holland and France
Posts: 132
Default

Reply to PrOpHeT:

Quote:
So that would lead to the next question, be that the solution, how does one make the form transparent and not respond to user input where the image is not if not using the forms region?
You define the Region however you were going to define it, and use that for filtering the mouse events (If Lbl.Region.Isvisible(MousePosition) Then...). But you don't need to use the same Region for drawing the control. If necessary, perhaps you could define a Graphics Path and use that separately for defining the Region and drawing the control.

Besides, you don't want the Region to be antialiased for hit testing mouse events. You want a Yes or No answer, not a Maybe.

regards, BB
Reply With Quote
  #10  
Old 09-20-2007, 05:21 PM
PrOpHeT's Avatar
PrOpHeT PrOpHeT is offline
Hopelessly confused...

* Expert *
 
Join Date: Mar 2001
Location: Tyler, Tx.
Posts: 3,054
Default

AtmaWeapon I believe you are on to something there, a closer inspection of the skin I used as an example will reveal that the edges are slightly jagged in areas, so I would say that they are "carefully rounded" however they do achieve angles that seem to defy what I am capable of.

However using the code I used in my UFO example, I increased the height of the ellipse and therefore changed the angle. The result was a significantly smoother edge.


It does seem however as though the limitations of the angle at which is comfortably viewed as "smooth" is in the system that renders it, not a pixel on the screen. If you see the attached image it is a 100X100 square with a 1pixel lines at °45 angles. It resolves MUCH smoother than anything I have rendered in a form.
Attached Images
File Type: png Image1.png (907 Bytes, 150 views)
__________________
When you earnestly believe you can compensate for a lack of skill by doubling your efforts, there's no end to what you can't do ;)

For the love of Gold...
Reply With Quote
  #11  
Old 09-20-2007, 08:29 PM
AtmaWeapon's Avatar
AtmaWeapon AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Of course it depends on the system; your primary factors are monitor dot pitch, DPI settings, and resolution.

The higher these are, the more you'll be able to get away without antialiasing. The line is a digital representation of an analog concept, and the more resolution you have the better the digital approximation is.

Regardless of how smooth you perceive that line to be, a 45 degree line is always going to be rendered as diagonal pixels. If you don't believe me, you should have a magnifier at Start>Programs>Accessories>Accessibility you can use to zoom in and see. On my cruddy (but large) 19" 1280x960 monitor, the lines are quite smooth. If I drop it down to 800x600, suddenly I can see the jaggedness. Your monitor is a 2d array of pixels, and regardless of your resolution settings there are a fixed number of dots on the screen. If you have 2000 horizontal dots and you set your resolution to 800x600, then every windows pixel is going to render as 2.5 pixels on your screen (probably rounded down to 2. Set the same monitor to 1024x768, and now each horizontal pixel is 1.9 pixels. The size of the pixels hasn't changed, but how the monitor has to approximate them has.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #12  
Old 09-21-2007, 07:07 AM
PrOpHeT's Avatar
PrOpHeT PrOpHeT is offline
Hopelessly confused...

* Expert *
 
Join Date: Mar 2001
Location: Tyler, Tx.
Posts: 3,054
Default

No doubt I agree, I know the °45 is jagged, I created it so, the point I was trying to make is given the same monitor & same resolution the °45 line in my image is much smoother than the °45 form angle. Granted when the image was saved it slightly blended due to the image format, but under magnification it is still a staggered line. The blocks my form are built from appear to be larger than a single pixel.

Are they perhaps rendered in twips?
__________________
When you earnestly believe you can compensate for a lack of skill by doubling your efforts, there's no end to what you can't do ;)

For the love of Gold...
Reply With Quote
  #13  
Old 09-27-2007, 12:59 PM
AtmaWeapon's Avatar
AtmaWeapon AtmaWeapon is offline
Fabulous Florist

Forum Leader
* Guru *
 
Join Date: Feb 2004
Location: Austin, TX
Posts: 9,500
Default

Ooooooh I got it I got it! I saw this Extended Vertical Label Control drawing antialiased text on a label with a transparent background and I thought, "Huh, didn't I tell PrOpHeT that was impossible?"

So I downloaded the source and got to digging around to figure out he did it.

You don't have to use a region, in fact using a region makes it impossible to antialias, as we've explored above. But there is a way, and it only takes a little extra effort. I always forget about this and I suspect there's a lot of neat things I can do with this particular method that I never knew about.

There's a few pieces to this puzzle, I'll start with the obscure and move up to the obvious.

CreateParams
Control defines a CreateParams property that gets or sets a CreateParams object that contains some information about how the control is going to be created. What's nice about this is it lets you get at the parameters that are passed to the API functions CreateWindow and CreateWindowEx that you'd normally have to do some kind of PInvoke to get to.

The documentation for CreateWindowEx lays out all the valid values for the dwExStyle parameter; most interesting is WS_EX_TRANSPARENT:
Quote:
Specifies that a window created with this style should not be painted until siblings beneath the window (that were created by the same thread) have been painted. The window appears transparent because the bits of underlying sibling windows have already been painted.
Oh boy this is just the problem we were trying to solve!

Unfortunately, the integer value of this style is not provided in the documentation, but the CreateParams documentation is actually very helpful in this respect by pointing out that we need to look in the Winuser.h file for these definitions. A quick search finds the file in C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\Include and our definiton:
Code:
#define WS_EX_TRANSPARENT       0x00000020L
Joy!

Don't inherit from Label
I mean it! I didn't research too far into this, but I did spend 45 minutes trying to figure out why my background was solid black. My guess is something is different between the CreateParams of Label and Control. You could endeavor to figure out what the difference is and make it the same or you could just inherit from Control and everything will work.

Set the control's style to Opaque
I figure this has something to do with the CreateParams as well, but you need to add a line in your constructor to set the control's style to Opaque. This stops Windows from trying to draw the background.

Fill the background with Color.Empty
It seems like Color.Transparent works too, and honestly I'm not sure how vital this step is. When I comment out this line the appearance is the same, but don't blame me if you leave it out and something goes wonky.

This is a corrected SmoothLabel class:
Code:
Public Class SmoothLabel
    Inherits Control

    'Winuser.h: #define WS_EX_TRANSPARENT       0x00000020L
    Private Const WS_EX_TRANSPARENT As Integer = &H20

    Public Sub New()
        MyBase.New()

        Me.SetStyle(ControlStyles.Opaque, True)
    End Sub

    Protected Overrides Sub OnSizeChanged(ByVal e As System.EventArgs)
        MyBase.OnSizeChanged(e)
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
        MyBase.OnClick(e)
        MessageBox.Show("I was clicked!")
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
        Dim g As Graphics = e.Graphics
        Dim backBrush As New SolidBrush(Color.Empty)

        Dim foreBrush As Brush = New Drawing2D.LinearGradientBrush(Me.ClientRectangle, Me.BackColor, Me.ForeColor, Drawing2D.LinearGradientMode.Vertical)
        g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
        g.FillRectangle(backBrush, 0, 0, Me.Size.Width, Me.Size.Height)
        g.FillEllipse(foreBrush, 0, 0, Me.Width, Me.Height)

        foreBrush.Dispose()
        backBrush.Dispose()
    End Sub

    Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
        Get
            Dim cp As CreateParams = MyBase.CreateParams
            cp.ExStyle = cp.ExStyle Or &H20
            Return cp
        End Get
    End Property
End Class
Now the only problem is if you want user clicks on transparent areas to not count. I know of no built-in way for this to happen. I've seen weird things about using WndProc to block a click before, and I think in conjunction with a process that does some hit-testing this could work, but it might be troublesome.
__________________
.NET Resources
My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
Reply With Quote
  #14  
Old 09-27-2007, 02:56 PM
PrOpHeT's Avatar
PrOpHeT PrOpHeT is offline
Hopelessly confused...

* Expert *
 
Join Date: Mar 2001
Location: Tyler, Tx.
Posts: 3,054
Default

I will definitely give that the go round, I am currently up to my you know what in alligators, but I thank you for the time and research, when I get back to the project in question I will defiantly let you know how it all works out.
__________________
When you earnestly believe you can compensate for a lack of skill by doubling your efforts, there's no end to what you can't do ;)

For the love of Gold...
Reply With Quote
  #15  
Old 06-30-2009, 09:31 AM
MyFaJoArCo MyFaJoArCo is offline
Newcomer
 
Join Date: Jun 2009
Posts: 1
Default

Hi! AtmaWeapons method is realy good! I translated it to C# and made region window with antialiased borders! Yesterday i thought it's impossible with GDI+ and Windows.Forms abilities. One problem - when I moving it background don't refresh.
P.S. Sorry for my english, not native.

tmp.png

Last edited by MyFaJoArCo; 06-30-2009 at 09:45 AM.
Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump

Advertisement:





Free Publications
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET.
subscribe
Programmers Heaven C# School Book -Free 338 Page eBook
The Programmers Heaven C# School book covers the .NET framework and the C# language.
subscribe
Build Your Own ASP.NET 3.5 Web Site Using C# & VB, 3rd Edition - Free 219 Page Preview!
This comprehensive step-by-step guide will help get your database-driven ASP.NET web site up and running in no time..
subscribe
 
 
-->