I've been thinking a bit more about how to implement large fonts. There are 2 big challenges.
#1: A compact data format is needed. For example, that 20x32 font array is 60800 bytes, or 7600 bytes if reduced to 1 bit per pixel. For a 60x96 font, which will make beautiful large letters on these 320x240 displays, it'll grow to 68400 bytes, using 1 bit per pixel. That still fits in Teensy 3.1's flash, but it's getting really large.
#2: Drawing to the display memory is done most efficiently with rectangles. Setup to draw a rectangle costs 11 bytes of SPI communication, and 2 bytes for each pixel. If each pixel is treated as a 1x1 rectangle, as Adafruit's implementation does, then 13 bytes per pixel are transmitted to the display. Drawing a 3x3 rectangle costs 3.22 bytes/pixel, which is 4 times faster then drawing all 9 pixels individually.
A really ideal data format would describe each character as a list of rectangles. A pretty sophisticated Python script would be needed to convert the bitmap into a rectangle list. A really awesome approach would compute the SPI communication cost for alternate rectangle sets. An ideal solution might even allow overlapping rectangles. But even a less-than-ideal script could make a huge improvement in drawing speed.
How to efficiently encode the rectangle list is also a good question.
There's probably not much point using more than 4 bits to encode the rectangle size, allowing rectangles 1 to 4 pixels in size. A large region to be painted in with 4x4 rectangles would require about 2.7 bytes/pixel on the SPI bus. Limiting the number of rectangles to only 16 sizes could also place some nice bounds on the number of different combinations to be compared in the Python script to find the most efficient packing.
If an integer number of bytes are used for each rectangle, 3 bits could be used to encode the relative position of each rectangle (perhaps in relation to the previously drawn one), and 1 bit could specify that a 2-byte format is used to expand the position spec to 11 bits. Or maybe there'd be 3 relative position options, 3, 10 and 18 bits (or maybe other encodings...), so the script could always manage to encode any bitmap when some rectangle is a long distance away from all others.
As a quick experiment, I took the number "7" from chartable.h and converted to ASCII art.
Code:
11111
012345678901234
0 ***************
1 ***************
2 ***************
3 ***
4 ***
5 ***
6 ***
7 ****
8 *****
9 *****
10 *****
11 *****
12 *****
13 *****
14 *****
15 *****
16 *****
17 *****
18 *****
19 ****
20 ***
Here's one possible packing into rectangles:
Code:
11111
012345678901234
0 AAAABBBBCCCCDDD
1 AAAABBBBCCCCDDD
2 AAAABBBBCCCCDDD
3 EEE
4 EEE
5 EEE
6 EEE
7 FFFF
8 mFFFF
9 GGGGn
10 oGGGG
11 HHHHp
12 qHHHH
13 IIIIr
14 sIIII
15 JJJJt
16 uJJJJ
17 KKKKv
18 wKKKK
19 LLLx
20 LLL
This may not be the most efficient packing (i just made it up), but here's the rectangle list. "Offset" is the position from the pixel to the right of the top row of the prior rectangle.
Code:
Rect Size Offset
---- ---- ------
A 4x3 -
B 4x3 0, 0
C 4x3 0, 0
D 3x3 0, 0
E 3x4 -3, 3
F 4x2 -4, 4
F 4x2 -6, 2
G 4x2 -6, 2
H 4x2 -6, 2
I 4x2 -6, 2
J 4x2 -6, 2
K 4x2 -6, 2
L 3x2 -5, 2
m 1x1 7, -11
n 1x1 2, 1
o 1x1 -6, 1
p 1x1 2, 1
q 1x1 -6, 1
r 1x1 2, 1
s 1x1 -6, 1
t 1x1 2, 1
u 1x1 -6, 1
v 1x1 2, 1
w 1x1 -6, 1
x 1x1 2, 1
SPI Communication:
131 pixels = 262 bytes pixel data
25 rectanges = 275 bytes overhead
If each rectangle is encoded using 2 bytes, this character takes 50 bytes to store (not including metadata for font metrics and indexing). That's not wonderful, since the bounding box is 15x21 = 315 pixels, would would take only 40 bytes to store as an uncompressed bitmap.
Encoding the rectangle list efficiently will really depend on getting as many rectangles as possible into a compact single-byte format. Perhaps one way to accomplish this would be using the 4 bits non-size bits as the index into a lookup table (provided by the script which encoded the font), of 14 X-Y offset pairs. The other 2 combinations could be specify a 2 or 3 byte rectangle, allowing offsets -16 to +15 or -128 to +127, so the Python script could always encode any offset for the ones that don't fall into a list of the 14 most common.
Then again, there could be other smart ways to do this stuff. This is just my current thinking about it.....