Skip to content

Commit c3919bd

Browse files
authored
Merge pull request #840 from MichaelBell/patch-pretty-poly-perf
Improve pretty_poly performance
2 parents ed3ce45 + 80e1e16 commit c3919bd

File tree

8 files changed

+206
-64
lines changed

8 files changed

+206
-64
lines changed

libraries/pico_vector/alright_fonts.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ 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) {
44+
template<typename mat_t>
45+
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, mat_t transform) {
4546
if(tm.face.glyphs.count(codepoint) == 1) {
4647
glyph_t glyph = tm.face.glyphs[codepoint];
4748

@@ -51,26 +52,35 @@ namespace alright_fonts {
5152
unsigned scale = tm.size << 9;
5253

5354
std::vector<pretty_poly::contour_t<int8_t>> contours;
55+
contours.reserve(glyph.contours.size());
5456

57+
unsigned int total_points = 0;
5558
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);
59+
total_points += glyph.contours[i].count;;
60+
}
61+
62+
point_t<int8_t> *points = (point_t<int8_t> *)malloc(sizeof(point_t<int8_t>) * total_points);
63+
64+
for(auto i = 0u; i < glyph.contours.size(); i++) {
65+
const unsigned int count = glyph.contours[i].count;
5866
for(auto j = 0u; j < count; j++) {
5967
point_t<float> point(glyph.contours[i].points[j].x, glyph.contours[i].points[j].y);
6068
point *= transform;
6169
points[j] = point_t<int8_t>(point.x, point.y);
6270
}
6371
contours.emplace_back(points, count);
72+
points += count;
6473
}
6574

6675
pretty_poly::draw_polygon<int8_t>(contours, origin, scale);
6776

68-
for(auto contour : contours) {
69-
free(contour.points);
70-
}
77+
free(contours[0].points);
7178
}
7279
}
7380

81+
template void render_character<pretty_poly::mat3_t>(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, pretty_poly::mat3_t transform);
82+
template void render_character<pretty_poly::mat2_t>(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, pretty_poly::mat2_t transform);
83+
7484
/*
7585
load functions
7686
*/

libraries/pico_vector/alright_fonts.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,6 @@ 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);
73+
template<typename mat_t>
74+
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, mat_t transform);
7475
}

libraries/pico_vector/pico_vector.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ add_library(pico_vector
66

77
target_include_directories(pico_vector INTERFACE ${CMAKE_CURRENT_LIST_DIR})
88

9-
target_link_libraries(pico_vector pico_stdlib)
9+
target_link_libraries(pico_vector pico_stdlib hardware_interp)

libraries/pico_vector/pico_vector.cpp

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,44 +10,42 @@ namespace pimoroni {
1010
}
1111

1212
void PicoVector::rotate(std::vector<pretty_poly::contour_t<picovector_point_type>> &contours, Point origin, float angle) {
13-
pretty_poly::mat3_t t2 = pretty_poly::mat3_t::translation(origin.x, origin.y);
14-
pretty_poly::mat3_t t1 = pretty_poly::mat3_t::translation(-origin.x, -origin.y);
15-
angle = 2 * M_PI * (angle / 360.0f);
16-
pretty_poly::mat3_t r = pretty_poly::mat3_t::rotation(angle);
13+
pretty_poly::point_t<picovector_point_type> t{(picovector_point_type)origin.x, (picovector_point_type)origin.y};
14+
angle = (2 * (float)M_PI / 360.f) * angle;
15+
pretty_poly::mat2_t r = pretty_poly::mat2_t::rotation(angle);
1716
for(auto &contour : contours) {
1817
for(auto i = 0u; i < contour.count; i++) {
19-
contour.points[i] *= t1;
18+
contour.points[i] -= t;
2019
contour.points[i] *= r;
21-
contour.points[i] *= t2;
20+
contour.points[i] += t;
2221
}
2322
}
2423
}
2524

2625
void PicoVector::translate(std::vector<pretty_poly::contour_t<picovector_point_type>> &contours, Point translation) {
27-
pretty_poly::mat3_t t = pretty_poly::mat3_t::translation(translation.x, translation.y);
26+
pretty_poly::point_t<picovector_point_type> t{(picovector_point_type)translation.x, (picovector_point_type)translation.y};
2827
for(auto &contour : contours) {
2928
for(auto i = 0u; i < contour.count; i++) {
30-
contour.points[i] *= t;
29+
contour.points[i] += t;
3130
}
3231
}
3332
}
3433

3534
void PicoVector::rotate(pretty_poly::contour_t<picovector_point_type> &contour, Point origin, float angle) {
36-
pretty_poly::mat3_t t2 = pretty_poly::mat3_t::translation(origin.x, origin.y);
37-
pretty_poly::mat3_t t1 = pretty_poly::mat3_t::translation(-origin.x, -origin.y);
38-
angle = 2 * M_PI * (angle / 360.0f);
39-
pretty_poly::mat3_t r = pretty_poly::mat3_t::rotation(angle);
35+
pretty_poly::point_t<picovector_point_type> t{(picovector_point_type)origin.x, (picovector_point_type)origin.y};
36+
angle = (2 * (float)M_PI / 360.f) * angle;
37+
pretty_poly::mat2_t r = pretty_poly::mat2_t::rotation(angle);
4038
for(auto i = 0u; i < contour.count; i++) {
41-
contour.points[i] *= t1;
39+
contour.points[i] -= t;
4240
contour.points[i] *= r;
43-
contour.points[i] *= t2;
41+
contour.points[i] += t;
4442
}
4543
}
4644

4745
void PicoVector::translate(pretty_poly::contour_t<picovector_point_type> &contour, Point translation) {
48-
pretty_poly::mat3_t t = pretty_poly::mat3_t::translation(translation.x, translation.y);
46+
pretty_poly::point_t<picovector_point_type> t{(picovector_point_type)translation.x, (picovector_point_type)translation.y};
4947
for(auto i = 0u; i < contour.count; i++) {
50-
contour.points[i] *= t;
48+
contour.points[i] += t;
5149
}
5250
}
5351

@@ -115,8 +113,8 @@ namespace pimoroni {
115113
pretty_poly::point_t<float> caret(0, 0);
116114

117115
// 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);
116+
angle = (2 * (float)M_PI / 360.f) * angle;
117+
pretty_poly::mat2_t transform = pretty_poly::mat2_t::rotation(angle);
120118

121119
// Align text from the bottom left
122120
caret.y += text_metrics.line_height;

libraries/pico_vector/pretty_poly.cpp

Lines changed: 133 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "pretty_poly.hpp"
1010

11+
#include "hardware/interp.h"
1112

1213
#ifdef PP_DEBUG
1314
#define debug(...) printf(__VA_ARGS__)
@@ -79,35 +80,63 @@ namespace pretty_poly {
7980
std::swap(sx, ex);
8081
}
8182

82-
/*sx <<= settings::antialias;
83-
ex <<= settings::antialias;
84-
sy <<= settings::antialias;
85-
ey <<= settings::antialias;*/
83+
// Early out if line is completely outside the tile, or has no lines
84+
if (ey < 0 || sy >= (int)node_buffer_size || sy == ey) return;
8685

86+
debug(" + line segment from %d, %d to %d, %d\n", sx, sy, ex, ey);
87+
88+
// Determine how many in-bounds lines to render
89+
int y = std::max(0, sy);
90+
int count = std::min((int)node_buffer_size, ey) - y;
91+
92+
// Handle cases where x is completely off to one side or other
93+
if (std::max(sx, ex) <= 0) {
94+
while (count--) {
95+
nodes[y][node_counts[y]++] = 0;
96+
++y;
97+
}
98+
return;
99+
}
100+
101+
const int full_tile_width = (tile_bounds.w << settings::antialias);
102+
if (std::min(sx, ex) >= full_tile_width) {
103+
while (count--) {
104+
nodes[y][node_counts[y]++] = full_tile_width;
105+
++y;
106+
}
107+
return;
108+
}
109+
110+
// Normal case
87111
int x = sx;
88-
int y = sy;
89112
int e = 0;
90113

91-
int xinc = sign(ex - sx);
92-
int einc = abs(ex - sx) + 1;
114+
const int xinc = sign(ex - sx);
115+
const int einc = abs(ex - sx) + 1;
116+
const int dy = ey - sy;
117+
118+
// If sy < 0 jump to the start, note this does use a divide
119+
// but potentially saves many wasted loops below, so is likely worth it.
120+
if (sy < 0) {
121+
e = einc * -sy;
122+
int xjump = e / dy;
123+
e -= dy * xjump;
124+
x += xinc * xjump;
125+
}
126+
127+
interp1->base[1] = full_tile_width;
128+
interp1->accum[0] = x;
93129

94-
// todo: preclamp sy and ey (and therefore count) no need to perform
95-
// that test inside the loop
96-
int dy = ey - sy;
97-
int count = dy;
98-
debug(" + line segment from %d, %d to %d, %d\n", sx, sy, ex, ey);
99130
// loop over scanlines
100131
while(count--) {
101132
// consume accumulated error
102-
while(e > dy) {e -= dy; x += xinc;}
103-
104-
if(y >= 0 && y < (int)node_buffer_size) {
105-
// clamp node x value to tile bounds
106-
int nx = std::max(std::min(x, (int)(tile_bounds.w << settings::antialias)), 0);
107-
debug(" + adding node at %d, %d\n", x, y);
108-
// add node to node list
109-
nodes[y][node_counts[y]++] = nx;
110-
}
133+
while(e > dy) {e -= dy; interp1->add_raw[0] = xinc;}
134+
135+
// clamp node x value to tile bounds
136+
const int nx = interp1->peek[0];
137+
debug(" + adding node at %d, %d\n", x, y);
138+
// add node to node list
139+
nodes[y][node_counts[y]++] = nx;
111140

112141
// step to next scanline and accumulate error
113142
y++;
@@ -138,14 +167,23 @@ namespace pretty_poly {
138167
}
139168
}
140169

141-
void render_nodes(const tile_t &tile) {
170+
void render_nodes(const tile_t &tile, rect_t &bounds) {
171+
int maxy = -1;
172+
bounds.y = 0;
173+
bounds.x = tile.bounds.w;
174+
int maxx = 0;
175+
int anitialias_mask = (1 << settings::antialias) - 1;
176+
142177
for(auto y = 0; y < (int)node_buffer_size; y++) {
143178
if(node_counts[y] == 0) {
179+
if (y == bounds.y) ++bounds.y;
144180
continue;
145181
}
146182

147183
std::sort(&nodes[y][0], &nodes[y][0] + node_counts[y]);
148184

185+
uint8_t* row_data = &tile.data[(y >> settings::antialias) * tile.stride];
186+
bool rendered_any = false;
149187
for(auto i = 0u; i < node_counts[y]; i += 2) {
150188
int sx = nodes[y][i + 0];
151189
int ex = nodes[y][i + 1];
@@ -154,13 +192,55 @@ namespace pretty_poly {
154192
continue;
155193
}
156194

195+
rendered_any = true;
196+
197+
maxx = std::max((ex - 1) >> settings::antialias, maxx);
198+
157199
debug(" - render span at %d from %d to %d\n", y, sx, ex);
158200

159-
for(int x = sx; x < ex; x++) {
160-
tile.data[(x >> settings::antialias) + (y >> settings::antialias) * tile.stride]++;
161-
}
201+
if (settings::antialias) {
202+
int ax = sx >> settings::antialias;
203+
const int aex = ex >> settings::antialias;
204+
205+
bounds.x = std::min(ax, bounds.x);
206+
207+
if (ax == aex) {
208+
row_data[ax] += ex - sx;
209+
continue;
210+
}
211+
212+
row_data[ax] += (1 << settings::antialias) - (sx & anitialias_mask);
213+
for(ax++; ax < aex; ax++) {
214+
row_data[ax] += (1 << settings::antialias);
215+
}
216+
217+
// This might add 0 to the byte after the end of the row, we pad the tile data
218+
// by 1 byte to ensure that is OK
219+
row_data[ax] += ex & anitialias_mask;
220+
}
221+
else {
222+
bounds.x = std::min(sx, bounds.x);
223+
for(int x = sx; x < ex; x++) {
224+
row_data[x]++;
225+
}
226+
}
227+
}
228+
229+
if (rendered_any) {
230+
debug(" - rendered line %d\n", y);
231+
maxy = y;
232+
}
233+
else if (y == bounds.y) {
234+
debug(" - render nothing on line %d\n", y);
235+
++bounds.y;
162236
}
163237
}
238+
239+
bounds.y >>= settings::antialias;
240+
maxy >>= settings::antialias;
241+
bounds.w = (maxx >= bounds.x) ? maxx + 1 - bounds.x : 0;
242+
bounds.h = (maxy >= bounds.y) ? maxy + 1 - bounds.y : 0;
243+
debug(" - rendered tile bounds %d, %d (%d x %d)\n", bounds.x, bounds.y, bounds.w, bounds.h);
164244
}
165245

166246
template<typename T>
@@ -172,7 +252,7 @@ namespace pretty_poly {
172252
}
173253

174254
template<typename T>
175-
void draw_polygon(std::vector<contour_t<T>> contours, point_t<int> origin, int scale) {
255+
void draw_polygon(const std::vector<contour_t<T>>& contours, point_t<int> origin, int scale) {
176256

177257
debug("> draw polygon with %lu contours\n", contours.size());
178258

@@ -194,8 +274,16 @@ namespace pretty_poly {
194274
debug(" - bounds %d, %d (%d x %d)\n", polygon_bounds.x, polygon_bounds.y, polygon_bounds.w, polygon_bounds.h);
195275
debug(" - clip %d, %d (%d x %d)\n", settings::clip.x, settings::clip.y, settings::clip.w, settings::clip.h);
196276

277+
interp_hw_save_t interp1_save;
278+
interp_save(interp1, &interp1_save);
279+
280+
interp_config cfg = interp_default_config();
281+
interp_config_set_clamp(&cfg, true);
282+
interp_config_set_signed(&cfg, true);
283+
interp_set_config(interp1, 0, &cfg);
284+
interp1->base[0] = 0;
197285

198-
memset(nodes, 0, node_buffer_size * sizeof(unsigned) * 32);
286+
//memset(nodes, 0, node_buffer_size * sizeof(unsigned) * 32);
199287

200288
// iterate over tiles
201289
debug(" - processing tiles\n");
@@ -218,22 +306,34 @@ namespace pretty_poly {
218306
memset(tile.data, 0, tile_buffer_size);
219307

220308
// build the nodes for each contour
221-
for(contour_t<T> &contour : contours) {
309+
for(const contour_t<T> &contour : contours) {
222310
debug(" : build nodes for contour\n");
223311
build_nodes(contour, tile, origin, scale);
224312
}
225313

226314
debug(" : render the tile\n");
227315
// render the tile
228-
render_nodes(tile);
316+
rect_t bounds;
317+
render_nodes(tile, bounds);
318+
if (bounds.empty()) {
319+
continue;
320+
}
321+
322+
tile.data += bounds.x + tile.stride * bounds.y;
323+
tile.bounds.x += bounds.x;
324+
tile.bounds.y += bounds.y;
325+
tile.bounds.w = bounds.w;
326+
tile.bounds.h = bounds.h;
229327

230328
settings::callback(tile);
231329
}
232330
}
331+
332+
interp_restore(interp1, &interp1_save);
233333
}
234334
}
235335

236-
template void pretty_poly::draw_polygon<int>(std::vector<contour_t<int>> contours, point_t<int> origin, int scale);
237-
template void pretty_poly::draw_polygon<float>(std::vector<contour_t<float>> contours, point_t<int> origin, int scale);
238-
template void pretty_poly::draw_polygon<uint8_t>(std::vector<contour_t<uint8_t>> contours, point_t<int> origin, int scale);
239-
template void pretty_poly::draw_polygon<int8_t>(std::vector<contour_t<int8_t>> contours, point_t<int> origin, int scale);
336+
template void pretty_poly::draw_polygon<int>(const std::vector<contour_t<int>>& contours, point_t<int> origin, int scale);
337+
template void pretty_poly::draw_polygon<float>(const std::vector<contour_t<float>>& contours, point_t<int> origin, int scale);
338+
template void pretty_poly::draw_polygon<uint8_t>(const std::vector<contour_t<uint8_t>>& contours, point_t<int> origin, int scale);
339+
template void pretty_poly::draw_polygon<int8_t>(const std::vector<contour_t<int8_t>>& contours, point_t<int> origin, int scale);

0 commit comments

Comments
 (0)