Skip to content

Commit cfe8b3c

Browse files
committed
PicoVector: Text rotation support.
1 parent 9d0501a commit cfe8b3c

File tree

5 files changed

+116
-3
lines changed

5 files changed

+116
-3
lines changed

libraries/pico_vector/alright_fonts.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,36 @@ namespace alright_fonts {
4141
}
4242
}
4343

44+
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, pretty_poly::mat3_t transform) {
45+
if(tm.face.glyphs.count(codepoint) == 1) {
46+
glyph_t glyph = tm.face.glyphs[codepoint];
47+
48+
// scale is a fixed point 16:16 value, our font data is already scaled to
49+
// -128..127 so to get the pixel size we want we can just shift the
50+
// users requested size up one bit
51+
unsigned scale = tm.size << 9;
52+
53+
std::vector<pretty_poly::contour_t<int8_t>> contours;
54+
55+
for(auto i = 0u; i < glyph.contours.size(); i++) {
56+
unsigned int count = glyph.contours[i].count;
57+
point_t<int8_t> *points = (point_t<int8_t> *)malloc(sizeof(point_t<int8_t>) * count);
58+
for(auto j = 0u; j < count; j++) {
59+
point_t<float> point(glyph.contours[i].points[j].x, glyph.contours[i].points[j].y);
60+
point *= transform;
61+
points[j] = point_t<int8_t>(point.x, point.y);
62+
}
63+
contours.emplace_back(points, count);
64+
}
65+
66+
pretty_poly::draw_polygon<int8_t>(contours, origin, scale);
67+
68+
for(auto contour : contours) {
69+
free(contour.points);
70+
}
71+
}
72+
}
73+
4474
/*
4575
load functions
4676
*/

libraries/pico_vector/alright_fonts.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,5 @@ namespace alright_fonts {
7070
*/
7171

7272
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin);
73+
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, pretty_poly::mat3_t transform);
7374
}

libraries/pico_vector/pico_vector.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,80 @@ namespace pimoroni {
109109

110110
return Point(caret.x, caret.y);
111111
}
112+
113+
Point PicoVector::text(std::string_view text, Point origin, float angle) {
114+
// TODO: Normalize types somehow, so we're not converting?
115+
pretty_poly::point_t<float> caret(0, 0);
116+
117+
// Prepare a transformation matrix for character and offset rotation
118+
angle = 2 * M_PI * (angle / 360.0f);
119+
pretty_poly::mat3_t transform = pretty_poly::mat3_t::rotation(angle);
120+
121+
// Align text from the bottom left
122+
caret.y += text_metrics.line_height;
123+
caret *= transform;
124+
125+
pretty_poly::point_t<float> space;
126+
pretty_poly::point_t<float> carriage_return(0, text_metrics.line_height);
127+
128+
space.x = alright_fonts::measure_character(text_metrics, ' ').w;
129+
if (space.x == 0) {
130+
space.x = text_metrics.word_spacing;
131+
}
132+
133+
space *= transform;
134+
carriage_return *= transform;
135+
136+
size_t i = 0;
137+
138+
while(i < text.length()) {
139+
size_t next_space = text.find(' ', i + 1);
140+
141+
if(next_space == std::string::npos) {
142+
next_space = text.length();
143+
}
144+
145+
size_t next_linebreak = text.find('\n', i + 1);
146+
147+
if(next_linebreak == std::string::npos) {
148+
next_linebreak = text.length();
149+
}
150+
151+
size_t next_break = std::min(next_space, next_linebreak);
152+
153+
uint16_t word_width = 0;
154+
for(size_t j = i; j < next_break; j++) {
155+
word_width += alright_fonts::measure_character(text_metrics, text[j]).w;
156+
word_width += text_metrics.letter_spacing;
157+
}
158+
159+
if(caret.x != 0 && caret.x + word_width > graphics->clip.w) {
160+
caret -= carriage_return;
161+
carriage_return.x = 0;
162+
}
163+
164+
for(size_t j = i; j < std::min(next_break + 1, text.length()); j++) {
165+
if (text[j] == '\n') { // Linebreak
166+
caret -= carriage_return;
167+
carriage_return.x = 0;
168+
} else if (text[j] == ' ') { // Space
169+
caret += space;
170+
carriage_return += space;
171+
} else {
172+
alright_fonts::render_character(text_metrics, text[j], pretty_poly::point_t<int>(origin.x + caret.x, origin.y + caret.y), transform);
173+
}
174+
pretty_poly::point_t<float> advance(
175+
alright_fonts::measure_character(text_metrics, text[j]).w + text_metrics.letter_spacing,
176+
0
177+
);
178+
advance *= transform;
179+
caret += advance;
180+
carriage_return += advance;
181+
}
182+
183+
i = next_break + 1;
184+
}
185+
186+
return Point(caret.x, caret.y);
187+
}
112188
}

libraries/pico_vector/pico_vector.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ namespace pimoroni {
7070
void translate(pretty_poly::contour_t<picovector_point_type> &contour, Point translation);
7171

7272
Point text(std::string_view text, Point origin);
73+
Point text(std::string_view text, Point origin, float angle);
7374

7475
void polygon(std::vector<pretty_poly::contour_t<picovector_point_type>> contours, Point origin = Point(0, 0), int scale=65536);
7576

micropython/modules/picovector/picovector.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,12 +325,13 @@ mp_obj_t VECTOR_set_antialiasing(mp_obj_t self_in, mp_obj_t aa) {
325325
}
326326

327327
mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
328-
enum { ARG_self, ARG_text, ARG_x, ARG_y };
328+
enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_angle };
329329
static const mp_arg_t allowed_args[] = {
330330
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
331331
{ MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ },
332332
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT },
333-
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }
333+
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT },
334+
{ MP_QSTR_angle, MP_ARG_OBJ, {.u_obj = mp_const_none} }
334335
};
335336

336337
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)
349350
int x = args[ARG_x].u_int;
350351
int y = args[ARG_y].u_int;
351352

352-
self->vector->text(t, Point(x, y));
353+
if(args[ARG_angle].u_obj == mp_const_none) {
354+
self->vector->text(t, Point(x, y));
355+
} else {
356+
self->vector->text(t, Point(x, y), mp_obj_get_float(args[ARG_angle].u_obj));
357+
}
353358

354359
return mp_const_none;
355360
}

0 commit comments

Comments
 (0)