Graphics Programming in Visual Basic - Part 2

"Basic API 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 have already learned about pure VB routines for graphics processing; this tutorial covers the basic API routines of GetPixel and SetPixel/V; third comes the more advanced GetBitmapBits, SetBitmapBits, and DIB sections; the last tutorial will cover general optimizations if your programs still aren't fast enough. 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 two 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 the simple API routines of GetPixel and SetPixel/SetPixelV. I recommend reading the previous tutorial, "Visual Basic Pixel Routines," as it provides the foundation for the advanced graphics principles discussed in this and the next two tutorials.

-SIMPLE API GRAPHICS ROUTINES-

Now that you understand how to use Visual Basic to get per-pixel data, it is time to extend that information a little bit further to include the Windows API. For those who don't know, the Windows API is a collection of dynamically linked libraries (so-called 'DLL files') that contain commonly used programming routines. These "interfaces" (API = Application Programming Interface) range from I/O to networking to multimedia, and they generally work much faster than the intrinsic Visual Basic routines. The API that we are going to be looking at is called "GDI32," which stands for Graphic Device Interface: 32-bit version. (This dll is included with every Windows OS since Win95, so no matter what OS you are using this method will work.)

The GDI32 is a collection of - surprise, surprise - routines commonly used in graphic and image manipulation. This includes brushes for drawing, bit-block transfers (BitBlt) for painting image sections, and what we're most interested in: ways to get and set pixel data. We will be looking at three functions in particular: GetPixel, SetPixel, and SetPixelV.

PART I - DECLARING OUR API FUNCTIONS

Because the GDI32 functions aren't an integral part of the Visual Basic programming language, we have to declare them in the General Declarations section just as we would a variable or a type. The syntax each of our API functions is as follows:

________________________________________________________

Private Declare Function GetPixel Lib "GDI32" (ByVal hDC As Long, ByVal x As Long, ByVal y As Long) As Long

Private Declare Function SetPixel Lib "GDI32" (ByVal hDC As Long, ByVal x As Long, ByVal y As Long, ByVal crColor As Long) As Long

Private Declare Function SetPixelV Lib "GDI32" (ByVal hDC As Long, ByVal x As Long, ByVal y As Long, ByVal crColor As Long) As Byte

________________________________________________________

The format might look a little imposing at first, but it's really easy.

The values inside of the parentheses are nothing more than variable declarations, just as you would see for a typical sub or function.

Looks pretty familiar, eh? You should be able to recognize some major similarities between these calls and Point/PSet. If you don't, you may want to reread tutorial number 1. :)

PART 2 - USING OUR NEW FRIEND, GETPIXEL

Now that we have told Visual Basic everything it needs to know about our "GDI32" functions, we can use them anywhere and everywhere we want to!

Let's demonstrate. If you wanted to get a color from pixel (35, 42) of picture box "Picture1", you would use the following:

________________________________________________________

Dim Color as Long

Color = GetPixel(Picture1.hDC, 35, 42)

________________________________________________________

Pretty darn easy, isn't it?

Now that you've gotten your color into a variable of type Long, you have to extract the individual red, green, and blue components just like before. I'm not going to repeat all that stuff, so just cut and paste those functions out of tutorial 1 if you need them.

PART 3 - USING SETPIXEL AND SETPIXELV

Again, setting a pixel's color is almost identical to getting its color:

________________________________________________________

Dim APIReturnValue as Long

APIReturnValue = SetPixel(PictureBox.hDC, x, y, Color)

OR

SetPixel PictureBox.hDC, x, y, Color

OR

Dim APIReturnValue as Byte

APIReturnValue = SetPixelV(PictureBox.hDC, x, y, Color)

OR

SetPixelV PictureBox.hDC, x, y, Color

________________________________________________________

Ahhh! Four different ways to do exactly the same thing - seems a little weird, doesn't it? Let me explain why this is:

Every function returns a value of some type. If you'll scroll back up and look at the SetPixel and SetPixelV declarations, you'll notice a slight difference between the two: SetPixel is of type Long and SetPixelV is of type Byte. This is because SetPixel returns the color that it was able to set, while SetPixelV only returns whether or not the pixel was set (1 or 0). Because SetPixelV only has to return one byte instead of four, it is slightly faster. However, SetPixel could be useful in color modes other than 24-bit, because you could determine the difference between the color you wanted to set and the color that actually got set. Hopefully that makes some sense... :)

So that's the difference between the actual SetPixel and SetPixelV calls. The reason for the other two declarations is whether or not we care what value SetPixel/SetPixelV returns. If we don't care, it is easier to just use the second form of the call - the one without the extra variable declaration. If, however, we want to know what value they return, we need to use the first form.

Now you know four different ways to set pixels using the API! Use that to impress your friends, if you dare… :)

Let's quickly demonstrate exactly how to use SetPixel/SetPixelV. Using our example from tutorial 1, let's say that you want to set pixel (35,42) of Picture1 to the value of variable 'Color:'

________________________________________________________

SetPixelV Picture1.hDC, 35, 42, Color

________________________________________________________

Again we note that Color is of type Long - you could use the RGB() function just as you did in tutorial one. In that case, you could write: ________________________________________________________

SetPixelV Picture1.hDC, 35, 42, RGB(255, 0, 0)

________________________________________________________

To set pixel (35, 42) of Picture1 to pure red. See how easy the API calls are to use?

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

DOWNLOAD THE GETPIXEL/SETPIXEL EXAMPLE PROGRAM

The included .zip file contains a program identical to the first tutorial's, except that this one utilizes the API for its pixel interaction. Compare the results of this program to the PSet/Point one - notice a pretty significant difference? As a good exercise, build a function to time how long each method takes and compare the results. The API calls can be anywhere from 4x to 20x faster than PSet and Point - a pretty noticeable difference for hardly any extra work.

PART 5 - THE NEED FOR SPEED: GETPIXEL/SETPIXEL VS. PSET/POINT AND BEYOND…

I hope that you are beginning to see the advantages to well-used API calls within your Visual Basic programs: if you can master the Windows API your VB capabilities are almost endless. GetPixel/SetPixel are just the tip of the iceberg, too - the next tutorial will show you a method 10-50x faster than this one! Pretty impressive, isn't it?

So before we head into the next tutorial, let's think of ways that GetPixel and SetPixel/SetPixelV could be improved. For one, we still have to manually extract the red, green, and blue values out of a Long-type variable - this is not only annoying, but it's slow as well. Things would go faster if we could somehow get Windows to separate the Long variable for us.

Also, there is this problem of having to use GetPixel/SetPixel for every single pixel. Any way you slice it, running two functions for each of our 120,000 pixels is a pretty slow proposition. What if there was a way to get the data for every single pixel at once - that way, we'd only have to use a single API call to get all of our pixel data. That would be incredibly fast!

Well believe-it-or-not, our next tutorial will explain how to do just that - get Windows to give us all of an image's pixel data at once, nicely parsed into its red, green, and blue components. These are commonly referred to as DIB sections, the most powerful API graphics tool you can use from VB.


So what are you waiting for? Go read the next tutorial! :)

 

CONTINUE TO TUTORIAL 3

 

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.