diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index 677d7695..9953f754 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -143,6 +143,12 @@ namespace pimoroni { typedef int Pen; + struct Tile { + int32_t x, y, w, h; + uint32_t stride; + uint8_t *data; + }; + struct Rect; struct Point { @@ -307,7 +313,7 @@ namespace pimoroni { virtual void frame_convert(PenType type, conversion_callback_func callback); virtual void sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent); - virtual bool render_pico_vector_tile(const Rect &bounds, uint8_t* alpha_data, uint32_t stride, uint8_t alpha_type) { return false; } + virtual bool render_tile(const Tile *tile) { return false; } void set_font(const bitmap::font_t *font); void set_font(const hershey::font_t *font); @@ -454,6 +460,8 @@ namespace pimoroni { static size_t buffer_size(uint w, uint h) { return w * h / 2; } + + bool render_tile(const Tile *tile); }; class PicoGraphics_PenP8 : public PicoGraphics { @@ -487,6 +495,8 @@ namespace pimoroni { static size_t buffer_size(uint w, uint h) { return w * h; } + + bool render_tile(const Tile *tile); }; class PicoGraphics_PenRGB332 : public PicoGraphics { @@ -511,6 +521,8 @@ namespace pimoroni { static size_t buffer_size(uint w, uint h) { return w * h; } + + bool render_tile(const Tile *tile); }; class PicoGraphics_PenRGB565 : public PicoGraphics { @@ -535,6 +547,8 @@ namespace pimoroni { void set_pixel_alpha(const Point &p, const uint8_t a) override; bool supports_alpha_blend() override {return true;} + + bool render_tile(const Tile *tile); }; class PicoGraphics_PenRGB888 : public PicoGraphics { @@ -551,6 +565,8 @@ namespace pimoroni { static size_t buffer_size(uint w, uint h) { return w * h * sizeof(uint32_t); } + + bool render_tile(const Tile *tile); }; diff --git a/libraries/pico_graphics/pico_graphics_pen_p4.cpp b/libraries/pico_graphics/pico_graphics_pen_p4.cpp index af21fd48..6b5f8e15 100644 --- a/libraries/pico_graphics/pico_graphics_pen_p4.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_p4.cpp @@ -185,4 +185,27 @@ namespace pimoroni { } } } + bool PicoGraphics_PenP4::render_tile(const Tile *tile) { + for(int y = 0; y < tile->h; y++) { + uint8_t *palpha = &tile->data[(y * tile->stride)]; + uint8_t *pdest = &((uint8_t *)frame_buffer)[(tile->x / 2) + ((tile->y + y) * (bounds.w / 2))]; + for(int x = 0; x < tile->w; x++) { + uint8_t shift = (x & 1) ? 0 : 4; + uint8_t alpha = *palpha; + + if(alpha == 0) { + } else { + *pdest &= shift ? 0x0f : 0xf0; + *pdest |= color << shift; + } + + if(x & 1) { + pdest++; + } + palpha++; + } + } + + return true; + } } diff --git a/libraries/pico_graphics/pico_graphics_pen_p8.cpp b/libraries/pico_graphics/pico_graphics_pen_p8.cpp index fd952690..10f32098 100644 --- a/libraries/pico_graphics/pico_graphics_pen_p8.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_p8.cpp @@ -171,4 +171,24 @@ namespace pimoroni { } } } + + bool PicoGraphics_PenP8::render_tile(const Tile *tile) { + for(int y = 0; y < tile->h; y++) { + uint8_t *palpha = &tile->data[(y * tile->stride)]; + uint8_t *pdest = &((uint8_t *)frame_buffer)[tile->x + ((tile->y + y) * bounds.w)]; + for(int x = 0; x < tile->w; x++) { + uint8_t alpha = *palpha; + + if(alpha == 0) { + } else { + *pdest = color; + } + + pdest++; + palpha++; + } + } + + return true; + } } diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp index 3c8ca852..cdb24da2 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp @@ -145,4 +145,24 @@ namespace pimoroni { } } } + bool PicoGraphics_PenRGB332::render_tile(const Tile *tile) { + for(int y = 0; y < tile->h; y++) { + uint8_t *palpha = &tile->data[(y * tile->stride)]; + uint8_t *pdest = &((uint8_t *)frame_buffer)[tile->x + ((tile->y + y) * bounds.w)]; + for(int x = 0; x < tile->w; x++) { + uint8_t alpha = *palpha; + + // TODO: Try to alpha blend RGB332... somewhat? + if(alpha == 0) { + } else { + *pdest = color; + } + + pdest++; + palpha++; + } + } + + return true; + } } diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp index 6c5b3910..6cf2d863 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp @@ -93,4 +93,41 @@ namespace pimoroni { } } } + + bool PicoGraphics_PenRGB565::render_tile(const Tile *tile) { + for(int y = 0; y < tile->h; y++) { + uint8_t *palpha = &tile->data[(y * tile->stride)]; + uint16_t *pdest = &((uint16_t *)frame_buffer)[tile->x + ((tile->y + y) * bounds.w)]; + for(int x = 0; x < tile->w; x++) { + uint16_t dest = *pdest; + uint8_t alpha = *palpha; + + if(alpha == 255) { + *pdest = color; + }else if(alpha == 0) { + }else{ + // blend tha pixel + uint16_t sr = (color & 0b1111100000000000) >> 11; + uint16_t sg = (color & 0b0000011111100000) >> 5; + uint16_t sb = (color & 0b0000000000011111); + + uint16_t dr = (dest & 0b1111100000000000) >> 11; + uint16_t dg = (dest & 0b0000011111100000) >> 5; + uint16_t db = (dest & 0b0000000000011111); + + uint8_t r = ((sr * alpha) + (dr * (255 - alpha))) >> 8; + uint8_t g = ((sg * alpha) + (dg * (255 - alpha))) >> 8; + uint8_t b = ((sb * alpha) + (db * (255 - alpha))) >> 8; + + // recombine the channels + *pdest = (r << 11) | (g << 5) | (b); + } + + pdest++; + palpha++; + } + } + + return true; + } } diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp index 0b145239..642557a4 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp @@ -36,4 +36,24 @@ namespace pimoroni { *buf++ = color; } } + bool PicoGraphics_PenRGB888::render_tile(const Tile *tile) { + for(int y = 0; y < tile->h; y++) { + uint8_t *palpha = &tile->data[(y * tile->stride)]; + uint32_t *pdest = &((uint32_t *)frame_buffer)[tile->x + ((tile->y + y) * bounds.w)]; + for(int x = 0; x < tile->w; x++) { + uint8_t alpha = *palpha; + + // TODO: Alpha blending + if(alpha == 0) { + } else { + *pdest = color; + } + + pdest++; + palpha++; + } + } + + return true; + } } \ No newline at end of file diff --git a/libraries/pico_vector/pico_vector.cpp b/libraries/pico_vector/pico_vector.cpp index 933ef2a1..9c20f073 100644 --- a/libraries/pico_vector/pico_vector.cpp +++ b/libraries/pico_vector/pico_vector.cpp @@ -6,9 +6,6 @@ namespace pimoroni { PicoGraphics *PicoVector::graphics = nullptr; - uint8_t PicoVector::max_alpha = 4; - const uint8_t *PicoVector::alpha_map = alpha_map_x4; - void PicoVector::draw(pp_poly_t *poly) { pp_transform(NULL); pp_render(poly); diff --git a/libraries/pico_vector/pico_vector.hpp b/libraries/pico_vector/pico_vector.hpp index 534fe3bd..1bcaf340 100644 --- a/libraries/pico_vector/pico_vector.hpp +++ b/libraries/pico_vector/pico_vector.hpp @@ -31,10 +31,6 @@ namespace pimoroni { private: static PicoGraphics *graphics; af_text_metrics_t text_metrics; - static constexpr uint8_t alpha_map_x4[4] {0, 128, 192, 255}; - static constexpr uint8_t alpha_map_x16[16] {0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 255}; - static uint8_t max_alpha; - static const uint8_t *alpha_map; public: PicoVector(PicoGraphics *graphics, void *mem = nullptr) { @@ -57,50 +53,13 @@ namespace pimoroni { } static void tile_callback(const pp_tile_t *tile) { - uint8_t *tile_data = tile->data; - - if(PicoVector::graphics->supports_alpha_blend() && _pp_antialias != PP_AA_NONE) { - if (PicoVector::graphics->render_pico_vector_tile({tile->x, tile->y, tile->w, tile->h}, - tile->data, - tile->stride, - (uint8_t)_pp_antialias)) { - return; - } - - for(auto y = 0; y < tile->h; y++) { - for(auto x = 0; x < tile->w; x++) { - uint8_t alpha = *tile_data++; - if (alpha >= max_alpha) { - PicoVector::graphics->pixel({x + tile->x, y + tile->y}); - } else if (alpha > 0) { - alpha = alpha_map[alpha]; - PicoVector::graphics->set_pixel_alpha({x + tile->x, y + tile->y}, alpha); - } - } - tile_data += tile->stride - tile->w; - } - } else { - for(auto y = 0; y < tile->h; y++) { - for(auto x = 0; x < tile->w; x++) { - uint8_t alpha = *tile_data++; - if (alpha) { - PicoVector::graphics->pixel({x + tile->x, y + tile->y}); - } - } - tile_data += tile->stride - tile->w; - } - } + // TODO: we're using a cast here to avoid a hard dependency link between + // PicoGraphics and PicoVector. These types might subtly mismatch, though... + PicoVector::graphics->render_tile((pimoroni::Tile *)tile); } void set_antialiasing(pp_antialias_t antialias) { pp_antialias(antialias); - if(antialias == PP_AA_X16) { - alpha_map = alpha_map_x16; - max_alpha = 16; - } else { - alpha_map = alpha_map_x4; - max_alpha = 4; - } } void set_font_size(unsigned int font_size) { diff --git a/libraries/pico_vector/pretty-poly.h b/libraries/pico_vector/pretty-poly.h index 0bd55455..c7a7c10e 100644 --- a/libraries/pico_vector/pretty-poly.h +++ b/libraries/pico_vector/pretty-poly.h @@ -31,30 +31,25 @@ #include #include +#include #include #include #include -#ifndef PP_MALLOC -#define PP_MALLOC(size) malloc(size) -#define PP_REALLOC(p, size) realloc(p, size) -#define PP_FREE(p) free(p) -#endif - #ifndef PP_COORD_TYPE #define PP_COORD_TYPE float #endif -#ifndef PP_NODE_BUFFER_HEIGHT -#define PP_NODE_BUFFER_HEIGHT 16 -#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 4096 +#define PP_TILE_BUFFER_SIZE 64 +#endif + +#ifndef PP_SCALE_TO_ALPHA +#define PP_SCALE_TO_ALPHA 1 #endif #if defined(PICO_ON_DEVICE) && PICO_ON_DEVICE @@ -62,12 +57,6 @@ #include "hardware/interp.h" #endif -#ifdef PP_DEBUG -#define debug(...) printf(__VA_ARGS__) -#else -#define debug(...) -#endif - #ifdef __cplusplus extern "C" { #endif @@ -103,7 +92,7 @@ pp_rect_t pp_rect_merge(pp_rect_t *r1, pp_rect_t *r2); pp_rect_t pp_rect_transform(pp_rect_t *r, pp_mat3_t *m); // antialias levels -typedef enum {PP_AA_NONE = 0, PP_AA_X4 = 1, PP_AA_X16 = 2} pp_antialias_t; +typedef enum {PP_AA_NONE = 0, PP_AA_FAST = 1, PP_AA_X4 = 1, PP_AA_BEST = 2, PP_AA_X16 = 2} pp_antialias_t; typedef struct { int32_t x, y, w, h; @@ -144,7 +133,19 @@ pp_rect_t pp_polygon_bounds(pp_poly_t *p); #ifdef PP_IMPLEMENTATION -pp_rect_t _pp_clip = (pp_rect_t){0, 0, 320, 240}; +#ifndef PP_MALLOC +#define PP_MALLOC(size) malloc(size) +#define PP_REALLOC(p, size) realloc(p, size) +#define PP_FREE(p) free(p) +#endif + +#ifdef PP_DEBUG +#define debug(...) printf(__VA_ARGS__) +#else +#define debug(...) +#endif + +pp_rect_t _pp_clip = (pp_rect_t){-INT_MAX, -INT_MAX, INT_MAX, INT_MAX}; pp_tile_callback_t _pp_tile_callback = NULL; pp_antialias_t _pp_antialias = PP_AA_X4; pp_mat3_t *_pp_transform = NULL; @@ -245,7 +246,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) * tile->stride] * (255 >> _pp_antialias >> _pp_antialias); + return tile->data[(x - tile->x) + (y - tile->y) * PP_TILE_BUFFER_SIZE]; } // pp_contour_t implementation @@ -273,14 +274,16 @@ pp_rect_t pp_polygon_bounds(pp_poly_t *p) { // buffer that each tile is rendered into before callback // allocate one extra byte to allow a small optimization in the row renderer -const uint32_t tile_buffer_size = PP_TILE_BUFFER_SIZE; -uint8_t tile_buffer[PP_TILE_BUFFER_SIZE + 1]; +uint8_t 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 nodes[PP_NODE_BUFFER_HEIGHT][PP_MAX_NODES_PER_SCANLINE * 2]; -uint32_t node_counts[PP_NODE_BUFFER_HEIGHT]; +int32_t nodes[PP_TILE_BUFFER_SIZE * 4][PP_MAX_NODES_PER_SCANLINE * 2]; +uint32_t 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_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}; @@ -291,12 +294,8 @@ void pp_tile_callback(pp_tile_callback_t callback) { } // maximum tile bounds determined by antialias level -uint32_t _pp_tile_width, _pp_tile_height; void pp_antialias(pp_antialias_t antialias) { _pp_antialias = antialias; - // recalculate the tile size for rendering based on antialiasing level - _pp_tile_height = PP_NODE_BUFFER_HEIGHT >> _pp_antialias; - _pp_tile_width = (int)(tile_buffer_size / _pp_tile_height); } pp_mat3_t *pp_transform(pp_mat3_t *transform) { @@ -329,34 +328,15 @@ void add_line_segment_to_nodes(const pp_point_t start, const pp_point_t end) { int32_t tx = sx; sx = ex; ex = tx; } - // Early out if line is completely outside the tile, or has no lines - if (ey < 0 || sy >= (int)PP_NODE_BUFFER_HEIGHT || sy == ey) return; + // early out if line is completely outside the tile, or has no gradient + if (ey < 0 || sy >= (int)(PP_TILE_BUFFER_SIZE << _pp_antialias) || sy == ey) return; debug(" + line segment from %d, %d to %d, %d\n", sx, sy, ex, ey); - // Determine how many in-bounds lines to render + // determine how many in-bounds lines to render int y = _pp_max(0, sy); - int count = _pp_min((int)PP_NODE_BUFFER_HEIGHT, ey) - y; + int count = _pp_min((int)(PP_TILE_BUFFER_SIZE << _pp_antialias), ey) - y; - // Handle cases where x is completely off to one side or other - if (_pp_max(sx, ex) <= 0) { - while (count--) { - nodes[y][node_counts[y]++] = 0; - ++y; - } - return; - } - - const int full_tile_width = (_pp_tile_width << _pp_antialias); - if (_pp_min(sx, ex) >= full_tile_width) { - while (count--) { - nodes[y][node_counts[y]++] = full_tile_width; - ++y; - } - return; - } - - // Normal case int x = sx; int e = 0; @@ -364,7 +344,7 @@ void add_line_segment_to_nodes(const pp_point_t start, const pp_point_t end) { const int einc = abs(ex - sx) + 1; const int dy = ey - sy; - // If sy < 0 jump to the start, note this does use a divide + // if sy < 0 jump to the start, note this does use a divide // but potentially saves many wasted loops below, so is likely worth it. if (sy < 0) { e = einc * -sy; @@ -373,34 +353,34 @@ void add_line_segment_to_nodes(const pp_point_t start, const pp_point_t end) { x += xinc * xjump; } -#ifdef USE_RP2040_INTERP - interp1->base[1] = full_tile_width; - interp1->accum[0] = x; +// #ifdef USE_RP2040_INTERP +// interp1->base[1] = full_tile_width; +// interp1->accum[0] = x; - // loop over scanlines - while(count--) { - // consume accumulated error - while(e > dy) {e -= dy; interp1->add_raw[0] = xinc;} +// // loop over scanlines +// while(count--) { +// // consume accumulated error +// while(e > dy) {e -= dy; interp1->add_raw[0] = xinc;} - // clamp node x value to tile bounds - const int nx = interp1->peek[0]; - debug(" + adding node at %d, %d\n", x, y); - // add node to node list - nodes[y][node_counts[y]++] = nx; +// // clamp node x value to tile bounds +// const int nx = interp1->peek[0]; +// debug(" + adding node at %d, %d\n", x, y); +// // add node to node list +// nodes[y][node_counts[y]++] = nx; - // step to next scanline and accumulate error - y++; - e += einc; - } -#else +// // step to next scanline and accumulate error +// y++; +// e += einc; +// } +// #else // loop over scanlines while(count--) { // consume accumulated error while(e > dy) {e -= dy; x += xinc;} // clamp node x value to tile bounds - int nx = _pp_max(_pp_min(x, full_tile_width), 0); - debug(" + adding node at %d, %d\n", x, y); + int nx = _pp_max(_pp_min(x, (PP_TILE_BUFFER_SIZE << _pp_antialias)), 0); + //debug(" + adding node at %d, %d\n", x, y); // add node to node list nodes[y][node_counts[y]++] = nx; @@ -408,7 +388,7 @@ void add_line_segment_to_nodes(const pp_point_t start, const pp_point_t end) { y++; e += einc; } -#endif +//#endif } void build_nodes(pp_path_t *contour, pp_rect_t *bounds) { @@ -419,34 +399,17 @@ void build_nodes(pp_path_t *contour, pp_rect_t *bounds) { .y = bounds->y * aa_scale }; - // start with the last point to close the loop - pp_point_t last = { - .x = (contour->points[contour->count - 1].x), - .y = (contour->points[contour->count - 1].y) - }; - - if(_pp_transform) { - last = pp_point_transform(&last, _pp_transform); - } - - last.x *= aa_scale; - last.y *= aa_scale; - + // start with the last point to close the loop, transform it, scale for antialiasing, and offset to tile origin + pp_point_t last = contour->points[contour->count - 1]; + if(_pp_transform) last = pp_point_transform(&last, _pp_transform); + last.x *= aa_scale; last.y *= aa_scale; last = pp_point_sub(&last, &tile_origin); for(uint32_t i = 0; i < contour->count; i++) { - pp_point_t point = { - .x = (contour->points[i].x), - .y = (contour->points[i].y) - }; - - if(_pp_transform) { - point = pp_point_transform(&point, _pp_transform); - } - - point.x *= aa_scale; - point.y *= aa_scale; - + // fetch next point, transform it, scale for antialiasing, and offset to tile origin + pp_point_t point = contour->points[i]; + if(_pp_transform) point = pp_point_transform(&point, _pp_transform); + point.x *= aa_scale; point.y *= aa_scale; point = pp_point_sub(&point, &tile_origin); add_line_segment_to_nodes(last, point); @@ -459,81 +422,75 @@ int compare_nodes(const void* a, const void* b) { return *((int*)a) - *((int*)b); } -pp_rect_t render_nodes(uint8_t *buffer, pp_rect_t *tb) { - int maxy = -1; +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; + debug(" + render tile %d, %d - %d, %d\n", tb->x, tb->y, tb->w, tb->h); - pp_rect_t rb; // render bounds - rb.y = 0; - rb.x = tb->w; - int maxx = 0; - PP_COORD_TYPE aa_scale = (PP_COORD_TYPE)(1 << _pp_antialias); - int anitialias_mask = (1 << _pp_antialias) - 1; + for(int y = 0; y < ((int)PP_TILE_BUFFER_SIZE << _pp_antialias); y++) { - for(int32_t y = 0; y < PP_NODE_BUFFER_HEIGHT; y++) { - if(node_counts[y] == 0) { - if (y == rb.y) ++rb.y; - continue; - } + // debug(" : row %d node count %d\n", y, node_counts[y]); + + if(node_counts[y] == 0) continue; // no nodes on this raster line qsort(&nodes[y][0], node_counts[y], sizeof(int), compare_nodes); - unsigned char* row_data = &buffer[(y >> _pp_antialias) * _pp_tile_width]; - bool rendered_any = false; + unsigned char* row_data = &tile_buffer[(y >> _pp_antialias) * PP_TILE_BUFFER_SIZE]; + for(uint32_t i = 0; i < node_counts[y]; i += 2) { int sx = nodes[y][i + 0]; int ex = nodes[y][i + 1]; - if(sx == ex) { + if(sx == ex) { // empty span, nothing to do continue; } - rendered_any = true; + // update render bounds + rb.x = _pp_min(rb.x, sx); + rb.y = _pp_min(rb.y, y); + minx = _pp_min(_pp_min(sx, ex), minx); + maxx = _pp_max(_pp_max(sx, ex), maxx); + rb.h = y - rb.y + 1; - maxx = _pp_max((ex - 1) >> _pp_antialias, maxx); + //debug(" - render span at %d from %d to %d\n", y, sx, ex); - debug(" - render span at %d from %d to %d\n", y, sx, ex); - - if (_pp_antialias) { - int ax = sx / aa_scale; - const int aex = ex / aa_scale; - - rb.x = _pp_min(ax, rb.x); - - if (ax == aex) { - row_data[ax] += ex - sx; - continue; - } - - row_data[ax] += aa_scale - (sx & anitialias_mask); - for(ax++; ax < aex; ax++) { - row_data[ax] += aa_scale; - } - - // This might add 0 to the byte after the end of the row, we pad the tile data - // by 1 byte to ensure that is OK - row_data[ax] += ex & anitialias_mask; - } else { - rb.x = _pp_min(sx, rb.x); - for(int x = sx; x < ex; x++) { - row_data[x]++; - } - } - } - - if (rendered_any) { - debug(" - rendered line %d\n", y); - maxy = y; - } - else if (y == rb.y) { - debug(" - render nothing on line %d\n", y); - ++rb.y; + // rasterise the span into the tile buffer + do { + row_data[sx >> _pp_antialias]++; + } while(++sx < ex); } } + rb.w = maxx - minx; + + // shifting the width and height effectively "floors" the result which can + // mean we lose a pixel off the right or bottom edge of the tile. by adding + // either 1 (at x4) or 3 (at x16) we change that to a "ceil" instead ensuring + // the full tile bounds are returned + if(_pp_antialias) { + rb.w += (_pp_antialias | 0b1); + rb.h += (_pp_antialias | 0b1); + } + + rb.x >>= _pp_antialias; rb.y >>= _pp_antialias; - maxy >>= _pp_antialias; - rb.w = (maxx >= rb.x) ? maxx + 1 - rb.x : 0; - rb.h = (maxy >= rb.y) ? maxy + 1 - rb.y : 0; + rb.w >>= _pp_antialias; + rb.h >>= _pp_antialias; + + uint8_t *p_alpha_map = _pp_alpha_map_none; + if(_pp_antialias == 1) p_alpha_map = _pp_alpha_map_x4; + 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 = &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++; + } + } + #endif + + debug(" : rendered tile bounds %d, %d (%d x %d)\n", rb.x, rb.y, rb.w, rb.h); return rb; } @@ -553,7 +510,7 @@ void pp_render(pp_poly_t *polygon) { polygon_bounds = pp_rect_transform(&polygon_bounds, _pp_transform); } - debug(" - bounds %d, %d (%d x %d)\n", polygon_bounds.x, polygon_bounds.y, polygon_bounds.w, polygon_bounds.h); + debug(" - polygon bounds %d, %d (%d x %d)\n", polygon_bounds.x, polygon_bounds.y, polygon_bounds.w, polygon_bounds.h); debug(" - clip %d, %d (%d x %d)\n", _pp_clip.x, _pp_clip.y, _pp_clip.w, _pp_clip.h); #ifdef USE_RP2040_INTERP @@ -569,9 +526,9 @@ void pp_render(pp_poly_t *polygon) { // iterate over tiles debug(" - processing tiles\n"); - for(int32_t y = polygon_bounds.y; y < polygon_bounds.y + polygon_bounds.h; y += _pp_tile_height) { - for(int32_t x = polygon_bounds.x; x < polygon_bounds.x + polygon_bounds.w; x += _pp_tile_width) { - pp_rect_t tb = (pp_rect_t){.x = x, .y = y, .w = _pp_tile_width, .h = _pp_tile_height}; + for(int32_t y = polygon_bounds.y; y < polygon_bounds.y + polygon_bounds.h; y += PP_TILE_BUFFER_SIZE) { + for(int32_t x = polygon_bounds.x; x < polygon_bounds.x + polygon_bounds.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); @@ -580,7 +537,7 @@ void pp_render(pp_poly_t *polygon) { // clear existing tile data and nodes memset(node_counts, 0, sizeof(node_counts)); - memset(tile_buffer, 0, tile_buffer_size); + memset(tile_buffer, 0, PP_TILE_BUFFER_SIZE * PP_TILE_BUFFER_SIZE); // build the nodes for each pp_path_t for(uint32_t i = 0; i < polygon->count; i++) { @@ -592,17 +549,15 @@ void pp_render(pp_poly_t *polygon) { debug(" : render the tile\n"); // render the tile - pp_rect_t rb = render_nodes(tile_buffer, &tb); + pp_rect_t rb = render_nodes(&tb); tb.x += rb.x; tb.y += rb.y; tb.w = rb.w; tb.h = rb.h; - debug(" - adjusted render tile bounds %d, %d (%d x %d)\n", rb.x, rb.y, rb.w, rb.h); - if(pp_rect_empty(&tb)) { debug(" : empty after rendering, skipping\n"); continue; } pp_tile_t tile = { .x = tb.x, .y = tb.y, .w = tb.w, .h = tb.h, - .stride = (uint32_t)_pp_tile_width, - .data = tile_buffer + rb.x + _pp_tile_width * rb.y + .stride = PP_TILE_BUFFER_SIZE, + .data = tile_buffer + rb.x + (PP_TILE_BUFFER_SIZE * rb.y) }; _pp_tile_callback(&tile); diff --git a/micropython/modules/picovector/picovector.c b/micropython/modules/picovector/picovector.c index f724447f..f5921ea2 100644 --- a/micropython/modules/picovector/picovector.c +++ b/micropython/modules/picovector/picovector.c @@ -114,6 +114,8 @@ static const mp_map_elem_t VECTOR_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_ANTIALIAS_NONE), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_ANTIALIAS_X4), MP_ROM_INT(1) }, { MP_ROM_QSTR(MP_QSTR_ANTIALIAS_X16), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_ANTIALIAS_FAST), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_ANTIALIAS_BEST), MP_ROM_INT(2) }, }; static MP_DEFINE_CONST_DICT(mp_module_VECTOR_globals, VECTOR_globals_table);