PicoVector: Text rotation support.
This commit is contained in:
parent
9d0501a43c
commit
cfe8b3c096
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue