-
June 17th, 2007, 08:38 AM
#1
PictureBox1.Refresh() to Slow.
I 've written some code that produces a Bifurcation Diagram on a PictureBox in a Windows Form. I'm using PictureBox1.Refresh() to Draw the screen after each Iteration, so that I can watch the Diagram as it develops. It is so painfully slow though.
How can I speed this up?
Code:
Ux = 0
N = 730
y = 0.05
For i = -295 To N
x = i
u = 3 + (x / N)
Ux = Ux + 1
For j = 0 To 500
y = u * y * (1 - y)
Uy = 765 - y * 765
If Uy < 1 Or Uy > 750 Then Exit Sub
If Ux < 1 Or Ux > 1006 Then Exit Sub
gr.DrawEllipse(Pens.White, CInt(Ux), CInt(Uy), 1, 1)
Next
PictureBox1.Refresh()
End If
Next i
Last edited by HanneSThEGreaT; June 17th, 2007 at 11:12 AM.
Reason: Added [CODE] [/CODE] Tags.
Visual Basic 2005 Express Edition
Microsoft Net version 2
-
June 18th, 2007, 12:35 AM
#2
Re: PictureBox1.Refresh() to Slow.
Refresh will redraw the entire control every time. Use the Invalidate method to specify the smallest possible area to redraw based on the area that has changed, then call Update to force a repaint.
-
June 18th, 2007, 12:41 AM
#3
Re: PictureBox1.Refresh() to Slow.
Here is a fairly generic way to speed this up using a custom PictureBox.
Code:
Public Class customPictureBox : Inherits PictureBox
Public Delegate Sub PaintDelegate(ByVal graphics As Graphics)
Private _invoker As PaintDelegate
Protected Overrides Sub OnPaint(ByVal pe As System.Windows.Forms.PaintEventArgs)
If _invoker Is Nothing Then
MyBase.OnPaint(pe)
Else
_invoker(pe.Graphics)
End If
End Sub
Public Sub RegisterPaintFunction(ByVal PaintFunction As PaintDelegate)
_invoker = PaintFunction
End Sub
Public Sub New()
'
'GDI+ Speed Optimizations
'
SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
SetStyle(ControlStyles.UserPaint, True)
SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Me.UpdateStyles()
End Sub
End Class
Add that code to your project and re-build the solution. You will now see the custom control in your toolbox. Add it to the form.
Here is an example of how to use it, once you added the control to your form:
Code:
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Me.CustomPictureBox1.RegisterPaintFunction(AddressOf PaintAlgo)
End Sub
Public Sub PaintAlgo(ByVal g As Graphics)
'
'Insert Algorithm Here!
'
For x As Integer = 0 To 100
g.DrawString("Hello World!", New Font("Arial", 10), Brushes.Orange, x, x)
g.DrawString("Hello World!", New Font("Arial", 10), Brushes.Orange, 100 - x, x)
Next
End Sub
End Class
You could also just handle the Paint event of a standard PictureBox control, but with this little bit of code, you get a lot more performance.
Good Luck,
Craig - CRG IT Solutions - Microsoft Gold Partner
-My posts after 08/2015 = .NET 4.x and Visual Studio 2015
-My posts after 11/2011 = .NET 4.x and Visual Studio 2012
-My posts after 02/2010 = .NET 4.0 and Visual Studio 2010
-My posts after 12/2007 = .NET 3.5 and Visual Studio 2008
-My posts after 04/2007 = .NET 3.0 and Visual Studio 2005
-My posts before 04/2007 = .NET 1.1/2.0
*I do not follow all threads, so if you have a secondary question, message me.
-
June 18th, 2007, 07:05 PM
#4
Re: PictureBox1.Refresh() to Slow.
This is really interesting, thanks for the code.
So, here is my new code with your optimizations.
It works, I don't know if it's faster, but it won't show me the drawing as it's been created. It only shows the CustomPictureBox1 after thr Drawing is completed. Can the code be altered to let us watch the Drawing in progress?
Code:
Public Class Form1
Dim gr As Graphics
Dim bitmap As Bitmap
Dim N, Ux, Uy, x, i, j, k, u, y As Single
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Me.CustomPictureBox1.RegisterPaintFunction(AddressOf PaintAlgo)
N = 700
End Sub
Public Sub PaintAlgo(ByVal g As Graphics)
For i = -295 To N
x = i
u = 3 + (x / N)
Ux = Ux + 1
y = 0.05
For j = 0 To 500
y = u * y * (1 - y)
Next
For j = 1 To 400
y = u * y * (1 - y)
Uy = 765 - y * 765
g.DrawEllipse(Pens.White, CInt(Ux), CInt(Uy), 1, 1)
Next
Next i
End Sub
End Class
Public Class customPictureBox : Inherits PictureBox
Public Delegate Sub PaintDelegate(ByVal graphics As Graphics)
Private _invoker As PaintDelegate
Protected Overrides Sub OnPaint(ByVal pe As System.Windows.Forms.PaintEventArgs)
If _invoker Is Nothing Then
MyBase.OnPaint(pe)
Else
_invoker(pe.Graphics)
End If
End Sub
Public Sub RegisterPaintFunction(ByVal PaintFunction As PaintDelegate)
_invoker = PaintFunction
End Sub
Public Sub New()
'GDI+ Speed Optimizations
SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
SetStyle(ControlStyles.UserPaint, True)
SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Me.UpdateStyles()
End Sub
End Class
Last edited by HanneSThEGreaT; June 19th, 2007 at 12:53 AM.
Reason: Added [CODE] [/CODE] Tags!
Visual Basic 2005 Express Edition
Microsoft Net version 2
-
June 18th, 2007, 10:30 PM
#5
Re: PictureBox1.Refresh() to Slow.
you may try this optimized code
Code:
Dim gr As Graphics
Dim bitmap As bitmap
Private Sub Form1_Load1(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' create bitmap (for persistency)
bitmap = New Bitmap(PictureBox1.Bounds.Width, PictureBox1.Bounds.Height)
PictureBox1.Image = bitmap
' get control graphics
gr = Graphics.FromImage(bitmap)
End Sub
Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
gr.Dispose()
bitmap.Dispose()
End Sub
Public Sub PaintAlgo(ByVal g As Graphics)
Dim Uy, x, i, j, k, u, y As Single
Dim N, Ux As Integer
N = 700
Dim p As New Pen(Color.Navy, 1)
Dim a(bitmap.Width, bitmap.Height) As Boolean
gr.Clear(PictureBox1.BackColor)
g.Clear(PictureBox1.BackColor)
For i = -295 To N
x = i
u = 3 + (x / N)
Ux = Ux + 1
y = 0.05
For j = 0 To 500
y = u * y * (1 - y)
Next
For j = 1 To 400
y = u * y * (1 - y)
Uy = 765 - y * 765
If Ux <= a.GetUpperBound(0) AndAlso Uy <= a.GetUpperBound(1) AndAlso Not a(Ux, CType(Uy, Integer)) Then
g.DrawLine(p, Ux, Uy, Ux, Uy + 1) ' non-persistent graphics
gr.DrawLine(p, Ux, Uy, Ux, Uy + 1) ' persistent graphics
a(Ux, CType(Uy, Integer)) = True
End If
'g.DrawEllipse(Pens.Navy, CInt(Ux), CInt(Uy), 1, 1)
Next
Next i
p.Dispose()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
PaintAlgo(PictureBox1.CreateGraphics())
End Sub
Last edited by Thread1; June 18th, 2007 at 10:37 PM.
Busy 
-
June 18th, 2007, 10:46 PM
#6
Re: PictureBox1.Refresh() to Slow.
Ok, I overlooked a couple of things on the first post.
- Don't use DrawEllipse to draw a single pixel. The quickest method here is to create a 1x1 bmp and just copy it directly using Graphics.DrawImageUnscaled. The following code is with a standard picturebox, and a button to kick things off.
Code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim bmp As New Bitmap(1, 1)
Dim g As Graphics = Me.PictureBox1.CreateGraphics
Dim N, Ux, Uy, x, i, j, k, u, y As Single
bmp.SetPixel(0, 0, Color.White)
N = 700
For i = -295 To N
x = i
u = 3 + (x / N)
Ux = Ux + 1
y = 0.05
For j = 0 To 500
y = u * y * (1 - y)
Next
For j = 1 To 400
y = u * y * (1 - y)
Uy = 765 - y * 765
g.DrawImageUnscaled(bmp, CInt(Ux), CInt(Uy))
Next
Next i
End Sub
- Now, with that out of the way, let's focus on the root of this problem. It's not the drawing methods that are holding you back; it's the algorithms use of the drawing methods. You really have only one option here... write a quicker algorithm. The issue is with the repetition of points when you convert the points to integers.
You are actually drawing the same exact pixel many times. If you can eliminate this redundancy, you will see a HUGE improvement.
Check out this code which will check if the point has been hit or not already:
Edit: I just saw Thread1's post, and it's almost exactly what I had here, so I'll just remove mine.
Last edited by Craig Gemmill; June 18th, 2007 at 10:58 PM.
Good Luck,
Craig - CRG IT Solutions - Microsoft Gold Partner
-My posts after 08/2015 = .NET 4.x and Visual Studio 2015
-My posts after 11/2011 = .NET 4.x and Visual Studio 2012
-My posts after 02/2010 = .NET 4.0 and Visual Studio 2010
-My posts after 12/2007 = .NET 3.5 and Visual Studio 2008
-My posts after 04/2007 = .NET 3.0 and Visual Studio 2005
-My posts before 04/2007 = .NET 1.1/2.0
*I do not follow all threads, so if you have a secondary question, message me.
-
June 19th, 2007, 09:02 AM
#7
Re: PictureBox1.Refresh() to Slow.
 Originally Posted by Craig Gemmill
Ok, I overlooked a couple of things on the first post.
- Don't use DrawEllipse to draw a single pixel. The quickest method here is to create a 1x1 bmp and just copy it directly using Graphics.DrawImageUnscaled. The following code is with a standard picturebox, and a button to kick things off.
- Now, with that out of the way, let's focus on the root of this problem. It's not the drawing methods that are holding you back; it's the algorithms use of the drawing methods. You really have only one option here... write a quicker algorithm. The issue is with the repetition of points when you convert the points to integers.
You are actually drawing the same exact pixel many times. If you can eliminate this redundancy, you will see a HUGE improvement.
Check out this code which will check if the point has been hit or not already:
Edit: I just saw Thread1's post, and it's almost exactly what I had here, so I'll just remove mine.
Thanks for the interest, your code works very well, but it is just as slow. I don't understand your last sentence about 'Thread1's post'. How do I find this 'Thread1'? Does it contain information pertinent to our code?
Visual Basic 2005 Express Edition
Microsoft Net version 2
-
June 19th, 2007, 11:08 AM
#8
Re: PictureBox1.Refresh() to Slow.
No no, Thread1 is the user that posted right before my last post. Read up ^. Should make more sense now
Combine the 1x1 bmp drawing with Thread1's code, and you should see a major difference.
Good Luck,
Craig - CRG IT Solutions - Microsoft Gold Partner
-My posts after 08/2015 = .NET 4.x and Visual Studio 2015
-My posts after 11/2011 = .NET 4.x and Visual Studio 2012
-My posts after 02/2010 = .NET 4.0 and Visual Studio 2010
-My posts after 12/2007 = .NET 3.5 and Visual Studio 2008
-My posts after 04/2007 = .NET 3.0 and Visual Studio 2005
-My posts before 04/2007 = .NET 1.1/2.0
*I do not follow all threads, so if you have a secondary question, message me.
-
June 19th, 2007, 10:04 PM
#9
Re: PictureBox1.Refresh() to Slow.
No speed change, but thanks for the help guys.
Last edited by CitizenOlek; June 19th, 2007 at 10:28 PM.
Visual Basic 2005 Express Edition
Microsoft Net version 2
-
June 20th, 2007, 05:50 AM
#10
Re: PictureBox1.Refresh() to Slow.
if you try post #5 you will see a big difference. as already pointed out by Craig there will be a huge improvement if you change your algo a bit such that it will eliminate redundant drawing operations - drawing on same pixel locations - as shown on that post (using array).
this portion:
Code:
If Ux <= a.GetUpperBound(0) AndAlso Uy <= a.GetUpperBound(1) AndAlso Not a(Ux, CType(Uy, Integer)) Then
g.DrawLine(p, Ux, Uy, Ux, Uy + 1) ' non-persistent graphics
gr.DrawLine(p, Ux, Uy, Ux, Uy + 1) ' persistent graphics
a(Ux, CType(Uy, Integer)) = True
End If
Busy 
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
On-Demand Webinars (sponsored)
|