Would it be faster to draw lines on a device context rather than a visible picturebox?
And if so, (that would be splendid), would one then use the BltBit API function to copy the image to the picturebox?
Printable View
Would it be faster to draw lines on a device context rather than a visible picturebox?
And if so, (that would be splendid), would one then use the BltBit API function to copy the image to the picturebox?
Only theorising but wouldn't that mean twice as much work, firstly the calls to draw to the DC then a copy from one DC to another??
BITBLT to a picture box is supposed to be the fastest graffix method, except for maybe directX.
It just seems to be an abstraction to me.
G
Well it comes down to is this:
You may draw 5,000,000 lines in a loop to the picturebox
versus
Drawing 5,000,000 lines in memory, and blitting that all at once to a picturebox.....
Which is faster? (I haven't work with bltbit yet, so I can't easily test it out)
Here's my sample code (the important stuff in bold) so far for a form with a picturebox1 and a button (command1):
VB Code:
Option Explicit 'draws line from current position to point passsed API Private Declare Function LineTo Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) As Long 'moves to new drawing position API Private Declare Function MoveToEx Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, lpPoint As POINTAPI) As Long 'determines performance of execution for testing purposes only Private Declare Function GetTickCount Lib "kernel32.dll" () As Long 'allows us to create a pen of certain thickness and color Private Declare Function CreatePen Lib "gdi32.dll" (ByVal fnPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As Long 'necessary to delete pen created with above API function so we don't have memory leaks Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long 'selects our newly created pen or our old pen Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long Private Declare Function LockWindowUpdate Lib "user32" (ByVal hWnd As Long) As Long Private Type mapLineType XPosStart As Single YPosStart As Single XPosEnd As Single YPosEnd As Single Colour As Long Thickness As Single End Type Private mapLine(500000) As mapLineType 'below type is needed for LineTo and MoveEx API functions Private Type POINTAPI x As Long y As Long End Type Private Sub Command1_Click() Dim mypointapi As POINTAPI Dim hPen As Long ' handle to the pen created API Dim hOldPen As Long ' handle to Picture1's previously selected pen API Dim retval As Long ' dummy api function return value API Dim i As Long 'needed for conversion from twips to pixels with API functions Dim picture1width As Long Dim picture1height As Long Dim flp As Long 'performance measuring variable Dim start As Long 'stores the h/w of the picturebox for use with API functions picture1width = (Picture1.Width / Screen.TwipsPerPixelX) picture1height = (Picture1.Height / Screen.TwipsPerPixelY) 'make some RANDOM DATA For i = 1 To 500000 mapLine(i).XPosStart = Int(picture1width * Rnd) mapLine(i).YPosStart = Int(picture1height * Rnd) mapLine(i).XPosEnd = Int(picture1width * Rnd) mapLine(i).YPosEnd = Int(picture1height * Rnd) mapLine(i).Colour = Int(10000 * Rnd + 1) mapLine(i).Thickness = Int(1 * Rnd + 1) Next 'save the random data Open ("C:\testfile.tst") For Binary As #1 Put #1, 1, mapLine Close #1 'Begin load start = GetTickCount Open ("C:\testfile.tst") For Binary As #1 Get #1, 1, mapLine Close #1 Debug.Print "loaded in : " & (GetTickCount - start) / 1000 & " seconds" 'being draw start = GetTickCount [b] ' Draw the rectangle filled using the solid yellow brush flp = Picture1.hdc LockWindowUpdate (flp) ' Stores pciture1's default brush so we can restore it after our new pen is deleted hOldPen = SelectObject(flp, hPen) For i = 1 To 500000 'create a pen based on passed thickness and color hPen = CreatePen(0, mapLine(i).Thickness, mapLine(i).Colour) 'select the pen retval = SelectObject(flp, hPen) 'move to starting point retval = MoveToEx(ByVal flp, mapLine(i).XPosStart, mapLine(i).YPosStart, mypointapi) 'draw line to ending point retval = LineTo(ByVal flp, ByVal mapLine(i).XPosEnd, ByVal mapLine(i).YPosEnd) ' Delete the pen we created to free up resources. retval = DeleteObject(hPen) ' Select the old pen for use by picture1 Next retval = SelectObject(flp, hOldPen) LockWindowUpdate (0) Picture1.Refresh[/b] Debug.Print "Drawn in :" & (GetTickCount - start) / 1000 & " seconds" End Sub
Well the reason I ask is BITBLT uses DC's, thats how it works, if you control the refresh IE draw all the lines to the picture box and just do 1 refresh then it should work out quicker.
BITBLT Bit BLock Transfer, its only real function is to copy memory anyway. It is down to the control to render the screen.
G
Well, if you run the above code and comment out the LineTO method, it processes the loop in 1.2 seconds on a 1.5ghz machine versus 6.9 seconds if you don't comment out the LineTO method.
I imagine the Picturebox control would draw faster if it simply was given the whole array at once, instead of looping through a series of LineTO's...
So, how do I use LineTo to a device context, then bitblt that to a picturebox. Then I can test which method is faster, or if it even makes a difference.
This is what I have come up with... but the PictureBox doesn't display the lines...
VB Code:
p1hdc = Picture1.hdc flp = CreateCompatibleDC(p1hdc) ' Draw the lines For i = 1 To 500000 'create a pen based on passed thickness and color hPen = CreatePen(0, mapLine(i).Thickness, mapLine(i).Colour) 'select the pen retval = SelectObject(flp, hPen) 'move to starting point retval = MoveToEx(ByVal flp, mapLine(i).XPosStart, mapLine(i).YPosStart, mypointapi) 'draw line to ending point retval = LineTo(ByVal flp, ByVal mapLine(i).XPosEnd, ByVal mapLine(i).YPosEnd) ' Delete the pen we created to free up resources. retval = DeleteObject(hPen) Next 'assign Flp to picturebox1 retval = BitBlt(p1hdc, 0, 0, picture1width, picture1height, flp, 0, 0, vbSrcCopy) 'delete created DC DeleteDC (flp)
I haven't done this part before but I think you might be able to use createdevicecontext
http://216.26.168.92/vbapi/ref/c/createdc.html
However it looks as if it needs an object in the first place
G
Check the autorefresh property of the picturebox, make sure its false and call the picture box refresh sub after the BitBlt
G
That unfortunately didn't do it....
Any suggestions from anyone?
bumpity bump
heave
ho
what no one has attempted this before?
I know you're out there....
one more time..
I'm actually curious about this as well... have you tried the CreateDC/CreateCompatibleDC API? From MSDN, on the CreateCompatibleDC function:Quote:
A memory DC exists only in memory. When the memory DC is created, its display surface is exactly one monochrome pixel wide and one monochrome pixel high. Before an application can use a memory DC for drawing operations, it must select a bitmap of the correct width and height into the DC. To select a bitmap into a DC, use the CreateCompatibleBitmap function, specifying the height, width, and color organization required.
Thanks Victor, with ur help, I implemented a CreateCompatibleBitmap.... and proved that writing to a memory device context was not any appreciably faster than writing directly to a picutrebox.
Drawing to a memory DC is going to be MUCH faster than drawing to the picture boxes DC or using the Line and Paint methods of the picture box.
Depending on how much actual drawing you do, I would recommend almost ALWAYS using the DC. The single biggest advantage is that it is pretty much flicker free.
So, create a compatible DC based on the picture box DC, use the API drawing functions to do waht you want, then flip the memory DC onto the picture box DC using BitBlt.
To get you going, here's some sample code for creating, painting and destroying a memory DC:
VB Code:
Public Sub CreateDeviceContext(phSourceDC As Long, pWidth As Long, pHeight As Long, _ ByRef phBufferDC As Long, ByRef phBitmap As Long, _ ByRef phOldBitmap As Long) 'Creates a matching device context 'based on the source context. Passes 'out the handle to the device context 'and the handle to the bitmap that is 'in the device context 'Create the device context phBufferDC = CreateCompatibleDC(phSourceDC) 'Create a matching bitmap phBitmap = CreateCompatibleBitmap(phSourceDC, pWidth, pHeight) 'Copy the bitmap into the device context phOldBitmap = SelectObject(phBufferDC, phBitmap) End Sub Public Sub DeleteDeviceContext(phBufferDC As Long, phBitmap As Long, _ phOldBitmap As Long) 'Cleans up the device contexts and bitmaps 'that have been used to draw on. 'Copy the old bitmap back into the device context phBitmap = SelectObject(phBufferDC, phOldBitmap) 'Delete the bitmap DeleteObject phBitmap 'Delete the device context DeleteDC phBufferDC End Sub Public Sub PaintBufferToScreen(phDestDC As Long, phBufferDC As Long, _ pWidth As Long, pHeight As Long) 'Copies the contents of the graphics buffer 'to the destination device context (generally 'the user controls DC) BitBlt phDestDC, 0, 0, pWidth, pHeight, phBufferDC, 0, 0, SRCCOPY End Sub
You'll have to get the API declarations, but it's pretty easy to use:
VB Code:
Dim lhBufferDC as Long Dim lhBitmap as Long Dim lhOldBitmap as Long 'Create the memory DC CreateDeviceContext Picture1.hDC, Picture1.ScaleWidth, Picture1.ScaleHeight, lhBufferDC, lhBitmap, lhOldBitmap 'Do all you drawing against the DC in lhBufferDC 'Copy back to the Picture box PaintBufferToScreen Picture1.hDC, lhBufferDC, Picture1.ScaleWidth, Picture1.ScaleHeight 'Clean up DeleteDeviceContext lhBufferDC, lhBitmap, lhOldBitmap
Just remember that when you use a memory DC< you MUST clean up (resotre old bitmaps, brushes, pens etc) otherwise you will eventually run out of device handles and everything will go haywire
- gaffa
That's what I thought too Gaffa, but when I implemented a Blitting version of the program, it still took the same amount of time (just ever so slightly less 6.8 vs 7.0) to draw 500,000 lines..
From what you posted, my code seems in order... so I guess it does not make a difference. (I had used API lineTo MoveEX and passing the picture1.hwnd as a parameter before) The relative part is in bold.
VB Code:
Option Explicit 'draws line from current position to point passsed API Private Declare Function LineTo Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) As Long 'moves to new drawing position API Private Declare Function MoveToEx Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, lpPoint As POINTAPI) As Long 'determines performance of execution for testing purposes only Private Declare Function GetTickCount Lib "kernel32.dll" () As Long 'allows us to create a pen of certain thickness and color Private Declare Function CreatePen Lib "gdi32.dll" (ByVal fnPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As Long 'necessary to delete pen created with above API function so we don't have memory leaks Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long 'selects our newly created pen or our old pen Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, _ ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, _ ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long Private Declare Function CreateCompatibleBitmap Lib "gdi32" _ (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long Private Type mapLineType XPosStart As Single YPosStart As Single XPosEnd As Single YPosEnd As Single Colour As Long Thickness As Single End Type Private mapLine(500000) As mapLineType 'below type is needed for LineTo and MoveEx API functions Private Type POINTAPI x As Long y As Long End Type Private Sub Command1_Click() Dim mypointapi As POINTAPI Dim hPen As Long ' handle to the pen created API Dim hOldPen As Long ' handle to Picture1's previously selected pen API Dim retval As Long ' dummy api function return value API Dim i As Long 'needed for conversion from twips to pixels with API functions Dim picture1width As Long Dim picture1height As Long Dim p1hdc As Long Dim flp As Long Dim lBMP As Long 'performance measuring variable Dim start As Long picture1width = (Picture1.Width / Screen.TwipsPerPixelX) picture1height = (Picture1.Height / Screen.TwipsPerPixelY) 'make some RANDOM DATA For i = 1 To 500000 mapLine(i).XPosStart = Int(picture1width * Rnd) mapLine(i).YPosStart = Int(picture1height * Rnd) mapLine(i).XPosEnd = Int(picture1width * Rnd) mapLine(i).YPosEnd = Int(picture1height * Rnd) mapLine(i).Colour = Int(10000 * Rnd + 1) mapLine(i).Thickness = Int(1 * Rnd + 1) Next Open ("C:\testfile.tst") For Binary As #1 Put #1, 1, mapLine Close #1 'Begin load and draw start = GetTickCount Open ("C:\testfile.tst") For Binary As #1 Get #1, 1, mapLine Close #1 Debug.Print "loaded in : " & (GetTickCount - start) / 1000 & " seconds" start = GetTickCount [b] p1hdc = Picture1.hdc lBMP = CreateCompatibleBitmap(Picture1.hdc, picture1width, picture1height) flp = CreateCompatibleDC(p1hdc) Call SelectObject(flp, lBMP) ' Draw For i = 1 To 500000 'create a pen based on passed thickness and color hPen = CreatePen(0, mapLine(i).Thickness, mapLine(i).Colour) 'select the pen retval = SelectObject(flp, hPen) 'move to starting point retval = MoveToEx(ByVal flp, mapLine(i).XPosStart, mapLine(i).YPosStart, mypointapi) 'draw line to ending point retval = LineTo(ByVal flp, ByVal mapLine(i).XPosEnd, ByVal mapLine(i).YPosEnd) ' Delete the pen we created to free up resources. retval = DeleteObject(hPen) DoEvents Next retval = SelectObject(flp, hOldPen) 'assign Flp to picturebox1 BitBlt p1hdc, 0, 0, picture1width, picture1height, flp, 0, 0, vbSrcCopy 'delete created DC and compatible bitmap DeleteDC (flp) Call DeleteObject(lBMP) Picture1.Refresh[/b] Debug.Print "Drawn in :" & (GetTickCount - start) / 1000 & " seconds" End Sub
Yeah, it takes about 5 seconds on my machine (about 2.5 if I use a single Pen handle instead of creationg and destroying one each time)
I haven't tested the line method in the PictureBox...
All the API drawing stuff I've done (and I write a lot of owner drawn user controls) , I've always used the memory DC (double buffering) approach, cos it IS faster than anything else I've tried. Admittedly, I'm not drawing 500000 lines in a box - generally I draw a lot of boxes and text etc...
So I guess it's what ever suits...
- gaffa
Well, this whole thing developed out of a question another forum user was asking.... on loading a 100mb file containing x,y coordinates and line thickness and color. The file was generated from a sorta CAD program used by municipalities or utilities to visualize a whole city and the pipes and streets, etc... we tackled the loading of the data aspect, but the drawing was another issue. And he realized, there was not just 500,000 lines, but 5,000,000 lines...CAD program output JPEG :http://www.vbforums.com/attachment.p...postid=1171042
thread:
http://www.vbforums.com/showthread.p...hreadid=198679