Calling ClearImage() Twice

The ClearImage() function operates like the GsSort routines in that, when in interlaced mode, it only paints half the lines (odd or even depending on what frame you're in). So to clear a section of the frame buffer you need to call ClearImage() in two successive frames.

Strangely LoadImage(), StoreImage(), and MoveImage() don't seem to operate the same way. They all affect both odd and even lines at the same time.

Clearing 4- and 8-bit Images using ClearImage()

ClearImage() takes a rectangle and R, G, and B components and clears a section of the frame buffer to a constant color. While it seems this can only be used to clear 16-bit images, you can actually use it for 4-bit or 8-bit TIMs as well.

Here's the situation. When you're using a 4-bit or 8-bit TIM, the values don't represent RGB levels. They are actually indices into the CLUT. So the trick is to pass ClearImage() the correct RGB values so that the index we want gets written.

NOTE: For purposes of discussion I will only talk about 4-bit images. The same concepts can be applied to 8-bit TIMs too.

Let's say we want to clear an 8x8 section to CLUT entry #3. So after we're done clearing we want the frame buffer memory to look like:

  0011 0011 0011 0011 0011 0011 0011 0011 
  0011 0011 0011 0011 0011 0011 0011 0011
  0011 0011 0011 0011 0011 0011 0011 0011
  0011 0011 0011 0011 0011 0011 0011 0011
  0011 0011 0011 0011 0011 0011 0011 0011
  0011 0011 0011 0011 0011 0011 0011 0011
  0011 0011 0011 0011 0011 0011 0011 0011
  0011 0011 0011 0011 0011 0011 0011 0011

Well, we can treat each group of four 4-bit values as one 16-bit RGB value:

  0011001100110011 0011001100110011 
  0011001100110011 0011001100110011
  0011001100110011 0011001100110011
  0011001100110011 0011001100110011
  0011001100110011 0011001100110011
  0011001100110011 0011001100110011
  0011001100110011 0011001100110011
  0011001100110011 0011001100110011

Break down these 16-bit values into RGB components. Then pass these components to ClearImage() and we'll end up "tricking" the function into doing what we want. Since our 16-bit value is 0011001100110011, that would be R=10011, G=11001, and B=01100.

0011001100110011 = 0 01100 11001 10011
                   | |   | |   | |   |
                   | +-+-+ +-+-+ +-+-+
                   |   |     |     |
                   T   B     G     R

So call ClearImage() as follows:


  u_char r, g, b;

  r = 0x13;
  g = 0x19;
  b = 0x0C;
  ClearImage(&rect, r << 3, g << 3, b << 3);  

Note ClearImage() is expecting 8-bit values for R, G, and B so we must left shift each by 3 bits. ClearImage() will just end up right shifting them again to translate back to 5-bit numbers, but oh well.

We can make this even more generic by decoding our original number in the function:


  void main() {
      .
      .
      .
    clear_4bit_rect(&rect, 0x3333);
      .
      .
      .
  }

  void clear_4bit_rect(RECT *rect, u_short value) {
    u_char r, g, b;

    r =  value & 0x001F;                // Bits 0-4
    g = (value & 0x03E0) >> 5;          // Bits 5-9
    b = (value & 0x7C00) >> 10;         // Bits 10-14  
    ClearImage(rect, r << 3, g << 3, b << 3);
  }

Of course this can be optimized by combining the shifts, but it's clearer this way.

Caveat

There is a restriction to this (isn't there always?). Since the RGB value is actually only 15 bits long, ClearImage() will never set the 16th bit (the transparency bit). This means that the top bit of your input value ("3" in our case) cannot be set or else it will be lost (a value of "A" would become "2"). So you actually only have 3 bits to play with. This isn't a problem if you order your CLUT correctly such that the color to which you are clearing occupies a CLUT entry from 0 to 7.

Further ideas

You don't have to clear to a constant value. What if we alternated colors? Something like:

    clear_4bit_rect(&rect, 0x1515);

All even columns would have CLUT entry 1 while the odd columns would get CLUT entry 5:

  1 5 1 5 1 5 1 5 
  1 5 1 5 1 5 1 5
  1 5 1 5 1 5 1 5
  1 5 1 5 1 5 1 5
  1 5 1 5 1 5 1 5
  1 5 1 5 1 5 1 5
  1 5 1 5 1 5 1 5
  1 5 1 5 1 5 1 5 

What if you want a checkerboard pattern instead of the alternating columns? Since ClearImage() only paints half the pixels every frame (see topic above this one), you can shift the input value during the second frame.


  // "frame_cnt" gets incremented every frame

  if (frame_cnt & 0x01) clear_4bit_rect(&rect, 0x1515);  
  else                  clear_4bit_rect(&rect, 0x5151);

In this case you would end up with:

  1 5 1 5 1 5 1 5 
  5 1 5 1 5 1 5 1
  1 5 1 5 1 5 1 5
  5 1 5 1 5 1 5 1
  1 5 1 5 1 5 1 5
  5 1 5 1 5 1 5 1
  1 5 1 5 1 5 1 5
  5 1 5 1 5 1 5 1

With 4-bit TIMs you can even pass 4 different colors if you want to:

  clear_4bit_rect(&rect, 0x1234);
Example

Here is a zip containing an example of all of this. When you run it you'll see four squares. The top is solid white that was "cleared" only one time. Thus it flickers. The second was cleared to white twice so it's rock solid. The bottom two show how you can specify a pattern when clearing. The first draws vertical lines of white separated by three lines of black (WBBBWBBBWBBB). The second is a checkerboard pattern; it's similar to the previous one, but alternate lines are shifted. If you look at it long enough you can tell the difference between the last two by noticing how the bottom one flickers slightly.



Top

This web page and all other pages on this site are © 1997 Scott Cartier