Compression and space

Back when I was in my making games mode years ago, I was interested in space. Specifically data storage.

Blitting images to the screen as quickly as possible to keep the frame rate consistent was a thing. Texture images are pre-created. Then there’s the concept of generating textures as the program was executed. This was how demos pack data into a small space.

I was studying fractals then, the iterated function system was a thing. I found it a pain to implement though…

And then I was interested in having moving images in my game engine. Packing movies was way harder than images.

Due to the copyrights of all the video formats, in my infinite wisdom, I decided to create my own. I mean, it’s a series of images, right?

The resulting customised movie file was just slightly larger in size than the QuickTime .mov format. I took that as an encouragement. I’ll tell you what I did next time.

Remember the stride

I was reading this article by Raymond Chen where he mentioned this:

Most of the time, your code won’t care (there are just pixels out there that you aren’t using), but if you use the Get­Buffered­Paint­Bits function to obtain direct access to the bits, don’t forget to take the stride into account.

The word “stride” evoked a ton of memories where I used to track (laboriously) the dimensions of a bitmap. This was back in the days when I was fiddling with writing my own computer games and computer graphics. Specifically, I was working with the screen graphics buffer.

Double buffers, 8-bit colours and screen resolutions

What happens (happened?) is that for fast visuals, we need a decent frame rate, and 30 frames per second is fast enough to fool human eyes that we’re looking at smooth uninterrupted motion. Back in those days, computer monitors weren’t that advanced, so the double buffer trick was used. (Is it still used? I don’t know…).

The double buffer trick refers to having 2 sets of screen buffers. The first one is used to “blit” the contents onto the screen. I’m using blit as a verb too, but this will take too long to explain if I told the story of blitting too… While this blitting is being done, the pixels of the next frame is drawn on the second buffer. When the drawing of pixels is completed, this second buffer is used to blit to the screen.

When you force the program to use double buffering, the next frame is held until the buffer has the drawing completed. This is what “lag” means. The frame rate is sort of ignored, because only when the next frame is completely drawn, will the buffer contents be displayed. Usually this isn’t a problem because the pixel drawing for one frame is within 0.03333 seconds (based on 30 FPS). As you go for higher resolutions and more complex objects being drawn and more complex calculations being done (such as calculating hit points and bullet trajectories), this next-frame-drawing gets slowed down.

In code, what you have are 2 bitmaps in memory and 1 pointer (yay, pointers!). You actually point the pointer to either bitmap based on which bitmap’s contents are to be blit.

  • First bitmap on screen, second bitmap drawing next frame, point to first bitmap.
  • Second bitmap done drawing.
  • Point to second bitmap.
  • Draw next frame on first bitmap.
  • Continue till application (most probably a game) is done.

“So what’s the stride got to do with this? And what’s a stride?”

Well, the thing is, when you request a bitmap from within your code, you might not get the exact dimensions you want. What you get is, as Raymond mentioned:

it only promises that the bitmap will be at least the size you requested.

So the bitmap given to you can be larger in size. And the larger size is based on the stride. I’ve not done a whole lot of research, and the following explanation is based on what I remember from those game-developing days. Let’s say the stride is 4 bytes. This means the memory size of the bitmap given to you will be in multiples of 4. … Uh, yeah, I think that’s about it.

If you ask for a bitmap with dimensions such that the projected final size memory is not in multiples of 4, you will be given a bitmap such that it is.

This “problem” is compounded by the fact that you also have to take care of the red, green and blue bytes (3 contiguous bytes for 1 pixel). Sometimes, there’s an alpha component. There’s also the 8-bit colour, where the first 2 bits are for red, next 3 bits are for green, and the final 2 bits are for blue. Not 1 byte for each colour component. (Just FYI, green has more bits because we can differentiate more shades of green than red or blue).

This is why you might find that in graphics programming, you are advised to have dimensions in multiples of 2. For example, your texture images used for mapping onto 2D planes and 3D objects should preferably have dimensions in powers of 2.

Wait, why powers of 2, and not just multiples of 2? I believe it has something to do with the texture mapping functions, because those functions don’t work well when the bitmap/image/texture argument doesn’t have dimensions with powers of 2. This is why I prefer to use square images at 128, 256 or 512 pixels. Mipmaps were used to alleviate this, but that’s another topic…

And the final complication? “What? There’s more?!?” Yes.

The bitmap you requested in code, the one where you might have to take note of the stride? That bitmap might have a different dimension than the screen dimension of the computer monitor. Computer monitors weren’t quite “standard” back then (I’m starting to feel old…). The computer monitor also has its own stride (I’m basing this on the memories of my research. Don’t just take my word for it). This means blitting pixels from a bitmap buffer to the screen isn’t quite so straightforward.

For example, if you’re working on a screen resolution of 800 by 600 pixels (yes, I know that’s like ancient. Work with me here…), and then you ask for a bitmap to represent that. Well, you might get a bitmap with dimensions 1024 by 768 pixels. Maybe it’s because it’s more memory efficient that way. 1024 = 2^10, and 768 = 2^9 + 2^8. Wait, now we have sums of powers of 2?!? I don’t know a whole lot about these things… I’m just trying to make a computer game…

So based on the above example, you have an “extra” width of 224 pixels (1024 – 800) and “extra” height of 168 pixels (768 – 600). So even if the stride is taken note of, the computer might just decide to throw you more memory space. Just for the heck of it.

In summary…

The bitmap you request in code might have a different dimension than what you wanted. The computer monitor might have a different dimension to work with. You have to remember each pixel has a red, green, blue and even alpha component (each of which uses a byte of storage. Or not, in the case of 8-bit colours). Then you have to take note of the dimensions of the textures you’re using to map onto 2D/3D objects.

And none of that has very much to do with the “fun” part of game programming. You’re just trying to work with one image.

I hope you learnt something. Because I sure as heck don’t know what’s the point I was trying to make, other than bombard you with seemingly random pieces of information…

Texture maps and the soul of programmers

Texture mapping is a technique which adds detail to 3 dimensional models. You’ll see it in graphically rendered movies like Toy Story or Shrek, and in games such as Doom and Final Fantasy XII.

Suppose we have two 3D models, a sphere and a fractal-generated terrain.
3D models of sphere and terrain
When we map a texture (which is just an image) such as
Sunset onto the 3D models, we get
3D models with sunset texture

If we change the texture to
Quiet campus and map that onto the 3D models, we get
3D models with quiet campus texture

Fundamentally, the 3D model is the same. The output changes because the texture is changed. It’s the same with programming. Two programmers will produce two different sets of program code, yet produce the same required logic.

The unique coding fingerprint of the programmer becomes the texture that maps onto the required logic (a 3D model). It’s the way the programmer thinks up a solution. It’s the way the programmer writes and organises code. It’s the personal touch that programmer has imparted onto the resulting program. And a skilled programmer can even alter the logic, such as adding checks not specified in requirements, to create a more useful resulting program.

When you’ve done enough programming of your own, reading other programmers’ code gives you insight into their minds. Understand why a piece of code is written the way it is written. There are many web sites providing programming help with sample code, most of them with further explanation of the code. So read through the entire solution, explanation, code and all.

Yes, you really really just need the code to solve your problem. When you’ve solved your problem, read through the code and try viewing the problem as the original programmer would have. You’ll learn even more because you’ll have gained some fraction of wisdom, and be better at creating solutions. You’ll have developed your unique texture, and the distinctive soul of a programmer.