Coverage Report

Created: 2026-03-19 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/misc/dlo/dlo-font-mcufont.c
Line
Count
Source
1
/*
2
 * lws abstract display
3
 *
4
 * Copyright (C) 2013 Petteri Aimonen
5
 * Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to
9
 * deal in the Software without restriction, including without limitation the
10
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11
 * sell copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23
 * IN THE SOFTWARE.
24
 *
25
 * Display List Object: mcufont font
26
 *
27
 * The mcu decoding is rewritten from the mcufont implementation at
28
 * https://github.com/mcufont/mcufont, which is licensed under MIT already,
29
 * to use a stateful decoder.
30
 *
31
 * The decoder only brings in new compression codes when needed to produce more
32
 * pixels on the line of the glyphs being decoded.
33
 */
34
35
#include <private-lib-core.h>
36
#include "private-lib-drivers-display-dlo.h"
37
38
0
#define DICT_START    24
39
0
#define REF_FILLZEROS   16
40
41
0
#define RLE_CODEMASK      0xC0
42
0
#define RLE_VALMASK       0x3F
43
0
#define RLE_ZEROS         0x00
44
0
#define RLE_64ZEROS       0x40
45
0
#define RLE_ONES          0x80
46
0
#define RLE_SHADE         0xC0
47
48
0
#define DICT_START7BIT    4
49
0
#define DICT_START6BIT    132
50
0
#define DICT_START5BIT    196
51
0
#define DICT_START4BIT    228
52
0
#define DICT_START3BIT    244
53
0
#define DICT_START2BIT    252
54
55
enum {
56
  RS_IDLE,
57
  RS_SKIP_PX,
58
  RS_WRITE_PX,
59
  RS_ALLZERO,
60
61
  COMP      = 0,
62
  DICT1,
63
  DICT1_CONT,
64
  DICT2,
65
  DICT3
66
};
67
68
typedef struct mcu_stack {
69
  const uint8_t   *dict;
70
  int16_t     dictlen;
71
  int16_t     runlen; /* for accumilation on DICT1 */
72
  uint8_t     byte;
73
  uint8_t     bitcount;
74
  uint8_t     state;
75
} mcu_stack_t;
76
77
typedef struct mcu_glyph {
78
  lws_font_glyph_t  fg;
79
  const uint8_t   *comp;
80
81
  mcu_stack_t   st[3];
82
  int32_t     runlen;
83
84
  int8_t      sp;
85
86
  uint8_t     runstate;
87
  uint8_t     alpha;
88
  uint8_t     code;
89
} mcu_glyph_t;
90
91
/* Get bit count for the "fill entries" */
92
static uint8_t
93
fillentry_bitcount(uint8_t index)
94
0
{
95
0
    if (index >= DICT_START2BIT)
96
0
        return 2;
97
0
    else if (index >= DICT_START3BIT)
98
0
        return 3;
99
0
    else if (index >= DICT_START4BIT)
100
0
        return 4;
101
0
    else if (index >= DICT_START5BIT)
102
0
        return 5;
103
0
    else if (index >= DICT_START6BIT)
104
0
        return 6;
105
0
    else
106
0
        return 7;
107
0
}
108
109
void
110
draw_px(lws_dlo_text_t *t, mcu_glyph_t *g)
111
0
{
112
0
  lws_display_colour_t c = (lws_display_colour_t)((lws_display_colour_t)(g->alpha << 24) |
113
0
          (lws_display_colour_t)((lws_display_colour_t)t->dlo.dc & 0xffffffu));
114
0
  lws_fx_t t1, x;
115
0
  int ex;
116
117
0
  t1.whole = g->fg.x;
118
119
0
  if (!g->alpha)
120
0
    return;
121
122
0
  t1.frac = 0;
123
0
  lws_fx_add(&x, &g->fg.xpx, &t1);
124
125
#if 0
126
  { char b1[22], b2[22], b3[22];
127
    lwsl_err("fadj %s = %s + %s\n",
128
      lws_fx_string(&x, b1, sizeof(b1)),
129
      lws_fx_string(&g->fg.xpx, b2, sizeof(b2)),
130
      lws_fx_string(&g->fg.xorg, b3, sizeof(b3))); }
131
#endif
132
133
0
  ex = x.whole;// - t->dlo.box.x.whole;
134
0
  if (ex < 0 || ex >= t->dlo.box.w.whole) {
135
    //lwsl_err("%s: ex %d (lim %d)\n", __func__, ex, t->dlo.box.w.whole);
136
0
    return;
137
0
  }
138
0
  lws_fx_add(&x, &x, &g->fg.xorg);
139
140
0
  lws_fx_add(&t1, &t->dlo.box.x, &x);
141
0
  lws_surface_set_px(t->ic, t->line, t1.whole, &c);
142
0
}
143
144
static void
145
write_ref_codeword(mcu_glyph_t *g, const uint8_t *bf, uint8_t c)
146
0
{
147
0
  uint32_t o, o1;
148
149
0
  if (!c) {
150
0
    g->runlen = 1;
151
0
    g->runstate = RS_SKIP_PX;
152
0
    return;
153
0
  }
154
0
  if (c <= 15) {
155
0
    g->alpha = (uint8_t)(0x11 * c);
156
0
    g->runlen = 1;
157
0
    g->runstate = RS_WRITE_PX;
158
0
    return;
159
0
  }
160
0
  if (c == REF_FILLZEROS) {
161
    /* Fill with zeroes to end */
162
0
    g->alpha = 0;
163
0
    g->runlen = 1000000;
164
0
    g->runstate = RS_WRITE_PX;
165
0
    return;
166
0
  }
167
0
  if (c < DICT_START)
168
0
    return;
169
170
0
  if (c < DICT_START + lws_ser_ru32be(bf + MCUFO_COUNT_RLE_DICT)) {
171
    /* write_rle_dictentry */
172
0
    o1 = lws_ser_ru32be(bf + MCUFO_FOFS_DICT_OFS);
173
0
    o = lws_ser_ru16be(bf + o1 + ((c - DICT_START) * 2));
174
0
    g->st[(int)++g->sp].dictlen = (int16_t)(lws_ser_ru16be(bf + o1 +
175
0
            ((c - DICT_START + 1) * 2)) - o);
176
177
0
    g->st[(int)g->sp].dict = bf + lws_ser_ru32be(bf + MCUFO_FOFS_DICT_DATA) + o;
178
0
    g->st[(int)g->sp].state = DICT2;
179
0
    return;
180
0
  }
181
182
0
  g->st[(int)++g->sp].bitcount = fillentry_bitcount(c);
183
0
  g->st[(int)g->sp].byte = (uint8_t)(c - DICT_START7BIT);
184
0
  g->st[(int)g->sp].state = DICT1;
185
0
  g->runlen = 0;
186
0
}
187
188
static void
189
mcufont_next_code(mcu_glyph_t *g)
190
0
{
191
0
  lws_dlo_text_t *t = lws_container_of(g->fg.list.owner, lws_dlo_text_t,
192
0
               glyphs);
193
0
  const uint8_t *bf = (const uint8_t *)t->font->data;
194
0
  uint8_t c = *g->comp++;
195
0
  uint32_t o, o1;
196
197
0
  if (c < DICT_START + lws_ser_ru32be(&bf[MCUFO_COUNT_RLE_DICT]) ||
198
0
      c >= DICT_START + lws_ser_ru32be(&bf[MCUFO_COUNT_REF_RLE_DICT])) {
199
0
    write_ref_codeword(g, bf, c);
200
0
    return;
201
0
  }
202
203
  /* write_ref_dictentry() */
204
205
0
  o1 = lws_ser_ru32be(bf + MCUFO_FOFS_DICT_OFS);
206
0
  o = lws_ser_ru16be(bf + o1 + ((c - DICT_START) * 2));
207
0
  g->st[(int)++g->sp].dictlen = (int16_t)(lws_ser_ru16be(bf + o1 +
208
0
          ((c - DICT_START + 1) * 2)) - o);
209
210
0
  g->st[(int)g->sp].dict = bf + lws_ser_ru32be(bf + MCUFO_FOFS_DICT_DATA) + o;
211
0
  g->st[(int)g->sp].state = DICT3;
212
0
}
213
214
/* lookup and append a glyph for specific unicode to the text glyph list */
215
216
static uint32_t
217
font_mcufont_uniglyph_lookup(lws_dlo_text_t *text, uint32_t unicode)
218
0
{
219
0
  const uint8_t *bf = (const uint8_t *)text->font->data,
220
0
           *r = bf + lws_ser_ru32be(&bf[MCUFO_FOFS_CHAR_RANGE_TABLES]);
221
0
  uint32_t entries = lws_ser_ru32be(&bf[MCUFO_COUNT_CHAR_RANGE_TABLES]);
222
0
  unsigned int n;
223
224
0
  if (entries > 8) /* coverity sanity */
225
0
    return 0;
226
227
0
  do {
228
0
    for (n = 0; n < entries; n++) {
229
0
      uint32_t cs = lws_ser_ru32be(r + 0), ce = lws_ser_ru32be(r + 4);
230
231
0
      if (cs >= 0x100000 || !ce || ce > 0x10000)
232
0
        return 0;
233
234
0
      if (unicode >= cs && unicode < cs + ce) {
235
0
        uint32_t cbo = lws_ser_ru32be(r + 0xc);
236
237
0
        if (cbo >= text->font->data_len)
238
0
          return 0;
239
240
0
        cbo += lws_ser_ru16be(bf +
241
0
            lws_ser_ru32be(r + 8) + ((unicode - cs) * 2));
242
243
0
                                 if (cbo >= text->font->data_len)
244
0
                                        return 0;
245
246
0
         return cbo;
247
0
      }
248
249
0
      r += 16;
250
0
    }
251
252
0
    if (unicode == lws_ser_ru32be(&bf[MCUFO_UNICODE_FALLBACK]))
253
0
      return 0;
254
0
    unicode = lws_ser_ru32be(&bf[MCUFO_UNICODE_FALLBACK]);
255
256
0
  } while (1);
257
0
}
258
259
static mcu_glyph_t *
260
font_mcufont_uniglyph(lws_dlo_text_t *text, uint32_t unicode)
261
0
{
262
0
  const uint8_t *bf = (const uint8_t *)text->font->data;
263
0
  uint32_t ofs;
264
0
  mcu_glyph_t *g;
265
266
0
  ofs = font_mcufont_uniglyph_lookup(text, unicode);
267
0
  if (!ofs)
268
0
    return NULL;
269
270
//  lwsl_warn("%s: text->text_len %u: %c\n", __func__, text->text_len, (char)unicode);
271
0
  g = lwsac_use_zero(&text->ac_glyphs, sizeof(*g),
272
0
               (text->text_len + 1) * sizeof(*g));
273
0
  if (!g)
274
0
    return NULL;
275
276
0
  g->comp = bf + ofs;
277
0
  g->fg.cwidth.whole = *g->comp++;
278
0
  g->fg.cwidth.frac = 0;
279
280
0
  lws_dll2_add_tail(&g->fg.list, &text->glyphs);
281
282
0
  return g;
283
0
}
284
285
int
286
lws_display_font_mcufont_getcwidth(lws_dlo_text_t *text, uint32_t unicode,
287
           lws_fx_t *fx)
288
0
{
289
0
  const uint8_t *bf = (const uint8_t *)text->font->data;
290
0
  uint32_t ofs = font_mcufont_uniglyph_lookup(text, unicode);
291
292
0
  if (!ofs)
293
0
    return 1;
294
295
0
  fx->whole = bf[ofs];
296
0
  fx->frac = 0;
297
298
0
  return 0;
299
0
}
300
301
lws_font_glyph_t *
302
lws_display_font_mcufont_image_glyph(lws_dlo_text_t *text, uint32_t unicode,
303
             char attach)
304
0
{
305
0
  const uint8_t *bf = (const uint8_t *)text->font->data;
306
0
  mcu_glyph_t *g;
307
308
  /* one text dlo has glyphs from all the same fonts and attributes */
309
0
  if (!text->font_height) {
310
0
    text->font_height = (int16_t)lws_ser_ru16be(&bf[MCUFO16_HEIGHT]);
311
0
    text->font_y_baseline = (int16_t)(text->font_height -
312
0
           lws_ser_ru16be(&bf[MCUFO16_BASELINE_Y]));
313
0
    text->font_line_height = (int16_t)lws_ser_ru16be(&bf[MCUFO16_LINE_HEIGHT]);
314
0
  }
315
316
0
  lws_display_font_mcufont_getcwidth(text, unicode, &text->_cwidth);
317
318
0
  if (!attach)
319
0
    return NULL;
320
321
0
  g = font_mcufont_uniglyph(text, unicode);
322
0
  if (!g)
323
0
    return NULL;
324
325
0
  g->fg.height.whole = lws_ser_ru16be(bf + MCUFO16_HEIGHT);
326
0
  g->fg.height.frac = 0;
327
328
0
  return &g->fg;
329
0
}
330
331
lws_stateful_ret_t
332
lws_display_font_mcufont_render(struct lws_display_render_state *rs)
333
0
{
334
0
  lws_dlo_t *dlo = rs->st[rs->sp].dlo;
335
0
  lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo);
336
0
  const uint8_t *bf = (const uint8_t *)text->font->data;
337
0
  lws_fx_t ax, ay, t, t1, t2, t3;
338
0
  mcu_glyph_t *g;
339
0
  int s, e, yo;
340
0
  uint8_t c, el;
341
342
0
  lws_fx_add(&ax, &rs->st[rs->sp].co.x, &dlo->box.x);
343
0
  lws_fx_add(&t, &ax, &dlo->box.w);
344
0
  lws_fx_add(&ay, &rs->st[rs->sp].co.y, &dlo->box.y);
345
0
  lws_fx_add(&t1, &ay, &dlo->box.h);
346
347
0
  lws_fx_add(&t2, &ax, &text->bounding_box.w);
348
349
0
  text->curr = rs->curr;
350
0
  text->ic = rs->ic;
351
0
  text->line = rs->line;
352
353
0
  s = ax.whole;
354
0
  e = lws_fx_roundup(&t2);
355
356
0
  if (e <= 0)
357
0
    return LWS_SRET_OK; /* wholly off to the left */
358
0
  if (s >= rs->ic->wh_px[0].whole)
359
0
    return LWS_SRET_OK; /* wholly off to the right */
360
361
0
  if (e >= rs->ic->wh_px[0].whole)
362
0
    e = rs->ic->wh_px[0].whole;
363
364
  /* figure out our y position inside the glyph bounding box */
365
0
  yo = rs->curr - ay.whole;
366
367
0
  if (!yo) {
368
0
    lws_display_dlo_text_attach_glyphs(text);
369
370
0
    t3.whole = lws_ser_ru16be(bf + MCUFO16_BASELINE_X);
371
0
    t3.frac = 0;
372
0
    lws_start_foreach_dll(struct lws_dll2 *, d,
373
0
              lws_dll2_get_head(&text->glyphs)) {
374
0
      lws_font_glyph_t *fg = lws_container_of(d, lws_font_glyph_t, list);
375
0
      lws_fx_sub(&fg->xpx, &fg->xpx, &t3);
376
0
      fg->xorg = rs->st[rs->sp].co.x;
377
0
    } lws_end_foreach_dll(d);
378
0
  }
379
380
#if 0
381
  {
382
    uint32_t dc = 0xff0000ff;
383
    int s1 = s;
384
    /* from origin.x + dlo->box.x */
385
    for (s1 = ax.whole; s1 < t2.whole; s1++)
386
      lws_surface_set_px(ic, line, s1, &dc);
387
388
    memset(&ce, 0, sizeof(ce));
389
  }
390
#endif
391
392
0
  lws_start_foreach_dll(struct lws_dll2 *, d,
393
0
            lws_dll2_get_head(&text->glyphs)) {
394
0
    lws_font_glyph_t *fg = lws_container_of(d, lws_font_glyph_t, list);
395
396
0
    g = (mcu_glyph_t *)fg;
397
0
    fg->x = 0;
398
399
0
    while (yo < (int)fg->height.whole &&
400
0
           fg->x < lws_ser_ru16be(bf + MCUFO16_WIDTH)) {
401
0
      switch (g->runstate) {
402
0
      case RS_IDLE:
403
0
        switch (g->st[(int)g->sp].state) {
404
0
        case COMP:
405
0
          mcufont_next_code(g);
406
0
          break;
407
408
0
        case DICT1_CONT:
409
0
          --g->sp; /* back to DICT1 after doing the skip */
410
0
          g->runstate = RS_SKIP_PX;
411
0
          g->runlen = 1;
412
0
          continue;
413
414
0
        case DICT1:
415
          /* write_bin_codeword() states */
416
0
          el = 0;
417
0
          while (g->st[(int)g->sp].bitcount--) {
418
0
            c = g->st[(int)g->sp].byte;
419
0
            g->st[(int)g->sp].byte >>= 1;
420
0
            if (c & 1)
421
0
              g->st[(int)g->sp].runlen++;
422
0
            else {
423
0
              if (g->st[(int)g->sp].runlen) {
424
0
                g->alpha = 255;
425
0
                g->runstate = RS_WRITE_PX;
426
0
                g->runlen = g->st[(int)g->sp].runlen;
427
0
                g->st[(int)g->sp].runlen = 0;
428
0
                g->st[(int)++g->sp].state = DICT1_CONT;
429
0
                el = 1;
430
0
                break;
431
0
              }
432
0
              g->runstate = RS_SKIP_PX;
433
0
              g->runlen = 1;
434
0
              el = 1;
435
0
              break;
436
0
            }
437
0
          }
438
439
0
          if (el)
440
0
            continue;
441
442
          /* back out of DICT1 */
443
0
          if (!g->sp)
444
0
            assert(0);
445
0
          g->sp--;
446
447
0
          if (g->st[(int)g->sp + 1].runlen) {
448
0
            g->alpha = 255;
449
0
            g->runstate = RS_WRITE_PX;
450
0
            g->runlen = g->st[(int)g->sp + 1].runlen;
451
0
            g->st[(int)g->sp + 1].runlen = 0;
452
0
            continue;
453
0
          }
454
0
          break;
455
456
0
        case DICT2: /* write_rle_dictentry */
457
0
          c = (*g->st[(int)g->sp].dict++);
458
0
          if (!--g->st[(int)g->sp].dictlen) {
459
0
            if (!g->sp)
460
0
              assert(0);
461
0
            g->sp--;
462
0
          }
463
0
          if ((c & RLE_CODEMASK) == RLE_ZEROS) {
464
0
            g->runstate = RS_SKIP_PX;
465
0
            g->runlen = c & RLE_VALMASK;
466
0
            continue;
467
0
          }
468
0
          if ((c & RLE_CODEMASK) == RLE_64ZEROS) {
469
0
            g->runstate = RS_SKIP_PX;
470
0
            g->runlen = ((c & RLE_VALMASK) + 1) * 64;
471
0
            continue;
472
0
          }
473
0
          if ((c & RLE_CODEMASK) == RLE_ONES) {
474
0
            g->alpha = 255;
475
0
            g->runstate = RS_WRITE_PX;
476
0
            g->runlen = (c & RLE_VALMASK) + 1;
477
0
            continue;
478
0
          }
479
0
          if ((c & RLE_CODEMASK) == RLE_SHADE) {
480
0
            g->alpha = (uint8_t)(((c & RLE_VALMASK) & 0xf) * 0x11);
481
0
            g->runstate = RS_WRITE_PX;
482
0
            g->runlen = ((c & RLE_VALMASK) >> 4) + 1;
483
0
            continue;
484
0
          }
485
0
          break;
486
487
0
        case DICT3:
488
0
          c = *g->st[(int)g->sp].dict++;
489
0
          if (!--g->st[(int)g->sp].dictlen) {
490
0
            if (!g->sp)
491
0
              assert(0);
492
493
0
            g->sp--;
494
0
          }
495
496
0
          write_ref_codeword(g, bf,  c);
497
0
          break;
498
0
        }
499
0
        break;
500
0
      case RS_SKIP_PX:
501
0
        fg->x++;
502
0
        if (--g->runlen)
503
0
          break;
504
0
        g->runstate = RS_IDLE;
505
0
        break;
506
507
0
      case RS_WRITE_PX:
508
0
        if (g->alpha)
509
0
          draw_px(text, g);
510
0
        g->fg.x++;
511
0
        if (--g->runlen)
512
0
          break;
513
0
        g->runstate = RS_IDLE;
514
0
        break;
515
516
0
      case RS_ALLZERO:
517
0
        fg->x++;
518
0
        if (--g->runlen)
519
0
          break;
520
0
        g->runstate = RS_IDLE;
521
0
        break;
522
0
      }
523
0
    }
524
525
0
  } lws_end_foreach_dll(d);
526
527
0
  return LWS_SRET_OK;
528
0
}