Our Bitmap LibraryWe are now ready to write our bitmap library. We will start like the Direct Draw library by determining what we need. As far as I can tell right now, we should be good with two simple routines: a bitmap loader, and a draw routine. Since we will be using surfaces, the draw routine should draw onto the passed surface. Our loader will load our special file format which I will cover in a moment. That should be it, there isn't that much that is needed for bitmaps nowadays. DirectX is how most manipulation occurs, especially since many things can be done in hardware. With that in mind we will cover our unique file format. Normally, creating your own file format is a headache and isn't worth the trouble. However, in our case it greatly simplifies the code and I have provided the conversion utility with the download package. This format is probably one of the easiest you will ever encounter. It has five main parts: Width, Height, BPP, Size of Buffer, and Buffer. The first three give information on the image. I have our library setup for 16 bpp only but implementing other bit depths would be fairly easy. The fourth section tells us how large of a buffer we need for the image, and the fifth section is that buffer. Having our own format not only makes the code we need to write a lot easier, it also prevents other people from seeing our work before they were meant to see it! Now, how do we load this bad boy? The code starts out by creating the file, which, in Windows, is how you open it, and then retrieves the file size. This allows us to allocate enough memory to load our entire file in. The process of reading in the file is fairly simple we just make a call. As usual the most important parts are those that check for errors. Once the file is in memory we compute the size of the desired image based upon the width and height in our header, and the "desired_bpp" level that was passed in to the function. Then we allocate yet another buffer with the information we calculated. This is the buffer that is kept in the end. The next step is the heart of our load function. Here we read in 3 bytes, since our pictures are stored as 24-bit images, and create the proper color value ( 5-6-5 or 5-5-5 ) for the buffer. We then store that value in the new buffer that we just created. We loop through all pixels in our bitmap and convert each to the desired format. The conversion is based on a pre-defined macro. You could also implement the function by using the members we filled, when we called the function to get the pixel format. This second way would allow you to have a more abstract interface to the code ... but for our purposes it was better to see what was really happening to the bits. At the completion of our loop we free the main buffer and return the address of the buffer with our converted pixel values. If an error occurs at any point, we jump to our error code which frees the possible buffer we could have created. This is to prevent memory leaks. And ... that is it for the load function. Once the bitmap is loaded into memory we need to be able to draw it onto a Direct Draw surface. Whether we are loading it in there permanently, or just drawing a quick picture onto the back buffer should not matter. So, we will look at a function that draws the passed bitmap onto our passed surface. Here is the code: This function is a little bit more advanced than some of the others we have seen, so pay attention. We know, as assembly programmers, that if we can get everything into a register things will be faster than if we had to access memory. So, in that spirit, we place the starting source and destination addresses into registers. Then, we compute the number of WORDS in our line. We can then divide this number by 2, so that we have the number of DWORDS in a line. I have hard-coded this number in since we will always be in 640 x 480 x 16 for our game. Once we have this number we place it in the register ECX. The reason for this is our next instruction MOVSD can be combined with the REP label. This will move a DWORD, decrement ECX by 1, compare ECX to ZERO if not equal then MOVE A DWORD, etc. until ECX is equal to zero. In short it is like having a For loop with the counter in ECX. As we have the code right now, it is moving a DWORD from the source into the destination until we have exhausted the number of DWORDS in our line. At which point it does this over again until we have reached the number of lines in our height ( 480 in our case ). Those are our only two functions in the bitmap module. They are short and sweet. More importantly, now that we have our bitmap and Direct Draw routines coded we can write the code to display our loading game screen! |
|