This piece of knowledge is especially useful for understanding and hacking the
Wise Clock 3 software, but can be also applied to any software that deals with pixel manipulation.
To define a small bitmap, start from a grid/matrix on paper (see photo below). Each square of the matrix will represent a pixel. Blacken the squares until the matrix resembles your bitmap. Next step is to translate the content of the matrix to numbers.
Let's take a closer look at the example below, a bitmap of 5x5 pixels.
This translates into an array of 5 numbers, one number per line. Each number is calculated by adding the values (at the top) of the selected columns.
So, the number for the first line is 14 (2 + 4 + 8), second number is 21 (1 + 4 + 16) and so on.
For the binary oriented fellows :), the hex values are between brackets.
Let's now jump to the actual code. The bitmap definition will look like this:
byte PROGMEM smallBitmap[3][5] = {
{
0x0E, // _XXX_
0x15, // X_X_X
0x1F, // XXXXX
0x1F, // XXXXX
0x15, // X_X_X
},
...
}
The above piece of code declares a two-dimensional ("[3][5]") array of bytes, in program space ("PROGMEM"). The first dimension ("3") represents the number of defined bitmaps. The second dimension ("5") represents the number of lines in each bitmap. A line in the bitmap definition is referenced using values for both dimensions. For example smallBitmap[0][4] is 0x15 and represents the last (index 4) line of the first (index 0) bitmap. [
Note that array indexes start with 0, that is, first element in an array has index 0.]
Similarly, snippets of font definitions are shown below:
// define all ascii characters starting with 32 (blank);
unsigned char PROGMEM myfont[95][8] = {
{
0x00, // ________ blank (ascii 32)
0x00, // ________
0x00, // ________
0x00, // ________
0x00, // ________
0x00, // ________
0x00, // ________
0x00 // ________
},
{
0x00, // ________ !
0x08, // ____X___
0x08, // ____X___
0x08, // ____X___
0x08, // ____X___
0x00, // ________
0x08, // ____X___
0x00, // ________
},
...
{
0x00, // ________ 0
0x1C, // ___XXX__
0x26, // __X__XX_
0x26, // __X__XX_
0x26, // __X__XX_
0x26, // __X__XX_
0x1C, // ___XXX__
0x00, // ________
},
{
0x00, // ________ 1
0x0C, // ____XX__
0x1C, // ___XXX__
0x0C, // ____XX__
0x0C, // ____XX__
0x0C, // ____XX__
0x1E, // ___XXXX_
0x00, // ________
},
{
0x00, // ________ 2
0x1C, // ___XXX__
0x26, // __X__XX_
0x0C, // ____XX__
0x18, // ___XX___
0x30, // __XX____
0x3E, // __XXXXX_
0x00, // ________
},
{
0x00, // ________ 3
0x3E, // __XXXXX_
0x06, // _____XX_
0x1C, // ___XXX__
0x06, // _____XX_
0x26, // __X__XX_
0x1C, // ___XXX__
0x00, // ________
},
...
}
The data structure "myfont" above is an array of 95 character-bitmaps, each bitmap having 8 lines and 6 columns.
Bigger bitmaps, those having more than 8 columns, require more than a byte for each line.
Take a look at the definition of the
Pacman bitmaps below.
uint16_t PROGMEM bitmap[7][14] = {
{
0x02E0, // ____XXXXX_____
0x0FF8, // __XXXXXXXXX___
0x07FC, // ___XXXXXXXXX__
0x03FC, // ____XXXXXXXX__
0x01FE, // _____XXXXXXXX_
0x00FE, // ______XXXXXXX_
0x007E, // _______XXXXXX_
0x007E, // _______XXXXXX_
0x00FE, // ______XXXXXXX_
0x01FE, // _____XXXXXXXX_
0x03FC, // ____XXXXXXXX__
0x07FC, // ___XXXXXXXXX__
0x0FF8, // __XXXXXXXXX___
0x02E0, // ___XXXXX______
},
{
0x03E0, // ____XXXXX_____
0x0FF8, // __XXXXXXXXX___
0x1FFC, // _XXXXXXXXXXX__
0x1FFC, // _XXXXXXXXXXX__
0x07FE, // ___XXXXXXXXXX_
0x01FE, // _____XXXXXXXX_
0x007E, // _______XXXXXX_
0x007E, // _______XXXXXX_
0x01FE, // _____XXXXXXXX_
0x07FE, // ___XXXXXXXXXX_
0x1FFC, // _XXXXXXXXXXX__
0x1FFC, // _XXXXXXXXXXX__
0x0FF8, // __XXXXXXXXX___
0x03E0, // ____XXXXX_____
},
{
0x03E0, // ____XXXXX_____
0x0FF8, // __XXXXXXXXX___
0x1FFC, // _XXXXXXXXXXX__
0x1FFC, // _XXXXXXXXXXX__
0x3FFE, // XXXXXXXXXXXXX_
0x07FE, // ___XXXXXXXXXX_
0x00FE, // ______XXXXXXX_
0x00FE, // ______XXXXXXX_
0x07FE, // ___XXXXXXXXXX_
0x3FFE, // XXXXXXXXXXXXX_
0x1FFC, // _XXXXXXXXXXX__
0x1FFC, // _XXXXXXXXXXX__
0x0FF8, // __XXXXXXXXX___
0x03E0, // ____XXXXX_____
},
{
0x03E0, // ____XXXXX_____
0x0FF8, // __XXXXXXXXX___
0x1FFC, // _XXXXXXXXXXX__
0x1FFC, // _XXXXXXXXXXX__
0x3FFE, // XXXXXXXXXXXXX_
0x3FFE, // XXXXXXXXXXXXX_
0x3FFE, // XXXXXXXXXXXXX_
0x3FFE, // XXXXXXXXXXXXX_
0x3FFE, // XXXXXXXXXXXXX_
0x3FFE, // XXXXXXXXXXXXX_
0x1FFC, // _XXXXXXXXXXX__
0x1FFC, // _XXXXXXXXXXX__
0x0FF8, // __XXXXXXXXX___
0x03E0, // ____XXXXX_____
},
...
};
And this is the function that uses the actual Pacman bitmaps defined above.
/************************
* load and display a given bitmap (defined in bitmaps.h);
*/
void putBitmap(int x, int y, byte nBmp, byte color)
{
for (byte row=0; row < 14; row++)
{
uint16_t rowDots = pgm_read_word_near(&bitmap[nBmp][row]);
for (byte col=0; col<14; col++)
{
if (rowDots & (1<<(13-col)))
ht1632_plot(x+col, y+row, color);
else
ht1632_plot(x+col, y+row, BLACK);
}
}
Now you should be all set to start your own animation (which is just a sequence of bitmaps).