Skip to content

Commit

Permalink
PicoVector: Add optional text max width and max height.
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelBell committed Nov 17, 2024
1 parent 225ecfc commit 0425d77
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 107 deletions.
58 changes: 38 additions & 20 deletions libraries/pico_vector/alright-fonts.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <math.h>
#include <stdbool.h>
#include <wchar.h>
#include <float.h>

#ifdef AF_MALLOC
#ifndef PP_MALLOC
Expand Down Expand Up @@ -104,8 +105,8 @@ typedef struct {

bool af_load_font_file(AF_FILE file, af_face_t *face);
void af_render_character(af_face_t *face, const char codepoint, af_text_metrics_t *tm);
void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm);
pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm);
void af_render(af_face_t *face, const char *text, size_t tlen, float max_line_width, float max_height, af_text_metrics_t *tm);
pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, float max_line_width, af_text_metrics_t *tm);

#ifdef AF_USE_PRETTY_POLY
#endif
Expand Down Expand Up @@ -240,20 +241,30 @@ void af_render_character(af_face_t *face, const char c, af_text_metrics_t *tm) {
af_render_glyph(glyph, tm);
}

int get_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
int line_width = 0;
char *end = (char *)text + tlen;
float get_line_width(af_face_t *face, const char *text, size_t *tlen, float max_line_width, af_text_metrics_t *tm) {
float line_width = 0;
const char *start = text;
const char *end = text + *tlen;
const char *last_space = nullptr;
for(char c = *text; text < end; text++, c = *text) {
af_glyph_t *glyph = find_glyph(face, c);
if(!glyph) {
continue;
}

float char_width;
if(c == L' ') {
line_width += (glyph->advance * tm->word_spacing) / 100.0f;
char_width = (glyph->advance * tm->word_spacing) / 100.0f;
last_space = text;
} else {
line_width += (glyph->advance * tm->letter_spacing) / 100.0f;
char_width = (glyph->advance * tm->letter_spacing) / 100.0f;
}

if (max_line_width > 0 && line_width + char_width > max_line_width && last_space) {
*tlen = last_space - start;
break;
}
line_width += char_width;
}
return line_width;
}
Expand All @@ -270,14 +281,14 @@ size_t line_length(const char *text, const char *end) {
return line_ending - text;
}

int get_max_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
int max_width = 0;
float get_max_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
float max_width = 0;
char *line = (char *)text;
char *tend = line + tlen;

size_t line_len = line_length(line, tend);
while(line_len) {
int width = get_line_width(face, line, line_len, tm);
float width = get_line_width(face, line, &line_len, 0, tm);
max_width = max_width < width ? width : max_width;
line += line_len + 1;
line_len = line_length(line, tend);
Expand All @@ -286,7 +297,7 @@ int get_max_line_width(af_face_t *face, const char *text, size_t tlen, af_text_m
return max_width;
}

void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
void af_render(af_face_t *face, const char *text, size_t tlen, float max_line_width, float max_height, af_text_metrics_t *tm) {
char *line = (char *)text;
char *tend = line + tlen;
size_t line_len = 0;
Expand All @@ -304,15 +315,23 @@ void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t
caret.y = 0;

// find maximum line length
int max_line_width = get_max_line_width(face, text, tlen, tm);
if (max_line_width == 0.f) {
max_line_width = get_max_line_width(face, text, tlen, tm);
} else {
max_line_width /= scale;
}
if (max_height == 0.f) {
max_height = FLT_MAX;
} else {
max_height /= scale;
}

line_len = line_length(line, tend);

while(line_len) {
while(line_len && caret.y + line_height <= max_height) {
int line_width = get_line_width(face, line, &line_len, max_line_width, tm);
char *end = line + line_len;

int line_width = get_line_width(face, line, line_len, tm);

for(char c = *line; line < end; line++, c = *line) {
af_glyph_t *glyph = find_glyph(face, c);
if(!glyph) {
Expand Down Expand Up @@ -356,10 +375,10 @@ void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t
}

void _af_render(af_face_t *face, const char *text, af_text_metrics_t *tm) {
af_render(face, text, strlen(text), tm);
af_render(face, text, strlen(text), 0, 0, tm);
}

pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, float max_line_width, af_text_metrics_t *tm) {
pp_rect_t result;
bool first = true;
char *line = (char *)text;
Expand All @@ -377,15 +396,14 @@ pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, af_text_met
caret.y = 0;

// find maximum line length
int max_line_width = get_max_line_width(face, text, tlen, tm);
if (max_line_width == 0.f) max_line_width = get_max_line_width(face, text, tlen, tm);

line_len = line_length(line, tend);

while(line_len) {
int line_width = get_line_width(face, line, &line_len, max_line_width, tm);
char *end = line + line_len;

int line_width = get_line_width(face, line, line_len, tm);

for(char c = *line; line < end; line++, c = *line) {
af_glyph_t *glyph = find_glyph(face, c);
if(!glyph) {
Expand Down
84 changes: 2 additions & 82 deletions libraries/pico_vector/pico_vector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,93 +60,13 @@ namespace pimoroni {
}
}

pp_point_t PicoVector::text(std::string_view text, pp_mat3_t *t) {
pp_point_t PicoVector::text(std::string_view text, int max_width, int max_height, pp_mat3_t *t) {
pp_point_t caret = {0, 0};

text_metrics.transform = t;

af_render(text_metrics.face, text.data(), text.size(), &text_metrics);
af_render(text_metrics.face, text.data(), text.size(), max_width, max_height, &text_metrics);

return caret;
/*
// Align text from the bottom left
caret.y += (PP_COORD_TYPE)text_metrics.line_height;
caret = pp_point_transform(&caret, t);
caret.x += offset.x;
caret.y += offset.y;
pp_point_t space;
pp_point_t carriage_return = {0, -(PP_COORD_TYPE)text_metrics.line_height};
char spc = ' ';
space.x = af_measure(text_metrics.face, &spc, &text_metrics).w;
if (space.x == 0) {
space.x = text_metrics.word_spacing;
}
space = pp_point_transform(&space, t);
carriage_return = pp_point_transform(&carriage_return, t);
pp_point_t initial_carriage_return = carriage_return;
size_t i = 0;
while(i < text.length()) {
size_t next_space = text.find(' ', i + 1);
if(next_space == std::string::npos) {
next_space = text.length();
}
size_t next_linebreak = text.find('\n', i + 1);
if(next_linebreak == std::string::npos) {
next_linebreak = text.length();
}
size_t next_break = std::min(next_space, next_linebreak);
uint16_t word_width = 0;
for(size_t j = i; j < next_break; j++) {
word_width += af_measure(text_metrics.face, &text[j], &text_metrics).w;
word_width += text_metrics.letter_spacing;
}
if(caret.x != 0 && caret.x + word_width > graphics->clip.w) {
caret = pp_point_sub(&caret, &carriage_return);
carriage_return = initial_carriage_return;
}
for(size_t j = i; j < std::min(next_break + 1, text.length()); j++) {
if (text[j] == '\n') { // Linebreak
caret = pp_point_sub(&caret, &carriage_return);
carriage_return = initial_carriage_return;
} else if (text[j] == ' ') { // Space
caret = pp_point_add(&caret, &space);
carriage_return = pp_point_add(&carriage_return, &space);
} else {
// apply the caret offset...
pp_mat3_t pos = pp_mat3_identity();
pp_mat3_mul(&pos, t);
pp_mat3_translate(&pos, caret.x, caret.y);
text_metrics.transform = &pos;
af_render_character(text_metrics.face, text[j], &text_metrics);
}
pp_point_t advance = {
(PP_COORD_TYPE)af_measure(text_metrics.face, &text[j], &text_metrics).w + text_metrics.letter_spacing,
(PP_COORD_TYPE)0
};
advance = pp_point_transform(&advance, t);
caret = pp_point_add(&caret, &advance);
carriage_return = pp_point_add(&carriage_return, &advance);
}
i = next_break + 1;
}
return {caret.x, caret.y};
*/
}
}
4 changes: 2 additions & 2 deletions libraries/pico_vector/pico_vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ namespace pimoroni {

pp_rect_t measure_text(std::string_view text, pp_mat3_t *t) {
text_metrics.transform = t;
return af_measure(text_metrics.face, text.data(), text.size(), &text_metrics);
return af_measure(text_metrics.face, text.data(), text.size(), 0, &text_metrics);
}

bool set_font(std::string_view font_path, unsigned int font_size) {
Expand Down Expand Up @@ -116,7 +116,7 @@ namespace pimoroni {
return result;
}

pp_point_t text(std::string_view text, pp_mat3_t *t=nullptr);
pp_point_t text(std::string_view text, int max_width, int max_height, pp_mat3_t *t=nullptr);

void transform(pp_path_t *path, pp_mat3_t *t);
void transform(pp_poly_t *poly, pp_mat3_t *t);
Expand Down
10 changes: 7 additions & 3 deletions micropython/modules/picovector/picovector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -705,13 +705,15 @@ mp_obj_t VECTOR_set_antialiasing(mp_obj_t self_in, mp_obj_t aa) {
}

mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_angle };
enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_angle, ARG_max_width, ARG_max_height };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_angle, MP_ARG_OBJ, {.u_obj = mp_const_none} }
{ MP_QSTR_angle, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_max_width, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_max_height, MP_ARG_INT, {.u_int = 0} }
};

mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
Expand All @@ -729,6 +731,8 @@ mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)

int x = args[ARG_x].u_int;
int y = args[ARG_y].u_int;
int max_width = args[ARG_max_width].u_int;
int max_height = args[ARG_max_height].u_int;

pp_mat3_t tt = pp_mat3_identity();

Expand All @@ -738,7 +742,7 @@ mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)

pp_mat3_translate(&tt, (float)x, (float)y);

self->vector->text(t, &tt);
self->vector->text(t, max_width, max_height, &tt);

return mp_const_none;
}
Expand Down

0 comments on commit 0425d77

Please sign in to comment.