Graphics Programming in Visual Basic - Part 4
"Tanner's Top 10 List of Graphics Code Optimizations"
By: Tanner "DemonSpectre" Helland
Alright - you're on the last of the four tutorials. If I were a betting man, I'd wager that you've learned way more than you ever wanted to know about graphics programming in Visual Basic! :) But don't stop yet - this tutorial will tie together everything you've learned so far, so pay close attention!
-THE PURPOSE OF THIS TUTORIAL-
This tutorial is slightly different from the previous three tutorials. Instead of discussing specific graphics routines, I'm going to give you my "Top 10 List of Graphics Code Optimizations." This checklist of optimization techniques will give you the perfect guide to speeding up your graphics application. We'll start with the easiest ways to speed up your code and end with the most dramatic (but effective) ways. If your code is too slow, run it through this checklist and by the end of it your code will be running as fast as possible! Only Tanner could bring you so great a resource, eh? :) (Please contact
tannerhelland@hotmail.com for information about your cash or check donation...)-THE TOP 10 LIST-
10. Compile to native code, enabling every advanced optimization.
While it sounds stupid, this is the easiest but least utilized way to speed up your graphics code. Enabling one advanced optimization in particular: 'Remove Array Bound Checks,' will speed up DIB sections a huge - huge - amount. When you check that box, it keeps VB from ever checking whether or not the array location you are trying to access is invalid. This has a good part and a bad part, of course - the good part is that your code involving arrays could be as much as 10x faster. The bad part is that if you do try and access an invalid array location, you won't get an error - instead you'll get a critical fault and your program will crash. (If you write good code this won't be a problem, though :)9. Check your ScaleMode: use pixels, never twips. Why the default ScaleMode setting is twips instead of pixels, I don't know. It's a stupid, stupid way to do it (IMHO). If your code a) doesn't work, or b) does work but is strangely slow, make sure that you're using pixels. (This really only applies to PSet and Point, but it's good to remember). As a general rule of thumb, set every object's ScaleMode to pixels even if you don't plan on using it for graphics. That way you'll never get bitten by the twips bug.
8. Don't make your code refreshing. AutoRedraw is your friend as a programmer. If you have AutoRedraw set to false, your poor computer is going to try and refresh the image every chance it gets. This is bad :). Refreshing an image takes a great deal of time, so try and refresh an image only after you're completely done with it.
Another thing worth mentioning is progress bars - if you're using a progress bar to track your image processing, don't refresh it for every pixel or every line! Refreshing a progress bar is nearly as slow as refreshing an image, so if you insist on using a progress bar then refresh it every 15-20 lines so that it doesn't slow you down too much.
7. Optimize your variables. Because graphics programming involves lots and lots of variables, here are a couple of tips to make sure that you're not wasting time with bad variable usage:
6. In-line optimization: fix your math. In-line optimization refers to optimizing your code one line at a time. This usually involves using faster math functions and better programming techniques. There are about a million different things I could list under this category, so I'll only mention a few of the most critical math optimizations you can do:
For a much more in-depth discussion of VB optimization techniques, search the net for VBFibre - it's a great website about nothing but optimizing your Visual Basic code.
5. Use look-up tables. Few things will speed up your code like a look-up table will. For those who don't know, consider the following code example (used to invert a pixel's color):
For x = 0 to 100
For y = 0 to 100
'Color = GetPixel()… goes here
'Color extraction goes here
R = 255 - R
G = 255 - G
B = 255 - B
'SetPixel()… goes here
Next y
Next x
For every pixel VB has to do three 'Subtract' functions. This is bad coding - for this simple 101x101 picture example that's over 30,000 'Subtracts'! Try the following code instead:
Dim LookUpTable(0 to 255) as Byte
For x = 0 to 255
'Fill the look-up table with every possible color value and it's corresponding inverted value
LookUpTable(x) = 255 - x
Next x
For x = 0 to 100
For y = 0 to 100
'Color = GetPixel… goes here
'Color extraction goes here
R = LookUpTable(R)
G = LookUpTable(G)
B = LookUpTable(B)
'SetPixel… goes here
Next y
Next x
With this code VB only does 256 'Subtracts' and then uses the table of values to change R, G, and B. This makes a huge difference in execution time - use look-up tables every chance you get!
4. Forget about PSet and Point - use the API. If you haven't read the previous three tutorials, now is the time to do it. PSet and Point are the worst! Use GetPixel and SetPixel/V for a huge speed increase.
3. Forget about GetPixel and SetPixel/V - use DIB sections. While GetPixel and SetPixel/V are nice, they're still slow. DIB sections (or Bitmap bits) will give you much better results - read tutorial 3 for more information about this.
2. DIB Sections do better in streams. Remember back in tutorial 3 when we talked about how to declare your 'ImageData' array? If you take a quick trip down memory lane, you will remember that we discussed a method like "Redim ImageData(0 to 2, 0 to Width - 1, 0 to Height)". While this creates a very easy-to-use array, we can improve it by declaring it differently. "Redim ImageData (0 to (Width * Height * 3))" gives us an array the exact same size as the first statement, but we are only using one dimension instead of three - making our array at least 3x faster for VB to access. The only problem with this is that we can no longer access direct pixels or colors - but for most graphics functions (like the 'Invert' example on (5), or brightness, or contrast, etc.) we do the same thing to every pixel so it doesn't matter. For example:
For x = 0 to (Width * Height * 3)
ImageData(x) = 255 - ImageData(x)
Next x
would invert the image just the same as the example in optimization (5). The reason I call this method a "stream" is that we treat the image as a continuous stream of colors, not as separate pixels. For a more in-depth example of this, download my Advanced Brightness program
1. If you do all this and your program is still too slow, try something extreme.
I hate to say it, but if you've done everything above and your program is still too slow, you're out of luck with traditional methods. Here are some things you could look into, though:Well, my friends, this is the end of my graphics tutorials. It's taken me many, many hours to write these up, so I hope you've gained something from reading them. If you have any comments, questions, suggestions, ideas for future tutorials, or want to send me a generous donation, contact me as always at
tannerhelland@hotmail.com. If you have specific graphics questions I probably won't be able to e-mail you a specific response - if I did that I'd spend 24:7 answering questions and never have any time for myself. But please contact me with your feelings about the tutorials, and I'll use your ideas to revise these ones and maybe write more!Happy programming!
Copyright 2002 by Tanner "DemonSpectre" Helland. This article may not be reproduced in any form (printed or electronic) without prior written consent from the author. This site may, however, be hyperlinked on the world wide web without permission from the author.
This programming source code is provided "as is". In no event shall the author or any of his affiliates be liable for any consequential, special, incidental or indirect damages of any kind arising out of the delivery, performance or use of this source code, to the maximum extent permitted by applicable law. While the source code has been developed with great care, it is not possible to warrant that it is error free. This source code is not designed or intended to be used in any activity that may cause personal injury, death or any other severe damage or loss.
Please contact tannerhelland@hotmail.com with feedback and questions regarding this tutorial.