Even Faster?
This part of the tutorial is slightly different from the previous sections. 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 a good guide for 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 your code will be running as fast as possible! Am I generous or what? (Please head to the contact page ASAP to send me a cash or check donation… ;)
So with the expectation that donations will be rolling in by this time tomorrow, let’s hit the top 10 list!
Tanner’s Top 10 List of Graphics Optimizations
10. Compile to native code, enabling every advanced optimization. While it sounds stupid, this is the easiest – but oftentimes 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, VB stops checking whether or not the array locations you try to access are 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 10-15x 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. (But if you write decent code, this shouldn’t ever be a problem!)
9. Check your ScaleMode: use pixels, never twips. Why the default ScaleMode setting is twips instead of pixels, I don’t know. The most logical conclusion I’ve heard is that VB was designed for business applications and not image processing, so twips (a standard for measuring printed images, not monitor-displayed ones) was made the default. IMHO, this still doesn’t justify the default use of twips.
If your code a) doesn’t work, or b) does work but is strangely slow, make sure that you’re using pixels. 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 fall victim to stupid twips-related errors.
8. Don’t make your code refreshing. AutoRedraw is your friend. 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 messing with it. (There are several exceptions to this rule, but they apply only for experienced users who are doing unconventional tricks with picture boxes; generally speaking, unless using a picture box as the receiver of a buffer keep AutoRedraw on.)
Another thing worth mentioning here is progress bars – if you’re using a progress bar to track your image processing, don’t refresh it for every pixel or even every line! Refreshing a progress bar is almost as slow as refreshing an image, so if you must use a progress bar then refresh it every 15-20 lines so it doesn’t slow you down too much.
7. Optimize your variables. Because graphics programming usually 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:
Variantssuck. Never – EVER – useVarianttypes in your code. They’re slow, they take up a lot of memory, and unless you’re the laziest bum on the planet you can find a way to write code that doesn’t require them. (Another thing worth mentioning is to never declare your variables like this:Dim x, y as Long.In this example, VB will declarexas aVariantandyas aLong. The proper programming technique would be:Dim x as Long, y as Long.)- Don’t use decimal variables. Generally speaking,
SinglesandDoublesare slower,LongsandIntegersare faster. In every Visual Basic case I’ve researched (there are exceptions in other languages, particularly ASM, and under different hardware configurations), floating-point/decimal math is slower than integer/whole number math. Therefore, avoid decimals if possible. For example,Red = Red * 1.5tends to be slower thanRed = (Red * 15) \ 10. Longsare fastest. As long as computers remain 32-bit systems (which may not be for too much longer, heh), both Windows and your processor will be optimized forLong-type variables (which is why every API function uses them). So, believe it or not,Longvariables are slightly faster in most cases thanIntegerorBytevariables – and significantly faster thanSingle,Double, orVariantones (typically ~3-5x faster thanSingleones, and moreso for the other data types). UseLongsunless you absolutely need to conserve that extra memory. (This is especially important for looping variables; because they get accessed so many times, always useLong-type variables for your loops. VBFibre – the famous VB optimization site shows in this example the significant difference between using aSingleand aLongasFor...Nextvariables.)- Avoid type conversions. Regardless of what type of variable you end up using, keep it consistent. When you intermix variable types, any programming language slows down – a line like
Integer = Longrequires VB to temporarily changeLonginto an integer, and that takes time. (Note: this doesn’t apply to DIB section arrays – they must always be of typeByte.)
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 graphics code:
- Watch for dividing code. Two points here: 1) always use \ instead of /. The difference is that backslash is integer divide – it ignores any decimals that arise from the division, while frontslash uses decimal math. Backslash tends towards 3x faster on my AMD processor. 2) Don’t divide if you don’t have to. Dividing is the slowest of the four basic math functions – so try to avoid it.
- Avoid calling functions and subs within
For/NextorDo/Whileloops. Though convenient, functions and subs are slower than manually inserting that code into the loop. (This is because the processor has to jump around in memory between the different routines, which takes more time than just running a straight line through your code). The difference isn’t really noticeable unless you’re calling them from within a loop because then the speed loss is accentuated thousands of times over. - If you must use functions and subs, use
ByRefinstead ofByVal.ByRefvariables simply send a function the address of the variable.ByValvariables require VB to make a copy of the variable before it sends it – and creating a new variable takes up precious processing time. - Lengthy math equations take a lengthy time to process. Short lines of math allow the compiler to better optimize their order and purpose. One giant line of code might impress your friends, but the processor can’t optimize it nearly as well as a bunch of smaller lines.
For a much more in-depth discussion of VB optimization techniques, visit VBFibre at http://www.persistentrealities.com/vbfibre/index.php. Every VB programmer should look through this site – it’s the most accurate and well-explained information ANYWHERE about Visual Basic optimizations. Phenomenal site, donate if you can.
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
'RGB 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 101×101 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
'RGB 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! (There is, however, a disclaimer worth placing here – unless you follow step 10 (particularly the “Remove Array Bound Checks” option) – look-up tables can accidentally slow you down on small images. These steps are in order for a reason!) An excellent example of look-up tables can be found in the Real-time Brightness program.
4. Forget about PSet and Point – use the API. If you haven’t read the previous three sections of these tutorials, now is the time to do it. PSet and Point are the absolute worst for per-pixel image processing! 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 BitmapBits) will give you significantly better results.
2. DIB Sections do better in streams. Remember last page 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, 0 to Height). While this creates a very easy-to-use array, we can improve its speed 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 more than 3x faster for VB to access (and for large arrays, the gain can surpass 10x). The only problem with this is that we can no longer access direct pixels or colors – but for many graphics functions (like the ‘Invert’ example on (5), or brightness, or contrast, etc.) we do the same thing to every color within a pixel so it doesn’t matter. For example:
For x = 0 to (Width * Height * 3)
ImageData(x) = 255 - ImageData(x)
Next x
would invert an 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 values, not as separate pixels or colors. For a more in-depth example of this, see the Real-Time 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 probably out of luck with traditional methods. Here are some things you could look into, though:
- Consider switching to SafeArrays. Students of Game Design has an excellent tutorial detailing the structure and use of “SafeArrays”, the internal VB format for arrays. SafeArrays won’t give you a speed increase in editing your array information, but they will get and set pixel information faster than
GetDIBitsandSet/StretchDIBits. If your code requires a large amount of getting and setting pixels, SafeArrays may give you the performance boost you need without a lot of extra coding (as they are structured almost identically to the arrays returned byGetDIBits). - Look into assembly language extensions. Planet-Source-Code has several excellent programs by the aforementioned Robert Rayment demonstrating how to use ASM (assembly language) within VB to create graphics routines slightly faster than traditional VB methods. Assembly language is the fastest programming language available to Windows programmers (next to machine language, but you don’t want to try writing that :) ), and with some clever work you can use it within your VB program. Be forewarned, however, that this is most definitely not an easy thing to do – but it may give you a little extra speed.
- Search the net for 3rd party SDKs or ActiveX controls dealing with graphics (DirectX, OpenGL, ActiveX controls, etc.). In almost every case, DirectX, OpenGL, or similar SDKs (software development kits) won’t help your per-pixel graphics code to be any faster. They may, however, provide you with hardware support for your effect. For example, DirectX has built-in gamma correction if the computer’s hardware supports it. This is a lot faster than trying to perform gamma correction in code. Also, you could buy a VB-compatible OCX or DLL written in another language that does your effect for you. This can be, however, an expensive option.
- Learn another programming language. The sad but true fact is that some other programming languages provide faster graphics programming techniques via pointers and other tricks. Even java contains many powerful image editing routines as part of the language, so if you’re really desperate than you can try learning (or rewriting your project in) another programming language. Also, if you rewrite your routine in C/C++ you could compile it into a DLL and use that from VB, should you want to.
- Go to the library and check out some graphics programming books. Some of my favorite programming techniques were learned from non-VB graphics programming books. Even if you don’t understand the code associated with a book, you can still learn a lot about different filters, optimization routines, etc. If your library doesn’t have anything good, search Amazon.com – you’ll be amazed at how many graphics programming books are out there.
- Use your imagination. Sometimes the best optimizations are written by programmers who aren’t planning on writing a good optimization. Your ingenuity is your best programming tool – try to think of new, clever ways to speed up your code. If you think up something great, be sure to let me know about it! :)
As a demonstration of many of these techniques, feel free to check out the Real-time Brightness example below:
DOWNLOAD THE REAL-TIME BRIGHTNESS PROGRAM
And that, my friends, this is the end of these 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. I would also consider myself thanked if you head to www.tannerhelland.com and comment on some of the original video game music I’ve written. If you write me with specific graphics questions not related to these tutorials 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!
Disclaimer and Legal Stuff
Copyright ©2005-2009 Tanner Helland. This article may not be reproduced in any form (printed or electronic) without prior written consent from the author. This document may, however, be hyperlinked on the world wide web without permission from the author.
All programming source code associated with these articles 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 this 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.
Related articles:



Great article.
I’m interested in computer vision where the same principles can be applied to obtain a cheap working platform.
Thank you very much for your work.
Miguel
I am absolutely loving these articles! I did discover a few more problems though when you converted your website:
“Therefore, avoid decimals if possible. For example, Red = Red * 1.5 tends to be slower than Red = (Red * 15) 10. ”
Should be:
Therefore, avoid decimals if possible. For example, Red = Red * 1.5 tends to be slower than Red = (Red * 15) \ 10.
“Watch for dividing code. Two points here: 1) always use instead of /. ”
Should Be:
Watch for dividing code. Two points here: 1) always use \ instead of /.
Ah, nice catch Mike. I’ve corrected both mistakes and will double-check for any other escape character problems.
Many thanks for pointing those out, and I’m glad the articles have been helpful to you.
i’ve read the articles and the tips&triks are great but i’ve found a little problem applying the code. I use GetImageData and SetImageData (from the brightness demo) to store and retrieve an image (into a picturebox) acquired from a webcam. If the sourcepicbox has the same dimensions of the destpicbox all works fine. but if the destpicbox is different, the images looks like ripped. someone can help me?