PictureBox1.Refresh() to Slow.
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 10 of 10

Thread: PictureBox1.Refresh() to Slow.

  1. #1
    Join Date
    May 2007
    Location
    Winnipeg, Manitoba, Canada
    Posts
    47

    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

  2. #2
    Join Date
    Jul 2005
    Location
    Sydney, Australia
    Posts
    1,080

    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.
    Tutorials: Home & Learn | Start VB.NET | Learn VB.NET | C# Station | GotDotNet | Games in VB.NET 101 Samples: 2002 | 2003 | 2005 | More .NET 2.0 (VB.NET, C#) Articles: VB.NET | C# | ASP.NET | MoreFree Components: WFC | XPCC | ElementsEx | VBPP | Mentalis | ADO.NET/MySQL | VisualStyles | Charting (NPlot, ZedGraph) | iTextSharp (PDF) | SDF (CF) ● Free Literature: VB 2005 (eBook) | VB6 to VB.NET (eBook) | MSDN Magazine (CHM format) ● Bookmarks: MSDN | WinForms .NET | ASP.NET | WinForms FAQ | WebForms FAQ | GotDotNet | Code Project | DevBuzz (CF) ● Code Converter: C#/VB.NET | VB.NET/C# | VS 2005 add-in

  3. #3
    Join Date
    Feb 2000
    Location
    OH - USA
    Posts
    1,891

    Arrow 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 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 threads, so if you have a secondary question, message me.

  4. #4
    Join Date
    May 2007
    Location
    Winnipeg, Manitoba, Canada
    Posts
    47

    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

  5. #5
    Join Date
    Jan 2003
    Location
    7,107 Islands
    Posts
    2,487

    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

  6. #6
    Join Date
    Feb 2000
    Location
    OH - USA
    Posts
    1,891

    Arrow 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 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 threads, so if you have a secondary question, message me.

  7. #7
    Join Date
    May 2007
    Location
    Winnipeg, Manitoba, Canada
    Posts
    47

    Re: PictureBox1.Refresh() to Slow.

    Quote 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

  8. #8
    Join Date
    Feb 2000
    Location
    OH - USA
    Posts
    1,891

    Arrow 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 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 threads, so if you have a secondary question, message me.

  9. #9
    Join Date
    May 2007
    Location
    Winnipeg, Manitoba, Canada
    Posts
    47

    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

  10. #10
    Join Date
    Jan 2003
    Location
    7,107 Islands
    Posts
    2,487

    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
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center