10 August 2006

Doing Low-Res Graphics in Windows

Chances are you are reading this post on a screen that's running in excess of 1280 x 1024 resolution, and you are wondering "why would I want low resolution graphics?"

What do I mean by 'low resolution' graphics? Generally, anything less than the full screen resolution, where one lo-res 'pixel' maps to an N x N patch of real screen pixels. I'm not talking about throwing the graphics adapter into a low-resolution mode, but simply using some (or all) of the windows screen to display low resolution images, leaving the graphics adapter settings alone. For example, suppose you want a display that's 160 x 160 pixels. You might make each lo-res pixel map to a 4 x 4 chunk of your 1280 x 1024 display. Then you create your low-res images as though you had a 160 x 160 screen, but that 'screen' is just a 640 x 640 patch of your (unchanged!) 1280 x 1024 display. In some cases you might even want to take over the whole display, so called 'full screen' mode, for the production of low res images.

Why do I want low res graphics? Well, sometimes you might have a program whose output is inherently low resolution. For example, maybe you have a simple neural net arranged as a 16 x 16 grid of nodes, and you want to see the overall network activity as a color map. If you use single pixels on your 1280 x 1024, your network color map will be a tiny patch about half the size of the standard desktop icon! Or, suppose you want to emulate an old computer that had say, 128 x 48 resolution - again, you need to map the low res pixels to something larger so you can simply see it.

HOW can you get efficient low res graphics? Suppose you want to map 80 x 64 images onto a 640 x 512 chunk of real estate. Each of your low-res pixels will be an 8 x 8 chunk of real pixels. You could try writing your own routine to do 64 separate calls to SetPixel, but this will run like absolute molasses. Slightly better, you can create an off-screen device context with the 80 x 64 bitmap on it, and call StretchBlt to do the heavy lifting, but this is still pretty slow.

There are any number of better ways. Probably one of the better ones is to use Open GL, map a quad to your display area, and put a low-res texture map onto this quad; then operate on the texture map's 80 x 64 pixels, and let the Open GL renderer handle creating the 8 x 8 patches. Unfortunately, using Open GL like this incurs a bunch of calls to get GL set up, and a bunch more GL calls per low-res pixel, and though it's pretty fast, it tends to be finicky code.

But here's the cool and surprising thing I discovered today. Just make sure opengl32.lib gets linked into your Win API code. Somehow, when the .exe includes opengl32.lib (and maybe glu32.lib), StretchBlt goes into warp speed. You may need to put a dummy call to a GL function in your code to cause the linker to import the gl libs, but your code doesn't need to call any GL functions at runtime, to realize GL-like performance. Apparently, when Windows loads your .exe, and imports GL, the GL lib somehow overrides the implementation of StretchBlt. The up-side to this is that you can just use single calls to lowly SetPixel (against your off screen, low res bitmap) and then when you're ready to update the on-screen view, call StretchBlt. I have seen each pixel of a 40 x 32 offscreen map touched with SetPixel, then blitted to a full 1280 x 1024 display at 100Hz using no more than about 20% CPU utilization.

I realize this is a bit hack, but in terms of performance vs simplicity of code, it's worth looking at.

0 Comments:

Post a Comment

<< Home