diff --git a/components/flow3r_bsp/flow3r_bsp.h b/components/flow3r_bsp/flow3r_bsp.h index bdfb1c4a..5aed7b16 100644 --- a/components/flow3r_bsp/flow3r_bsp.h +++ b/components/flow3r_bsp/flow3r_bsp.h @@ -27,6 +27,12 @@ void flow3r_bsp_display_send_fb_osd(void *fb_data, int bits, int scale, void *osd_data, int osd_x0, int osd_y0, int osd_x1, int osd_y1); +// Send pre-rendered pixel data to a rectangular region of the display. +// Data is w*h pixels of 16bpp RGB565 byteswapped, contiguous row-major. +void flow3r_bsp_display_send_rect(const void *data, + uint16_t x, uint16_t y, + uint16_t w, uint16_t h); + // Set display backlight, as integer percent value (from 0 to 100, clamped). // No-op if display hasn't been successfully initialized. void flow3r_bsp_display_set_backlight(uint8_t percent); diff --git a/components/flow3r_bsp/flow3r_bsp_display.c b/components/flow3r_bsp/flow3r_bsp_display.c index 29ebc59b..c38f8a85 100644 --- a/components/flow3r_bsp/flow3r_bsp_display.c +++ b/components/flow3r_bsp/flow3r_bsp_display.c @@ -64,6 +64,16 @@ void flow3r_bsp_display_send_fb(void *fb_data, int bits) { flow3r_bsp_display_send_fb_osd(fb_data, bits, 1, NULL, 0, 0, 0, 0); } +void flow3r_bsp_display_send_rect(const void *data, + uint16_t x, uint16_t y, + uint16_t w, uint16_t h) { + if (!gc9a01_initialized) return; + esp_err_t ret = flow3r_bsp_gc9a01_blit_rect(&gc9a01, data, x, y, w, h); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "display rect blit failed: %s", esp_err_to_name(ret)); + } +} + void flow3r_bsp_display_set_backlight(uint8_t percent) { if (!gc9a01_initialized) { return; diff --git a/components/flow3r_bsp/flow3r_bsp_gc9a01.c b/components/flow3r_bsp/flow3r_bsp_gc9a01.c index 36163912..98b0de6a 100644 --- a/components/flow3r_bsp/flow3r_bsp_gc9a01.c +++ b/components/flow3r_bsp/flow3r_bsp_gc9a01.c @@ -1096,3 +1096,23 @@ esp_err_t flow3r_bsp_gc9a01_blit_full(flow3r_bsp_gc9a01_t *gc9a01, const void *fb, int bits) { return flow3r_bsp_gc9a01_blit_osd(gc9a01, fb, bits, 1, NULL, 0, 0, 0, 0); } + +esp_err_t flow3r_bsp_gc9a01_blit_rect(flow3r_bsp_gc9a01_t *gc9a01, + const void *data, + uint16_t x, uint16_t y, + uint16_t w, uint16_t h) { + if (x >= 240 || y >= 240 || w == 0 || h == 0) return ESP_OK; + if (x + w > 240) w = 240 - x; + if (y + h > 240) h = 240 - y; + + esp_err_t ret; + ret = flow3r_bsp_gc9a01_column_set(gc9a01, x, x + w - 1); + if (ret != ESP_OK) return ret; + ret = flow3r_bsp_gc9a01_row_set(gc9a01, y, y + h - 1); + if (ret != ESP_OK) return ret; + ret = flow3r_bsp_gc9a01_cmd_sync(gc9a01, Cmd_RAMWR); + if (ret != ESP_OK) return ret; + ret = flow3r_bsp_gc9a01_data_sync(gc9a01, data, w * h * 2); + + return ret; +} diff --git a/components/flow3r_bsp/flow3r_bsp_gc9a01.h b/components/flow3r_bsp/flow3r_bsp_gc9a01.h index 28ed0cb5..d142f5a0 100644 --- a/components/flow3r_bsp/flow3r_bsp_gc9a01.h +++ b/components/flow3r_bsp/flow3r_bsp_gc9a01.h @@ -74,6 +74,15 @@ esp_err_t flow3r_bsp_gc9a01_blit_osd(flow3r_bsp_gc9a01_t *gc9a01, const void *osd_fb, int osd_x0, int osd_y0, int osd_x1, int osd_y1); +// Send pre-rendered pixel data to a rectangular region of the display. +// Data must be w*h pixels in 16bpp RGB565 byteswapped format, packed +// contiguously (row-major, no stride gaps). +// Coordinates are clamped to display bounds. +esp_err_t flow3r_bsp_gc9a01_blit_rect(flow3r_bsp_gc9a01_t *gc9a01, + const void *data, + uint16_t x, uint16_t y, + uint16_t w, uint16_t h); + // Set backlight for display, using integer percent value (0-100, clamped). esp_err_t flow3r_bsp_gc9a01_backlight_set(flow3r_bsp_gc9a01_t *gc9a01, uint8_t value); diff --git a/drivers/gc9a01/display.c b/drivers/gc9a01/display.c index cca7ddff..1cabcee6 100644 --- a/drivers/gc9a01/display.c +++ b/drivers/gc9a01/display.c @@ -30,38 +30,62 @@ static MP_DEFINE_CONST_FUN_OBJ_0(get_fps_obj, get_fps); #define TILDAGON_DISPLAY_WIDTH 240 #define TILDAGON_DISPLAY_HEIGHT 240 -//EXT_RAM_BSS_ATTR -static uint8_t tildagon_fb[TILDAGON_DISPLAY_WIDTH * TILDAGON_DISPLAY_HEIGHT * 2]; static Ctx *tildagon_ctx = NULL; +// #define TILDAGON_CTX_CB_MODE + +#ifdef TILDAGON_CTX_CB_MODE + +#define TILDAGON_SCRATCH_ROWS 32 + +// EXT_RAM_BSS_ATTR +static uint8_t tildagon_scratch[TILDAGON_DISPLAY_WIDTH * TILDAGON_SCRATCH_ROWS * 2]; + +static void tildagon_set_pixels(Ctx *ctx, void *user_data, + int x, int y, int w, int h, void *buf) { + flow3r_bsp_display_send_rect(buf, x, y, w, h); +} + Ctx *tildagon_gfx_ctx(void) { if (tildagon_ctx == NULL) { - tildagon_ctx = ctx_new_for_framebuffer (tildagon_fb, TILDAGON_DISPLAY_WIDTH, TILDAGON_DISPLAY_HEIGHT, TILDAGON_DISPLAY_WIDTH * 2, CTX_FORMAT_RGB565_BYTESWAPPED); + tildagon_ctx = ctx_new_cb(TILDAGON_DISPLAY_WIDTH, TILDAGON_DISPLAY_HEIGHT, + CTX_FORMAT_RGB565_BYTESWAPPED, + tildagon_set_pixels, NULL, + NULL, NULL, + sizeof(tildagon_scratch), + tildagon_scratch, + CTX_FLAG_HASH_CACHE); } return tildagon_ctx; } -void tildagon_start_frame(Ctx *ctx) +void tildagon_end_frame(Ctx *ctx) { - int32_t offset_x = FLOW3R_BSP_DISPLAY_WIDTH / 2; - int32_t offset_y = FLOW3R_BSP_DISPLAY_HEIGHT / 2; - - ctx_save (ctx); - ctx_identity (ctx); - ctx_apply_transform (ctx, 1.0f, 0.0f, offset_x, 0.0f, 1.0f, offset_y, 0.0f, 0.0f, 1.0f); + ctx_restore (ctx); + ctx_end_frame (ctx); + st3m_gfx_fps_update (); } -static mp_obj_t get_ctx() { - Ctx *ctx = tildagon_gfx_ctx(); - assert (ctx); - tildagon_start_frame (ctx); - return mp_ctx_from_ctx(ctx); +#else /* framebuffer mode */ + +// EXT_RAM_BSS_ATTR +static uint8_t tildagon_fb[TILDAGON_DISPLAY_WIDTH * TILDAGON_DISPLAY_HEIGHT * 2]; + +Ctx *tildagon_gfx_ctx(void) +{ + if (tildagon_ctx == NULL) + { + tildagon_ctx = ctx_new_for_framebuffer(tildagon_fb, + TILDAGON_DISPLAY_WIDTH, TILDAGON_DISPLAY_HEIGHT, + TILDAGON_DISPLAY_WIDTH * 2, + CTX_FORMAT_RGB565_BYTESWAPPED); + } + return tildagon_ctx; } -static MP_DEFINE_CONST_FUN_OBJ_0(get_ctx_obj, get_ctx); -void tildagon_blit_fb (void) +static void tildagon_blit_fb(void) { flow3r_bsp_display_send_fb(tildagon_fb, 16); } @@ -77,6 +101,32 @@ void tildagon_end_frame(Ctx *ctx) st3m_gfx_fps_update (); } +#endif /* TILDAGON_CTX_CB_MODE */ + +void tildagon_start_frame(Ctx *ctx) +{ + int32_t offset_x = FLOW3R_BSP_DISPLAY_WIDTH / 2; + int32_t offset_y = FLOW3R_BSP_DISPLAY_HEIGHT / 2; + + ctx_save (ctx); +#ifndef TILDAGON_CTX_CB_MODE + // In framebuffer mode, identity resets any leftover state since + // ctx_end_frame() is never called. In cb mode ctx_end_frame() resets + // the state and the identity would override the tile offset that the + // cb backend applies via ctx_translate before replaying the drawlist. + ctx_identity (ctx); +#endif + ctx_apply_transform (ctx, 1.0f, 0.0f, offset_x, 0.0f, 1.0f, offset_y, 0.0f, 0.0f, 1.0f); +} + +static mp_obj_t get_ctx() { + Ctx *ctx = tildagon_gfx_ctx(); + assert (ctx); + tildagon_start_frame (ctx); + return mp_ctx_from_ctx(ctx); +} +static MP_DEFINE_CONST_FUN_OBJ_0(get_ctx_obj, get_ctx); + static mp_obj_t end_frame(mp_obj_t ctx) { mp_ctx_obj_t *self = MP_OBJ_TO_PTR(ctx); tildagon_end_frame (self->ctx); @@ -98,25 +148,25 @@ static mp_obj_t hexagon(size_t n_args, const mp_obj_t *args) { float x = mp_obj_get_float(args[1]); float y = mp_obj_get_float(args[2]); float dim = mp_obj_get_float(args[3]); - + // All the internal angles are 120 degrees, or 2/3 pi radians // This translates to either an offset of (1, 0) or the pair below float minor_component = cos(M_PI / 3); float major_component = sin(M_PI / 3); - + // Stash the caller's axes ctx_save(ctx->ctx); - + // Set the origin to the centre of the hexagon and scale to the size ctx_translate (ctx->ctx, x, y); ctx_scale (ctx->ctx, dim, dim); - + // Rotate so point is at the top - the drawing code has the flat side at the top ctx_rotate(ctx->ctx, M_PI / 2.0f); - + // Move to the start of the top left line ctx_move_to(ctx->ctx, -minor_component, -major_component); - + // Draw the six segments ctx_rel_line_to(ctx->ctx, 1.0f, 0.0f); ctx_rel_line_to(ctx->ctx, minor_component, major_component); @@ -124,10 +174,10 @@ static mp_obj_t hexagon(size_t n_args, const mp_obj_t *args) { ctx_rel_line_to(ctx->ctx, -1.0f, 0.0f); ctx_rel_line_to(ctx->ctx, -minor_component, -major_component); ctx_rel_line_to(ctx->ctx, minor_component, -major_component); - + // Fill the hexagon ctx_fill(ctx->ctx); - + // Restore the axes ctx_restore(ctx->ctx);