Results 1 to 9 of 9

Thread: [RESOLVED] Fast transfer byte data to picturebox?

Hybrid View

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Aug 2004
    Location
    Essex, UK
    Posts
    774

    Re: Fast transfer byte data to picturebox?

    Thanks BB. The pinned arrary sounds very interesting. I should have made one thing clearer. I actually have a picturebox/bitmap that is 512 x 512 pixels and I need to add blocks of 512 x 4 to it at intervals of around 100mS. Obviously I need to specify the location (within in the main picturebox) of the new block that is added each time, although it will always be four lines down from the last block. I'm not sure if that changes your recommendations for a fast pixel plotter.

    I really appreciate your help with this.

  2. #2
    Fanatic Member
    Join Date
    Jul 2009
    Posts
    629

    Re: Fast transfer byte data to picturebox?

    With SetPixel I meant the SetPixel API.

    What it does is create a Device Context out of a bitmap, on which it performs SetPixel a lot quicker. Not sure what they did to the standard SetPixel function of the Bitmap, but it is indeed terribly slow. Perhaps they created and destroyed the DC every time you call the function, not sure.

    And yes, creating a new bitmap in memory IS slow. I did it once as some sort of buffer for a drawing program. Eventually it was faster to draw everything in one go than to draw the stored buffer. (buffer was also drawn over).

    I can remember testing this; generating a 800x600 bitmap in memory took me 5 ms.

    If anyone would like to test the API vs. the lockbits function, feel free. I haven't tested it completely. Here my custom DC class: (API.vb)
    Code:
        Public Class DC
            Implements System.IDisposable
    
            Private Declare Function BitBlt Lib "gdi32" (ByVal hdc As IntPtr, ByVal nXDest As Integer, ByVal nYDest As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hdcSrc As IntPtr, ByVal nXSrc As Integer, ByVal nYSrc As Integer, ByVal dwRop As CopyPixelOperation) As Boolean
            Private Declare Function GetPixel Lib "gdi32" Alias "GetPixel" (ByVal hdc As IntPtr, ByVal X As Int32, ByVal Y As Int32) As Int32
            Private Declare Function SetPixel Lib "gdi32" Alias "SetPixel" (ByVal hdc As IntPtr, ByVal X As Int32, ByVal Y As Int32, ByVal crColor As UInt32) As UInt32
            Private Declare Function ReleaseDC Lib "user32" (ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Boolean
            Private Declare Function GetClipBox Lib "gdi32" (ByVal hdc As IntPtr, ByRef lprc As WRECT) As Integer
            Private Declare Function GetClipRgn Lib "gdi32" (ByVal hdc As IntPtr, ByRef hrgn As Region) As Integer
            Private Declare Function GetRandomRgn Lib "gdi32" (ByVal hdc As IntPtr, ByRef hrgn As Region, ByVal inum As Integer) As Integer
    
            Private managedtype As DCSource
            Private managedobject As Object
            Private managedgraphics As Graphics
            Private hdc As IntPtr
            Private _disposed As Boolean
            Public Enum DCSource
                None
                Handle
                Graphics
            End Enum
            Sub New(ByVal g As Graphics)
                Me.managedobject = g
                Me.managedtype = DCSource.Graphics
                Me.hdc = g.GetHdc
            End Sub
            Sub New(ByVal hwnd As IntPtr, ByVal hdc As IntPtr)
                Me.managedobject = hwnd
                Me.managedtype = DCSource.Handle
                Me.hdc = hdc
            End Sub
            Sub New()
                Me.managedobject = Nothing
                Me.managedtype = DCSource.None
            End Sub
            Public ReadOnly Property Source() As DCSource
                Get
                    Return Me.managedtype
                End Get
            End Property
            Public Sub SetPixel(ByVal X As Integer, ByVal Y As Integer, ByVal C As Color)
                SetPixel(Me.hdc, X, Y, C.ToArgb)
            End Sub
            Public Sub SetPixel(ByVal p As Point, ByVal C As Color)
                SetPixel(p.X, p.Y, C)
            End Sub
            Public Function GetPixel(ByVal X As Integer, ByVal Y As Integer) As Color
                Return ColorTranslator.FromOle(GetPixel(Me.hdc, X, Y))
            End Function
            Public Function GetPixel(ByVal p As Point) As Color
                Return GetPixel(p.X, p.Y)
            End Function
            Public Property Pixel(ByVal X As Integer, ByVal Y As Integer) As Color
                Get
                    Return GetPixel(X, Y)
                End Get
                Set(ByVal value As Color)
                    SetPixel(X, Y, value)
                End Set
            End Property
    
            Public ReadOnly Property ClipBox() As Rectangle
                Get
                    Dim w As WRECT
                    GetClipBox(Me.hdc, w)
                    Return w.Value
                End Get
            End Property
            Public ReadOnly Property ClipSize() As Size
                Get
                    Return Me.ClipBox.Size
                End Get
            End Property
    
            Public Property Handle() As IntPtr
                Get
                    Return Me.hdc
                End Get
                Set(ByVal value As IntPtr)
                    Me.hdc = value
                End Set
            End Property
    
            Public ReadOnly Property Disposed() As Boolean
                Get
                    Return Me._disposed
                End Get
            End Property
    
            Public Sub Draw(ByVal source As Graphics, ByVal destrect As Rectangle, ByVal sourceoffset As Point, ByVal operation As CopyPixelOperation)
                Dim hdc As IntPtr = source.GetHdc
                Draw(hdc, destrect, sourceoffset, operation)
                source.ReleaseHdc(hdc)
            End Sub
            Public Sub Draw(ByVal source As DC, ByVal destrect As Rectangle, ByVal sourceoffset As Point, ByVal operation As CopyPixelOperation)
                Draw(source.hdc, destrect, sourceoffset, operation)
            End Sub
            Public Sub Draw(ByVal sourcehdc As IntPtr, ByVal destrect As Rectangle, ByVal sourceoffset As Point, ByVal operation As CopyPixelOperation)
                BitBlt(Me.hdc, destrect.X, destrect.Y, destrect.Width, destrect.Height, sourcehdc, sourceoffset.X, sourceoffset.Y, operation)
            End Sub
    
            Public Sub CopyTo(ByVal destination As Graphics, Optional ByVal operation As CopyPixelOperation = CopyPixelOperation.SourceCopy Or CopyPixelOperation.CaptureBlt)
                CopyTo(destination, Me.ClipBox, operation)
            End Sub
            Public Sub CopyTo(ByVal destination As Graphics, ByVal destrect As Rectangle, Optional ByVal operation As CopyPixelOperation = CopyPixelOperation.SourceCopy Or CopyPixelOperation.CaptureBlt)
                CopyTo(destination, destrect, New Point(0, 0), operation)
            End Sub
            Public Sub CopyTo(ByVal destination As Graphics, ByVal destrect As Rectangle, ByVal sourceoffset As Point, Optional ByVal operation As CopyPixelOperation = CopyPixelOperation.SourceCopy Or CopyPixelOperation.CaptureBlt)
                Dim hdc As IntPtr = destination.GetHdc
                CopyTo(hdc, destrect, sourceoffset, operation)
                destination.ReleaseHdc(hdc)
            End Sub
            Public Sub CopyTo(ByVal destination As DC, Optional ByVal operation As CopyPixelOperation = CopyPixelOperation.SourceCopy Or CopyPixelOperation.CaptureBlt)
                CopyTo(destination.hdc, Me.ClipBox, New Point(0, 0), operation)
            End Sub
            Public Sub CopyTo(ByVal destination As DC, ByVal destrect As Rectangle, Optional ByVal operation As CopyPixelOperation = CopyPixelOperation.SourceCopy Or CopyPixelOperation.CaptureBlt)
                CopyTo(destination.hdc, destrect, New Point(0, 0), operation)
            End Sub
            Public Sub CopyTo(ByVal destination As DC, ByVal destrect As Rectangle, ByVal sourceoffset As Point, Optional ByVal operation As CopyPixelOperation = CopyPixelOperation.SourceCopy Or CopyPixelOperation.CaptureBlt)
                CopyTo(destination.hdc, destrect, sourceoffset, operation)
            End Sub
            Public Sub CopyTo(ByVal desthdc As IntPtr, ByVal destrect As Rectangle, ByVal sourceoffset As Point, Optional ByVal operation As CopyPixelOperation = CopyPixelOperation.SourceCopy Or CopyPixelOperation.CaptureBlt)
                BitBlt(desthdc, destrect.X, destrect.Y, destrect.Width, destrect.Height, Me.hdc, sourceoffset.X, sourceoffset.Y, operation)
            End Sub
    
            Public Function ToBitmap(Optional ByVal operation As CopyPixelOperation = CopyPixelOperation.SourceCopy Or CopyPixelOperation.CaptureBlt) As Bitmap
                Return ToBitmap(Me.ClipSize, operation)
            End Function
            Public Function ToBitmap(ByVal Resolution As Size, Optional ByVal operation As CopyPixelOperation = CopyPixelOperation.SourceCopy Or CopyPixelOperation.CaptureBlt) As Bitmap
                ToBitmap = New Bitmap(Resolution.Width, Resolution.Height)
                Dim g As Graphics = Graphics.FromImage(ToBitmap)
                Dim ghdc As IntPtr = g.GetHdc
                BitBlt(ghdc, 0, 0, Resolution.Width, Resolution.Height, Me.hdc, 0, 0, operation)
                g.ReleaseHdc(ghdc)
                g.Dispose()
            End Function
    
            Public ReadOnly Property Graphics() As Graphics
                Get
                    If Me.managedgraphics Is Nothing Then Me.managedgraphics = Graphics.FromHdc(Me.hdc)
                    Return Me.managedgraphics
                End Get
            End Property
            Public Shared Function FromHandle(ByVal handle As IntPtr) As DC
                FromHandle = New DC()
                FromHandle.hdc = handle
            End Function
    
            Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
                If Me._disposed = False Then
                    Select Case managedtype
                        Case DCSource.Handle
                            ReleaseDC(CType(Me.managedobject, IntPtr), Me.hdc)
                        Case DCSource.Graphics
                            CType(Me.managedobject, Graphics).ReleaseHdc(Me.hdc)
                    End Select
                    If managedgraphics IsNot Nothing Then managedgraphics.Dispose()
                    Me.managedgraphics = Nothing
                    Me.managedobject = Nothing
                    Me.managedtype = Nothing
                    Me.hdc = Nothing
                    Me._disposed = True
                End If
            End Sub
            Public Overloads Sub Dispose() Implements IDisposable.Dispose
                Dispose(True)
                GC.SuppressFinalize(Me)
            End Sub
        End Class
    You can construct it from a graphics object or a window (handle)
    To construct it from a bitmap, first create a new graphics object for this bitmap and then perform the operation on the graphics object.

    Code:
    Dim b As Bitmap = Image.FromFile("test.png")
    Dim g As Graphics = Graphics.FromImage(b)
    Dim dc As New DC(g)
    For x As Integer = 0 To b.Width - 1
        For y As Integer = 0 To b.Height - 1
            Dim c As Color = dc.Pixel(x, y)
            c = Color.FromArgb(255 - c.R, 255 - c.G, 255 - c.B)
            dc.Pixel(x, y) = c
        Next
    Next
    dc.Dispose()
    g.Dispose()
    EDIT

    WRECT is the standard Windows rectangle API structure:
    Code:
        Public Structure WRECT
            Public Left, Top, Right, Bottom As Integer
            Sub New(ByVal r As Rectangle)
                Me.Value = r
            End Sub
            Public Property Value() As Rectangle
                Get
                    Return New Rectangle(Left, Top, Right - Left, Bottom - Top)
                End Get
                Set(ByVal value As Rectangle)
                    Me.Left = value.Left
                    Me.Right = value.Right
                    Me.Top = value.Top
                    Me.Bottom = value.Bottom
                End Set
            End Property
        End Structure
    EDIT2

    Ignore me please, it was slow after all. Not sure why it was so fast during my previous test.
    I guess handling the raw bitmap data using (un)lockbits is a lot faster, but only if you want to perform lots of pixel operations on it.

  3. #3
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    3,201

    Re: Fast transfer byte data to picturebox?

    Quote Originally Posted by bergerkiller View Post
    With SetPixel I meant the SetPixel API.
    What it does is create a Device Context out of a bitmap, on which it performs SetPixel a lot quicker. Not sure what they did to the standard SetPixel function of the Bitmap, but it is indeed terribly slow. Perhaps they created and destroyed the DC every time you call the function, not sure.

    And yes, creating a new bitmap in memory IS slow. I did it once as some sort of buffer for a drawing program. Eventually it was faster to draw everything in one go than to draw the stored buffer. (buffer was also drawn over).

    I can remember testing this; generating a 800x600 bitmap in memory took me 5 ms.
    It doesn't surprise me that the API Setpixel is faster. But the place to look for the difference in performance compared to Lockbits might be explained not by one or the other method, but by the processing loop. Resolving references to methods or properties outside the class or even outside the sub is costly -- much more so than arithmetic and logic operations. That's why I always declare a local copy of the FastPix.PixelArray. A simple rule is to avoid "anything with a dot in it" in the inner loop. Your references to the DC.Pixel property, to Color.FromArgb and to c.R etc. are probably the culprits. Compare that to the inner loop in Post #3: not a dot in sight.

    About the time to make a bitmap. I think we need to be clear about what we are timing. If I time the creation of an empty 800 * 600 bitmap like this:
    Code:
    		Dim sw As Stopwatch = Stopwatch.StartNew
    		Dim bmp As New Bitmap(800, 600)
    		MessageBox.Show((sw.ElapsedTicks * 1000 / Stopwatch.Frequency).ToString("0.00"))
    I get a time of about 1.2 milliseconds. But if I also force updating of the pixels on the screen by inserting this in the timing loop:
    Code:
    	PictureBox1.Image = bmp
    	PictureBox1.Invalidate(New Rectangle(0, 0, _W, _H))
    	PictureBox1.Update()
    the time rises to over 8 milliseconds. Maybe that's equivalent to what you were timing but on a slower machine. The time is proprtional to the number of pixels drawn, so filling a full-screen picturebox (1680*1000 approx) on my machine takes over 30 milliseconds. That's where accelerated graphics techniques such as DC.BitBlt (or DirectX, XNA, WPF etc.) are useful.

    BB

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width