Monday, June 13, 2011

How to define bitmaps and fonts

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).


No comments:

Post a Comment