GDI Graphics Are Inexact

  • Thread starter existential.philosophy
  • Start date
E

existential.philosophy

When I use standard GDI graphics methods, like DrawRectangle or
FillEllipse, I've noticed that the results are slightly off.

I seldom work with graphics, but I'm doing a project now which
requires me to draw precise shapes. I've discovered that, in order to
get crisp results, I have to "nudge" the coordinates. For instance,
instead of drawing a rectangle with

DrawRectangle(Pen1, X, Y, Width, Height)

I must use

DrawRectangle(Pen1, X + 0.001953, Y + 0.001953, Width, Height)

Is anyone familiar with this issue? I'm guessing it is a problem with
Windows (I'm using Windows XP Pro), but I'd like to learn more.


-TC
 
T

Tom Dacon

Ultimately, whatever coordinates you send to the API have to get transformed
and rounded to pixel coordinates on the screen, which depend on screen
resolution. In addition, nost screen resolutions these days are not
isotropic (equally scaled in X and Y), because the X and Y resolutions are
not exactly in proportion to the actual viewable dimensions of the display
surface (CRT or LCD screen).

Sorry - fact of life.

Tom Dacon
Dacon Software Consulting
 
S

SurturZ

To sort this out in GDI+ is pretty tricky; you need to learn about the World
Transformation, Page transformation and Affine transformations in general.

http://en.wikibooks.org/wiki/Visual_Basic_.NET/GDI+ is a bluffer's guide,
but really you need to study GDI+ a bit to get it right.

If you can ensure that you dpi (dots per inch) is the same at every step,
you'll have a fighting chance. You need to turn off any "smoothing" etc that
your printer might perform.
 
E

existential.philosophy

When I use standard GDI graphics methods, like DrawRectangle or
FillEllipse, I've noticed that the results are slightly off.

I seldom work with graphics, but I'm doing a project now which
requires me to draw precise shapes. I've discovered that, in order to
get crisp results, I have to "nudge" the coordinates. For instance,
instead of drawing a rectangle with

  DrawRectangle(Pen1, X, Y, Width, Height)

I must use

  DrawRectangle(Pen1, X + 0.001953, Y + 0.001953, Width, Height)

Is anyone familiar with this issue? I'm guessing it is a problem with
Windows (I'm using Windows XP Pro), but I'd like to learn more.

-TC

I wasn't very clear in my original post. What I meant to say is that
in pixel mode with no smoothing, I find that the graphics methods are
imprecise. Specifically, they seem to suffer from rounding errors when
Single arguments are used, and they seem to have inconsistent
definitions of "Width" and "Height". The result is shapes that don't
look as expected, and which must be "nudged" to fit properly into
place.

I wrote a simple form which illustrates what I'm talking about. I'd be
grateful if other people would try this form and let me know if the
problems I'm seeing affect only me, only XP systems, or all Windows
systems.

-TC



Imports System.Drawing
Imports System.Windows.Forms

Public Class GraphicsTestForm
Inherits Form

Private FlowLayoutPanel1 As FlowLayoutPanel
Private TextBox1 As TextBox
Private WithEvents ComboBox1 As ComboBox
Private TextBox2 As TextBox
Private WithEvents Panel1 As Panel

Public Sub New()

'TextBox1
TextBox1 = New TextBox
With TextBox1
.BorderStyle = BorderStyle.FixedSingle
.Multiline = True
.ReadOnly = True
.Size = New Size(400, 60)
.TabStop = False
.Text = "This form demonstrates the implementation of graphics
methods by drawing shapes inside a 100x100 white square with a 1-pixel
margin."
End With

'ComboBox1
ComboBox1 = New ComboBox
With ComboBox1
.Width = 400
.Items.AddRange(New String() {"DrawLine", "DrawRectangle 1",
"DrawRectangle 2", "FillRectangle 1", "FillRectangle 2",
"DrawEllipse", "FillEllipse"})
.MaxDropDownItems = 20
End With

'TextBox2
TextBox2 = New TextBox
With TextBox2
.BorderStyle = BorderStyle.FixedSingle
.Multiline = True
.ReadOnly = True
.Size = New Size(400, 60)
.TabStop = False
End With

'Panel1
Panel1 = New Panel
With Panel1
.BackColor = Color.White
.BorderStyle = BorderStyle.FixedSingle
.ClientSize = New Size(100, 100)
End With

'FlowLayoutPanel1
FlowLayoutPanel1 = New FlowLayoutPanel
With FlowLayoutPanel1
.Controls.Add(TextBox1)
.Controls.Add(ComboBox1)
.Controls.Add(TextBox2)
.Controls.Add(Panel1)
.Dock = DockStyle.Fill
.FlowDirection = FlowDirection.TopDown
End With

'Me
With Me
.Controls.Add(FlowLayoutPanel1)
.ClientSize = New Size(420, 300)
End With

End Sub

Private Sub Panel1_Paint(ByVal sender As Object, ByVal e As
System.Windows.Forms.PaintEventArgs) Handles Panel1.Paint

Select Case ComboBox1.Text
Case "DrawLine"
e.Graphics.DrawLine(Pens.Red, 1, 1, 1, 98)
e.Graphics.DrawLine(Pens.Red, 1, 98, 98, 98)
e.Graphics.DrawLine(Pens.Red, 98, 98, 98, 1)
e.Graphics.DrawLine(Pens.Red, 98, 1, 1, 1)
Case "DrawRectangle 1"
e.Graphics.DrawRectangle(Pens.Red, 1, 1, 98, 98)
Case "DrawRectangle 2"
e.Graphics.DrawRectangle(Pens.Red, 0.501, 0.501, 98, 98)
Case "FillRectangle 1"
e.Graphics.FillRectangle(Brushes.Red, 1, 1, 98, 98)
Case "FillRectangle 2"
e.Graphics.FillRectangle(Brushes.Red, 1.1, 1.1, 98, 98)
Case "DrawEllipse"
e.Graphics.DrawEllipse(Pens.Red, 1, 1, 98, 98)
Case "FillEllipse"
e.Graphics.FillEllipse(Brushes.Red, 1, 1, 98, 98)
End Select

End Sub

Private Sub ComboBox1_TextChanged(ByVal sender As Object, ByVal e As
System.EventArgs) Handles ComboBox1.TextChanged
Panel1.Invalidate()

Select Case ComboBox1.Text
Case "DrawLine"
TextBox2.Text = "DrawLine with integer arguments: This method
works as expected."
Case "DrawRectangle 1"
TextBox2.Text = "DrawRectangle(Pens.Red, 1, 1, 98, 98): This
method does not work as expected. The rectangle is too big."
Case "DrawRectangle 2"
TextBox2.Text = "DrawRectangle(Pens.Red, 0.501, 0.501, 98,
98): This method does not work as expected. The rectangle is
positioned improperly and is too big."
Case "FillRectangle 1"
TextBox2.Text = "FillRectangle(Brushes.Red, 1, 1, 98, 98):
This method works as expected."
Case "FillRectangle 2"
TextBox2.Text = "FillRectangle(Brushes.Red, 1.1, 1.1, 98, 98):
This method does not work as expected. The rectangle is positioned
improperly."
Case "DrawEllipse"
TextBox2.Text = "DrawEllipse(Pens.Red, 1, 1, 98, 98): This
method does not work as expected. The ellipse is too big and is not
centered properly."
Case "FillEllipse"
TextBox2.Text = "FillEllipse(Brushes.Red, 1, 1, 98, 98): This
method does not work as expected. The ellipse is too small and is not
centered properly."
End Select

End Sub

End Class
 
S

SurturZ

I see what you mean!

I am far from an assembly programmer, but for fun I thought I'd see how far
I could get looking at the IL disassembly (to see whether the different
DrawLine functions add or subtract 1 to yield the error).

There are several wrapper functions in different assemblies, but I hit a
brick wall when I got to gdiplus.dll - I can only assume this is a real (not
PE) executable, since it won't disassemble into ILAsm. None of the wrapper
functions convert from Single to Integer, so if there is a bug, it's probably
there.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top