Porównaj commity

...

17 Commity

Autor SHA1 Wiadomość Data
Phil Howard 34b8b4d81f PicoVector: MPY bindings for line and arc. 2024-11-26 11:55:32 +00:00
Phil Howard a2c48a8f44 PicoVector: Tweak for C++ compatibility. 2024-11-26 11:12:24 +00:00
Mike Bell c1e0631102 PicoVector: Avoid clipping bottom right AA edges. 2024-11-26 10:43:11 +00:00
Jonathan Williamson c91b498505 PicoVector: Add star and line primitives. 2024-11-26 10:43:08 +00:00
Phil Howard ad5120a8d2 PicoGraphics: Implement RGB888 alpha blending. 2024-11-21 22:28:10 +00:00
Mike Bell 18d3ed3940 Hub75: Reformat loop for performance.
~3.09ms to ~2.87ms per frame without overclock.
2024-11-21 22:08:10 +00:00
Phil Howard acf6e517f6 Hub75: Performance improvements and stacked mode.
Inline and simplify the pixel flip for a 13.2ms -> 3.8ms speedup at 128x128 on RP2350 stock.

Drop RGB565 mode.

Add the ability to stack some panels, eg: 2x128x64 in a 128x127 configuration.
2024-11-21 20:59:39 +00:00
Phil Howard 896353af11 PicoVector: Avoid MicroPython GC.
Since we're not using tracked allocation, any memory we don't explicitly
hold a reference to will be assumed unused by MicroPython's GC.

Pass up the pp_nodes and pp_node_counts points (hackily) to fix this.
2024-11-21 12:08:48 +00:00
Phil Howard b184475bc0 PicoVector: Remove * 4 from pp_nodes lookup. 2024-11-21 11:14:07 +00:00
Phil Howard d8c6883fc0 PicoGraphics/Hub75: Add support for 128x128. 2024-11-20 12:46:05 +00:00
Phil Howard adfa429d47 PicoVector: Update example with text bounds. 2024-11-20 11:21:27 +00:00
Phil Howard d44f00886b PicoGraphics: Don't force Presto to RGB565. 2024-11-20 10:46:06 +00:00
Mike Bell 5f08e113bd PicoGraphics: Presto full res option. 2024-11-19 11:01:28 +00:00
Mike Bell 0807dc6810 PicoVector: Apply overall transform to text rendering. 2024-11-19 11:01:20 +00:00
Mike Bell 8471580c71 PicoVector: Add optional text max width and max height. 2024-11-19 11:01:12 +00:00
Phil Howard 6b0195a00b PicoVector: Revert the tile buffer to be fixed.
Ensure that MicroPython doesn't ever place the tile buffer into PSRAM
and trash performance.
2024-11-18 14:00:46 +00:00
Phil Howard ab81c5df56 PicoVector: Runtime buffer allocation. 2024-11-18 14:00:37 +00:00
16 zmienionych plików z 417 dodań i 190 usunięć

Wyświetl plik

@ -37,10 +37,48 @@ Hub75::Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool
}
if (brightness == 0) {
#if PICO_RP2350
brightness = 6;
#else
if (width >= 64) brightness = 6;
if (width >= 96) brightness = 3;
if (width >= 128) brightness = 2;
if (width >= 160) brightness = 1;
#endif
}
switch (color_order) {
case COLOR_ORDER::RGB:
r_shift = 0;
g_shift = 10;
b_shift = 20;
break;
case COLOR_ORDER::RBG:
r_shift = 0;
g_shift = 20;
b_shift = 10;
break;
case COLOR_ORDER::GRB:
r_shift = 20;
g_shift = 0;
b_shift = 10;
break;
case COLOR_ORDER::GBR:
r_shift = 10;
g_shift = 20;
b_shift = 0;
break;
case COLOR_ORDER::BRG:
r_shift = 10;
g_shift = 00;
b_shift = 20;
break;
case COLOR_ORDER::BGR:
r_shift = 20;
g_shift = 10;
b_shift = 0;
break;
}
}
@ -58,26 +96,16 @@ void Hub75::set_color(uint x, uint y, Pixel c) {
}
void Hub75::set_pixel(uint x, uint y, uint8_t r, uint8_t g, uint8_t b) {
switch(color_order) {
case COLOR_ORDER::RGB:
set_color(x, y, Pixel(r, g, b));
break;
case COLOR_ORDER::RBG:
set_color(x, y, Pixel(r, b, g));
break;
case COLOR_ORDER::GRB:
set_color(x, y, Pixel(g, r, b));
break;
case COLOR_ORDER::GBR:
set_color(x, y, Pixel(g, b, r));
break;
case COLOR_ORDER::BRG:
set_color(x, y, Pixel(b, r, g));
break;
case COLOR_ORDER::BGR:
set_color(x, y, Pixel(b, g, r));
break;
int offset = 0;
if(x >= width || y >= height) return;
if(y >= height / 2) {
y -= height / 2;
offset = (y * width + x) * 2;
offset += 1;
} else {
offset = (y * width + x) * 2;
}
back_buffer[offset] = (GAMMA_10BIT[b] << b_shift) | (GAMMA_10BIT[g] << g_shift) | (GAMMA_10BIT[r] << r_shift);
}
void Hub75::FM6126A_write_register(uint16_t value, uint8_t position) {
@ -249,28 +277,88 @@ void Hub75::dma_complete() {
void Hub75::update(PicoGraphics *graphics) {
if(graphics->pen_type == PicoGraphics::PEN_RGB888) {
uint32_t *p = (uint32_t *)graphics->frame_buffer;
for(uint y = 0; y < height; y++) {
for(uint x = 0; x < width; x++) {
uint32_t col = *p;
uint8_t r = (col & 0xff0000) >> 16;
uint8_t g = (col & 0x00ff00) >> 8;
uint8_t b = (col & 0x0000ff) >> 0;
set_pixel(x, y, r, g, b);
p++;
uint8_t *p = (uint8_t *)graphics->frame_buffer;
if(graphics->bounds.w == int32_t(width / 2) && graphics->bounds.h == int32_t(height * 2)) {
for(int y = 0; y < graphics->bounds.h; y++) {
int offsety = 0;
int sy = y;
int basex = 0;
// Assuming our canvas is 128x128 and our display is 256x64,
// consisting of 2x128x64 panels, remap the bottom half
// of the canvas to the right-half of the display,
// This gives us an optional square arrangement.
if (sy >= int(height)) {
sy -= height;
basex = width / 2;
} else {
// Awkward hack to *TEMPORARILY* rotate the top panel
sy = height - 1 - sy;
basex = (width / 2) - 1;
}
// Interlace the top and bottom halves of the panel.
// Since these are scanned out simultaneously to two chains
// of shift registers we need each pair of rows
// (N and N + height / 2) to be adjacent in the buffer.
offsety = width * 2;
if(sy >= int(height / 2)) {
sy -= height / 2;
offsety *= sy;
offsety += 1;
} else {
offsety *= sy;
}
for(int x = 0; x < graphics->bounds.w; x++) {
int sx = x;
uint8_t b = *p++;
uint8_t g = *p++;
uint8_t r = *p++;
// Assumes width / 2 is even.
if (basex & 1) {
sx = basex - sx;
} else {
sx += basex;
}
int offset = offsety + sx * 2;
back_buffer[offset] = (GAMMA_10BIT[b] << b_shift) | (GAMMA_10BIT[g] << g_shift) | (GAMMA_10BIT[r] << r_shift);
// Skip the empty byte in out 32-bit aligned 24-bit colour.
p++;
}
}
}
}
else if(graphics->pen_type == PicoGraphics::PEN_RGB565) {
uint16_t *p = (uint16_t *)graphics->frame_buffer;
for(uint y = 0; y < height; y++) {
for(uint x = 0; x < width; x++) {
uint16_t col = __builtin_bswap16(*p);
uint8_t r = (col & 0b1111100000000000) >> 8;
uint8_t g = (col & 0b0000011111100000) >> 3;
uint8_t b = (col & 0b0000000000011111) << 3;
set_pixel(x, y, r, g, b);
p++;
} else {
for(uint y = 0; y < height; y++) {
for(uint x = 0; x < width; x++) {
int offset = 0;
int sy = y;
int sx = x;
uint8_t b = *p++;
uint8_t g = *p++;
uint8_t r = *p++;
// Interlace the top and bottom halves of the panel.
// Since these are scanned out simultaneously to two chains
// of shift registers we need each pair of rows
// (N and N + height / 2) to be adjacent in the buffer.
offset = width * 2;
if(sy >= int(height / 2)) {
sy -= height / 2;
offset *= sy;
offset += 1;
} else {
offset *= sy;
}
offset += sx * 2;
back_buffer[offset] = (GAMMA_10BIT[b] << b_shift) | (GAMMA_10BIT[g] << g_shift) | (GAMMA_10BIT[r] << r_shift);
// Skip the empty byte in out 32-bit aligned 24-bit colour.
p++;
}
}
}
}

Wyświetl plik

@ -65,6 +65,9 @@ class Hub75 {
};
uint width;
uint height;
uint r_shift = 0;
uint g_shift = 10;
uint b_shift = 20;
Pixel *back_buffer;
bool managed_buffer = false;
PanelType panel_type;

Wyświetl plik

@ -51,7 +51,7 @@ int main() {
pp_mat3_translate(&pos, 50, 50);
pp_mat3_rotate(&pos, a);
vector.draw(poly);
vector.text("Hello World", &pos);
vector.text("Hello World", 320, 240, &pos);
// update screen
st7789.update(&graphics);

Wyświetl plik

@ -38,19 +38,35 @@ namespace pimoroni {
}
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)];
uint8_t *p_alpha = &tile->data[(y * tile->stride)];
uint32_t *p_dest = &((uint32_t *)frame_buffer)[tile->x + ((tile->y + y) * bounds.w)];
for(int x = 0; x < tile->w; x++) {
uint8_t alpha = *palpha;
uint16_t dest = *p_dest;
uint8_t alpha = *p_alpha;
// TODO: Alpha blending
if(alpha == 0) {
if(alpha == 255) {
*p_dest = color;
}else if(alpha == 0) {
} else {
*pdest = color;
// blend tha pixel
uint32_t sr = (color >> 16) & 0xff;
uint32_t sg = (color >> 8) & 0xff;
uint32_t sb = (color >> 0) & 0xff;
uint32_t dr = (dest >> 16) & 0xff;
uint32_t dg = (dest >> 8) & 0xff;
uint32_t db = (dest >> 0) & 0xff;
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;
*p_dest = (r << 16) | (g << 8) | b;
}
pdest++;
palpha++;
p_dest++;
p_alpha++;
}
}

Wyświetl plik

@ -31,6 +31,7 @@
#include <math.h>
#include <stdbool.h>
#include <wchar.h>
#include <float.h>
#ifdef AF_MALLOC
#ifndef PP_MALLOC
@ -104,8 +105,8 @@ typedef struct {
bool af_load_font_file(AF_FILE file, af_face_t *face);
void af_render_character(af_face_t *face, const char codepoint, af_text_metrics_t *tm);
void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm);
pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm);
void af_render(af_face_t *face, const char *text, size_t tlen, float max_line_width, float max_height, af_text_metrics_t *tm);
pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, float max_line_width, af_text_metrics_t *tm);
#ifdef AF_USE_PRETTY_POLY
#endif
@ -240,20 +241,30 @@ void af_render_character(af_face_t *face, const char c, af_text_metrics_t *tm) {
af_render_glyph(glyph, tm);
}
int get_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
int line_width = 0;
char *end = (char *)text + tlen;
float get_line_width(af_face_t *face, const char *text, size_t *tlen, float max_line_width, af_text_metrics_t *tm) {
float line_width = 0;
const char *start = text;
const char *end = text + *tlen;
const char *last_space = nullptr;
for(char c = *text; text < end; text++, c = *text) {
af_glyph_t *glyph = find_glyph(face, c);
if(!glyph) {
continue;
}
float char_width;
if(c == L' ') {
line_width += (glyph->advance * tm->word_spacing) / 100.0f;
char_width = (glyph->advance * tm->word_spacing) / 100.0f;
last_space = text;
} else {
line_width += (glyph->advance * tm->letter_spacing) / 100.0f;
char_width = (glyph->advance * tm->letter_spacing) / 100.0f;
}
if (max_line_width > 0 && line_width + char_width > max_line_width && last_space) {
*tlen = last_space - start;
break;
}
line_width += char_width;
}
return line_width;
}
@ -270,14 +281,14 @@ size_t line_length(const char *text, const char *end) {
return line_ending - text;
}
int get_max_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
int max_width = 0;
float get_max_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
float max_width = 0;
char *line = (char *)text;
char *tend = line + tlen;
size_t line_len = line_length(line, tend);
while(line_len) {
int width = get_line_width(face, line, line_len, tm);
float width = get_line_width(face, line, &line_len, 0, tm);
max_width = max_width < width ? width : max_width;
line += line_len + 1;
line_len = line_length(line, tend);
@ -286,7 +297,7 @@ int get_max_line_width(af_face_t *face, const char *text, size_t tlen, af_text_m
return max_width;
}
void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
void af_render(af_face_t *face, const char *text, size_t tlen, float max_line_width, float max_height, af_text_metrics_t *tm) {
char *line = (char *)text;
char *tend = line + tlen;
size_t line_len = 0;
@ -304,15 +315,23 @@ void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t
caret.y = 0;
// find maximum line length
int max_line_width = get_max_line_width(face, text, tlen, tm);
if (max_line_width == 0.f) {
max_line_width = get_max_line_width(face, text, tlen, tm);
} else {
max_line_width /= scale;
}
if (max_height == 0.f) {
max_height = FLT_MAX;
} else {
max_height /= scale;
}
line_len = line_length(line, tend);
while(line_len) {
while(line_len && caret.y + line_height <= max_height) {
int line_width = get_line_width(face, line, &line_len, max_line_width, tm);
char *end = line + line_len;
int line_width = get_line_width(face, line, line_len, tm);
for(char c = *line; line < end; line++, c = *line) {
af_glyph_t *glyph = find_glyph(face, c);
if(!glyph) {
@ -331,7 +350,9 @@ void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t
pp_mat3_translate(&caret_transform, (max_line_width - line_width), 0);
}
pp_transform(&caret_transform);
pp_mat3_t final_transform = *old;
pp_mat3_mul(&final_transform, &caret_transform);
pp_transform(&final_transform);
af_render_glyph(glyph, tm);
@ -356,10 +377,10 @@ void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t
}
void _af_render(af_face_t *face, const char *text, af_text_metrics_t *tm) {
af_render(face, text, strlen(text), tm);
af_render(face, text, strlen(text), 0, 0, tm);
}
pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, float max_line_width, af_text_metrics_t *tm) {
pp_rect_t result;
bool first = true;
char *line = (char *)text;
@ -377,15 +398,14 @@ pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, af_text_met
caret.y = 0;
// find maximum line length
int max_line_width = get_max_line_width(face, text, tlen, tm);
if (max_line_width == 0.f) max_line_width = get_max_line_width(face, text, tlen, tm);
line_len = line_length(line, tend);
while(line_len) {
int line_width = get_line_width(face, line, &line_len, max_line_width, tm);
char *end = line + line_len;
int line_width = get_line_width(face, line, line_len, tm);
for(char c = *line; line < end; line++, c = *line) {
af_glyph_t *glyph = find_glyph(face, c);
if(!glyph) {

Wyświetl plik

@ -60,93 +60,13 @@ namespace pimoroni {
}
}
pp_point_t PicoVector::text(std::string_view text, pp_mat3_t *t) {
pp_point_t PicoVector::text(std::string_view text, int max_width, int max_height, pp_mat3_t *t) {
pp_point_t caret = {0, 0};
text_metrics.transform = t;
af_render(text_metrics.face, text.data(), text.size(), &text_metrics);
af_render(text_metrics.face, text.data(), text.size(), max_width, max_height, &text_metrics);
return caret;
/*
// Align text from the bottom left
caret.y += (PP_COORD_TYPE)text_metrics.line_height;
caret = pp_point_transform(&caret, t);
caret.x += offset.x;
caret.y += offset.y;
pp_point_t space;
pp_point_t carriage_return = {0, -(PP_COORD_TYPE)text_metrics.line_height};
char spc = ' ';
space.x = af_measure(text_metrics.face, &spc, &text_metrics).w;
if (space.x == 0) {
space.x = text_metrics.word_spacing;
}
space = pp_point_transform(&space, t);
carriage_return = pp_point_transform(&carriage_return, t);
pp_point_t initial_carriage_return = carriage_return;
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 += af_measure(text_metrics.face, &text[j], &text_metrics).w;
word_width += text_metrics.letter_spacing;
}
if(caret.x != 0 && caret.x + word_width > graphics->clip.w) {
caret = pp_point_sub(&caret, &carriage_return);
carriage_return = initial_carriage_return;
}
for(size_t j = i; j < std::min(next_break + 1, text.length()); j++) {
if (text[j] == '\n') { // Linebreak
caret = pp_point_sub(&caret, &carriage_return);
carriage_return = initial_carriage_return;
} else if (text[j] == ' ') { // Space
caret = pp_point_add(&caret, &space);
carriage_return = pp_point_add(&carriage_return, &space);
} else {
// apply the caret offset...
pp_mat3_t pos = pp_mat3_identity();
pp_mat3_mul(&pos, t);
pp_mat3_translate(&pos, caret.x, caret.y);
text_metrics.transform = &pos;
af_render_character(text_metrics.face, text[j], &text_metrics);
}
pp_point_t advance = {
(PP_COORD_TYPE)af_measure(text_metrics.face, &text[j], &text_metrics).w + text_metrics.letter_spacing,
(PP_COORD_TYPE)0
};
advance = pp_point_transform(&advance, t);
caret = pp_point_add(&caret, &advance);
carriage_return = pp_point_add(&carriage_return, &advance);
}
i = next_break + 1;
}
return {caret.x, caret.y};
*/
}
}

Wyświetl plik

@ -31,12 +31,22 @@ namespace pimoroni {
class PicoVector {
private:
af_text_metrics_t text_metrics;
// Hold copies of pretty-poly's pointers
// so MicroPython does not garbage collect them!
void *_pp_nodes;
void *_pp_node_counts;
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);
_pp_nodes = pp_nodes;
_pp_node_counts = pp_node_counts;
pp_tile_callback(PicoVector::tile_callback);
set_antialiasing(graphics->supports_alpha_blend() ? PP_AA_X4 : PP_AA_NONE);
@ -48,11 +58,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...
@ -85,7 +99,7 @@ namespace pimoroni {
pp_rect_t measure_text(std::string_view text, pp_mat3_t *t) {
text_metrics.transform = t;
return af_measure(text_metrics.face, text.data(), text.size(), &text_metrics);
return af_measure(text_metrics.face, text.data(), text.size(), 0, &text_metrics);
}
bool set_font(std::string_view font_path, unsigned int font_size) {
@ -116,7 +130,7 @@ namespace pimoroni {
return result;
}
pp_point_t text(std::string_view text, pp_mat3_t *t=nullptr);
pp_point_t text(std::string_view text, int max_width, int max_height, pp_mat3_t *t=nullptr);
void transform(pp_path_t *path, pp_mat3_t *t);
void transform(pp_poly_t *poly, pp_mat3_t *t);

Wyświetl plik

@ -61,10 +61,25 @@ typedef struct {
PP_COORD_TYPE f, t; // angle from and to
} ppp_arc_def;
typedef struct {
PP_COORD_TYPE x, y; // coordinates
int c; // number of points on star
PP_COORD_TYPE ro, ri; // outer and inner radius for points
PP_COORD_TYPE s; // stroke thickness (0 == filled)
} ppp_star_def;
typedef struct {
PP_COORD_TYPE x1, y1; // start point
PP_COORD_TYPE x2, y2; // end point
PP_COORD_TYPE s; // thickness
} ppp_line_def;
pp_poly_t* ppp_rect(ppp_rect_def d);
pp_poly_t* ppp_regular(ppp_regular_def d);
pp_poly_t* ppp_circle(ppp_circle_def d);
pp_poly_t* ppp_arc(ppp_arc_def d);
pp_poly_t* ppp_star(ppp_star_def d);
pp_poly_t* ppp_line(ppp_line_def d);
#ifdef __cplusplus
}
@ -176,6 +191,36 @@ pp_poly_t* ppp_arc(ppp_arc_def d) {
return poly;
}
pp_poly_t* ppp_star(ppp_star_def d) {
pp_poly_t *poly = pp_poly_new();
pp_path_t *path = pp_poly_add_path(poly);
pp_path_t *inner = d.s != 0.0f ? pp_poly_add_path(poly) : NULL;
for(int i = 0; i < d.c * 2; i++) {
float step = ((M_PI * 2) / (float)(d.c * 2)) * (float)i;
PP_COORD_TYPE r = i % 2 == 0 ? d.ro : d.ri;
pp_path_add_point(path, (pp_point_t){sin(step) * r + d.x, cos(step) * r + d.y});
if(inner) { // append the inner path
PP_COORD_TYPE ior = d.ro - (d.s * d.ro / d.ri);
PP_COORD_TYPE iir = d.ri - d.s;
PP_COORD_TYPE ir = i % 2 == 0 ? ior : iir;
pp_path_add_point(inner, (pp_point_t){sin(step) * ir + d.x, cos(step) * ir + d.y});
}
}
return poly;
}
pp_poly_t* ppp_line(ppp_line_def d) {
pp_poly_t *poly = pp_poly_new();
pp_path_t *path = pp_poly_add_path(poly);
// create a normalised perpendicular vector
pp_point_t v = {d.y2 - d.y1, d.x2 - d.x1};
float mag = sqrt(v.x * v.x + v.y * v.y);
v.x /= mag; v.y /= mag; v.x *= -(d.s / 2.0f); v.y *= (d.s / 2.0f);
pp_point_t points[] = {{d.x1 + v.x, d.y1 + v.y}, {d.x2 + v.x, d.y2 + v.y}, {d.x2 - v.x, d.y2 - v.y}, {d.x1 - v.x, d.y1 - v.y}};
pp_path_add_points(path, points, 4);
return poly;
}
#endif // PPP_IMPLEMENTATION
#endif // PPP_INCLUDE_H

Wyświetl plik

@ -40,10 +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
@ -131,12 +127,20 @@ extern pp_tile_callback_t _pp_tile_callback;
extern pp_antialias_t _pp_antialias;
extern pp_mat3_t *_pp_transform;
// Our parent scope might want to hold a pointer to these
// ie: MicroPython to avoid garbage collection
extern int32_t *pp_nodes;
extern uint32_t *pp_node_counts;
void pp_clip(int32_t x, int32_t y, int32_t w, int32_t h);
void pp_tile_callback(pp_tile_callback_t callback);
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 max_nodes_per_scanline);
void pp_deinit();
#ifdef __cplusplus
}
@ -380,19 +384,33 @@ pp_rect_t pp_poly_bounds(pp_poly_t *p) {
return b;
}
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
// This allocates 4k up-front to ensure it's stored in Pico's RAM
// Rather than potentially allocating into PSRAM at runtime and trashing perf
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;
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 max_nodes_per_scanline) {
_pp_max_nodes_per_scanline = max_nodes_per_scanline;
pp_nodes = (int32_t *)PP_MALLOC(PP_TILE_BUFFER_SIZE * 4 * max_nodes_per_scanline * 2 * sizeof(int32_t));
pp_node_counts = (uint32_t *)PP_MALLOC(PP_TILE_BUFFER_SIZE * 4 * sizeof(uint32_t));
}
void pp_deinit() {
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 * _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++;
@ -530,18 +551,19 @@ pp_rect_t render_nodes(pp_rect_t *tb) {
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++) {
int32_t *pp_scanline_nodes = &pp_nodes[y * _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];
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;
@ -570,14 +592,14 @@ pp_rect_t render_nodes(pp_rect_t *tb) {
// 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);
}
int maxx = rb.x + rb.w + (_pp_antialias | 0b1);
int maxy = rb.y + rb.h + (_pp_antialias | 0b1);
rb.x >>= _pp_antialias;
rb.y >>= _pp_antialias;
rb.w >>= _pp_antialias;
rb.h >>= _pp_antialias;
rb.x >>= _pp_antialias;
rb.y >>= _pp_antialias;
rb.w = (maxx >> _pp_antialias) - rb.x;
rb.h = (maxy >> _pp_antialias) - rb.y;
}
uint8_t *p_alpha_map = _pp_alpha_map_none;
if(_pp_antialias == 1) p_alpha_map = _pp_alpha_map_x4;
@ -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, sizeof(pp_tile_buffer));
// build the nodes for each pp_path_t
pp_path_t *path = polygon->paths;

Wyświetl plik

@ -164,6 +164,7 @@ static const mp_map_elem_t picographics_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_128X64), MP_ROM_INT(DISPLAY_INTERSTATE75_128X64) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_192X64), MP_ROM_INT(DISPLAY_INTERSTATE75_192X64) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_256X64), MP_ROM_INT(DISPLAY_INTERSTATE75_256X64) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_128X128), MP_ROM_INT(DISPLAY_INTERSTATE75_128X128) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_FRAME_7), MP_ROM_INT(DISPLAY_INKY_FRAME_7) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_COSMIC_UNICORN), MP_ROM_INT(DISPLAY_COSMIC_UNICORN) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_STELLAR_UNICORN), MP_ROM_INT(DISPLAY_STELLAR_UNICORN) },
@ -172,6 +173,7 @@ static const mp_map_elem_t picographics_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_PICO_W_EXPLORER), MP_ROM_INT(DISPLAY_PICO_W_EXPLORER) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_EXPLORER), MP_ROM_INT(DISPLAY_EXPLORER) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_PRESTO), MP_ROM_INT(DISPLAY_PRESTO) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_PRESTO_FULL_RES), MP_ROM_INT(DISPLAY_PRESTO_FULL_RES) },
{ MP_ROM_QSTR(MP_QSTR_PEN_1BIT), MP_ROM_INT(PEN_1BIT) },
{ MP_ROM_QSTR(MP_QSTR_PEN_P4), MP_ROM_INT(PEN_P4) },

Wyświetl plik

@ -211,6 +211,14 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height,
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_RGB888;
break;
case DISPLAY_INTERSTATE75_128X128:
width = 128;
height = 128;
bus_type = BUS_PIO;
// Portrait to match labelling
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_RGB888;
break;
case DISPLAY_INKY_FRAME_7:
width = 800;
height = 480;
@ -254,7 +262,14 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height,
height = 240;
bus_type = BUS_PIO;
rotate = (int)Rotation::ROTATE_0;
pen_type = PEN_RGB565;
if(pen_type == -1) pen_type = PEN_RGB565;
break;
case DISPLAY_PRESTO_FULL_RES:
width = 480;
height = 480;
bus_type = BUS_PIO;
rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_RGB565;
break;
default:
return false;
@ -397,7 +412,8 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
|| display == DISPLAY_STELLAR_UNICORN
|| display == DISPLAY_UNICORN_PACK
|| display == DISPLAY_SCROLL_PACK
|| display == DISPLAY_PRESTO) {
|| display == DISPLAY_PRESTO
|| display == DISPLAY_PRESTO_FULL_RES) {
// Create a dummy display driver
self->display = m_new_class(DisplayDriver, width, height, (Rotation)rotate);

Wyświetl plik

@ -25,6 +25,7 @@ enum PicoGraphicsDisplay {
DISPLAY_INTERSTATE75_128X64,
DISPLAY_INTERSTATE75_192X64,
DISPLAY_INTERSTATE75_256X64,
DISPLAY_INTERSTATE75_128X128,
DISPLAY_INKY_FRAME_7,
DISPLAY_COSMIC_UNICORN,
DISPLAY_STELLAR_UNICORN,
@ -32,7 +33,8 @@ enum PicoGraphicsDisplay {
DISPLAY_SCROLL_PACK,
DISPLAY_PICO_W_EXPLORER,
DISPLAY_EXPLORER,
DISPLAY_PRESTO
DISPLAY_PRESTO,
DISPLAY_PRESTO_FULL_RES
};
enum PicoGraphicsPenType {

Wyświetl plik

@ -19,6 +19,8 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR(POLYGON_path_obj, 4, POLYGON_path);
static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_regular_obj, 5, POLYGON_regular);
static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_circle_obj, 4, POLYGON_circle);
static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_arc_obj, 6, POLYGON_arc);
static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_star_obj, 6, POLYGON_star);
static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_line_obj, 5, POLYGON_line);
static const mp_rom_map_elem_t POLYGON_locals_dict_table[] = {
@ -39,6 +41,8 @@ static const mp_rom_map_elem_t POLYGON_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_path), MP_ROM_PTR(&POLYGON_path_obj) },
{ MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&POLYGON_circle_obj) },
{ MP_ROM_QSTR(MP_QSTR_arc), MP_ROM_PTR(&POLYGON_arc_obj) },
{ MP_ROM_QSTR(MP_QSTR_star), MP_ROM_PTR(&POLYGON_star_obj) },
{ MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&POLYGON_line_obj) },
};
static MP_DEFINE_CONST_DICT(POLYGON_locals_dict, POLYGON_locals_dict_table);

Wyświetl plik

@ -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;
@ -352,6 +351,72 @@ mp_obj_t POLYGON_arc(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
return self;
}
mp_obj_t POLYGON_star(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_points, ARG_inner_radius, ARG_outer_radius, ARG_stroke };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_points, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_inner_radius, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_outer_radius, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_stroke, MP_ARG_OBJ, { .u_obj = mp_const_none }},
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
_POLY_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _POLY_obj_t);
picovector_point_type x = mp_picovector_get_point_type(args[ARG_x].u_obj);
picovector_point_type y = mp_picovector_get_point_type(args[ARG_y].u_obj);
int p = args[ARG_points].u_int;
picovector_point_type r1 = mp_picovector_get_point_type(args[ARG_inner_radius].u_obj);
picovector_point_type r2 = mp_picovector_get_point_type(args[ARG_outer_radius].u_obj);
picovector_point_type s = args[ARG_stroke].u_obj == mp_const_none ? 0 : mp_picovector_get_point_type(args[ARG_stroke].u_obj);
pp_poly_merge(self->poly, ppp_star({
x, y,
p,
r1,
r2,
s
}));
return self;
}
mp_obj_t POLYGON_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_x2, ARG_y2, ARG_thickness };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_thickness, MP_ARG_OBJ, { .u_obj = mp_const_none }},
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
_POLY_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _POLY_obj_t);
picovector_point_type x = mp_picovector_get_point_type(args[ARG_x].u_obj);
picovector_point_type y = mp_picovector_get_point_type(args[ARG_y].u_obj);
picovector_point_type x2 = mp_picovector_get_point_type(args[ARG_x2].u_obj);
picovector_point_type y2 = mp_picovector_get_point_type(args[ARG_y2].u_obj);
picovector_point_type t = args[ARG_thickness].u_obj == mp_const_none ? 0 : mp_picovector_get_point_type(args[ARG_thickness].u_obj);
pp_poly_merge(self->poly, ppp_line({
x, y,
x2, y2,
t
}));
return self;
}
// Utility functions
mp_obj_t POLYGON_centroid(mp_obj_t self_in) {
@ -545,12 +610,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;
}
@ -705,13 +765,15 @@ 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, ARG_angle };
enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_angle, ARG_max_width, ARG_max_height };
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_angle, MP_ARG_OBJ, {.u_obj = mp_const_none} }
{ MP_QSTR_angle, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_max_width, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_max_height, MP_ARG_INT, {.u_int = 0} }
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -729,6 +791,8 @@ 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;
int max_width = args[ARG_max_width].u_int;
int max_height = args[ARG_max_height].u_int;
pp_mat3_t tt = pp_mat3_identity();
@ -738,7 +802,7 @@ mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
pp_mat3_translate(&tt, (float)x, (float)y);
self->vector->text(t, &tt);
self->vector->text(t, max_width, max_height, &tt);
return mp_const_none;
}

Wyświetl plik

@ -13,6 +13,8 @@ extern mp_obj_t POLYGON_regular(size_t n_args, const mp_obj_t *pos_args, mp_map_
extern mp_obj_t POLYGON_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t POLYGON_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t POLYGON_arc(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t POLYGON_star(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t POLYGON_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern void POLYGON_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
extern mp_obj_t POLYGON_centroid(mp_obj_t self_in);

Wyświetl plik

@ -1,5 +1,5 @@
from pimoroni import RGBLED, Button
from picographics import PicoGraphics, DISPLAY_INTERSTATE75_32X32, DISPLAY_INTERSTATE75_64X32, DISPLAY_INTERSTATE75_96X32, DISPLAY_INTERSTATE75_96X48, DISPLAY_INTERSTATE75_128X32, DISPLAY_INTERSTATE75_64X64, DISPLAY_INTERSTATE75_128X64, DISPLAY_INTERSTATE75_192X64, DISPLAY_INTERSTATE75_256X64
from picographics import PicoGraphics, DISPLAY_INTERSTATE75_32X32, DISPLAY_INTERSTATE75_64X32, DISPLAY_INTERSTATE75_96X32, DISPLAY_INTERSTATE75_96X48, DISPLAY_INTERSTATE75_128X32, DISPLAY_INTERSTATE75_64X64, DISPLAY_INTERSTATE75_128X64, DISPLAY_INTERSTATE75_192X64, DISPLAY_INTERSTATE75_256X64, DISPLAY_INTERSTATE75_128X128
from pimoroni_i2c import PimoroniI2C
import hub75
import sys
@ -29,6 +29,7 @@ class Interstate75:
DISPLAY_INTERSTATE75_128X64 = DISPLAY_INTERSTATE75_128X64
DISPLAY_INTERSTATE75_192X64 = DISPLAY_INTERSTATE75_192X64
DISPLAY_INTERSTATE75_256X64 = DISPLAY_INTERSTATE75_256X64
DISPLAY_INTERSTATE75_128X128 = DISPLAY_INTERSTATE75_128X128
PANEL_GENERIC = hub75.PANEL_GENERIC
PANEL_FM6126A = hub75.PANEL_FM6126A
@ -46,7 +47,15 @@ class Interstate75:
self.interstate75w = "Pico W" in sys.implementation._machine
self.display = PicoGraphics(display=display)
self.width, self.height = self.display.get_bounds()
self.hub75 = hub75.Hub75(self.width, self.height, panel_type=panel_type, stb_invert=stb_invert, color_order=color_order)
out_width = self.width
out_height = self.height
if display == DISPLAY_INTERSTATE75_128X128:
out_width = 256
out_height = 64
self.hub75 = hub75.Hub75(out_width, out_height, panel_type=panel_type, stb_invert=stb_invert, color_order=color_order)
self.hub75.start()
if self.interstate75w:
self._switch_pins = self.SWITCH_PINS_W