Graphics.DrawRectangle draws rectangles one pixel too big [modified]
-
If you pass it a pen with a width of 1. This is very annoying and very easy to see. Override the OnPaint function of a control class and put:
Dim x As New Rectangle(5, 5, 100, 100)
e.Graphics.FillRectangle(Brushes.Red, x)Dim y As New Pen(Color.Black)
y.Alignment = Drawing2D.PenAlignment.Inset
y.DashStyle = Drawing2D.DashStyle.Dot
e.Graphics.DrawRectangle(y, x)Inset alignment ensures (should ensure!) the drawn pen will draw inside our filled rectangle. This isn't required (the bug occurs regardless of what Alignment or DashStyle) but it helps us see that the Pen is properly drawing when we crank up the Pen's width (try it). The DashStyle allows us to see the underlying red rectangle boundary clearly. If you run this code snippet you'll see the problem... the DrawRectangle is one pixel too big in width and height! I think it might be a .NET bug... IIRC in GDI rectangles are purposefully drawn one pixel bigger than the rectangle you pass in but .NET seems to counteract this rule, at least in all cases but this. Fortunately, easy workaround:
Private Sub DrawRectangleFix(ByVal g As Graphics, ByVal p As Pen, ByVal r As Rectangle)
If p.Width = 1 Then
r.Width -= 1
r.Height -= 1
End If
g.DrawRectangle(p, r)
End Submodified on Saturday, July 25, 2009 3:58 PM
-
If you pass it a pen with a width of 1. This is very annoying and very easy to see. Override the OnPaint function of a control class and put:
Dim x As New Rectangle(5, 5, 100, 100)
e.Graphics.FillRectangle(Brushes.Red, x)Dim y As New Pen(Color.Black)
y.Alignment = Drawing2D.PenAlignment.Inset
y.DashStyle = Drawing2D.DashStyle.Dot
e.Graphics.DrawRectangle(y, x)Inset alignment ensures (should ensure!) the drawn pen will draw inside our filled rectangle. This isn't required (the bug occurs regardless of what Alignment or DashStyle) but it helps us see that the Pen is properly drawing when we crank up the Pen's width (try it). The DashStyle allows us to see the underlying red rectangle boundary clearly. If you run this code snippet you'll see the problem... the DrawRectangle is one pixel too big in width and height! I think it might be a .NET bug... IIRC in GDI rectangles are purposefully drawn one pixel bigger than the rectangle you pass in but .NET seems to counteract this rule, at least in all cases but this. Fortunately, easy workaround:
Private Sub DrawRectangleFix(ByVal g As Graphics, ByVal p As Pen, ByVal r As Rectangle)
If p.Width = 1 Then
r.Width -= 1
r.Height -= 1
End If
g.DrawRectangle(p, r)
End Submodified on Saturday, July 25, 2009 3:58 PM
Not a bug, but by design. GDI+ and GDI maybe behave differently as they are 2 completely different designs, APIs, implementations.
Software Kinetics (requires SL3 beta) - Moving software
-
If you pass it a pen with a width of 1. This is very annoying and very easy to see. Override the OnPaint function of a control class and put:
Dim x As New Rectangle(5, 5, 100, 100)
e.Graphics.FillRectangle(Brushes.Red, x)Dim y As New Pen(Color.Black)
y.Alignment = Drawing2D.PenAlignment.Inset
y.DashStyle = Drawing2D.DashStyle.Dot
e.Graphics.DrawRectangle(y, x)Inset alignment ensures (should ensure!) the drawn pen will draw inside our filled rectangle. This isn't required (the bug occurs regardless of what Alignment or DashStyle) but it helps us see that the Pen is properly drawing when we crank up the Pen's width (try it). The DashStyle allows us to see the underlying red rectangle boundary clearly. If you run this code snippet you'll see the problem... the DrawRectangle is one pixel too big in width and height! I think it might be a .NET bug... IIRC in GDI rectangles are purposefully drawn one pixel bigger than the rectangle you pass in but .NET seems to counteract this rule, at least in all cases but this. Fortunately, easy workaround:
Private Sub DrawRectangleFix(ByVal g As Graphics, ByVal p As Pen, ByVal r As Rectangle)
If p.Width = 1 Then
r.Width -= 1
r.Height -= 1
End If
g.DrawRectangle(p, r)
End Submodified on Saturday, July 25, 2009 3:58 PM
Back in the days of Inside Macintosh, Apple explained in simple but explicit terms that (1) screen coordinates actually represented the spaces between pixels, and (2) with the exception of lone lines, pixels, and polygons, all screen operations took place entirely within the specified region. So a rectangle from (0,0)-(8,8) drawn with a 2-pixel border would have a 4x4 empty area in the middle. Things are a bit mushier in the PC world, which seems to take its drawing model from Postscript (including the poorly conceived miter limit behavior(*)). There are times it is useful to define lines by their center, but it makes fitting things within a pixel region a little more difficult. (*) I sometimes wish I could go back in time and fix the definition of miter limit to the way it should be: an angle which is sufficiently acute that the miter limit would be exceeded should be drawn with a bevel whose distance from the centerpoint is equal to the stroke width times the miter limit. Thus, angles which were too acute would stick out as much as those which were at the miter limit, rather than sticking out less than even a rounded join. OTOH, I did discover a useful trick: to draw angle ABC nicely, compute a point D such that BD is very short and is perpendicular to the bisector of the angle, then draw ABDC. That will yield an appearance between that of bevel and miter joins, without ugliness at acute angles.
-
If you pass it a pen with a width of 1. This is very annoying and very easy to see. Override the OnPaint function of a control class and put:
Dim x As New Rectangle(5, 5, 100, 100)
e.Graphics.FillRectangle(Brushes.Red, x)Dim y As New Pen(Color.Black)
y.Alignment = Drawing2D.PenAlignment.Inset
y.DashStyle = Drawing2D.DashStyle.Dot
e.Graphics.DrawRectangle(y, x)Inset alignment ensures (should ensure!) the drawn pen will draw inside our filled rectangle. This isn't required (the bug occurs regardless of what Alignment or DashStyle) but it helps us see that the Pen is properly drawing when we crank up the Pen's width (try it). The DashStyle allows us to see the underlying red rectangle boundary clearly. If you run this code snippet you'll see the problem... the DrawRectangle is one pixel too big in width and height! I think it might be a .NET bug... IIRC in GDI rectangles are purposefully drawn one pixel bigger than the rectangle you pass in but .NET seems to counteract this rule, at least in all cases but this. Fortunately, easy workaround:
Private Sub DrawRectangleFix(ByVal g As Graphics, ByVal p As Pen, ByVal r As Rectangle)
If p.Width = 1 Then
r.Width -= 1
r.Height -= 1
End If
g.DrawRectangle(p, r)
End Submodified on Saturday, July 25, 2009 3:58 PM
Actually, I think the line drawing function is working perfectly. The FillRectangle function actually fills a rectangle defined as (top,left,right-1,bottom-1). The MSDN documentation doesnt clearly state that for FillRectangle, but it does for FillRect (and FillRectangle probably just makes a direct call to FillRect ) Check out the docs to FillRect here:
http://msdn.microsoft.com/en-us/library/aa932720.aspx
The important part being:
This function fills the rectangle's left and top borders, but excludes the right and bottom borders.
If it makes you feel better, I've been programming for windows SDK since 1987, and I still forget that from time to time. It's annoying. I disagree with Microsoft's decision process on that one. Dan K