Skip to content

Commit 51645da

Browse files
committed
PicoVector: Refactor text multiline support.
Drop dependence on null terminated strings, and for a final linebreak. Bound all text processing using the text length.
1 parent 362b9ef commit 51645da

File tree

5 files changed

+184
-57
lines changed

5 files changed

+184
-57
lines changed

libraries/pico_vector/alright-fonts.h

Lines changed: 107 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,14 @@ typedef struct {
9898
float line_height; // spacing between lines (%)
9999
float letter_spacing; // spacing between characters (%)
100100
float word_spacing; // spacing between words (%)
101-
af_align_t align; // horizontal and vertical alignment
101+
unsigned int align; // horizontal and vertical alignment
102102
pp_mat3_t *transform; // arbitrary transformation
103103
} af_text_metrics_t;
104104

105105
bool af_load_font_file(AF_FILE file, af_face_t *face);
106106
void af_render_character(af_face_t *face, const char codepoint, af_text_metrics_t *tm);
107-
void af_render(af_face_t *face, const char *text, af_text_metrics_t *tm);
108-
pp_rect_t af_measure(af_face_t *face, const char *text, af_text_metrics_t *tm);
107+
void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm);
108+
pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm);
109109

110110
#ifdef AF_USE_PRETTY_POLY
111111
#endif
@@ -240,10 +240,9 @@ void af_render_character(af_face_t *face, const char c, af_text_metrics_t *tm) {
240240
af_render_glyph(glyph, tm);
241241
}
242242

243-
int get_line_width(af_face_t *face, const char *text, af_text_metrics_t *tm) {
243+
int get_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
244244
int line_width = 0;
245-
char *end = strchr(text, '\n');
246-
if (!end) end = (char *)text + strlen(text);
245+
char *end = (char *)text + tlen;
247246
for(char c = *text; text < end; text++, c = *text) {
248247
af_glyph_t *glyph = find_glyph(face, c);
249248
if(!glyph) {
@@ -259,44 +258,62 @@ int get_line_width(af_face_t *face, const char *text, af_text_metrics_t *tm) {
259258
return line_width;
260259
}
261260

262-
int get_max_line_width(af_face_t *face, const char *text, af_text_metrics_t *tm) {
261+
size_t line_length(const char *text, const char *end) {
262+
if(text >= end) return 0;
263+
264+
char *line_ending = (char *)memchr(text, '\n', end - text);
265+
266+
if(line_ending == NULL || line_ending > end) {
267+
line_ending = (char *)end;
268+
}
269+
270+
return line_ending - text;
271+
}
272+
273+
int get_max_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
263274
int max_width = 0;
275+
char *line = (char *)text;
276+
char *tend = line + tlen;
264277

265-
char *end = strchr(text, '\n');
266-
while(end) {
267-
int width = get_line_width(face, text, tm);
278+
size_t line_len = line_length(line, tend);
279+
while(line_len) {
280+
int width = get_line_width(face, line, line_len, tm);
268281
max_width = max_width < width ? width : max_width;
269-
text = end + 1;
270-
end = strchr(text, '\n');
282+
line += line_len + 1;
283+
line_len = line_length(line, tend);
271284
}
272285

273286
return max_width;
274287
}
275288

276289
void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
290+
char *line = (char *)text;
291+
char *tend = line + tlen;
292+
size_t line_len = 0;
293+
277294
pp_mat3_t *old = pp_transform(NULL);
278295

279296
float line_height = (tm->line_height * 128.0f) / 100.0f;
280297
float scale = tm->size / 128.0f;
281298

282-
// find maximum line length
283-
int max_line_width = get_max_line_width(face, text, tm);
284-
285299
struct {
286300
float x, y;
287301
} caret;
288302

289303
caret.x = 0;
290304
caret.y = 0;
291305

292-
char *done = (char *)text + tlen;
293-
char *end = strchr(text, '\n');
294-
if (!end) end = done;
306+
// find maximum line length
307+
int max_line_width = get_max_line_width(face, text, tlen, tm);
308+
309+
line_len = line_length(line, tend);
310+
311+
while(line_len) {
312+
char *end = line + line_len;
295313

296-
while(true) {
297-
int line_width = get_line_width(face, text, tm);
314+
int line_width = get_line_width(face, line, line_len, tm);
298315

299-
for(char c = *text; text < end; text++, c = *text) {
316+
for(char c = *line; line < end; line++, c = *line) {
300317
af_glyph_t *glyph = find_glyph(face, c);
301318
if(!glyph) {
302319
continue;
@@ -306,11 +323,11 @@ void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t
306323
pp_mat3_scale(&caret_transform, scale, scale);
307324
pp_mat3_translate(&caret_transform, caret.x, caret.y);
308325

309-
if(tm->align == AF_H_ALIGN_CENTER) {
326+
if(tm->align & AF_H_ALIGN_CENTER) {
310327
pp_mat3_translate(&caret_transform, (max_line_width - line_width) / 2, 0);
311328
}
312329

313-
if(tm->align == AF_H_ALIGN_RIGHT) {
330+
if(tm->align & AF_H_ALIGN_RIGHT) {
314331
pp_mat3_translate(&caret_transform, (max_line_width - line_width), 0);
315332
}
316333

@@ -326,10 +343,8 @@ void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t
326343

327344
}
328345

329-
text = end + 1;
330-
if (*text == '\0' || text > done) break;
331-
end = strchr(text, '\n');
332-
if (!end) end = (char *)text + tlen;
346+
line += 1; // Skip over \n
347+
line_len = line_length(line, tend);
333348

334349
caret.x = 0;
335350
caret.y += line_height;
@@ -344,26 +359,75 @@ void _af_render(af_face_t *face, const char *text, af_text_metrics_t *tm) {
344359
af_render(face, text, strlen(text), tm);
345360
}
346361

347-
pp_rect_t af_measure(af_face_t *face, const char *text, af_text_metrics_t *tm) {
362+
pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
348363
pp_rect_t result;
349364
bool first = true;
350-
pp_mat3_t t = *tm->transform;
365+
char *line = (char *)text;
366+
char *tend = line + tlen;
367+
size_t line_len = 0;
368+
369+
float line_height = (tm->line_height * 128.0f) / 100.0f;
370+
float scale = tm->size / 128.0f;
371+
372+
struct {
373+
float x, y;
374+
} caret;
375+
376+
caret.x = 0;
377+
caret.y = 0;
378+
379+
// find maximum line length
380+
int max_line_width = get_max_line_width(face, text, tlen, tm);
381+
382+
line_len = line_length(line, tend);
383+
384+
while(line_len) {
385+
char *end = line + line_len;
386+
387+
int line_width = get_line_width(face, line, line_len, tm);
388+
389+
for(char c = *line; line < end; line++, c = *line) {
390+
af_glyph_t *glyph = find_glyph(face, c);
391+
if(!glyph) {
392+
continue;
393+
}
394+
395+
pp_mat3_t caret_transform = *tm->transform;
396+
pp_mat3_scale(&caret_transform, scale, scale);
397+
pp_mat3_translate(&caret_transform, caret.x, caret.y);
398+
399+
if(tm->align & AF_H_ALIGN_CENTER) {
400+
pp_mat3_translate(&caret_transform, (max_line_width - line_width) / 2, 0);
401+
}
402+
403+
if(tm->align & AF_H_ALIGN_RIGHT) {
404+
pp_mat3_translate(&caret_transform, (max_line_width - line_width), 0);
405+
}
406+
407+
pp_rect_t r = {glyph->x, glyph->y, glyph->x + glyph->w, glyph->y + glyph->h};
408+
//pp_rect_t r = af_glyph_bounds(glyph, tm);
409+
r = pp_rect_transform(&r, &caret_transform);
410+
411+
if(first) {
412+
result = r;
413+
first = false;
414+
} else {
415+
result = pp_rect_merge(&result, &r);
416+
}
417+
418+
if(c == L' ') {
419+
caret.x += (glyph->advance * tm->word_spacing) / 100.0f;
420+
} else {
421+
caret.x += (glyph->advance * tm->letter_spacing) / 100.0f;
422+
}
351423

352-
for(size_t i = 0; i < strlen(text); i++) {
353-
af_glyph_t *glyph = find_glyph(face, text[i]);
354-
if(!glyph) {
355-
continue;
356-
}
357-
pp_rect_t r = {glyph->x, glyph->y, glyph->x + glyph->w, glyph->y + glyph->h};
358-
r = pp_rect_transform(&r, &t);
359-
pp_mat3_translate(&t, glyph->advance, 0);
360-
361-
if(first) {
362-
result = r;
363-
first = false;
364-
}else{
365-
result = pp_rect_merge(&result, &r);
366424
}
425+
426+
line += 1; // Skip over \n
427+
line_len = line_length(line, tend);
428+
429+
caret.x = 0;
430+
caret.y += line_height;
367431
}
368432

369433
return result;

libraries/pico_vector/pico_vector.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@ namespace pimoroni {
7979
text_metrics.line_height = font_line_height;
8080
}
8181

82+
void set_font_align(unsigned int font_align) {
83+
text_metrics.align = font_align;
84+
}
85+
86+
pp_rect_t measure_text(std::string_view text, pp_mat3_t *t) {
87+
text_metrics.transform = t;
88+
return af_measure(text_metrics.face, text.data(), text.size(), &text_metrics);
89+
}
90+
8291
bool set_font(std::string_view font_path, unsigned int font_size) {
8392
if(text_metrics.face) {
8493
af_free(text_metrics.face->glyphs);

micropython/modules/picovector/picovector.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,27 +80,32 @@ MP_DEFINE_CONST_OBJ_TYPE(
8080
/* PicoVector */
8181

8282
static MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_text_obj, 4, VECTOR_text);
83+
static MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_measure_text_obj, 2, VECTOR_measure_text);
8384
static MP_DEFINE_CONST_FUN_OBJ_3(VECTOR_set_font_obj, VECTOR_set_font);
8485
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_font_size_obj, VECTOR_set_font_size);
8586
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_font_word_spacing_obj, VECTOR_set_font_word_spacing);
8687
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_font_letter_spacing_obj, VECTOR_set_font_letter_spacing);
8788
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_font_line_height_obj, VECTOR_set_font_line_height);
89+
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_font_align_obj, VECTOR_set_font_align);
8890
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_antialiasing_obj, VECTOR_set_antialiasing);
8991
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_transform_obj, VECTOR_set_transform);
9092
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_clip_obj, VECTOR_set_clip);
9193

9294
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_draw_obj, VECTOR_draw);
9395

9496
static const mp_rom_map_elem_t VECTOR_locals_dict_table[] = {
97+
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&VECTOR_text_obj) },
98+
{ MP_ROM_QSTR(MP_QSTR_measure_text), MP_ROM_PTR(&VECTOR_measure_text_obj) },
99+
95100
{ MP_ROM_QSTR(MP_QSTR_set_font), MP_ROM_PTR(&VECTOR_set_font_obj) },
96101
{ MP_ROM_QSTR(MP_QSTR_set_font_size), MP_ROM_PTR(&VECTOR_set_font_size_obj) },
97102
{ MP_ROM_QSTR(MP_QSTR_set_font_word_spacing), MP_ROM_PTR(&VECTOR_set_font_word_spacing_obj) },
98103
{ MP_ROM_QSTR(MP_QSTR_set_font_letter_spacing), MP_ROM_PTR(&VECTOR_set_font_letter_spacing_obj) },
99104
{ MP_ROM_QSTR(MP_QSTR_set_font_line_height), MP_ROM_PTR(&VECTOR_set_font_line_height_obj) },
105+
{ MP_ROM_QSTR(MP_QSTR_set_font_align), MP_ROM_PTR(&VECTOR_set_font_align_obj) },
100106
{ MP_ROM_QSTR(MP_QSTR_set_antialiasing), MP_ROM_PTR(&VECTOR_set_antialiasing_obj) },
101107
{ MP_ROM_QSTR(MP_QSTR_set_transform), MP_ROM_PTR(&VECTOR_set_transform_obj) },
102108
{ MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&VECTOR_set_clip_obj) },
103-
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&VECTOR_text_obj) },
104109

105110
{ MP_ROM_QSTR(MP_QSTR_draw), MP_ROM_PTR(&VECTOR_draw_obj) },
106111
};
@@ -127,6 +132,13 @@ static const mp_map_elem_t VECTOR_globals_table[] = {
127132
{ MP_ROM_QSTR(MP_QSTR_ANTIALIAS_X16), MP_ROM_INT(2) },
128133
{ MP_ROM_QSTR(MP_QSTR_ANTIALIAS_FAST), MP_ROM_INT(1) },
129134
{ MP_ROM_QSTR(MP_QSTR_ANTIALIAS_BEST), MP_ROM_INT(2) },
135+
136+
{ MP_ROM_QSTR(MP_QSTR_HALIGN_LEFT), MP_ROM_INT(0) },
137+
{ MP_ROM_QSTR(MP_QSTR_HALIGN_CENTER), MP_ROM_INT(1) },
138+
{ MP_ROM_QSTR(MP_QSTR_HALIGN_RIGHT), MP_ROM_INT(2) },
139+
{ MP_ROM_QSTR(MP_QSTR_VALIGN_TOP), MP_ROM_INT(8) },
140+
{ MP_ROM_QSTR(MP_QSTR_VALIGN_MIDDLE), MP_ROM_INT(16) },
141+
{ MP_ROM_QSTR(MP_QSTR_VALIGN_BOTTOM), MP_ROM_INT(32) },
130142
};
131143

132144
static MP_DEFINE_CONST_DICT(mp_module_VECTOR_globals, VECTOR_globals_table);

0 commit comments

Comments
 (0)