Jump to content


 


Register a free account to unlock additional features at BleepingComputer.com
Welcome to BleepingComputer, a free community where people like yourself come together to discuss and learn how to use their computers. Using the site is easy and fun. As a guest, you can browse and view the various discussions in the forums, but can not create a new topic or reply to an existing one unless you are logged in. Other benefits of registering an account are subscribing to topics and forums, creating a blog, and having no ads shown anywhere on the site.


Click here to Register a free account now! or read our Welcome Guide to learn how to use this site.

Photo

Tutorial: A fast method for drawing in VB .Net


  • Please log in to reply
14 replies to this topic

#1 Wolfy87

Wolfy87

  • Members
  • 414 posts
  • OFFLINE
  •  
  • Gender:Male
  • Location:England
  • Local time:02:52 AM

Posted 25 November 2009 - 05:34 PM

If you have ever draw onto your actual form in Visual Basic you will know how slow it is, I use a method involving images wich is 1000 times faster.

You need to start by placing an picture box on your form, make it 300 by 300 pixels, now change the name to display.

We are also going to need a timer, it is located in the components section of your IDE. Drag this on to your form, now it wont work straight away, we need to enable it in the properties pane, set enabled to True, and the interval to 10, this means every 0.01 of a second (My maths isnt so good so maby not) our timer will "Tick".

Now for some code.

First we need to create some variables:

Dim output As New Bitmap(300, 300)
Dim gfx As Graphics = Graphics.FromImage(output)
Dim SpriteX As Integer = 135
Dim SpriteY As Integer = 135
Dim moveU As Boolean = False
Dim moveR As Boolean = False
Dim moveD As Boolean = False
Dim moveL As Boolean = False
Dim moveStep As Integer = 4

The first one is our bitmap, we will draw to that using gfx and then display it in the picture box we made earlyer.
gfx is the thing we use to draw to the bitmap.
SpriteX and SpriteY will be the positions of out well you can probably guess, sprite, a sprite is just an object really.
The other four will be used in a second when we want to move our sprite...or box in this case.
And the moveStep will be how much our sprite moves every frame.

Now we need our function that draws and displays our data.

Sub refreshScreen() Handles Timer1.Tick

End Sub

So we now have a function or sub called refreshScreen, because we have a handle, Timer1.Tick, it will happen every 0.01 seconds.

The following code will go in our refreshScreen function.

gfx.FillRectangle(Brushes.White, 0, 0, 300, 300)
gfx.FillRectangle(Brushes.Blue, SpriteX, SpriteY, 25, 25)
display.Image = output

The first line makes our whole output bitmap white.
The second draws our sprite using our coordinates and a size of 25 by 25.
And the third displays our output bitmap in out picture box that we named display.

How interesting is that...yes a blue box...well this blue box likes to move so we better give him what he wants.

Private Sub startMoving(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
		Select Case e.KeyCode
			Case Keys.Up
				moveU = True
			Case Keys.Right
				moveR = True
			Case Keys.Down
				moveD = True
			Case Keys.Left
				moveL = True
		End Select
	End Sub

	Private Sub stopMoving(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp
		Select Case e.KeyCode
			Case Keys.Up
				moveU = False
			Case Keys.Right
				moveR = False
			Case Keys.Down
				moveD = False
			Case Keys.Left
				moveL = False
		End Select
	End Sub

Theese two functions turn our moving booleans between true and false when up, down, left or right are pushed down or up.

Now we need some more code in our refreshScreen function, this will check if the moving booleans are set to true, if so it will move the sprite, but only if it is not going to go thru the wall, this will make it go outside the bounds of the array, in other words 301 dose not fit into 300.

I reccomend placing this after your other code in this function.

If moveU = True And SpriteY > 0 Then
			SpriteY -= moveStep
		ElseIf SpriteY < 0 Then
			SpriteY = 0
		End If
		If moveR = True And SpriteX < 275 Then
			SpriteX += moveStep
		ElseIf SpriteX > 275 Then
			SpriteX = 275
		End If
		If moveD = True And SpriteY < 275 Then
			SpriteY += moveStep
		ElseIf SpriteY > 275 Then
			SpriteY = 275
		End If
		If moveL = True And SpriteX > 0 Then
			SpriteX -= moveStep
		ElseIf SpriteX < 0 Then
			SpriteX = 0
		End If

Compile and done, thats it, you should have about 64 lines, if not you might have missed somthing.

Hope this help a few people.

And heres the full code anyway...

Public Class Form1
	Dim output As New Bitmap(300, 300)
	Dim gfx As Graphics = Graphics.FromImage(output)
	Dim SpriteX As Integer = 135
	Dim SpriteY As Integer = 135
	Dim moveU As Boolean = False
	Dim moveR As Boolean = False
	Dim moveD As Boolean = False
	Dim moveL As Boolean = False
	Dim moveStep As Integer = 4

	Sub refreshScreen() Handles Timer1.Tick
		gfx.FillRectangle(Brushes.White, 0, 0, 300, 300)
		gfx.FillRectangle(Brushes.Blue, SpriteX, SpriteY, 25, 25)
		display.Image = output

		If moveU = True And SpriteY > 0 Then
			SpriteY -= moveStep
		ElseIf SpriteY < 0 Then
			SpriteY = 0
		End If
		If moveR = True And SpriteX < 275 Then
			SpriteX += moveStep
		ElseIf SpriteX > 275 Then
			SpriteX = 275
		End If
		If moveD = True And SpriteY < 275 Then
			SpriteY += moveStep
		ElseIf SpriteY > 275 Then
			SpriteY = 275
		End If
		If moveL = True And SpriteX > 0 Then
			SpriteX -= moveStep
		ElseIf SpriteX < 0 Then
			SpriteX = 0
		End If
	End Sub

	Private Sub startMoving(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
		Select Case e.KeyCode
			Case Keys.Up
				moveU = True
			Case Keys.Right
				moveR = True
			Case Keys.Down
				moveD = True
			Case Keys.Left
				moveL = True
		End Select
	End Sub

	Private Sub stopMoving(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp
		Select Case e.KeyCode
			Case Keys.Up
				moveU = False
			Case Keys.Right
				moveR = False
			Case Keys.Down
				moveD = False
			Case Keys.Left
				moveL = False
		End Select
	End Sub
End Class


BC AdBot (Login to Remove)

 


#2 Billy O'Neal

Billy O'Neal

    Visual C++ STL Maintainer


  • Malware Response Team
  • 12,304 posts
  • OFFLINE
  •  
  • Gender:Male
  • Location:Redmond, Washington
  • Local time:07:52 PM

Posted 25 November 2009 - 06:04 PM

I've copyedited the tut for you.

Just an FYI: Your drawing functions might be slow in your form because DoubleBuffering is turned on. Look for "DoubleBuffer" in your form's properties, turn it off, and it will likely beat the "render to offscreen image" method. You're essentially double buffering it yourself here, but since you're only forcing the form to redraw itself once, you're avoiding the cost here.

Also .. rather than the booleans, wouldn't it be easier to just check the key state in refreshScreen() rather than having to define event handlers for KeyUp and KeyDown?

Hope that helps.

Here's the copyedited version: http://www.mediafire.com/?yynmoymmb1n

Billy3
Twitter - My statements do not establish the official position of Microsoft Corporation, and are my own personal opinion. (But you already knew that, right?)
Posted Image

#3 Wolfy87

Wolfy87
  • Topic Starter

  • Members
  • 414 posts
  • OFFLINE
  •  
  • Gender:Male
  • Location:England
  • Local time:02:52 AM

Posted 25 November 2009 - 06:17 PM

I find it is the best and most manageble way of handleing keypresses, and thanks for the edit.

#4 Billy O'Neal

Billy O'Neal

    Visual C++ STL Maintainer


  • Malware Response Team
  • 12,304 posts
  • OFFLINE
  •  
  • Gender:Male
  • Location:Redmond, Washington
  • Local time:07:52 PM

Posted 25 November 2009 - 06:48 PM

You sure this isn't more readable?

Imports System.Windows.Input

Public Class Form1
	Dim output As New Bitmap(300, 300)
	Dim gfx As Graphics = Graphics.FromImage(output)
	Dim SpriteX As Integer = 135
	Dim SpriteY As Integer = 135
	Dim moveStep As Integer = 4

	Sub refreshScreen() Handles Timer1.Tick
		gfx.FillRectangle(Brushes.White, 0, 0, 300, 300)
		gfx.FillRectangle(Brushes.Blue, SpriteX, SpriteY, 25, 25)
		display.Image = output

		If Keyboard.IsKeyDown(Key.Up) And SpriteY > 0 Then
			SpriteY -= moveStep
		ElseIf SpriteY < 0 Then
			SpriteY = 0
		End If
		If Keyboard.IsKeyDown(Key.Right) And SpriteX < 275 Then
			SpriteX += moveStep
		ElseIf SpriteX > 275 Then
			SpriteX = 275
		End If
		If Keyboard.IsKeyDown(Key.Down) And SpriteY < 275 Then
			SpriteY += moveStep
		ElseIf SpriteY > 275 Then
			SpriteY = 275
		End If
		If Keyboard.IsKeyDown(Key.Left) And SpriteX > 0 Then
			SpriteX -= moveStep
		ElseIf SpriteX < 0 Then
			SpriteX = 0
		End If
	End Sub

End Class

EDIT: Even better:

Imports System.Windows.Input

Public Class Form1
	Dim output As New Bitmap(300, 300)
	Dim gfx As Graphics = Graphics.FromImage(output)
	Dim SpriteX As Integer = 135
	Dim SpriteY As Integer = 135
	Dim moveStep As Integer = 4

	Sub refreshScreen() Handles Timer1.Tick
		gfx.FillRectangle(Brushes.White, 0, 0, 300, 300)
		gfx.FillRectangle(Brushes.Blue, SpriteX, SpriteY, 25, 25)
		display.Image = output

		If Keyboard.IsKeyDown(Key.Up) Then
			SpriteY -= moveStep
		End If
		If Keyboard.IsKeyDown(Key.Right) Then
			SpriteX += moveStep
		End If
		If Keyboard.IsKeyDown(Key.Down) Then
			SpriteY += moveStep
		End If
		If Keyboard.IsKeyDown(Key.Left) Then
			SpriteX -= moveStep
		End If


		If SpriteX < 0 Then
			SpriteX = 0
		End If
 		If SpriteX > 275 Then
			SpriteX = 275
		End If
		If SpriteY < 0 Then
			SpriteY = 0
		End If
		If SpriteY > 275 Then
			SpriteY = 275
		End If

	End Sub

End Class

Edited by Billy O'Neal, 25 November 2009 - 06:53 PM.

Twitter - My statements do not establish the official position of Microsoft Corporation, and are my own personal opinion. (But you already knew that, right?)
Posted Image

#5 groovicus

groovicus

  • Security Colleague
  • 9,963 posts
  • OFFLINE
  •  
  • Gender:Male
  • Location:Centerville, SD
  • Local time:08:52 PM

Posted 25 November 2009 - 07:06 PM

I use a method involving images wich is 1000 times faster.


Show me your benchmarks that prove 3 magnitudes of better performance. Please explain how you timed it, and how you came to the conclusion that your method is "1000" times faster. :thumbsup:

#6 Wolfy87

Wolfy87
  • Topic Starter

  • Members
  • 414 posts
  • OFFLINE
  •  
  • Gender:Male
  • Location:England
  • Local time:02:52 AM

Posted 26 November 2009 - 10:13 AM

Why in the world would you pick up on that its clearly an exaggeration to get my point across.

#7 Billy O'Neal

Billy O'Neal

    Visual C++ STL Maintainer


  • Malware Response Team
  • 12,304 posts
  • OFFLINE
  •  
  • Gender:Male
  • Location:Redmond, Washington
  • Local time:07:52 PM

Posted 26 November 2009 - 10:38 AM

Because when you change algorithms a "1000 times" speedup is not unheard of. Switching from say... linear search to binary search can lead to hundreds of thousands of times speed increases. For example, to find a name in a phone book of a million records, a simple "check every element" search is O(n), resulting in a million comparisons to find your item. Binary search, on the other hand, is O(lg n), and requires only 20 comparisons to find what you're looking for. The difference gets worse the larger your data set is.

Point being -- several might take your statement literally ;)

Billy3
Twitter - My statements do not establish the official position of Microsoft Corporation, and are my own personal opinion. (But you already knew that, right?)
Posted Image

#8 PlyPencil

PlyPencil

  • Members
  • 51 posts
  • OFFLINE
  •  
  • Local time:03:52 AM

Posted 26 November 2009 - 01:07 PM

As I promised to you earlier wolfie I have made a speed test. The buffering method is 8 times faster (roughly) to the traditional method.

Posted Image

*edited bad maths

Edited by PlyPencil, 26 November 2009 - 01:08 PM.


#9 groovicus

groovicus

  • Security Colleague
  • 9,963 posts
  • OFFLINE
  •  
  • Gender:Male
  • Location:Centerville, SD
  • Local time:08:52 PM

Posted 26 November 2009 - 02:53 PM

Why in the world would you pick up on that its clearly an exaggeration to get my point across.



A good algorithm speaks for itself and doesn't need exaggeration. As Billy pointed out, different algorithms yield different results.

There are other considerations though. In Billy's example, binary search only works if the collection is sorted. It may be sufficient then to use a linear search than it would be to sort the collection first, then use binary search. You would have to consider the speed of the sort algorithm in addition of the search algorithm. So while binary search is much quicker than a linear search, that is a small part of the picture. One needs to look at the entire system of algorithms used in an application to determine performance. There are entire disciplines within computer science that revolve around measuring algorithms, improving algorithms, and creating new algorithms. You can imagine that someone using an application that tracks air traffic would want to know exactly how long it takes to update the positions of all aircraft in its range. Check out real-time computing. The corporate world spends lots of money hiring people to figure out how optimize legacy algorithms. I regularly work with large data sets that can take days to process; a 1% improvement can literally save half a day of processing time.

An 8 speed increase is respectable, but I still have questions about the testing method. What are your units of measurement? Is that minutes, hours, seconds, milliseconds, microseconds, or nano-seconds? What API did you use for timing? Did you vary image size? How many tests did you run? At what size image does buffering become more computationally expensive than without? Did you test what happens when the buffer flushes? Really, I'd like to see a mean over a million tests with one size image, then a million tests over a bigger image, etc. And I am not trying to pick on you, I just want a clarification. Really, I would love to play with the code myself but I ave other code to write. :

#10 Wolfy87

Wolfy87
  • Topic Starter

  • Members
  • 414 posts
  • OFFLINE
  •  
  • Gender:Male
  • Location:England
  • Local time:02:52 AM

Posted 26 November 2009 - 06:34 PM

Thank you for your input.

#11 Billy O'Neal

Billy O'Neal

    Visual C++ STL Maintainer


  • Malware Response Team
  • 12,304 posts
  • OFFLINE
  •  
  • Gender:Male
  • Location:Redmond, Washington
  • Local time:07:52 PM

Posted 26 November 2009 - 08:34 PM

As I promised to you earlier wolfie I have made a speed test. The buffering method is 8 times faster (roughly) to the traditional method.

Posted Image

*edited bad maths

Did you turn off double buffering on your form before running the test?

Billy3
Twitter - My statements do not establish the official position of Microsoft Corporation, and are my own personal opinion. (But you already knew that, right?)
Posted Image

#12 PlyPencil

PlyPencil

  • Members
  • 51 posts
  • OFFLINE
  •  
  • Local time:03:52 AM

Posted 27 November 2009 - 09:03 AM

I always turn off double buffering, I find it slows the program down and unfortuently it actually increaces flicker when drawing traditionally. As to why it does the opposite of what it is meant to I do not know.

#13 Billy O'Neal

Billy O'Neal

    Visual C++ STL Maintainer


  • Malware Response Team
  • 12,304 posts
  • OFFLINE
  •  
  • Gender:Male
  • Location:Redmond, Washington
  • Local time:07:52 PM

Posted 27 November 2009 - 11:48 AM

I always turn off double buffering, I find it slows the program down and unfortuently it actually increaces flicker when drawing traditionally. As to why it does the opposite of what it is meant to I do not know.

It decreases flicker. When you draw, it's drawn to an offscreen buffer, and then the buffer is bitblt'ed to the display. Since the Bitblt operation results in essentially no flicker, any flicker from the drawing operations is removed.

The problem with the .NET drawing functions is that they don't allow you to access this double buffer. So when you draw that way, you end up bitblt'ing the entire form, causing a redraw of the entire thing for every single drawing primitive you use.

I believe there's a way to draw directly to the double buffer eliminating this overhead.. but I'm not sure. This Site seems to have a few words on it.

Does that make sense?

Billy3
Twitter - My statements do not establish the official position of Microsoft Corporation, and are my own personal opinion. (But you already knew that, right?)
Posted Image

#14 PlyPencil

PlyPencil

  • Members
  • 51 posts
  • OFFLINE
  •  
  • Local time:03:52 AM

Posted 28 November 2009 - 06:51 AM

Yes that makes sense, however in all my programs it increaces flicker. And in that webpage you linked to me, a similar method has to be done first when initializing DirectX (I do a lot of work with it in VB). When setting up DirectX you have to disable the normal Double Buffer, then initialize the DirectX one, using very similar code to what was displayed on that page.

Edited by PlyPencil, 28 November 2009 - 06:53 AM.


#15 PlyPencil

PlyPencil

  • Members
  • 51 posts
  • OFFLINE
  •  
  • Local time:03:52 AM

Posted 28 November 2009 - 12:48 PM

Why in the world would you pick up on that its clearly an exaggeration to get my point across.



A good algorithm speaks for itself and doesn't need exaggeration. As Billy pointed out, different algorithms yield different results.

There are other considerations though. In Billy's example, binary search only works if the collection is sorted. It may be sufficient then to use a linear search than it would be to sort the collection first, then use binary search. You would have to consider the speed of the sort algorithm in addition of the search algorithm. So while binary search is much quicker than a linear search, that is a small part of the picture. One needs to look at the entire system of algorithms used in an application to determine performance. There are entire disciplines within computer science that revolve around measuring algorithms, improving algorithms, and creating new algorithms. You can imagine that someone using an application that tracks air traffic would want to know exactly how long it takes to update the positions of all aircraft in its range. Check out real-time computing. The corporate world spends lots of money hiring people to figure out how optimize legacy algorithms. I regularly work with large data sets that can take days to process; a 1% improvement can literally save half a day of processing time.

An 8 speed increase is respectable, but I still have questions about the testing method. What are your units of measurement? Is that minutes, hours, seconds, milliseconds, microseconds, or nano-seconds? What API did you use for timing? Did you vary image size? How many tests did you run? At what size image does buffering become more computationally expensive than without? Did you test what happens when the buffer flushes? Really, I'd like to see a mean over a million tests with one size image, then a million tests over a bigger image, etc. And I am not trying to pick on you, I just want a clarification. Really, I would love to play with the code myself but I ave other code to write. :


Sorry I didnt see your post first time.

The units are milliseconds and I recorded the time by using a Stopwatch. Programming one not a real one. It starts before the sub which calls the drawing of the 200x100 pixel panel and stops after. It outputs its time, resets then performs the other method.

By flusing I do not know about the traditional drawing method but wolfie's method (Because of the way I wrote it) does flush.

With your requests about a million tests for an image, and a million for another I will start work on it and should have a screenshot for you shortly. Or maybe a binary as it may be easier.



Here you go. I have the output from the test,
Speed Test Results


Initializing Test Traditional - Pixel Draw
Drawing 160,000 pixels took 4190 Milliseconds
Drawing 160,000 pixels took 3121 Milliseconds
Drawing 160,000 pixels took 3148 Milliseconds
Drawing 160,000 pixels took 3160 Milliseconds
Drawing 160,000 pixels took 3141 Milliseconds
Drawing 160,000 pixels took 3135 Milliseconds
Drawing 160,000 pixels took 3139 Milliseconds
Drawing 160,000 pixels took 3241 Milliseconds
Drawing 160,000 pixels took 3140 Milliseconds
Drawing 160,000 pixels took 3081 Milliseconds
Initializing Test Buffered - Pixel Draw
Drawing 160,000 pixels took 426 Milliseconds
Drawing 160,000 pixels took 426 Milliseconds
Drawing 160,000 pixels took 425 Milliseconds
Drawing 160,000 pixels took 427 Milliseconds
Drawing 160,000 pixels took 423 Milliseconds
Drawing 160,000 pixels took 426 Milliseconds
Drawing 160,000 pixels took 428 Milliseconds
Drawing 160,000 pixels took 427 Milliseconds
Drawing 160,000 pixels took 426 Milliseconds
Drawing 160,000 pixels took 429 Milliseconds
Traditional average: 3249.6 milliseconds
Buffered Average: 426.3 milliseconds

And here is the binary
http://www.mediafire.com/file/nynnnyd5wmm/Speed%20Test.zip

If you want to do it a million times I will send you another version which will do that :thumbsup:

Edited by PlyPencil, 28 November 2009 - 01:53 PM.





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users