add u8g2 font renderings

pull/1/head
flop 4 weeks ago
parent b9fa8ba3ac
commit 9d48c79c0b
  1. 0
      ts/src/font/fonts/5x7.ts
  2. 0
      ts/src/font/fonts/test.ts
  3. 137
      ts/src/font/fonts/u8g2_font_5x7_tf.u8g2font.ts
  4. 5
      ts/src/font/index.ts
  5. 10
      ts/src/font/types.ts
  6. 522
      ts/src/font/u8g2.ts
  7. 4
      ts/src/renderer.ts
  8. 2
      ts/src/types.ts

@ -0,0 +1,137 @@
export const u8g2_font_5x7_tf_u8g2font: number[] = [
0xbf, 0x00, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x07, 0x00,
0xff, 0x06, 0xff, 0x06, 0x00, 0x01, 0x0a, 0x02, 0x16, 0x06, 0x2f, 0x20,
0x05, 0x00, 0xbd, 0x01, 0x21, 0x06, 0xb1, 0xb1, 0x19, 0x29, 0x22, 0x07,
0x5b, 0xb7, 0x49, 0x56, 0x00, 0x23, 0x0a, 0x2d, 0xb1, 0xab, 0x86, 0xaa,
0x86, 0xaa, 0x00, 0x24, 0x09, 0x2d, 0xb1, 0x5b, 0xf5, 0x4e, 0x69, 0x01,
0x25, 0x08, 0x34, 0xb1, 0xc9, 0xb1, 0x77, 0x00, 0x26, 0x09, 0x2c, 0xb1,
0x8b, 0x29, 0x56, 0x31, 0x05, 0x27, 0x05, 0x99, 0xb7, 0x19, 0x28, 0x07,
0x72, 0xb1, 0x53, 0xcd, 0x00, 0x29, 0x08, 0x72, 0xb1, 0x89, 0xa9, 0x52,
0x00, 0x2a, 0x07, 0x6b, 0xb1, 0x49, 0xd5, 0x6a, 0x2b, 0x0a, 0x2d, 0xb1,
0xcd, 0x28, 0x0e, 0x99, 0x51, 0x04, 0x2c, 0x07, 0x5b, 0xaf, 0x53, 0x25,
0x00, 0x2d, 0x06, 0x0c, 0xb5, 0x19, 0x01, 0x2e, 0x06, 0x52, 0xb1, 0x19,
0x01, 0x2f, 0x07, 0x24, 0xb3, 0x8f, 0x6d, 0x00, 0x30, 0x08, 0x73, 0xb1,
0xab, 0x5c, 0x15, 0x00, 0x31, 0x07, 0x73, 0xb1, 0x4b, 0xb2, 0x35, 0x32,
0x09, 0x34, 0xb1, 0x53, 0x31, 0xc7, 0x72, 0x04, 0x33, 0x0a, 0x34, 0xb1,
0x19, 0x39, 0x69, 0x24, 0x93, 0x02, 0x34, 0x0a, 0x34, 0xb1, 0x8d, 0xaa,
0x1a, 0x31, 0x27, 0x00, 0x35, 0x0a, 0x34, 0xb1, 0x19, 0x7a, 0x23, 0x99,
0x14, 0x00, 0x36, 0x0a, 0x34, 0xb1, 0x53, 0x79, 0x45, 0x99, 0x14, 0x00,
0x37, 0x0a, 0x34, 0xb1, 0x19, 0x39, 0xe6, 0x98, 0x23, 0x00, 0x38, 0x0a,
0x34, 0xb1, 0x53, 0x31, 0xa9, 0x28, 0x93, 0x02, 0x39, 0x0a, 0x34, 0xb1,
0x53, 0x51, 0xa6, 0x9d, 0x14, 0x00, 0x3a, 0x07, 0x6a, 0xb1, 0x19, 0x71,
0x04, 0x3b, 0x08, 0x33, 0xaf, 0xb3, 0x91, 0x2a, 0x01, 0x3c, 0x07, 0x6b,
0xb1, 0x4d, 0x75, 0x01, 0x3d, 0x08, 0x1c, 0xb3, 0x19, 0x19, 0x8d, 0x00,
0x3e, 0x07, 0x6b, 0xb1, 0xc9, 0x55, 0x09, 0x3f, 0x09, 0x73, 0xb1, 0x6b,
0xa6, 0x0c, 0x13, 0x00, 0x40, 0x09, 0x34, 0xb1, 0x53, 0x51, 0xdd, 0x48,
0x01, 0x41, 0x09, 0x34, 0xb1, 0x53, 0x51, 0x8e, 0x29, 0x03, 0x42, 0x0a,
0x34, 0xb1, 0x59, 0x71, 0xa4, 0x28, 0x47, 0x02, 0x43, 0x09, 0x34, 0xb1,
0x53, 0x51, 0x97, 0x49, 0x01, 0x44, 0x09, 0x34, 0xb1, 0x59, 0xd1, 0x39,
0x12, 0x00, 0x45, 0x09, 0x34, 0xb1, 0x19, 0x7a, 0xe5, 0x3c, 0x02, 0x46,
0x08, 0x34, 0xb1, 0x19, 0x7a, 0xe5, 0x1a, 0x47, 0x09, 0x34, 0xb1, 0x53,
0x51, 0xa7, 0x99, 0x06, 0x48, 0x08, 0x34, 0xb1, 0x89, 0x72, 0x4c, 0x33,
0x49, 0x07, 0x73, 0xb1, 0x59, 0xb1, 0x35, 0x4a, 0x08, 0x34, 0xb1, 0x6f,
0xcb, 0xa4, 0x00, 0x4b, 0x0a, 0x34, 0xb1, 0x89, 0x2a, 0x49, 0x99, 0xca,
0x00, 0x4c, 0x07, 0x34, 0xb1, 0xc9, 0xdd, 0x23, 0x4d, 0x09, 0x34, 0xb1,
0x89, 0xe3, 0x88, 0x66, 0x00, 0x4e, 0x08, 0x34, 0xb1, 0x89, 0x6b, 0xa9,
0x33, 0x4f, 0x09, 0x34, 0xb1, 0x53, 0xd1, 0x99, 0x14, 0x00, 0x50, 0x0a,
0x34, 0xb1, 0x59, 0x51, 0x8e, 0x94, 0x33, 0x00, 0x51, 0x0a, 0x3c, 0xaf,
0x53, 0xd1, 0x5c, 0x49, 0xa3, 0x00, 0x52, 0x09, 0x34, 0xb1, 0x59, 0x51,
0x8e, 0xd4, 0x0c, 0x53, 0x0a, 0x34, 0xb1, 0x53, 0x31, 0x65, 0x54, 0x26,
0x05, 0x54, 0x07, 0x73, 0xb1, 0x59, 0xb1, 0x0b, 0x55, 0x08, 0x34, 0xb1,
0x89, 0x9e, 0x49, 0x01, 0x56, 0x09, 0x34, 0xb1, 0x89, 0xce, 0x24, 0x15,
0x00, 0x57, 0x09, 0x34, 0xb1, 0x89, 0xe6, 0x38, 0x62, 0x00, 0x58, 0x0a,
0x34, 0xb1, 0x89, 0x32, 0x49, 0x15, 0x65, 0x00, 0x59, 0x08, 0x73, 0xb1,
0x49, 0x56, 0x59, 0x01, 0x5a, 0x09, 0x34, 0xb1, 0x19, 0x39, 0xb6, 0x47,
0x00, 0x5b, 0x07, 0x73, 0xb1, 0x19, 0xb1, 0x39, 0x5c, 0x09, 0x24, 0xb3,
0xc9, 0x28, 0xa3, 0x8c, 0x02, 0x5d, 0x07, 0x73, 0xb1, 0x99, 0xcd, 0x11,
0x5e, 0x05, 0x53, 0xb9, 0x6b, 0x5f, 0x06, 0x0c, 0xb1, 0x19, 0x01, 0x60,
0x06, 0x52, 0xb9, 0x89, 0x01, 0x61, 0x08, 0x24, 0xb1, 0x1b, 0x51, 0xa9,
0x02, 0x62, 0x0a, 0x34, 0xb1, 0xc9, 0x79, 0x45, 0x39, 0x12, 0x00, 0x63,
0x06, 0x23, 0xb1, 0x9b, 0x59, 0x64, 0x08, 0x34, 0xb1, 0xaf, 0x46, 0x94,
0x69, 0x65, 0x08, 0x24, 0xb1, 0x53, 0x69, 0x64, 0x05, 0x66, 0x09, 0x34,
0xb1, 0xad, 0xca, 0x99, 0x23, 0x00, 0x67, 0x09, 0x2c, 0xaf, 0x1b, 0x31,
0xa9, 0x8c, 0x06, 0x68, 0x08, 0x34, 0xb1, 0xc9, 0x79, 0x45, 0x33, 0x69,
0x08, 0x73, 0xb1, 0xcb, 0x48, 0x56, 0x03, 0x6a, 0x09, 0x7b, 0xaf, 0xcd,
0xb0, 0x54, 0x15, 0x00, 0x6b, 0x09, 0x34, 0xb1, 0xc9, 0x55, 0x92, 0xa9,
0x0c, 0x6c, 0x07, 0x73, 0xb1, 0x91, 0x5d, 0x03, 0x6d, 0x08, 0x24, 0xb1,
0x49, 0x69, 0x48, 0x19, 0x6e, 0x07, 0x24, 0xb1, 0x59, 0xd1, 0x0c, 0x6f,
0x08, 0x24, 0xb1, 0x53, 0x51, 0x26, 0x05, 0x70, 0x09, 0x2c, 0xaf, 0x59,
0x51, 0x8e, 0x94, 0x01, 0x71, 0x08, 0x2c, 0xaf, 0x1b, 0x51, 0xa6, 0x1d,
0x72, 0x08, 0x24, 0xb1, 0x59, 0x51, 0x67, 0x00, 0x73, 0x08, 0x24, 0xb1,
0x1b, 0x1a, 0x0d, 0x05, 0x74, 0x09, 0x34, 0xb1, 0xcb, 0x71, 0xe6, 0x8c,
0x04, 0x75, 0x07, 0x24, 0xb1, 0x89, 0x66, 0x1a, 0x76, 0x07, 0x63, 0xb1,
0x49, 0x56, 0x05, 0x77, 0x07, 0x24, 0xb1, 0x89, 0x72, 0x1c, 0x78, 0x08,
0x24, 0xb1, 0x89, 0x49, 0xaa, 0x18, 0x79, 0x09, 0x2c, 0xaf, 0x89, 0x32,
0x95, 0x25, 0x00, 0x7a, 0x08, 0x24, 0xb1, 0x19, 0xb1, 0x1c, 0x01, 0x7b,
0x08, 0x73, 0xb1, 0x4d, 0x49, 0xd6, 0x01, 0x7c, 0x05, 0xb1, 0xb1, 0x39,
0x7d, 0x09, 0x73, 0xb1, 0xc9, 0x51, 0xc5, 0x14, 0x01, 0x7e, 0x07, 0x14,
0xb9, 0x4b, 0x2a, 0x01, 0xa0, 0x05, 0x00, 0xbd, 0x01, 0xa1, 0x06, 0xb1,
0xb1, 0x49, 0x23, 0xa2, 0x09, 0x34, 0xaf, 0x8d, 0x23, 0x35, 0x67, 0x02,
0xa3, 0x08, 0x2c, 0xb1, 0x55, 0x71, 0x56, 0x02, 0xa4, 0x0a, 0x2d, 0xb1,
0xc9, 0x69, 0xa6, 0xb8, 0x72, 0x00, 0xa5, 0x09, 0x73, 0xb1, 0x49, 0xaa,
0x5a, 0x31, 0x01, 0xa6, 0x06, 0xa9, 0xb1, 0x51, 0x02, 0xa7, 0x08, 0x7b,
0xaf, 0x9b, 0xaa, 0x92, 0x0b, 0xa8, 0x06, 0x4b, 0xbb, 0x49, 0x01, 0xa9,
0x0b, 0x3d, 0xaf, 0x5b, 0x59, 0xa5, 0xa9, 0x92, 0x4e, 0x0b, 0xaa, 0x06,
0x1b, 0xb7, 0x5b, 0x49, 0xab, 0x07, 0x1d, 0xb3, 0x8b, 0xb2, 0x01, 0xac,
0x06, 0x14, 0xb3, 0x19, 0x39, 0xad, 0x05, 0x4b, 0xb5, 0x19, 0xae, 0x0b,
0x3d, 0xaf, 0x5b, 0x79, 0xa4, 0x39, 0x75, 0x5a, 0x00, 0xaf, 0x06, 0x0c,
0xbb, 0x19, 0x01, 0xb0, 0x06, 0x5b, 0xb7, 0xeb, 0x02, 0xb1, 0x0b, 0x35,
0xb1, 0xcd, 0x28, 0x0e, 0x99, 0x51, 0x1c, 0x02, 0xb2, 0x06, 0x62, 0xb5,
0x51, 0x06, 0xb3, 0x06, 0x62, 0xb5, 0x19, 0x69, 0xb4, 0x06, 0x52, 0xb9,
0x53, 0x00, 0xb5, 0x08, 0x2c, 0xaf, 0x89, 0xe6, 0x48, 0x19, 0xb6, 0x08,
0x34, 0xb1, 0x1b, 0x6a, 0xf5, 0x03, 0xb7, 0x06, 0x52, 0xb5, 0x19, 0x01,
0xb8, 0x06, 0x52, 0xaf, 0x53, 0x00, 0xb9, 0x07, 0x63, 0xb5, 0x4b, 0x32,
0x0d, 0xba, 0x06, 0x1b, 0xb7, 0xeb, 0x02, 0xbb, 0x08, 0x1d, 0xb3, 0x89,
0xa5, 0x4c, 0x00, 0xbc, 0x09, 0x3c, 0xaf, 0xc9, 0xcd, 0xa8, 0x76, 0x00,
0xbd, 0x09, 0x3c, 0xaf, 0xc9, 0x2d, 0x1d, 0xb3, 0x00, 0xbe, 0x0a, 0x3c,
0xaf, 0x91, 0x3a, 0xaa, 0xa8, 0x76, 0x00, 0xbf, 0x09, 0x73, 0xb1, 0xcb,
0x30, 0xc5, 0x54, 0x01, 0xc0, 0x09, 0x34, 0xb1, 0x53, 0x51, 0x8e, 0x29,
0x03, 0xc1, 0x09, 0x34, 0xb1, 0x53, 0x51, 0x8e, 0x29, 0x03, 0xc2, 0x09,
0x34, 0xb1, 0x53, 0x51, 0x8e, 0x29, 0x03, 0xc3, 0x09, 0x34, 0xb1, 0x53,
0x51, 0x8e, 0x29, 0x03, 0xc4, 0x0a, 0x34, 0xb1, 0x89, 0x49, 0xc5, 0x31,
0x65, 0x00, 0xc5, 0x09, 0x34, 0xb1, 0x93, 0x2a, 0x8e, 0x29, 0x03, 0xc6,
0x09, 0x34, 0xb1, 0x1b, 0xa9, 0x1a, 0xaa, 0x25, 0xc7, 0x0a, 0x3c, 0xaf,
0x53, 0x51, 0x97, 0x49, 0x46, 0x00, 0xc8, 0x09, 0x34, 0xb1, 0x19, 0x7a,
0xe5, 0x3c, 0x02, 0xc9, 0x09, 0x34, 0xb1, 0x19, 0x7a, 0xe5, 0x3c, 0x02,
0xca, 0x09, 0x34, 0xb1, 0x19, 0x7a, 0xe5, 0x3c, 0x02, 0xcb, 0x09, 0x34,
0xb1, 0x19, 0x7a, 0xe5, 0x3c, 0x02, 0xcc, 0x07, 0x73, 0xb1, 0x59, 0xb1,
0x35, 0xcd, 0x07, 0x73, 0xb1, 0x59, 0xb1, 0x35, 0xce, 0x07, 0x73, 0xb1,
0x59, 0xb1, 0x35, 0xcf, 0x07, 0x73, 0xb1, 0x59, 0xb1, 0x35, 0xd0, 0x09,
0x34, 0xb1, 0x99, 0x69, 0x75, 0x8d, 0x04, 0xd1, 0x08, 0x34, 0xb1, 0x49,
0x73, 0xa9, 0x33, 0xd2, 0x09, 0x34, 0xb1, 0x53, 0xd1, 0x99, 0x14, 0x00,
0xd3, 0x09, 0x34, 0xb1, 0x53, 0xd1, 0x99, 0x14, 0x00, 0xd4, 0x09, 0x34,
0xb1, 0x53, 0xd1, 0x99, 0x14, 0x00, 0xd5, 0x09, 0x34, 0xb1, 0x53, 0xd1,
0x99, 0x14, 0x00, 0xd6, 0x0a, 0x34, 0xb1, 0x89, 0x49, 0x45, 0x33, 0x29,
0x00, 0xd7, 0x08, 0x24, 0xb1, 0x89, 0x49, 0xaa, 0x18, 0xd8, 0x09, 0x34,
0xb1, 0x1b, 0xe9, 0x48, 0x47, 0x02, 0xd9, 0x08, 0x34, 0xb1, 0x89, 0x9e,
0x49, 0x01, 0xda, 0x08, 0x34, 0xb1, 0x89, 0x9e, 0x49, 0x01, 0xdb, 0x08,
0x34, 0xb1, 0x89, 0x9e, 0x49, 0x01, 0xdc, 0x0a, 0x34, 0xb1, 0x89, 0x19,
0x45, 0x33, 0x29, 0x00, 0xdd, 0x08, 0x73, 0xb1, 0x49, 0x56, 0x59, 0x01,
0xde, 0x0a, 0x34, 0xb1, 0xc9, 0x2b, 0x8e, 0x94, 0x33, 0x00, 0xdf, 0x09,
0x34, 0xb1, 0x53, 0x51, 0x95, 0x56, 0x02, 0xe0, 0x0a, 0x34, 0xb1, 0xcb,
0x28, 0x8e, 0xa8, 0x54, 0x01, 0xe1, 0x09, 0x34, 0xb1, 0xad, 0x47, 0x54,
0xaa, 0x00, 0xe2, 0x09, 0x34, 0xb1, 0xad, 0xd2, 0x88, 0x4a, 0x15, 0xe3,
0x0a, 0x34, 0xb1, 0x4b, 0x2a, 0x8e, 0xa8, 0x54, 0x01, 0xe4, 0x09, 0x34,
0xb1, 0xab, 0xe1, 0x88, 0x4a, 0x15, 0xe5, 0x09, 0x34, 0xb1, 0x93, 0x72,
0x44, 0xa5, 0x0a, 0xe6, 0x08, 0x24, 0xb1, 0x1b, 0x69, 0xc5, 0x01, 0xe7,
0x08, 0x6b, 0xaf, 0x9b, 0x59, 0x25, 0x00, 0xe8, 0x0a, 0x34, 0xb1, 0xcb,
0x28, 0xaa, 0x34, 0xb2, 0x02, 0xe9, 0x09, 0x34, 0xb1, 0xad, 0x55, 0x1a,
0x59, 0x01, 0xea, 0x0a, 0x34, 0xb1, 0x8b, 0x29, 0xaa, 0x34, 0xb2, 0x02,
0xeb, 0x0a, 0x34, 0xb1, 0x49, 0x19, 0xab, 0x34, 0xb2, 0x02, 0xec, 0x08,
0x73, 0xb1, 0xc9, 0x49, 0x56, 0x03, 0xed, 0x07, 0x73, 0xb1, 0x2b, 0x65,
0x35, 0xee, 0x07, 0x73, 0xb1, 0xab, 0x66, 0x35, 0xef, 0x08, 0x73, 0xb1,
0x49, 0x59, 0x56, 0x03, 0xf0, 0x0a, 0x34, 0xb1, 0xcb, 0xc8, 0x8a, 0x32,
0x29, 0x00, 0xf1, 0x09, 0x34, 0xb1, 0x4b, 0x2a, 0xad, 0x68, 0x06, 0xf2,
0x0a, 0x34, 0xb1, 0xcb, 0x28, 0xaa, 0x28, 0x93, 0x02, 0xf3, 0x09, 0x34,
0xb1, 0xad, 0x55, 0x94, 0x49, 0x01, 0xf4, 0x0a, 0x34, 0xb1, 0xd3, 0x58,
0x45, 0x99, 0x14, 0x00, 0xf5, 0x0a, 0x34, 0xb1, 0x4b, 0x2a, 0xaa, 0x28,
0x93, 0x02, 0xf6, 0x0a, 0x34, 0xb1, 0xab, 0xa1, 0x8a, 0x32, 0x29, 0x00,
0xf7, 0x09, 0x2c, 0xb1, 0xd3, 0x70, 0x64, 0xa8, 0x00, 0xf8, 0x09, 0x24,
0xb1, 0x1b, 0x69, 0xa4, 0x91, 0x00, 0xf9, 0x09, 0x34, 0xb1, 0xcb, 0xa8,
0x34, 0xd3, 0x00, 0xfa, 0x07, 0x34, 0xb1, 0x6d, 0x9a, 0x69, 0xfb, 0x08,
0x34, 0xb1, 0xd3, 0x30, 0x9a, 0x69, 0xfc, 0x09, 0x34, 0xb1, 0xab, 0x51,
0x34, 0xd3, 0x00, 0xfd, 0x09, 0x3c, 0xaf, 0x6d, 0xca, 0x54, 0x96, 0x00,
0xfe, 0x0a, 0x34, 0xaf, 0xc9, 0x2b, 0xca, 0x91, 0x32, 0x00, 0xff, 0x0a,
0x3c, 0xaf, 0xab, 0x51, 0x94, 0xa9, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x04,
0xff, 0xff, 0x00, 0x00
];

@ -1,5 +1,8 @@
import { u8g2_font_5x7_tf_u8g2font } from "./fonts/u8g2_font_5x7_tf.u8g2font";
import { MonoDisplayFont } from "./types";
export * from "./types"; export * from "./types";
export * as _5x7 from "./5x7";
export * from "./u8g2"; export * from "./u8g2";
export const u8g2_font_5x7_tf = new MonoDisplayFont(u8g2_font_5x7_tf_u8g2font);

@ -1,5 +1,11 @@
export class MonoDisplayFont { export class MonoDisplayFont {
public static FONT_DATA: Uint8Array; public FONT_DATA: Uint8Array;
constructor(font_data: Uint8Array | number[]) {
this.FONT_DATA = new Uint8Array(font_data.length);
for (let i = 0; i < font_data.length; i++) {
this.FONT_DATA[i] = font_data[i] || 0;
}
}
} }
export type RasterizedText = { pixels: Uint8Array; width: number; height: number }; export type RasterizedText = { pixels: Uint8Array; width: number; height: number };
@ -7,5 +13,5 @@ export type RasterizedText = { pixels: Uint8Array; width: number; height: number
export type RasterizeTextFunction = ( export type RasterizeTextFunction = (
text: string, text: string,
cellHeight: number, cellHeight: number,
font?: MonoDisplayFont, font: MonoDisplayFont,
) => RasterizedText; ) => RasterizedText;

@ -1,398 +1,280 @@
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// U8G2 font binary format constants (header offsets) // U8G2 font binary format decoder
// Derived from: https://github.com/olikraus/u8g2/blob/master/csrc/u8g2_font.c
// https://github.com/olikraus/u8g2/blob/master/tools/font/bdfconv/bdf_rle.c
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// The U8G2 font blob layout (all offsets are byte positions):
// [0] glyph_cnt – number of glyphs
// [1] bbx_mode – bounding-box mode
// [2] bits_per_0 – RLE bits for "off" run
// [3] bits_per_1 – RLE bits for "on" run
// [4] bits_per_char_width
// [5] bits_per_char_height
// [6] bits_per_char_x
// [7] bits_per_char_y
// [8] bits_per_delta_x
// [9] max_char_width
// [10] max_char_height
// [11] x_offset (signed)
// [12] y_offset (signed)
// [13] ascent_A
// [14] descent_g (negative)
// [15] ascent_para
// [16] descent_para
// [17..18] start_pos_upper (uint16 BE) – offset of uppercase glyph block
// [19..20] start_pos_lower (uint16 BE) – offset of lowercase glyph block
// [21..22] start_pos_unicode (uint16 BE) – offset of Unicode glyph block
// Glyph records follow the three jump-table blocks.
// Each glyph record:
// • A variable-length RLE bitstream (width × height bits) – MSB first
// • Followed by a fixed-width metadata section:
// dwidth (bits_per_delta_x bits) – horizontal advance
// width (bits_per_char_width)
// height (bits_per_char_height)
// x (bits_per_char_x, signed)
// y (bits_per_char_y, signed)
// The very first two bytes of each record encode the encoding (codepoint)
// as a uint16 LE, then a uint16 LE jump offset to the next record.
import type { MonoDisplayFont } from "./types";
// --------------------------------------------------------------------------- const HDR_SIZE = 23;
// Built-in 5×7 font (printable ASCII 0x20–0x7E)
// Each entry is 5 column bytes, bit 0 = top row.
// ---------------------------------------------------------------------------
// prettier-ignore
const BUILTIN_FONT_DATA: Record<number, number[]> = {
0x20: [0x00, 0x00, 0x00, 0x00, 0x00], // space
0x21: [0x00, 0x00, 0x5F, 0x00, 0x00], // !
0x22: [0x00, 0x07, 0x00, 0x07, 0x00], // "
0x23: [0x14, 0x7F, 0x14, 0x7F, 0x14], // #
0x24: [0x24, 0x2A, 0x7F, 0x2A, 0x12], // $
0x25: [0x23, 0x13, 0x08, 0x64, 0x62], // %
0x26: [0x36, 0x49, 0x55, 0x22, 0x50], // &
0x27: [0x00, 0x05, 0x03, 0x00, 0x00], // '
0x28: [0x00, 0x1C, 0x22, 0x41, 0x00], // (
0x29: [0x00, 0x41, 0x22, 0x1C, 0x00], // )
0x2A: [0x14, 0x08, 0x3E, 0x08, 0x14], // *
0x2B: [0x08, 0x08, 0x3E, 0x08, 0x08], // +
0x2C: [0x00, 0x50, 0x30, 0x00, 0x00], // ,
0x2D: [0x08, 0x08, 0x08, 0x08, 0x08], // -
0x2E: [0x00, 0x60, 0x60, 0x00, 0x00], // .
0x2F: [0x20, 0x10, 0x08, 0x04, 0x02], // /
0x30: [0x3E, 0x51, 0x49, 0x45, 0x3E], // 0
0x31: [0x00, 0x42, 0x7F, 0x40, 0x00], // 1
0x32: [0x42, 0x61, 0x51, 0x49, 0x46], // 2
0x33: [0x21, 0x41, 0x45, 0x4B, 0x31], // 3
0x34: [0x18, 0x14, 0x12, 0x7F, 0x10], // 4
0x35: [0x27, 0x45, 0x45, 0x45, 0x39], // 5
0x36: [0x3C, 0x4A, 0x49, 0x49, 0x30], // 6
0x37: [0x01, 0x71, 0x09, 0x05, 0x03], // 7
0x38: [0x36, 0x49, 0x49, 0x49, 0x36], // 8
0x39: [0x06, 0x49, 0x49, 0x29, 0x1E], // 9
0x3A: [0x00, 0x36, 0x36, 0x00, 0x00], // :
0x3B: [0x00, 0x56, 0x36, 0x00, 0x00], // ;
0x3C: [0x08, 0x14, 0x22, 0x41, 0x00], // <
0x3D: [0x14, 0x14, 0x14, 0x14, 0x14], // =
0x3E: [0x00, 0x41, 0x22, 0x14, 0x08], // >
0x3F: [0x02, 0x01, 0x51, 0x09, 0x06], // ?
0x40: [0x32, 0x49, 0x79, 0x41, 0x3E], // @
0x41: [0x7E, 0x11, 0x11, 0x11, 0x7E], // A
0x42: [0x7F, 0x49, 0x49, 0x49, 0x36], // B
0x43: [0x3E, 0x41, 0x41, 0x41, 0x22], // C
0x44: [0x7F, 0x41, 0x41, 0x22, 0x1C], // D
0x45: [0x7F, 0x49, 0x49, 0x49, 0x41], // E
0x46: [0x7F, 0x09, 0x09, 0x09, 0x01], // F
0x47: [0x3E, 0x41, 0x49, 0x49, 0x7A], // G
0x48: [0x7F, 0x08, 0x08, 0x08, 0x7F], // H
0x49: [0x00, 0x41, 0x7F, 0x41, 0x00], // I
0x4A: [0x20, 0x40, 0x41, 0x3F, 0x01], // J
0x4B: [0x7F, 0x08, 0x14, 0x22, 0x41], // K
0x4C: [0x7F, 0x40, 0x40, 0x40, 0x40], // L
0x4D: [0x7F, 0x02, 0x0C, 0x02, 0x7F], // M
0x4E: [0x7F, 0x04, 0x08, 0x10, 0x7F], // N
0x4F: [0x3E, 0x41, 0x41, 0x41, 0x3E], // O
0x50: [0x7F, 0x09, 0x09, 0x09, 0x06], // P
0x51: [0x3E, 0x41, 0x51, 0x21, 0x5E], // Q
0x52: [0x7F, 0x09, 0x19, 0x29, 0x46], // R
0x53: [0x46, 0x49, 0x49, 0x49, 0x31], // S
0x54: [0x01, 0x01, 0x7F, 0x01, 0x01], // T
0x55: [0x3F, 0x40, 0x40, 0x40, 0x3F], // U
0x56: [0x1F, 0x20, 0x40, 0x20, 0x1F], // V
0x57: [0x3F, 0x40, 0x38, 0x40, 0x3F], // W
0x58: [0x63, 0x14, 0x08, 0x14, 0x63], // X
0x59: [0x07, 0x08, 0x70, 0x08, 0x07], // Y
0x5A: [0x61, 0x51, 0x49, 0x45, 0x43], // Z
0x5B: [0x00, 0x7F, 0x41, 0x41, 0x00], // [
0x5C: [0x02, 0x04, 0x08, 0x10, 0x20], // backslash
0x5D: [0x00, 0x41, 0x41, 0x7F, 0x00], // ]
0x5E: [0x04, 0x02, 0x01, 0x02, 0x04], // ^
0x5F: [0x40, 0x40, 0x40, 0x40, 0x40], // _
0x60: [0x00, 0x01, 0x02, 0x04, 0x00], // `
0x61: [0x20, 0x54, 0x54, 0x54, 0x78], // a
0x62: [0x7F, 0x48, 0x44, 0x44, 0x38], // b
0x63: [0x38, 0x44, 0x44, 0x44, 0x20], // c
0x64: [0x38, 0x44, 0x44, 0x48, 0x7F], // d
0x65: [0x38, 0x54, 0x54, 0x54, 0x18], // e
0x66: [0x08, 0x7E, 0x09, 0x01, 0x02], // f
0x67: [0x0C, 0x52, 0x52, 0x52, 0x3E], // g
0x68: [0x7F, 0x08, 0x04, 0x04, 0x78], // h
0x69: [0x00, 0x44, 0x7D, 0x40, 0x00], // i
0x6A: [0x20, 0x40, 0x44, 0x3D, 0x00], // j
0x6B: [0x7F, 0x10, 0x28, 0x44, 0x00], // k
0x6C: [0x00, 0x41, 0x7F, 0x40, 0x00], // l
0x6D: [0x7C, 0x04, 0x18, 0x04, 0x78], // m
0x6E: [0x7C, 0x08, 0x04, 0x04, 0x78], // n
0x6F: [0x38, 0x44, 0x44, 0x44, 0x38], // o
0x70: [0x7C, 0x14, 0x14, 0x14, 0x08], // p
0x71: [0x08, 0x14, 0x14, 0x18, 0x7C], // q
0x72: [0x7C, 0x08, 0x04, 0x04, 0x08], // r
0x73: [0x48, 0x54, 0x54, 0x54, 0x20], // s
0x74: [0x04, 0x3F, 0x44, 0x40, 0x20], // t
0x75: [0x3C, 0x40, 0x40, 0x20, 0x7C], // u
0x76: [0x1C, 0x20, 0x40, 0x20, 0x1C], // v
0x77: [0x3C, 0x40, 0x30, 0x40, 0x3C], // w
0x78: [0x44, 0x28, 0x10, 0x28, 0x44], // x
0x79: [0x0C, 0x50, 0x50, 0x50, 0x3C], // y
0x7A: [0x44, 0x64, 0x54, 0x4C, 0x44], // z
0x7B: [0x00, 0x08, 0x36, 0x41, 0x00], // {
0x7C: [0x00, 0x00, 0x7F, 0x00, 0x00], // |
0x7D: [0x00, 0x41, 0x36, 0x08, 0x00], // }
0x7E: [0x10, 0x08, 0x08, 0x10, 0x08], // ~
};
// Built-in font metrics (5×7, 1px column gap → advance = 6)
export const GLYPH_W = 5;
export const GLYPH_H = 7;
export const GLYPH_ADVANCE = 6; // width + 1px gap
/**
* Return column bytes for a codepoint from the built-in 5×7 font.
* Falls back to '?' (0x3F) for unknown codepoints.
*/
export function builtinGlyph(cp: number): Uint8Array {
const cols = BUILTIN_FONT_DATA[cp] ?? BUILTIN_FONT_DATA[0x3F]!;
return new Uint8Array(cols);
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// U8G2 binary font parser // BitReader — LSB-first, matching u8g2_font_decode_get_unsigned_bits exactly:
// val = *ptr >> bit_pos
// if (bit_pos + cnt >= 8): val |= *(ptr+1) << (8 - bit_pos); ptr++
// val &= (1 << cnt) - 1
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** Bit-level reader over a Uint8Array, MSB-first within each byte. */
class BitReader { class BitReader {
private pos = 0; // bit position private ptr: number;
constructor(private data: Uint8Array, startBit = 0) { private bitPos: number;
this.pos = startBit;
constructor(private data: Uint8Array, startByte: number) {
this.ptr = startByte;
this.bitPos = 0;
} }
read(n: number): number { read(cnt: number): number {
let v = 0; let val = (this.data[this.ptr]! >> this.bitPos) & 0xFF;
for (let i = 0; i < n; i++) { const newPos = this.bitPos + cnt;
const byteIdx = (this.pos) >> 3; if (newPos >= 8) {
const bitIdx = 7 - ((this.pos) & 7); // MSB first this.ptr++;
v = (v << 1) | ((this.data[byteIdx]! >> bitIdx) & 1); val |= (this.data[this.ptr]! << (8 - this.bitPos));
this.pos++; this.bitPos = newPos - 8;
} else {
this.bitPos = newPos;
} }
return v; return val & ((1 << cnt) - 1);
} }
/** Read a signed value stored in `n` bits (two's complement). */ // Offset-binary signed: stored as unsigned + (1 << (cnt-1)), so decode = val - (1 << (cnt-1))
readSigned(n: number): number { // Matches: v = get_unsigned(cnt); d = 1 << (cnt-1); v -= d;
const v = this.read(n); readSigned(cnt: number): number {
const sign = 1 << (n - 1); return this.read(cnt) - (1 << (cnt - 1));
return v & sign ? v - (sign << 1) : v;
} }
get bitPos(): number { return this.pos; }
} }
/** Read a big-endian uint16 from a byte array at `offset`. */ function readU16BE(data: Uint8Array, off: number): number {
function readU16BE(data: Uint8Array, offset: number): number { return ((data[off]! << 8) | data[off + 1]!) >>> 0;
return ((data[offset]! << 8) | data[offset + 1]!) >>> 0;
} }
/** // ---------------------------------------------------------------------------
* Glyph result from a U8G2 font blob. // Font header (23 bytes)
* `cols` column bytes (bit 0 = top row), length = w. // Offsets 17/18, 19/20, 21/22 are relative to end of header (byte 23)
*/ // ---------------------------------------------------------------------------
export interface U8G2GlyphData {
cols: Uint8Array;
w: number;
h: number;
yOff: number; // positive = shift down
}
/**
* Parse header fields from a U8G2 font blob and return the key metrics
* needed to decode glyphs.
*/
function parseHeader(font: Uint8Array) { function parseHeader(font: Uint8Array) {
return { return {
glyphCnt: font[0]!, m0: font[2]!,
bbxMode: font[1]!, m1: font[3]!,
bitsPerRle0: font[2]!,
bitsPerRle1: font[3]!,
bitsPerW: font[4]!, bitsPerW: font[4]!,
bitsPerH: font[5]!, bitsPerH: font[5]!,
bitsPerX: font[6]!, bitsPerX: font[6]!,
bitsPerY: font[7]!, bitsPerY: font[7]!,
bitsPerDx: font[8]!, bitsPerDx: font[8]!,
maxCharW: font[9]!, ascentA: font[13]! as number,
maxCharH: font[10]!, // offsets stored as BE uint16, relative to end of 23-byte header
xOff: font[11]! as number, // signed but treated as-is startUpper: HDR_SIZE + readU16BE(font, 17),
yOff: font[12]! as number, startLower: HDR_SIZE + readU16BE(font, 19),
ascentA: font[13]!, startUnicode: HDR_SIZE + readU16BE(font, 21),
descentG: font[14]!,
startUpper: readU16BE(font, 17),
startLower: readU16BE(font, 19),
startUnicode: readU16BE(font, 21),
}; };
} }
/** // ---------------------------------------------------------------------------
* Decode one glyph from the U8G2 font RLE bitstream at `byteOffset`. // RLE decoder — matches fd_decode / u8g2_font_decode_glyph in the C source.
* Returns decoded data or null if the stream is malformed. //
* // The bitstream is a sequence of (zeros-run, ones-run) pairs, repeated.
* U8G2 glyph encoding (bitstream, MSB-first): // Each pair:
* [bitsPerW] w glyph bitmap width // [m0 bits] count of zero pixels
* [bitsPerH] h glyph bitmap height // [m1 bits] count of one pixels
* [bitsPerX] x signed x bearing // [unary] repetition: read 1-bits until a 0-stop; reps = (number of 1s) + 1
* [bitsPerY] y signed y bearing (positive = up from baseline) //
* [bitsPerDx] dwidth horizontal advance // This matches bg_rle_compress which encodes pairs and then a unary repeat.
* Then w×h bits of RLE-encoded bitmap: // ---------------------------------------------------------------------------
* repeat: function decodeRLE(
* 1-bit type (0 = off-run, 1 = on-run) br: BitReader,
* bitsPerRleN count bits w: number,
*/ h: number,
m0: number,
m1: number,
): Uint8Array {
const total = w * h;
const bits = new Uint8Array(total);
let filled = 0;
while (filled < total) {
const zeros = br.read(m0);
const ones = br.read(m1);
let reps = 1;
while (br.read(1) === 1) reps++;
for (let r = 0; r < reps && filled < total; r++) {
for (let p = 0; p < zeros && filled < total; p++) bits[filled++] = 0;
for (let p = 0; p < ones && filled < total; p++) bits[filled++] = 1;
}
}
return bits;
}
// ---------------------------------------------------------------------------
// Glyph result
// ---------------------------------------------------------------------------
export interface U8G2GlyphData {
cols: Uint8Array; // column bytes (bit 0 = top row), length = w
w: number;
h: number;
dx: number; // horizontal advance
yOff: number; // downward offset from top of cell to top of glyph
}
// ---------------------------------------------------------------------------
// Decode the glyph bitstream at byteOffset (after the record header bytes).
// Field order from C source (u8g2_font.c lines 590–625):
// glyph_width, glyph_height, x (signed), y (signed), delta_x (signed)
// ---------------------------------------------------------------------------
function decodeGlyphAt( function decodeGlyphAt(
font: Uint8Array, font: Uint8Array,
byteOffset: number, byteOffset: number,
hdr: ReturnType<typeof parseHeader>, hdr: ReturnType<typeof parseHeader>,
): U8G2GlyphData | null { ): U8G2GlyphData {
try { const br = new BitReader(font, byteOffset);
const br = new BitReader(font, byteOffset * 8);
const w = br.read(hdr.bitsPerW); const w = br.read(hdr.bitsPerW);
const h = br.read(hdr.bitsPerH); const h = br.read(hdr.bitsPerH);
const xBrg = br.readSigned(hdr.bitsPerX); /* x */ br.readSigned(hdr.bitsPerX); // x bearing — consumed but not needed
const yBrg = br.readSigned(hdr.bitsPerY); // positive = up const yB = br.readSigned(hdr.bitsPerY);
br.read(hdr.bitsPerDx); // advance – not needed here const dx = br.readSigned(hdr.bitsPerDx);
if (w === 0 || h === 0) { if (w === 0 || h === 0) {
// Blank glyph (e.g. space) // Blank glyph (e.g. space) — advance is stored in dx
return { cols: new Uint8Array(GLYPH_W), w: GLYPH_W, h: GLYPH_H, yOff: 0 }; return { cols: new Uint8Array(1), w: 1, h: 0, dx: Math.max(dx, 1), yOff: 0 };
} }
// Decode RLE bitmap into a flat bit array [row-major, top-to-bottom] // Decode row-major horizontal bitmap
const totalBits = w * h; const bits = decodeRLE(br, w, h, hdr.m0, hdr.m1);
const bits = new Uint8Array(totalBits);
let filled = 0;
while (filled < totalBits) { // Convert row-major → column bytes (bit 0 = top row) for rasterizeText
const type = br.read(1); // 0=off, 1=on
const bitsN = type === 0 ? hdr.bitsPerRle0 : hdr.bitsPerRle1;
const count = br.read(bitsN) + 1; // stored as count-1
const val = type & 1;
for (let i = 0; i < count && filled < totalBits; i++) {
bits[filled++] = val;
}
}
// Convert row-major bitmap → column bytes (bit 0 = top row) to match
// the builtin font layout consumed by rasterizeText.
const cols = new Uint8Array(w); const cols = new Uint8Array(w);
for (let row = 0; row < h; row++) { for (let row = 0; row < h; row++) {
for (let col = 0; col < w; col++) { for (let col = 0; col < w; col++) {
if (bits[row * w + col]) { if (bits[row * w + col]) cols[col] |= (1 << row);
// ts-ignore
cols[col] |= 1 << row;
}
} }
} }
// yBrg is measured upward from baseline in U8G2 conventions. // yB: signed distance from baseline upward to bottom of glyph bounding box.
// Convert to a downward pixel offset relative to the top of GLYPH_H. // ascentA: pixels above baseline for cap-height reference.
// ascentA = pixels above baseline for capital 'A' // yOff: distance downward from top of the notional box to top of this glyph.
// yOff = (ascentA - yBrg - h): rows to push down from the top of const yOff = hdr.ascentA - yB - h;
// the glyph box so the baseline lands in the right place.
const yOff = Math.max(0, hdr.ascentA - yBrg - h);
return { cols, w, h, yOff }; return { cols, w, h, dx, yOff };
} catch {
return null;
}
} }
/** // ---------------------------------------------------------------------------
* Scan the correct block of a U8G2 font for `cp` and return decoded glyph // Glyph search
* data, or null if not found. //
* // Record layout (cp < 0x0100):
* Each glyph record in the jump table starts with: // [1 byte] encoding
* [1 byte] encoding (codepoint, 0x000xFF in ASCII blocks) // [1 byte] jump = byte distance from START of this record to next record
* [1 byte] jump offset to next record (0 = end of block) // [bitstream …]
* then the bitstream described in decodeGlyphAt. //
* // Record layout (cp >= 0x0100):
* For the Unicode block the encoding is 2 bytes (uint16 LE) and the jump // [2 bytes BE] encoding
* offset is also 2 bytes (uint16 LE). // [1 byte] jump (same semantics)
*/ // [bitstream …]
//
// Unicode section is preceded by a lookup table (added in v2.23):
// pairs of [uint16 BE delta | uint16 BE last-encoding], terminated by last==0xFFFF
// delta is cumulative byte offset from TABLE START to the glyph sub-block.
// ---------------------------------------------------------------------------
export function u8g2Glyph(cp: number, font: Uint8Array): U8G2GlyphData | null { export function u8g2Glyph(cp: number, font: Uint8Array): U8G2GlyphData | null {
if (font.length < 23) return null; if (font.length < HDR_SIZE) return null;
const hdr = parseHeader(font); const hdr = parseHeader(font);
let blockStart: number; try {
let wide: boolean; // true for the Unicode block (2-byte encoding + 2-byte jump) if (cp >= 0x0100) {
// ── Unicode block ────────────────────────────────────────────────────
if (cp >= 0x20 && cp <= 0x5F) { const tableBase = hdr.startUnicode;
blockStart = hdr.startUpper; if (tableBase + 4 > font.length) return null;
wide = false;
} else if (cp >= 0x60 && cp <= 0x9F) { // Walk the jump table (BE uint16 pairs) to find the right sub-block.
blockStart = hdr.startLower; // delta is offset from tableBase to start of sub-block.
wide = false; let glyphStart = tableBase;
} else { let pos = tableBase;
blockStart = hdr.startUnicode; while (pos + 4 <= font.length) {
wide = true; const delta = readU16BE(font, pos);
const lastEncoding = readU16BE(font, pos + 2);
pos += 4;
if (lastEncoding === 0xFFFF) {
glyphStart = tableBase + delta;
break;
}
if (lastEncoding >= cp) {
glyphStart = tableBase + delta;
break;
}
} }
if (blockStart === 0 || blockStart >= font.length) return null; // Scan glyph records in the sub-block
let recPos = glyphStart;
let pos = blockStart; while (recPos + 3 <= font.length) {
const recStart = recPos;
while (pos < font.length) { const encoding = readU16BE(font, recPos);
let encoding: number; const jump = font[recPos + 2]!;
let jump: number; if (jump === 0) break;
recPos += 3; // bitstream starts here
if (encoding === cp) return decodeGlyphAt(font, recPos, hdr);
recPos = recStart + jump;
}
return null;
if (wide) {
if (pos + 4 > font.length) break;
encoding = (font[pos]! | (font[pos + 1]! << 8)) >>> 0; // LE uint16
jump = (font[pos + 2]! | (font[pos + 3]! << 8)) >>> 0;
pos += 4;
} else { } else {
if (pos + 2 > font.length) break; // ── ASCII block (cp < 0x0100) ────────────────────────────────────────
encoding = font[pos]!; // Jump-start from the nearest block offset to avoid scanning from byte 0.
jump = font[pos + 1]!; let recPos: number;
pos += 2; if (cp >= 0x61) recPos = hdr.startLower;
else if (cp >= 0x41) recPos = hdr.startUpper;
else recPos = HDR_SIZE;
while (recPos + 2 <= font.length) {
const recStart = recPos;
const encoding = font[recPos]!;
const jump = font[recPos + 1]!;
if (jump === 0) break;
recPos += 2; // bitstream starts here
if (encoding === cp) return decodeGlyphAt(font, recPos, hdr);
recPos = recStart + jump;
} }
return null;
if (jump === 0) break; // end-of-block sentinel
if (encoding === cp) {
return decodeGlyphAt(font, pos, hdr);
} }
} catch {
pos += jump; // skip to next record return null;
} }
return null; // codepoint not in font
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Text → pixel-image rasteriser (as specified) // Text → pixel-image rasteriser
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
export function rasterizeText( export function rasterizeText(
text: string, text: string,
cellHeight: number, cellHeight: number,
font?: MonoDisplayFont, font: { FONT_DATA: Uint8Array },
): { pixels: Uint8Array; width: number; height: number } { ): { pixels: Uint8Array; width: number; height: number } {
const glyphs: Array<{ cols: Uint8Array; w: number; h: number; yOff: number }> = []; const hdr = parseHeader(font.FONT_DATA);
const glyphs: Array<U8G2GlyphData> = [];
for (const char of text) { for (const char of text) {
const cp = char.codePointAt(0) ?? 0x3F; const cp = char.codePointAt(0) ?? 0x3F;
const custom = font ? u8g2Glyph(cp, font.FONT_DATA) : null; const g = u8g2Glyph(cp, font.FONT_DATA);
if (custom) { if (g) glyphs.push(g);
glyphs.push({ cols: custom.cols, w: custom.w, h: custom.h, yOff: custom.yOff });
} else {
glyphs.push({ cols: builtinGlyph(cp), w: GLYPH_W, h: GLYPH_H, yOff: 0 });
} }
if (glyphs.length === 0) {
return { pixels: new Uint8Array(cellHeight), width: 1, height: cellHeight };
} }
const totalW = Math.max(1, glyphs.length * GLYPH_ADVANCE - 1); // Total width: sum all advances, but last glyph uses its actual pixel width
const totalW = glyphs.reduce((sum, g, i) =>
sum + (i < glyphs.length - 1 ? g.dx : g.w), 0);
const pixels = new Uint8Array(totalW * cellHeight); const pixels = new Uint8Array(totalW * cellHeight);
const yBase = Math.max(0, Math.floor((cellHeight - GLYPH_H) / 2)); // Centre the full font (ascent + |descent|) vertically in the cell.
let x = 0; // header byte 14 is descent of 'g' — stored as a negative signed value.
const descent = font.FONT_DATA[14]! > 127 ? font.FONT_DATA[14]! - 256 : font.FONT_DATA[14]!;
const fontH = hdr.ascentA - descent; // total font height in pixels
const topPad = Math.floor((cellHeight - fontH) / 2);
const baseline = topPad + hdr.ascentA;
let x = 0;
for (const g of glyphs) { for (const g of glyphs) {
const top = yBase + g.yOff; // yOff = ascentA - yBearing - h (may be negative if glyph taller than 'A')
const top = baseline - hdr.ascentA + g.yOff;
for (let col = 0; col < g.w; col++) { for (let col = 0; col < g.w; col++) {
const colByte = g.cols[col] ?? 0; const colByte = g.cols[col] ?? 0;
for (let row = 0; row < g.h; row++) { for (let row = 0; row < g.h; row++) {
@ -404,7 +286,7 @@ export function rasterizeText(
} }
} }
} }
x += GLYPH_ADVANCE; x += g.dx;
} }
return { pixels, width: totalW, height: cellHeight }; return { pixels, width: totalW, height: cellHeight };

@ -16,7 +16,7 @@ import {
type MonoFormatVScroll type MonoFormatVScroll
} from "./types.js"; } from "./types.js";
import { type MonoDisplayDriverOptions } from "./driver"; import { type MonoDisplayDriverOptions } from "./driver";
import { rasterizeText, MonoDisplayFont } from "./font"; import { rasterizeText, MonoDisplayFont, u8g2_font_5x7_tf } from "./font";
/** /**
* Renders a MonoFormatFile onto an HTMLCanvasElement. * Renders a MonoFormatFile onto an HTMLCanvasElement.
* Uses setInterval at 1000/fps ms per tick. All element state (animation * Uses setInterval at 1000/fps ms per tick. All element state (animation
@ -46,7 +46,7 @@ export class MonoDisplayRenderer {
this.ctx = ctx; this.ctx = ctx;
this.opts = opts; this.opts = opts;
this.builtinFonts = [ this.builtinFonts = [
u8g2_font_5x7_tf,
]; ];
} }

@ -31,7 +31,7 @@ export enum ElementType {
Line = 5, Line = 5,
ClippedText = 16, ClippedText = 16,
HScrollText = 17, HScrollText = 17,
CurrentTime = 18, CurrentTime = 32,
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

Loading…
Cancel
Save