Graphics Programming in Visual Basic - Part 1

"Visual Basic Pixel Routines"

By: Tanner "DemonSpectre" Helland

Despite what many programmers will tell you, Visual Basic is an excellent programming language for high-end graphic applications - with or without DirectX and OpenGL. Many VB programmers seem to believe that the only way to get fast 2D graphics is with DirectDraw…and they are completely wrong! Using some creativity and efficient coding, you can get the API to do anything that DirectDraw does - and nearly as fast (and in some cases faster!). But this is not to say that only the DirectX-paranoid can benefit from this tutorial, because even the best DX experts could use a good explanation of how Windows handles graphics routines. This series of articles will explain not only how to do fast graphics but how fast graphics work. You will first learn about pure VB routines for graphics processing; next comes the basic API routines of GetPixel and SetPixel/V; third comes the more advanced GetBitmapBits, SetBitmapBits, and DIB sections; the last tutorial will cover additional optimizations for the real speed demons out there. By the end of this series of tutorials, you will be able to effectively write any graphics application you can dream up using nothing but Visual Basic and the Windows API.

So if you're ready, here's part one of how to become a professional graphics programmer using good ol' VB.

-THE PURPOSE OF THIS TUTORIAL-

This tutorial will discuss the basics of per-pixel graphics programming using only built-in Visual Basic routines. I recommend that even hardened VB veterans at least glance through this document, as it provides the foundation for the advanced graphics principles discussed in the more advanced tutorials. We will discuss the only VB per-pixel graphics routines (Point and Pset), and after this is done you'll know a lot more than you ever wanted to about VB graphics, heh heh heh. :)

-PURE VISUAL BASIC PER-PIXEL GRAPHICS ROUTINES-

Most of you have probably heard of the "horrible twins of Pset and Point" (Matt Hart). This is the easiest possible way to do VB GP, but - of course - it's also the slowest. Unless you have an extremely fast machine you will most definitely want to avoid ever using these routines again. I include them here only for completeness; I have never used them in an actual program simply because they are so extremely slow. Why are they so slow? I'll discuss that later, after we've looked at their syntax.

PART I - GETTING THE COLOR OF A PIXEL

You can use the Point event in VB to get the color of a specified pixel. The format is as follows:

________________________________________________________

Dim Color as Long

Color = PictureBox.Point(x,y)

________________________________________________________

PictureBox is the name of the picture box or form you want to retrieve the pixel from, and (x,y) are the pixels coordinates. So, if you wanted to get a color from spot 35, 42 of picture box "Picture1", you would use the following:

________________________________________________________

Color = Picture1.Point(35,42)

________________________________________________________

It doesn't get much simpler than that, folks.

So now the question becomes, "what do we do once we've gotten the color?" After all, it's of Long type, which is some strange number from -2 billion to +2 billion...and that really doesn't lend itself to adjusting the color of that pixel. So, we have to figure out how to change one 4-bit number into three 1-bit numbers (red, green, and blue).

While this may sound easy, the theory behind doing this requires some knowledge of binary encoding…which I find extremely boring and don't want to write about, lol :). So instead, I'll give you the next best thing: functions that will automatically do the extraction for you.

________________________________________________________

Public Function ExtractR(ByVal CurrentColor As Long) As Byte

ExtractR = CurrentColor And 255

End Function

________________________________________________________

Public Function ExtractG(ByVal CurrentColor As Long) As Byte

ExtractG = (CurrentColor \ 256) And 255

End Function

________________________________________________________

Public Function ExtractB(ByVal CurrentColor As Long) As Byte

ExtractB = (CurrentColor \ 65536) And 255

End Function

________________________________________________________

To utilize these functions, use the following syntax:

________________________________________________________

Dim R as Byte, G as Byte, B as Byte

Dim Color as Long

Color = PictureBox.Point(0, 0)

R = ExtractR(Color)

G = ExtractG(Color)

B = ExtractB(Color)

________________________________________________________

Pretty neat, eh? On a brief side note: while you may find these very handy, I would still highly recommend reading up on binary encoding since it provides pretty much the entire foundation of the digital age. If you don't understand how binary data works, you're probably not going to get very far in the programming world - and besides, it's really pretty easy stuff provided that you can find a good teaching source. Consult your local library for best results.

Anyway, there you have it: how to get a pixel's data and break it down into its red, green, and blue components. Now let's quickly mention how to set that data back into a picture box.

PART 2 - SETTING THE COLOR OF A PIXEL

Setting a pixel's color is almost identical to getting its color. You use the VB event "Pset," which stands for "Pixel Set."

________________________________________________________

PictureBox.PSet (x,y), Color

________________________________________________________

Again, PictureBox is the name of the picture box or form you want to retrieve the pixel from and (x,y) are the pixel's coordinates. The only difference here is that we also include the color that we want to set. So, using the example above, if you wanted to set a color to spot 35, 42 of picture box "Picture1" you would use the following:

________________________________________________________

Picture1.PSet (35,42), Color

________________________________________________________

It is worth noting that Color is of type Long, which creates the same problem we discussed above - how to change the red/green/blue values into a single number. Fortunately, VB has a built-in command called RGB() that does this conversion for us. To illustrate it's use, let's use the same example saying that you want to change the color of the pixel at (35,42) to pure red:

________________________________________________________

Picture1.PSet (35,42), RGB(255, 0, 0)

________________________________________________________

The first RGB parameter is red, then green, then blue, so RGB(255,0,0) will set the color of pure red. Easy, isn't it?

PART 3 - USING THESE COMMANDS TO EDIT AN IMAGE'S DATA

First, a little disclaimer: entire books have been written on the theories behind GP and there are entire sub-disciplines whose job is nothing but optimizing graphics routines. So while what I'm showing you is a nice method, be advised that GP is an extremely complicated field, and to truly succeed in it you must be willing to do a little research. I have chosen a well-optimized and very standard method because it is easy to understand while still offering good results. But, for this first tutorial, don't be disappointed if the results aren't particularly incredible or lightning-fast. That's what the next three tutorials are for! :)

DOWNLOAD THE PSET/POINT EXAMPLE PROGRAM

The included .zip file will demonstrate how to change the brightness of an image using a standard linear brightness algorithm. The code is simple, and it pretty much speaks for itself. Read through the comments and make sure that you understand how everything works.

PART 4 - WHY ARE PSET AND POINT SO SLOW?

If you've tried out the sample code, you're probably not impressed…and rightfully so. PSet and Point - though easy to use - are extremely, extremely slow. Can you imagine trying to work with images 4 or 5 times the size of the demo one? You'd be dead and buried by the time it finished!

So why is this? To illustrate it, let's follow the path your computer takes for changing the color of a single pixel using Point and PSet, as done in the sample program. (Author's Note: I base these conclusions on general programming knowledge, not known facts; so, while I'm pretty sure that I'm right, I could be wrong on some of the details)

    1. Upon encountering a Point command VB's first task is to do a whole crapload of error checking. This involves things like making sure that the pixel is on the image, making sure that the PictureBox exists, seeing if an image has been loaded or if you're working with a blank image, etc. This step is speed killer #1 - but the advantage is that Point will never crash your machine, thankfully.
    2. Once VB has decided that there is actually a pixel at point (x,y), it now has to figure out where to get the pixel color from. This changes depending on both the status of AutoRedraw and whether or not you've updated the image. VB will usually go to the 'Image' property, but in certain cases AutoRedraw may tell it to go to the 'Picture' property. This step is speed killer #2 - but again, you never crash your machine and you always get predictable results.
    3. After VB knows where the pixel data resides, it can now go and get the pixel information. This step is all handled in memory, so there aren't many speed concerns here.
    4. After VB gets the pixel's color it must transfer that information into the variable specified by the original Point command. Again, this is all done in the RAM so it moves pretty quickly.
    5. Once a variable of type Long contains the color of the pixel, we must parse that long into its red, green, and blue components. This step is speed killer #3, because we gotta do three 'Ands' and two 'Divides' for every single pixel. For an image like the sample one, that's 400x300 or 120,000 pixels = 360,000 'Ands' and 240,000 'Divides.' This step is a very, very bad one for speed - the 'Ands' are quite fast, but the 'Divides' are quite slow.
    6. Once we have a red, green, and blue component, we change these values to the new values specified by the look-up table. This step is very fast because it's nothing more than simple memory transfers.
    7. Next comes the PSet step. This process is almost identical to Point, so I'm going to abbreviate its steps. First, it does the error checking. Speed killer #4 here.
    8. VB will automatically assign the new color to its appropriate location within the 'Image' property. This is fast - again, it's all done in memory.
    9. Now VB has to decide whether or not to refresh the image. If AutoRedraw is set to false, VB will attempt to redraw the entire image after each pixel has been set. Do not do this - ever - unless you know what you're doing :). If AutoRedraw is set to true, VB will only redraw the entire image after you explicitly tell it to or after you finish the loop containing the PSet calls. Redrawing the image is extremely slow because your computer has to copy the information for thousands of pixels from the Picture or Image property to wherever the screen data is located (either VRAM or RAM - this is yet one more thing VB to figure it; it too takes time). Although your RAM is fast, it will be slowed down by huge memory chunk transfers (like graphics). This is speed killer #5

So there you have it: steps 1, 2, 5, 7, and 9 are what's slowing down your PSet/Point-based graphics program. Visual Basic is very nice in that it does almost all of your error checking for you, but there is a definite speed trade-off. In languages like C/C++, those error checking steps are removed - which is why C/C++ is generally faster but more dangerous to use. In the next three tutorials we will discuss alternate methods of doing graphics that cut out some of these "speed killer" steps.


So there is your basic graphics lesson for the day. You can now program any graphics routine using nothing but VB! Yay for you!

Actually, to be totally honest, I hope that you completely forget that Pset and Point even exist after reading the next three tutorials. Both are extremely slow and, well, just bad programming. Visual Basic is good for a lot of things but its pixel interfacing is a total joke. So, thankfully, there are three more tutorials that will show you better, faster ways to do graphics programming - but at least you now know how to use PSet and Point if the need ever arises.

 

CONTINUE TO TUTORIAL 2

 

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.