|
-
Jul 17th, 2011, 02:28 AM
#1
Thread Starter
Fanatic Member
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.
-
Jul 17th, 2011, 05:29 AM
#2
Fanatic Member
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.
Last edited by bergerkiller; Jul 17th, 2011 at 05:49 AM.
-
Jul 17th, 2011, 08:11 AM
#3
Re: Fast transfer byte data to picturebox?
 Originally Posted by bergerkiller
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
Last edited by boops boops; Jul 17th, 2011 at 08:15 AM.
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
|