Re: GDI+ Performance sehr schlecht (?)

Tech Tip: Click here to run a free scan for Windows Errors and optimize PC performance



Hallo Peter,



also warum das abstürzen sollte weiß ich jetzt nicht.

Habe das ganze Projekt mal gepackt, den Download gibt's hier:

http://www.addware.de/UserControlDrawTests.zip



Gut, Redim hin oder her. Das geht alles im Vergleich zum reinen Rechnen noch
echt flott.

Ich bin auch sicher das man hier und da noch einiges optimieren kann - das
war einfach nur ein Quick-And-Dirty Test.

Allerdings zeigt sich genau das Zeichnen der Rechtecke als "Engpass" dar.

Ich habe schon alles mögliche Probiert, das mit dem DoubleBuffer war auch
nur ein Test. Das Ergebnis ist aber auch das gleiche wenn ich direkt im
Paint-Eregnis auf e.graphics zeichne (bis auf das dann auftretende Flackern
welches ich durch den DoubleBuffer beseitigt habe).



Würde gerne in vb2005 mal ein API-Test machen, aber das kann ja nicht im
Sinne des Erfinders sein.

Ich will ja auch die schönen Vorteile von GDI+ nutzen können.



Gruss



Stefan



"Stefan Uhlemann" <stefan@xxxxxxxxxxxxxx> schrieb im Newsbeitrag
news:fqr964$96s$1@xxxxxxxxxxxxxxx
Hallo,

Ich habe in VB6 früher viele UserControls entwickelt welche alle über
API-Zeichenroutinen liefen.
Jetzt bin ich auf vb 2005 umgestiegen und würde manche Controls gerne
umschreiben oder neue entwerfen.
Allerdings stoße ich dabe mit gdi+ ständig auf Probleme mit der
Performance.
Routinen die in vb6 mittels API gezeichnet haben zeichnen in GDI+ zigmal
langsamer so das sie völlig uninteressant sind.
Ich habe mich schon durch sämtliche Seiten gegoogelt, verschiedene
Lösungen probiert aber es hilft nix - gdi+ scheint untragbar langsam zu
sein.
(z.B. Rechtecke ausfüllen)

Jetzt meine Frage: Ist die Performance da wirklich so schlecht oder liegt
es an mir?

Folgend ein UserControl-Beispiel was meiner Meinung nach eigentlich rasend
schnell sein sollte (jedenfall mittels API in vb6), man in vb2005 den
einzelnen Kästchen beim Aufbau quasi zuschauen kann... Ich versteh die
Welt nicht mehr...

Hier der VB-Code:

-----------------------------------------------------------------------------
buffereddraw.vb
-----------------------------------------------------------------------------
Imports System.Drawing.Drawing2D

Public Class BufferedDraw
Inherits System.Windows.Forms.UserControl

Public Sub New()
InitializeComponent()
Me.DoubleBuffered = True
Me.CreateGraphicBuffer()
End Sub

' Wird vom Windows Form-Designer benötigt.
Private components As System.ComponentModel.IContainer
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.SuspendLayout()
'
'BufferedDraw
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.Name = "BufferedDraw"
Me.ResumeLayout(False)

End Sub

Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing Then
If components IsNot Nothing Then
components.Dispose()
End If
If _buffGraphics IsNot Nothing Then
_buffGraphics.Dispose()
End If
If _buffContext IsNot Nothing Then
_buffContext.Dispose()
End If
End If
Catch ex As Exception
' Wenn wir das Projekt Debuggen, sollen uns Fehler im Dispose
' auf jeden Fall erreichen.
#If DEBUG Then
MsgBox(ex.ToString)
#End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub

Private _buffContext As BufferedGraphicsContext

Protected ReadOnly Property BufferContext() As BufferedGraphicsContext
Get
Return _buffContext
End Get
End Property

Private _buffGraphics As BufferedGraphics

Protected ReadOnly Property BufferedGraphics() As BufferedGraphics
Get
Return _buffGraphics
End Get
End Property

<System.ComponentModel.Browsable(False)> _
Public ReadOnly Property Graphics() As Graphics
Get
If _buffGraphics IsNot Nothing Then
Return _buffGraphics.Graphics
End If
Return Nothing
End Get
End Property

Private _lastSize As Size

Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
' Testen, ob wir auch in der Laufzeit sind
If Me.DesignMode Then
MyBase.OnResize(e)
Return
End If

' Testen, ob Form auf dem das
' Control ist gerade mimimiert ist
If Me.Size.Width <= 0 Or Me.Size.Height <= 0 Then
MyBase.OnResize(e)
Return
End If

' wir merken uns die Größe
If Not _lastSize.Equals(Me.Size) Then
_lastSize = Me.Size
' und erzeugen die Buffer-Objekte
Me.CreateGraphicBuffer()
End If

' und rufen die Basisklasse auf,
' damit der Resize-Event geworfen wird
MyBase.OnResize(e)
End Sub

Protected Overridable Sub CreateGraphicBuffer()
Try
' neuen BufferContext erzeugen
Dim newCtx As BufferedGraphicsContext
newCtx = New BufferedGraphicsContext
' Größe festlegen
newCtx.MaximumBuffer = New Size( _
Me.DisplayRectangle.Width + 1, _
Me.DisplayRectangle.Height + 1)

' neue BufferedGraphics erzeugen
Dim newBuff As BufferedGraphics
newBuff = newCtx.Allocate(Me.CreateGraphics, _
Me.DisplayRectangle)

' Die Hintergrundfarbe zuweisen
newBuff.Graphics.Clear(Me.BackColor)

' Todo: eine bestehende Hintergrundgrafik in
' Buffer blitten.
' Wenn schon ein Grafik-Puffer da ist ->
' in neuen Puffer rendern und freigeben
If _buffGraphics IsNot Nothing Then
' Die alte Transformations-Matrix übertragen
newBuff.Graphics.Transform = _
_buffGraphics.Graphics.Transform.Clone
_buffGraphics.Render(newBuff.Graphics)
_buffGraphics.Dispose()
End If

' alten BufferedGraphicsContext ggf. freigeben
If _buffContext IsNot Nothing Then
_buffContext.Dispose()
End If

' Context und Puffer an Member zuweisen
_buffContext = newCtx
_buffGraphics = newBuff

Catch ex As Exception
Debug.WriteLine(ex.TargetSite)
Throw
End Try
End Sub

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
' Debug.WriteLine("OnPaint")
' den Buffer nur Rendern, wenn auch tatächlich einer
' vorhanden ist.
If Me.DesignMode OrElse Me.BufferedGraphics Is Nothing Then
Debug.WriteLine("DesignMode od. kein Buffer vorhanden")
Stop
MyBase.OnPaint(e)
Return
End If

Try
Me.BufferedGraphics.Render(e.Graphics)
Catch ex As Exception
Debug.WriteLine(ex.ToString)
Throw
Finally
' Zum Schluß sollten wir das Paint-Ereignis
' auslösen. Dies machen wir aber nicht mit
' MyBase.OnPaint(e), da dies sonst
' wieder über unseren gerenderten Inhalt
' zeichnen würde.
RaisePaintEvent(Nothing, e)
End Try
End Sub

Private m_ColWidth As Integer = 18
Private m_RowHeight As Integer = 18
Private Rectangles() As Rectangle

Private CurHoverIndex As Integer


Private Sub Draw(ByVal g As Graphics)

Dim RowIndex As Integer
Dim ColIndex As Integer
Dim Rect As Rectangle
Dim Counter As Integer

Dim CurX As Integer
Dim CurY As Integer

Graphics.SmoothingMode = Drawing2D.SmoothingMode.None

CurX = 0
CurY = 0

ReDim Rectangles((Int(Height / m_RowHeight) + 1) * (Int(Width /
m_ColWidth) + 1))

For RowIndex = 1 To (Int(Height / m_RowHeight) + 1)

CurX = 0

For ColIndex = 1 To (Int(Width / m_ColWidth) + 1)

Counter += 1

Rect = New Rectangle
Rect.X = CurX
Rect.Y = CurY
Rect.Width = m_ColWidth
Rect.Height = m_RowHeight

Rectangles(Counter) = Rect

If CurHoverIndex = Counter Then
g.FillRectangle(Brushes.Black, Rect)
Else
g.FillRectangle(Brushes.White, Rect)
g.DrawRectangle(SystemPens.ControlDark, Rect)
End If

CurX += m_ColWidth

Next ColIndex

CurY += m_RowHeight

Next RowIndex

End Sub

Private Sub BufferedDraw_MouseMove(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove

Dim Index As Integer

For index = 1 To UBound(Rectangles)

If Rectangles(Index).Contains(New Point(e.X, e.Y)) Then

If Not CurHoverIndex = Index Then
'Beep()
CurHoverIndex = Index
Draw(Graphics)
Me.Invalidate()
End If

Exit Sub

End If

Next
End Sub

Private Sub BufferedDraw_Resize(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Resize
Draw(Graphics)
Me.Invalidate()
End Sub

Private Sub BufferedDraw_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load

End Sub
End Class
-----------------------------------------------------------------------------

Danke,

Stefan



.



Relevant Pages