Programming:  Working with bitmaps

Now we're going to paint some bitmap here and there, just to keep the Pocket-Viewer busy. But first, we have to learn how to create a bitmap.

Here is an example:
This is a 12 rows and 12 columns monochrome bitmap, a clown. How do we code it? White is 0, red is 1. Let's create a binary representation. The pixels in each row are grouped 8 by 8 from left to right, we pack each octet getting rows of bytes. How many bytes make up a row? Here's a formula: bytes_per_row = (width + 7) / 8, [(12 + 7) / 8 = 2 bytes] To get 2 bytes out of 12 bits we pad the last byte with 4 zeros ;)
Here they are:

row  1: 00001111 0000(0000)
row  2: 00110000 1100(0000)
row  3: 01000000 0010(0000)
row  4: 01001001 0010(0000)
row  5: 10011001 1001(0000)
row  6: 10000000 0001(0000)
row  7: 10000110 0001(0000)
row  8: 10010000 1001(0000)
row  9: 01001111 0010(0000)
row 10: 01000110 0010(0000)
row 11: 00110000 1100(0000)
row 12: 00001111 0000(0000)

Now we collect our data into an array of bytes, using the hex representation:

byte bmpClown[4 + 2 * 12] =
{
	GSIZE(12, 12), /* = 0x0C, 0x00, 0x0C, 0x00  */
	0x0F, 0x00, 
	0x30, 0xC0,
	0x40, 0x20,
	0x49, 0x20,
	0x99, 0x90,
	0x80, 0x10,
	0x86, 0x10,
	0x90, 0x90,
	0x4F, 0x20,
	0x46, 0x20,
	0x30, 0xC0,
	0x0F, 0x00,
}

We are ready to draw the bitmap. Why don't we use LibPutGraph? Don't forget LibGetGraph, we need that too...


This sample shows a bitmap and removes it restoring the background it was over. Click once and the bitmap appears, click again and it's gone!!! (Over and over again)

Headers, just headers!

#include	<stdrom.h>
#include	"define.h"
#include	"libc.h"

Let's define the screen, bitmap position and size, we want it at the center.

#define OBJ_SCREEN 0x8000
#define SCREEN_W 160
#define SCREEN_H 160

#define BMP_X	44
#define BMP_Y	32
#define BMP_W	71
#define BMP_H	95

We wrap it all in formulas.

#define BMP_SIZE(w, h) ( ((w + 7) / 8) * h )
#define RND(a, b) ( ((float)rand() / RAND_MAX) * (b - a + 1) + a)

This is an empty bitmap, we need it to save the background our picture replaces.HOW? (Stay tuned). So we make room for as many bytes as the picture is made of.

byte bmpSave[4 + BMP_SIZE(BMP_W, BMP_H)];

Ladies and gentlemen this is Kitty!!! Neko's girlfriend :)
Can't you see her?

byte bmpPuppet[4 + BMP_SIZE(BMP_W, BMP_H)] =
{
	GSIZE(BMP_W, BMP_H),
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
	0x80, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x03,
	0x80, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x0E, 0x00, 0x03,
	0x80, 0x00, 0x00, 0x00, 0x03, 0x0C, 0x7F, 0x80, 0x03,
	0x80, 0x00, 0x00, 0x00, 0x06, 0x07, 0xC1, 0x80, 0x03,
	0x80, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0xC0, 0x03,
	0x80, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0xC0, 0x03,
	0x80, 0x00, 0x00, 0x00, 0x08, 0x01, 0x80, 0xC0, 0x03,
	0x80, 0x3F, 0xC0, 0x07, 0xF8, 0x39, 0x80, 0x40, 0x03,
	0x80, 0x30, 0xF8, 0x7F, 0x98, 0xC4, 0x80, 0x60, 0x03,
	0x80, 0x60, 0x1F, 0xE0, 0x18, 0x87, 0xF0, 0x60, 0x03,
	0x80, 0x60, 0x07, 0x80, 0x10, 0x8C, 0x18, 0x60, 0x03,
	0x80, 0x40, 0x00, 0x00, 0x10, 0x88, 0x0F, 0xF0, 0x03,
	0x80, 0xC0, 0x00, 0x00, 0x10, 0x58, 0x0E, 0x38, 0x03,
	0x80, 0xC0, 0x00, 0x00, 0x18, 0x30, 0x04, 0x0C, 0x03,
	0x80, 0xC0, 0x00, 0x00, 0x08, 0x10, 0x07, 0x0C, 0x03,
	0x80, 0xC0, 0x00, 0x00, 0x0C, 0x10, 0x0D, 0x84, 0x03,
	0x80, 0xC0, 0x00, 0x00, 0x07, 0xF8, 0x08, 0x84, 0x03,
	0x80, 0xC0, 0x00, 0x00, 0x01, 0xCC, 0x18, 0x8C, 0x03,
	0x80, 0xC0, 0x00, 0x00, 0x00, 0x0E, 0x39, 0x8C, 0x03,
	0x80, 0x58, 0x00, 0x00, 0x00, 0x03, 0xCF, 0x08, 0x03,
	0x80, 0x70, 0x00, 0x00, 0x00, 0x00, 0x40, 0x18, 0x03,
	0x80, 0x60, 0x00, 0x00, 0x00, 0x00, 0x60, 0x38, 0x03,
	0x80, 0x60, 0x00, 0x00, 0x00, 0x00, 0x20, 0x6C, 0x03,
	0x80, 0x60, 0x00, 0x00, 0x00, 0x00, 0x19, 0xCE, 0x03,
	0x80, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x86, 0x03,
	0x80, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03,
	0x80, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03,
	0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x83,
	0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x83,
	0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC3,
	0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0F, 0xFB,
	0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x83,
	0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x83,
	0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x83,
	0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x83,
	0x81, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00, 0x0F, 0x83,
	0x81, 0x80, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x01, 0xE3,
	0x81, 0x80, 0x03, 0xC0, 0x03, 0xC0, 0x00, 0x03, 0x73,
	0x81, 0x86, 0x01, 0x80, 0x06, 0x20, 0x00, 0x03, 0x13,
	0x80, 0xBE, 0x01, 0x80, 0x04, 0x20, 0x00, 0x03, 0x03,
	0x80, 0xF0, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x06, 0x03,
	0x83, 0xC0, 0x00, 0x00, 0x03, 0x80, 0x00, 0x3E, 0x03,
	0x86, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x83,
	0x80, 0x61, 0x80, 0x00, 0x00, 0x00, 0x00, 0x19, 0xC3,
	0x80, 0x37, 0x80, 0x00, 0x00, 0x00, 0x00, 0x30, 0xC3,
	0x80, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x63,
	0x80, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x63,
	0x80, 0x6E, 0x10, 0x00, 0x00, 0x00, 0x07, 0x80, 0x63,
	0x80, 0x47, 0x30, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x63,
	0x80, 0x03, 0xE0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x63,
	0x80, 0x00, 0xF0, 0x00, 0x00, 0x0F, 0xC6, 0x00, 0x43,
	0x80, 0x3F, 0xFF, 0x00, 0x01, 0xFF, 0x66, 0x00, 0xC3,
	0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x63, 0x01, 0xC3,
	0x83, 0x83, 0xE3, 0xFF, 0xFE, 0x03, 0x31, 0x81, 0x83,
	0x87, 0x02, 0x20, 0x61, 0xC0, 0x01, 0x10, 0xE3, 0x83,
	0x86, 0x00, 0x30, 0x20, 0xC0, 0x01, 0x18, 0x7E, 0x03,
	0x8C, 0x00, 0x10, 0x30, 0x60, 0x01, 0x0C, 0x1C, 0x03,
	0x8C, 0x00, 0x10, 0x30, 0x30, 0x03, 0x0C, 0x70, 0x03,
	0x8C, 0x00, 0x18, 0x20, 0x1E, 0x06, 0x07, 0xC0, 0x03,
	0x8C, 0x00, 0x18, 0x60, 0x07, 0xFE, 0x07, 0x00, 0x03,
	0x8C, 0x00, 0x19, 0xC0, 0x00, 0xE0, 0x72, 0x00, 0x03,
	0x84, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x03,
	0x87, 0x00, 0xFC, 0x1F, 0x03, 0xC0, 0x8B, 0x00, 0x03,
	0x83, 0x8F, 0xF0, 0x7F, 0xC2, 0x40, 0x8B, 0x00, 0x03,
	0x81, 0xFE, 0x61, 0xC0, 0x64, 0x20, 0xD9, 0x00, 0x03,
	0x80, 0x38, 0xC3, 0x00, 0x36, 0x60, 0x31, 0x00, 0x03,
	0x80, 0x38, 0xC6, 0x00, 0x1B, 0xC0, 0x01, 0x00, 0x03,
	0x80, 0x18, 0x8C, 0x00, 0x0C, 0x00, 0x01, 0x00, 0x03,
	0x80, 0x0D, 0x88, 0x00, 0x0C, 0x00, 0x01, 0x00, 0x03,
	0x80, 0x07, 0x98, 0x00, 0x0C, 0x00, 0x01, 0x00, 0x03,
	0x80, 0x01, 0x90, 0x00, 0x04, 0x00, 0x03, 0x00, 0x03,
	0x80, 0x01, 0xB0, 0x00, 0x04, 0x00, 0x03, 0x00, 0x03,
	0x80, 0x01, 0xB0, 0x00, 0x04, 0x00, 0x03, 0x00, 0x03,
	0x80, 0x00, 0xB0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x03,
	0x80, 0x00, 0xE0, 0x00, 0x04, 0x00, 0x06, 0x00, 0x03,
	0x80, 0x00, 0xE0, 0x00, 0x04, 0x00, 0x06, 0x00, 0x03,
	0x80, 0x00, 0x60, 0x00, 0x0C, 0x00, 0x0E, 0x00, 0x03,
	0x80, 0x00, 0x20, 0x00, 0x0C, 0x00, 0x1C, 0x00, 0x03,
	0x80, 0x00, 0x30, 0x00, 0x0C, 0x00, 0xFC, 0x00, 0x03,
	0x80, 0x00, 0x30, 0x00, 0x18, 0x0F, 0xD8, 0x00, 0x03,
	0x80, 0x00, 0x30, 0x00, 0x1F, 0xFE, 0x18, 0x00, 0x03,
	0x80, 0x00, 0x18, 0x00, 0x3F, 0x00, 0x30, 0x00, 0x03,
	0x80, 0x00, 0x18, 0x00, 0x70, 0x00, 0x60, 0x00, 0x03,
	0x80, 0x00, 0x0E, 0x00, 0xC0, 0x00, 0x60, 0x00, 0x03,
	0x80, 0x00, 0x07, 0x87, 0x80, 0x00, 0xC0, 0x00, 0x03,
	0x80, 0x00, 0x01, 0xFF, 0x00, 0x01, 0x80, 0x00, 0x03,
	0x80, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x80, 0x00, 0x03,
	0x80, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x03,
	0x80, 0x00, 0x00, 0x03, 0xC0, 0x0E, 0x00, 0x00, 0x03,
	0x80, 0x00, 0x00, 0x00, 0xFC, 0xF8, 0x00, 0x00, 0x03,
	0x80, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x03,
	0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};

We only need to know when the pen touches the screen

TCHTBL TchList[2] = 
{
	/* Screen      */ 
		0, 0, 159, 159,        
		ACT_MAKE,
		OBJ_SCREEN,
		0x0000,

	/* End */
		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

Let's fill the screen with a random background, this is computer art!!!

void FillScreen()
{
	int x, y, c;

	LibClrDisp();

	for (y = 0; y < 159; y += 10)
		for (x = 0; x < 159; x += 10)
		{
			c = RND('0', '9');
			LibPutProFont(IB_PFONT3, c, x, y);
		}

	LibPutDisp();
}

Time to remember what background we're going to replace, so that we can put it back when we want to hide the picture. LibGetGraph does what we want, we specify a rectangular area (BMP_X, BMP_Y, BMP_W, BMP_H in this case) and it takes a snapshot and saves it into our empty bitmap. Why do we skip 4 bytes? Remember, every bitmap needs 4 bytes to store width and height. No, LibGetGraph doesn't do that for us (hey don't throw yourselves out of the window, it's easy anyway). We do that before we call the function.

void DoInitKitty()
{
	*(word *) (bmpSave + 0) = BMP_W;
	*(word *) (bmpSave + 2) = BMP_H;
	LibGetGraph(BMP_X, BMP_Y, BMP_W, BMP_H, bmpSave + 4);
}

How do we hide Kitty? We just place back the saved portion of the background. LibPutGraph takes three parameters the position of the bitmap (top-left corner) and the bitmap. It knows width and height reading the first 2 words in the bitmap. It doesn't refersh the display, it just puts the bitmap in the screen buffer. We always flushed using LibPutDisp, now we had enough. LibPutDisp redraws the whole screen, but we've just updated a part of it. We want to redraw only what is changed. We call LibPutDispBox, it redraws the rectangular area whose coordinates and size we pass the function. It's FASTER if we have small regions.

void DoHideKitty()
{
	LibPutGraph(BMP_X, BMP_Y, bmpSave);
	LibPutDispBox(BMP_X, BMP_Y, BMP_W+1, BMP_H+1);
}

Here we show Kitty, isn't she nice?

void DoShowKitty()
{
	LibPutGraph(BMP_X, BMP_Y, bmpPuppet);
	LibPutDispBox(BMP_X, BMP_Y, BMP_W+1, BMP_H+1);
}

Same old stuff, then we 'FillScreen' and save (DoInitKitty) what we're going to cover. On odd touches we show Kitty, on even ones we hide her (she's a bit shy sometimes)

void main()
{
	TCHSTS tsts;
	bool bExit = FALSE;
	int toggle, h, m, s;

	LibGetTime(&h, &m, &s);
	srand(h*3600 + m*60 + s);

	FillScreen();
	DoInitKitty();

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]);

	LibTchInit();

	toggle = 1;

	for (;bExit == FALSE;)
	{
		LibTchWait(&tsts);           

		if (tsts.obj == OBJ_SCREEN)
		{
			if (toggle)
				DoShowKitty();
			else
				DoHideKitty();

			toggle = 1 - toggle;
		}

		if (tsts.obj == OBJ_HIC_ESC)        
			bExit = TRUE;
	}

	LibTchStackClr();
	LibJumpMenu();
}