MandoMando
MandoMando

Reputation: 5515

How to convert a bitmap font from C header format to raw bits

I have a C header file that contains 10x12 pixel bitmap font characters in an array of 2x12 (for a mono color lcd). This wastes the lower 6-bits of every other byte, not to mention being harder to render for having to skip the paddings.

What is the easiest way to convert this to simple serialized bits without the padded bits?

the solution that I see has a lot of complicated bit banging. Is there an easy way to do perform this?

0x00,0x00,  /*  ................  */
0x30,0x00,  /*  ..@@............  */
0x78,0x00,  /*  .@@@@...........  */
0x48,0x00,  /*  .@..@...........  */
0xCC,0x00,  /*  @@..@@..........  */
0xCC,0x00,  /*  @@..@@..........  */
0xCC,0x00,  /*  @@..@@..........  */
0xFC,0x00,  /*  @@@@@@..........  */
0xCC,0x00,  /*  @@..@@..........  */
0xCC,0x00,  /*  @@..@@..........  */
0x00,0x00,  /*  ................  */
0x00,0x00   /*  ................  */

Upvotes: 1

Views: 1955

Answers (5)

MandoMando
MandoMando

Reputation: 5515

A simplified version can create a producer consumer going one bit at a time which keeps the bit banging to a sane level. Here's what I ended up doing:

#define CHAR_W 10
#define CHAR_H 12
#define NUM_CHARS 100
#define CEILING(x,y) (((x) + (y) - 1) / (y))
#define BYTES CEILING(CHAR_W, 8)
#define BIT(x, n) ( ((1 << n) & x) >> n )
#define COPYBIT(b,x,n) (((0x1 & b) << n) | x)
#define CHAR_BYTES CEILING(CHAR_H * CHAR_W, 8)
#define OUTBYTES NUM_CHARS * CHAR_BYTES

int main()
{
    int totinbit = 0;
    int locinbit = 0;
    int locoutbit = 7;  // start with msb 
    int totoutbit = 0;
    int bytes = BYTES;

    unsigned char c = 0;
    unsigned char o = 0;
    unsigned char buf[OUTBYTES];

    while (totoutbit < NUM_CHARS * CHAR_H * CHAR_W)
    {
        c = fontArray[totinbit / 8];
        locinbit = 7 - totinbit % 8;

        o = COPYBIT(BIT(c,locinbit),o,locoutbit);

        // reset out counter, full byte produced
        if (--locoutbit < 0)
        {
            locoutbit = 7;
            buf[totoutbit  / 8] = o;
            o = 0;
        }

        // skip over the padding bits
        if ((totoutbit % CHAR_W) == (CHAR_W - 1))
            totinbit =  CEILING(totinbit,8) * 8 - 1;

        totinbit++;     
        // character boundary
        if ( (totinbit % (bytes * 8 * CHAR_H)) == 0 && totinbit > 0)
        {
                            // pad the last byte in the character if necessary
            if (locoutbit != 7)
                    locoutbit = 7;
        }

        totoutbit++;


        }
 // at this point buf contains the converted bitmap array
}

Upvotes: 0

marko
marko

Reputation: 9159

This is of course precisely the kind of problem that the JBIG CODEC is designed to handle - but there will be a monstrous amount of bit banging involved.

You'd probably also get decent results with dictionary encoding algorithms working on run-lengths of 0 as well.

Upvotes: 1

paddy
paddy

Reputation: 63471

As for the 'complicated bit-banging'... Yeah, it's complicated-ish, but if you write down your packed bytes and the index into the unpacked array where they come from, it's easy enough to work out how to construct each value...

|........|........|........|........|........|
|00000000|11222222|22334444|44445566|66666677|

The above cycle repeats 3 times.

char a[24] = {
    0x00,0x00,  /*  ................  */
    0x30,0x00,  /*  ..@@............  */
    0x78,0x00,  /*  .@@@@...........  */
    0x48,0x00,  /*  .@..@...........  */
    0xCC,0x00,  /*  @@..@@..........  */
    0xCC,0x00,  /*  @@..@@..........  */
    0xCC,0x00,  /*  @@..@@..........  */
    0xFC,0x00,  /*  @@@@@@..........  */
    0xCC,0x00,  /*  @@..@@..........  */
    0xCC,0x00,  /*  @@..@@..........  */
    0x00,0x00,  /*  ................  */
    0x00,0x00   /*  ................  */
};

void pack( char ap[15], const char a[24] )
{
    ap[0] = a[0];
    ap[1] = a[1] | (a[2] >> 2 );
    ap[2] = (a[2] << 6) | (a[3] >> 2 ) | (a[4] >> 4);
    ap[3] = (a[4] << 4) | (a[5] >> 4) | (a[6] >> 6);
    ap[4] = (a[6] << 2) | (a[7] >> 6);
    ap[5] = a[8];
    ap[6] = a[9] | (a[10] >> 2 );
    ap[7] = (a[10] << 6) | (a[11] >> 2 ) | (a[12] >> 4);
    ap[8] = (a[12] << 4) | (a[13] >> 4) | (a[14] >> 6);
    ap[9] = (a[14] << 2) | (a[15] >> 6);
    ap[10] = a[16];
    ap[11] = a[17] | (a[18] >> 2 );
    ap[12] = (a[18] << 6) | (a[19] >> 2 ) | (a[20] >> 4);
    ap[13] = (a[20] << 4) | (a[21] >> 4) | (a[22] >> 6);
    ap[14] = (a[22] << 2) | (a[23] >> 6);
}

You could do the above in a little loop if you like, to reduce the scope for making mistakes... Just loop from 0 to 2, and advance your arrays accordingly. You know, sort of like this (except you need proper pointers):

for( int i = 0; i < 3; i++ ) {
    ap[0] = a[0];
    ap[1] = a[1] | (a[2] >> 2 );
    ap[2] = (a[2] << 6) | (a[3] >> 2 ) | (a[4] >> 4);
    ap[3] = (a[4] << 4) | (a[5] >> 4) | (a[6] >> 6);
    ap[4] = (a[6] << 2) | (a[7] >> 6);
    ap += 5;
    a += 8;
}

I hope I got all those shifts right =)

Upvotes: 2

Carl
Carl

Reputation: 44458

What you've got is essentially an 8x15 sparse matrix.

There is a boost library - uBLAS - to help you deal with sparse matrices. It would help, for example, if your font dimensions change.

This question might also be of help.

Upvotes: 2

Kerrek SB
Kerrek SB

Reputation: 477060

Well, 10 × 12 = 120, and you can store 120 pixels in 15 bytes, exactly. Every 5 bytes encode 4 rows of pixels.

Upvotes: 2

Related Questions