PicoVector: Text rotation support.

This commit is contained in:
Phil Howard 2023-08-17 13:46:13 +01:00
parent 9d0501a43c
commit cfe8b3c096
5 changed files with 116 additions and 3 deletions

View File

@ -41,6 +41,36 @@ namespace alright_fonts {
}
}
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, pretty_poly::mat3_t transform) {
if(tm.face.glyphs.count(codepoint) == 1) {
glyph_t glyph = tm.face.glyphs[codepoint];
// scale is a fixed point 16:16 value, our font data is already scaled to
// -128..127 so to get the pixel size we want we can just shift the
// users requested size up one bit
unsigned scale = tm.size << 9;
std::vector<pretty_poly::contour_t<int8_t>> contours;
for(auto i = 0u; i < glyph.contours.size(); i++) {
unsigned int count = glyph.contours[i].count;
point_t<int8_t> *points = (point_t<int8_t> *)malloc(sizeof(point_t<int8_t>) * count);
for(auto j = 0u; j < count; j++) {
point_t<float> point(glyph.contours[i].points[j].x, glyph.contours[i].points[j].y);
point *= transform;
points[j] = point_t<int8_t>(point.x, point.y);
}
contours.emplace_back(points, count);
}
pretty_poly::draw_polygon<int8_t>(contours, origin, scale);
for(auto contour : contours) {
free(contour.points);
}
}
}
/*
load functions
*/

View File

@ -70,4 +70,5 @@ namespace alright_fonts {
*/
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin);
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, pretty_poly::mat3_t transform);
}

View File

@ -109,4 +109,80 @@ namespace pimoroni {
return Point(caret.x, caret.y);
}
Point PicoVector::text(std::string_view text, Point origin, float angle) {
// TODO: Normalize types somehow, so we're not converting?
pretty_poly::point_t<float> caret(0, 0);
// Prepare a transformation matrix for character and offset rotation
angle = 2 * M_PI * (angle / 360.0f);
pretty_poly::mat3_t transform = pretty_poly::mat3_t::rotation(angle);
// Align text from the bottom left
caret.y += text_metrics.line_height;
caret *= transform;
pretty_poly::point_t<float> space;
pretty_poly::point_t<float> carriage_return(0, text_metrics.line_height);
space.x = alright_fonts::measure_character(text_metrics, ' ').w;
if (space.x == 0) {
space.x = text_metrics.word_spacing;
}
space *= transform;
carriage_return *= transform;
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 += alright_fonts::measure_character(text_metrics, text[j]).w;
word_width += text_metrics.letter_spacing;
}
if(caret.x != 0 && caret.x + word_width > graphics->clip.w) {
caret -= carriage_return;
carriage_return.x = 0;
}
for(size_t j = i; j < std::min(next_break + 1, text.length()); j++) {
if (text[j] == '\n') { // Linebreak
caret -= carriage_return;
carriage_return.x = 0;
} else if (text[j] == ' ') { // Space
caret += space;
carriage_return += space;
} else {
alright_fonts::render_character(text_metrics, text[j], pretty_poly::point_t<int>(origin.x + caret.x, origin.y + caret.y), transform);
}
pretty_poly::point_t<float> advance(
alright_fonts::measure_character(text_metrics, text[j]).w + text_metrics.letter_spacing,
0
);
advance *= transform;
caret += advance;
carriage_return += advance;
}
i = next_break + 1;
}
return Point(caret.x, caret.y);
}
}

View File

@ -70,6 +70,7 @@ namespace pimoroni {
void translate(pretty_poly::contour_t<picovector_point_type> &contour, Point translation);
Point text(std::string_view text, Point origin);
Point text(std::string_view text, Point origin, float angle);
void polygon(std::vector<pretty_poly::contour_t<picovector_point_type>> contours, Point origin = Point(0, 0), int scale=65536);

View File

@ -325,12 +325,13 @@ 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 };
enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_angle };
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_y, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_angle, MP_ARG_OBJ, {.u_obj = mp_const_none} }
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -349,7 +350,11 @@ 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;
self->vector->text(t, Point(x, y));
if(args[ARG_angle].u_obj == mp_const_none) {
self->vector->text(t, Point(x, y));
} else {
self->vector->text(t, Point(x, y), mp_obj_get_float(args[ARG_angle].u_obj));
}
return mp_const_none;
}