Reputation: 5515
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
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
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
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
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
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