15

nearly every time I use Graphics.DrawRectangle or Graphics.FillRectangle (the int versions) I seem to miss the pixels on the right and bottom border of the rectangle I want to draw.

What is the exact definition of which pixels get filled by

Graphics.FillRectangle(brush,x,y,width,height)

and

Graphics.DrawRectangle(pen,x,y,width,height) // pen is one pixel width, i.e. width=0f

and is there a logical explanation for it? (so I can finally remember the behaviour :) ...)

EDIT:

Using Oleg Zhuk's marvelous Try GDI+ application, I was able to find a curious difference between FillRectangle and DrawRectangle:

FillRectangle(brush,0,0,1,1) draws a single dot at (0,0) which is somehow understandable (it's a rectangle with width and height one after all).

DrawRectangle(pen,0,0,1,1) on the other hand draws a small 2 by 2 pixel rectangle.

The same behaviour is shown for other width/height combinations, DrawRectangle always extends one pixel more to the left and the bottom (which is a little annoying e.g. when using the ClientRectangle property to draw a frame around a control)

My first question solved (how to they behave...) there still remains the second one: Why do they behave this way?

MartinStettner
  • 28,719
  • 15
  • 79
  • 106
  • Sorry about my deleted answer - it was late (by my body clock) and I wasn't reading properly, just jumping to conclusions. –  Jun 30 '10 at 16:50

3 Answers3

9

These methods work correctly, you just have to take into account effects of anti-aliasing and rounding.

First, turn on anti-aliasing to see everything (not only rounded result):

graphics.SmoothingMode = SmoothingMode.AntiAlias;

Now FillRectangle(10,10,10,10) will produce blurry result and DrawRectangle(10,10,10,10) will be sharp.

The blurry result means that you have pixel coordinates off-grid. If you need to call:

graphics.FillRectangle(9.5f, 9.5f, 10, 10);

to align top-left corner of the rectangle on grid.

The DrawRectangle is sharp because it uses Pen of width 1.0. So the line starts on the pixel center and goes 0.5 pixels in both directions, thus filling exactly 1 pixel.

Note that width and height are computed this way:

width = (right - left) = 19.5 - 9.5 = 10
height = (bottom - top) = ...

Notice that 19.5 is a right/bottom coordinate of the rectangle also aligned on the grid and the result is a whole number.

You can use different formula:

width2 = (right - left + 1)

But this applies on rounded coordinates only, since the smallest unit is 1 pixel. When working with GDI+ that uses anti-aliasing, be aware of the fact that the point has zero size and lies in the center of pixel (it is not the whole pixel).

Libor
  • 3,285
  • 1
  • 33
  • 41
  • 6
    Antialiasing does not matter. The real reason is described here: http://social.msdn.microsoft.com/Forums/en-US/a170efaf-829a-4cf1-b854-351938be24ae/drawrectangle-method-creates-oversize-rectangle?forum=vblanguage – Spook Jun 09 '14 at 06:56
  • Antialiasing does matter. This totally opened my eyes. Also, it made me realize that inflating the rectangle passed to FillRectangle by .5px is often what I want to do. The reason mentioned on the forums is just consequence. With anti-aliasing, the pixel on the path that would be drawn twice would be drawn twice at 50% opacity thus still rendering the correct result. – Filip Navara Dec 06 '17 at 13:00
1

The rectangle in both of the Graphics methods in your question is bounded by (x, y) in the upper left and (x + width - 1, y + height - 1) in the lower right. This is a consequence of specifying width and height, rather than the lower right point.

When you're calculating the width and height from two points, you have remember to include the origin pixel by adding 1 to the difference.

For example, say you want to fill a Rectangle from point (5, 5) to point (10, 20). The width of the rectangle is 6, not 5. The height of the rectangle is 16, not 15.

Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111
  • 3
    Thanks, but this doesnt seem to be the entire truth: DrawRectangle and FillRectangle behave differently: When using FillRectangle(brush,0,0,1,1), a single dot is drawn, DrawRectangle(pen,0,0,1,1) draws a rectangle that is 2 by 2 px. This is somewhat odd (imo) and I wonder, if there's some deeper reason for this behaviour :) ... – MartinStettner Jul 04 '10 at 17:08
  • Could be a quirk in the drawing routines. I just explained the math behind a rectangle. – Gilbert Le Blanc Jul 04 '10 at 23:29
-3

The difference in behavior is due to the somewhat counter intuitive fact that these two algorithms do completely different things.

DrawRectangle draws four lines in the shape of a box. FillRectangle produces a W*H clump of filled in pixels. From this it's easy to see the reason for the difference in behavior. Namely the wrongness of the assumption of similarity. Drawing four lines is quite different than producing a boxlike clump of pixels.

  • 2
    This doesn't answer the question. Try to view this issue from user view instead of that of developer. Help to Graphics.DrawRectangle method says that third and fourth parameter are width and height of the resulting geometric shape. http://msdn.microsoft.com/en-us/library/x6hb4eba.aspx – truthseeker Apr 18 '12 at 06:22
  • Point lies in the fact that mathematical edges are going through center of the pixels as Libor correctly pointed out. – truthseeker Apr 18 '12 at 06:24