locked
Is it possible to "cache" a Bimap for a PictureBox? RRS feed

  • Question

  • I'm running this ugly and possibly very slow piece of code inside a PictureBox.Paint Event:

    For j = Lower_Y To Higher_Y   ' from -200 to +200   
                For i = Lower_X To Higher_X ' from -200 to + 200
                    x1 = i
                    y1 = j
    
                    a2 = Ref_a + x1 / GridSize
                    b2 = Ref_b + y1 / GridSize
    
                    Result = Function(Ref_L, Ref_a, Ref_b, Ref_L, a2, b2)
    
                    If Result < Tolerance Then
                        e.Graphics.FillEllipse(Color, x1 - 2, y1 - 2, 2, 2)
                    End If
                Next
            Next

    I agree this is horrible code but it had the merit to allow me to test the method. Now that I know the method is valid, I need to make it work faster. That is why I was thinking, perhaps, instead of calling so many times the same FillEllipse routine, to draw  minute circles on screen, couldn't I do the same thing on a Bitmap, instead, and, when I'm done "constructing" the Bitmap, to display the bitmap in the PictureBox using ONE instruction?

    As it stands, the above loop executes a whopping 400 x 400 times or 160,000 times. Even on a fast Core i7 CPU, it still takes a second or so to execute. So, I'm thinking "poor user" who has to wail "all that time"! I tried to imagine different programming schemes to speed up the execution? Perhaps write this loop in C++? Or in unmanaged code?

    But I sense I could avoid all those "FillEllipse" calls if I was to work of a Bitmap "offscreen" and at the end of the PictureBox.Paint Event, when I'm reading, just issue an instruction like PictureBox = Bitmap?

    Is that possible?

    Sunday, April 19, 2020 12:54 AM

All replies

  • Sunday, April 19, 2020 1:50 AM
  • Hi

    If I understand correctly, then here is something that may help.

    I can't reproduce anything close to what your code may be doing, but maybe you can get the idea. Depending on your needs, there are 2 offerings below.

    ' Form1 with PictureBox1
    ' and Button1
    Option Strict On
    Option Explicit On
    Public Class Form1
      Dim cache As New Bitmap(400, 400)
      Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' this is the 'cached' image
        Using g As Graphics = Graphics.FromImage(cache)
          For j As Integer = 0 To 400
            For i As Integer = 0 To 400
              g.FillEllipse(New SolidBrush(Color.Blue), i, j, 2, 2)
            Next
          Next
        End Using
      End Sub
      Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
        ' persist the image
        e.Graphics.DrawImage(cache, 0, 0)
      End Sub
      Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ' this is the single line instruction 
        ' asked for.
        PictureBox1.Invalidate()
      End Sub
    End Class
    ' Form1 with PictureBox1
    Option Strict On
    Option Explicit On
    Public Class Form1
      Dim cache As New Bitmap(400, 400)
      Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' this is the 'cached' image
        Using g As Graphics = Graphics.FromImage(cache)
          For j As Integer = 0 To 400
            For i As Integer = 0 To 400
              g.FillEllipse(New SolidBrush(Color.Blue), i, j, 2, 2)
            Next
          Next
        End Using
        PictureBox1.BackgroundImage = cache
      End Sub
      Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
        ' persist the image
        ' e.Graphics.DrawImage(cache, 0, 0)
      End Sub
    End Class



    Regards Les, Livingston, Scotland




    • Edited by leshay Sunday, April 19, 2020 12:03 PM
    Sunday, April 19, 2020 2:04 AM
  • Note a minor possible optimisation:

       For j = Lower_Y To Higher_Y   ' from -200 to +200  

          y1 = j

          b2 = Ref_b + y1 / GridSize

          For i = Lower_X To Higher_X ' from -200 to + 200

             x1 = i

             y1 = j

             a2 = Ref_a + x1 / GridSize

             b2 = Ref_b + y1 / GridSize

       . . .

    Does it work much faster if you temporarily remove FillEllipse? Maybe it is possible to improve the called Function too.

    Sunday, April 19, 2020 8:46 AM
  • Good suggestion, Virel -- thank you!!
    Sunday, April 19, 2020 7:37 PM
  • I tried using a Bitmap but it did not resulted in a increase in performance:

    Dim Bitmap2 = New Bitmap(PictureBox6.Width, PictureBox6.Height, PixelFormat.Format24bppRgb)
    
    Dim graph As Graphics = Graphics.FromImage(Bitmap2)
    
    For j = Lower_Y To Upper_Y   ' From -205 to +205 =  410 x 410 pixels
       y1 = j
       b2 = Ref_b + y1 / GridStep
       For i = Lower_X To Higher_X
           x1 = i
           a2 = Ref_a + x1 / GridStep
           Result = Function(Ref_L, Ref_a, Ref_b, Ref_L, a2, b2)
           If Result < Tolerance Then
              graph.FillEllipse(Color, x1 - 2, y1 - 2, 2, 2)
                    End If
                Next
            Next
    
    PictureBox.Image = Bitmap2
    It is essentially the same code as when writing directly to the PictureBox control. So, no real improvement in speed. I still have to go through those two loops and, instead of calling the e.graphics event to draw the ellipse I have to call the graph.graphics to do the same. Maybe Double Buffering, as Casterix suggested.

    Sunday, April 19, 2020 7:44 PM