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


2 comments:

  1. Why using HEX here if you are actually working with binary data?
    So if you'd use binary instead you could “read” the bitmap pattern just of the code, especially if you have a monospace font in your IDE, e.g.:

    {
    B00111100,
    B01111110,
    B11111111,
    B11111111,
    B11111111,
    B11111111,
    B01111110,
    B00111100
    }

    ReplyDelete