diff --git a/libraries/pico_vector/pico_vector.hpp b/libraries/pico_vector/pico_vector.hpp index 2e88d8759..bf3b21174 100644 --- a/libraries/pico_vector/pico_vector.hpp +++ b/libraries/pico_vector/pico_vector.hpp @@ -34,9 +34,13 @@ namespace pimoroni { public: static PicoGraphics *graphics; - PicoVector(PicoGraphics *graphics, void *mem = nullptr) { + PicoVector(PicoGraphics *graphics) { PicoVector::graphics = graphics; + // TODO: Make these configurable? + // Tile buffer size, Max nodes per scanline + pp_init(16, 16); + pp_tile_callback(PicoVector::tile_callback); set_antialiasing(graphics->supports_alpha_blend() ? PP_AA_X4 : PP_AA_NONE); @@ -48,11 +52,15 @@ namespace pimoroni { text_metrics.letter_spacing = 95; text_metrics.word_spacing = 200; text_metrics.size = 48; - // Shoud be set before rendering chars + // Should be set before rendering chars //text_metrics.transform = (pp_mat3_t *)af_malloc(sizeof(pp_mat3_t)); //*text_metrics.transform = pp_mat3_identity(); } + ~PicoVector() { + pp_deinit(); + } + static void tile_callback(const pp_tile_t *tile) { // TODO: we're using a cast here to avoid a hard dependency link between // PicoGraphics and PicoVector. These types might subtly mismatch, though... diff --git a/libraries/pico_vector/pretty-poly.h b/libraries/pico_vector/pretty-poly.h index c92abec45..1f3287ebc 100644 --- a/libraries/pico_vector/pretty-poly.h +++ b/libraries/pico_vector/pretty-poly.h @@ -40,14 +40,6 @@ #define PP_COORD_TYPE float #endif -#ifndef PP_MAX_NODES_PER_SCANLINE -#define PP_MAX_NODES_PER_SCANLINE 16 -#endif - -#ifndef PP_TILE_BUFFER_SIZE -#define PP_TILE_BUFFER_SIZE 64 -#endif - #ifndef PP_SCALE_TO_ALPHA #define PP_SCALE_TO_ALPHA 1 #endif @@ -126,6 +118,8 @@ void pp_poly_merge(pp_poly_t *p, pp_poly_t *m); // user settings typedef void (*pp_tile_callback_t)(const pp_tile_t *tile); +extern uint32_t _pp_tile_buffer_size; + extern pp_rect_t _pp_clip; extern pp_tile_callback_t _pp_tile_callback; extern pp_antialias_t _pp_antialias; @@ -137,6 +131,9 @@ void pp_antialias(pp_antialias_t antialias); pp_mat3_t *pp_transform(pp_mat3_t *transform); void pp_render(pp_poly_t *polygon); +void pp_init(uint32_t tile_buffer_size, uint32_t max_nodes_per_scanline); +void pp_deinit(); + #ifdef __cplusplus } @@ -257,7 +254,7 @@ pp_rect_t pp_rect_transform(pp_rect_t *r, pp_mat3_t *m) { // pp_tile_t implementation uint8_t pp_tile_get(const pp_tile_t *tile, const int32_t x, const int32_t y) { - return tile->data[(x - tile->x) + (y - tile->y) * PP_TILE_BUFFER_SIZE]; + return tile->data[(x - tile->x) + (y - tile->y) * _pp_tile_buffer_size]; } pp_poly_t *pp_poly_new() { @@ -380,19 +377,40 @@ pp_rect_t pp_poly_bounds(pp_poly_t *p) { return b; } +uint32_t _pp_tile_buffer_size = 0; +uint32_t _pp_max_nodes_per_scanline = 0; + // buffer that each tile is rendered into before callback // allocate one extra byte to allow a small optimization in the row renderer -uint8_t pp_tile_buffer[PP_TILE_BUFFER_SIZE * PP_TILE_BUFFER_SIZE]; +uint8_t *pp_tile_buffer; +//uint8_t pp_tile_buffer[PP_TILE_BUFFER_SIZE * PP_TILE_BUFFER_SIZE]; // polygon node buffer handles at most 16 line intersections per scanline // is this enough for cjk/emoji? (requires a 2kB buffer) -int32_t pp_nodes[PP_TILE_BUFFER_SIZE * 4][PP_MAX_NODES_PER_SCANLINE * 2]; -uint32_t pp_node_counts[PP_TILE_BUFFER_SIZE * 4]; +int32_t *pp_nodes; +uint32_t *pp_node_counts; +//int32_t pp_nodes[PP_TILE_BUFFER_SIZE * 4][PP_MAX_NODES_PER_SCANLINE * 2]; +//uint32_t pp_node_counts[PP_TILE_BUFFER_SIZE * 4]; uint8_t _pp_alpha_map_none[2] = {0, 255}; uint8_t _pp_alpha_map_x4[5] = {0, 63, 127, 190, 255}; uint8_t _pp_alpha_map_x16[17] = {0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 255}; +void pp_init(uint32_t tile_buffer_size, uint32_t max_nodes_per_scanline) { + _pp_tile_buffer_size = tile_buffer_size; + _pp_max_nodes_per_scanline = max_nodes_per_scanline; + pp_tile_buffer = (uint8_t *)PP_MALLOC(tile_buffer_size * tile_buffer_size); + // tile_buffer_size * 4 | max_nodes_per_scanline * 2 + pp_nodes = (int32_t *)PP_MALLOC(tile_buffer_size * 4 * max_nodes_per_scanline * 2 * sizeof(int32_t)); + pp_node_counts = (uint32_t *)PP_MALLOC(tile_buffer_size * 4 * sizeof(uint32_t)); +} + +void pp_deinit() { + PP_FREE(pp_tile_buffer); + PP_FREE(pp_nodes); + PP_FREE(pp_node_counts); +} + void pp_clip(int32_t x, int32_t y, int32_t w, int32_t h) { _pp_clip = (pp_rect_t){.x = x, .y = y, .w = w, .h = h}; } @@ -482,7 +500,10 @@ void add_line_segment_to_nodes(const pp_point_t start, const pp_point_t end, pp_ // } // #else // loop over scanlines + while(count--) { + int32_t *pp_scanline_nodes = &pp_nodes[y * 4 * _pp_max_nodes_per_scanline * 2]; + // consume accumulated error while(e > dy) {e -= dy; x += xinc;} @@ -490,7 +511,7 @@ void add_line_segment_to_nodes(const pp_point_t start, const pp_point_t end, pp_ int nx = _pp_max(_pp_min(x, (tb->w << _pp_antialias)), 0); //debug(" + adding node at %d, %d\n", x, y); // add node to node list - pp_nodes[y][pp_node_counts[y]++] = nx; + pp_scanline_nodes[pp_node_counts[y]++] = nx; // step to next scanline and accumulate error y++; @@ -525,23 +546,24 @@ int compare_nodes(const void* a, const void* b) { } pp_rect_t render_nodes(pp_rect_t *tb) { - pp_rect_t rb = {PP_TILE_BUFFER_SIZE << _pp_antialias, PP_TILE_BUFFER_SIZE << _pp_antialias, 0, 0}; // render bounds - int maxx = 0, minx = PP_TILE_BUFFER_SIZE << _pp_antialias; + pp_rect_t rb = {_pp_tile_buffer_size << _pp_antialias, _pp_tile_buffer_size << _pp_antialias, 0, 0}; // render bounds + int maxx = 0, minx = _pp_tile_buffer_size << _pp_antialias; debug(" + render tile %d, %d - %d, %d\n", tb->x, tb->y, tb->w, tb->h); - for(int y = 0; y < ((int)PP_TILE_BUFFER_SIZE << _pp_antialias); y++) { + for(int y = 0; y < ((int)_pp_tile_buffer_size << _pp_antialias); y++) { + int32_t *pp_scanline_nodes = &pp_nodes[y * 4 * _pp_max_nodes_per_scanline * 2]; // debug(" : row %d node count %d\n", y, pp_node_counts[y]); if(pp_node_counts[y] == 0) continue; // no nodes on this raster line - qsort(&pp_nodes[y][0], pp_node_counts[y], sizeof(int), compare_nodes); + qsort(pp_scanline_nodes, pp_node_counts[y], sizeof(int), compare_nodes); - unsigned char* row_data = &pp_tile_buffer[(y >> _pp_antialias) * PP_TILE_BUFFER_SIZE]; + unsigned char* row_data = &pp_tile_buffer[(y >> _pp_antialias) * _pp_tile_buffer_size]; for(uint32_t i = 0; i < pp_node_counts[y]; i += 2) { - int sx = pp_nodes[y][i + 0]; - int ex = pp_nodes[y][i + 1]; + int sx = *pp_scanline_nodes++; + int ex = *pp_scanline_nodes++; if(sx == ex) { // empty span, nothing to do continue; @@ -584,7 +606,7 @@ pp_rect_t render_nodes(pp_rect_t *tb) { if(_pp_antialias == 2) p_alpha_map = _pp_alpha_map_x16; #if PP_SCALE_TO_ALPHA == 1 for(int y = rb.y; y < rb.y + rb.h; y++) { - unsigned char* row_data = &pp_tile_buffer[y * PP_TILE_BUFFER_SIZE + rb.x]; + unsigned char* row_data = &pp_tile_buffer[y * _pp_tile_buffer_size + rb.x]; for(int x = rb.x; x < rb.x + rb.w; x++) { *row_data = p_alpha_map[*row_data]; row_data++; @@ -626,9 +648,9 @@ void pp_render(pp_poly_t *polygon) { // iterate over tiles debug(" - processing tiles\n"); - for(int32_t y = pb.y; y < pb.y + pb.h; y += PP_TILE_BUFFER_SIZE) { - for(int32_t x = pb.x; x < pb.x + pb.w; x += PP_TILE_BUFFER_SIZE) { - pp_rect_t tb = (pp_rect_t){.x = x, .y = y, .w = PP_TILE_BUFFER_SIZE, .h = PP_TILE_BUFFER_SIZE}; + for(int32_t y = pb.y; y < pb.y + pb.h; y += _pp_tile_buffer_size) { + for(int32_t x = pb.x; x < pb.x + pb.w; x += _pp_tile_buffer_size) { + pp_rect_t tb = (pp_rect_t){.x = x, .y = y, .w = _pp_tile_buffer_size, .h = _pp_tile_buffer_size}; tb = pp_rect_intersection(&tb, &_pp_clip); debug(" : %d, %d (%d x %d)\n", tb.x, tb.y, tb.w, tb.h); @@ -636,8 +658,8 @@ void pp_render(pp_poly_t *polygon) { if(pp_rect_empty(&tb)) { debug(" : empty when clipped, skipping\n"); continue; } // clear existing tile data and nodes - memset(pp_node_counts, 0, sizeof(pp_node_counts)); - memset(pp_tile_buffer, 0, PP_TILE_BUFFER_SIZE * PP_TILE_BUFFER_SIZE); + memset(pp_node_counts, 0, _pp_tile_buffer_size * 4 * sizeof(uint32_t)); + memset(pp_tile_buffer, 0, _pp_tile_buffer_size * _pp_tile_buffer_size); // build the nodes for each pp_path_t pp_path_t *path = polygon->paths; @@ -658,8 +680,8 @@ void pp_render(pp_poly_t *polygon) { pp_tile_t tile = { .x = tb.x, .y = tb.y, .w = tb.w, .h = tb.h, - .stride = PP_TILE_BUFFER_SIZE, - .data = pp_tile_buffer + rb.x + (PP_TILE_BUFFER_SIZE * rb.y) + .stride = _pp_tile_buffer_size, + .data = pp_tile_buffer + rb.x + (_pp_tile_buffer_size * rb.y) }; _pp_tile_callback(&tile); diff --git a/micropython/modules/picovector/picovector.cpp b/micropython/modules/picovector/picovector.cpp index df0919cd7..c791197fb 100644 --- a/micropython/modules/picovector/picovector.cpp +++ b/micropython/modules/picovector/picovector.cpp @@ -21,7 +21,6 @@ typedef struct _ModPicoGraphics_obj_t { typedef struct _VECTOR_obj_t { mp_obj_base_t base; - void *mem; PicoVector *vector; } _VECTOR_obj_t; @@ -545,12 +544,7 @@ mp_obj_t VECTOR_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, self->base.type = &VECTOR_type; ModPicoGraphics_obj_t *graphics = (ModPicoGraphics_obj_t *)MP_OBJ_TO_PTR(args[ARG_picographics].u_obj); - // The PicoVector class calls `pretty_poly::init()` with the memory region - // it does not store a pointer to this, so we need to store one ourselves - // TODO: C Pretty Poly does not support runtime memory allocation - //self->mem = m_new(uint8_t, PicoVector::pretty_poly_buffer_size()); - - self->vector = m_new_class(PicoVector, graphics->graphics, self->mem); + self->vector = m_new_class(PicoVector, graphics->graphics); return self; }