From 4d1ce8ac7b40d9de72b4eafebaf5cb585c627450 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Tue, 27 Feb 2024 11:14:05 +0000 Subject: [PATCH 1/8] CI: Bump MicroPython to rp2-add-rp2350. CI tracking the unmerged RP2350 support branch. --- .github/workflows/micropython.yml | 3 ++- ci/micropython.sh | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/micropython.yml b/.github/workflows/micropython.yml index 9f9e007cc..e4e09c67c 100644 --- a/.github/workflows/micropython.yml +++ b/.github/workflows/micropython.yml @@ -7,7 +7,8 @@ on: types: [created] env: - MICROPYTHON_VERSION: v1.23.0 + MICROPYTHON_VERSION: rp2-add-rp2350 + MICROPYTHON_FLAVOUR: dpgeorge jobs: build: diff --git a/ci/micropython.sh b/ci/micropython.sh index 2f598c084..3f044e766 100644 --- a/ci/micropython.sh +++ b/ci/micropython.sh @@ -13,11 +13,9 @@ function log_warning { } function micropython_clone { - log_inform "Using MicroPython $MICROPYTHON_VERSION" - git clone https://github.com/micropython/micropython + log_inform "Using MicroPython $MICROPYTHON_FLAVOUR/$MICROPYTHON_VERSION" + git clone https://github.com/$MICROPYTHON_FLAVOUR/micropython -b $MICROPYTHON_VERSION --depth=1 cd micropython - git checkout $MICROPYTHON_VERSION - git cherry-pick -n 932f76c6ba64c5a3e68de3324556d9979f09303b git submodule update --init lib/pico-sdk git submodule update --init lib/cyw43-driver git submodule update --init lib/lwip From f5ffd2611ee7dfb47110443c5a49d61f7309aba6 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 12 Aug 2024 17:20:17 +0100 Subject: [PATCH 2/8] Wakeup: Port wakeup runtime functionality to SDK 2.0.0. --- .../{pico_sdk.patch => pico_sdk.off.patch} | 0 .../{pico_sdk.patch => pico_sdk.off.patch} | 0 micropython/modules/wakeup/wakeup.c | 36 ++++++++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) rename micropython/board/PICO_W_ENVIRO/{pico_sdk.patch => pico_sdk.off.patch} (100%) rename micropython/board/PICO_W_INKY/{pico_sdk.patch => pico_sdk.off.patch} (100%) diff --git a/micropython/board/PICO_W_ENVIRO/pico_sdk.patch b/micropython/board/PICO_W_ENVIRO/pico_sdk.off.patch similarity index 100% rename from micropython/board/PICO_W_ENVIRO/pico_sdk.patch rename to micropython/board/PICO_W_ENVIRO/pico_sdk.off.patch diff --git a/micropython/board/PICO_W_INKY/pico_sdk.patch b/micropython/board/PICO_W_INKY/pico_sdk.off.patch similarity index 100% rename from micropython/board/PICO_W_INKY/pico_sdk.patch rename to micropython/board/PICO_W_INKY/pico_sdk.off.patch diff --git a/micropython/modules/wakeup/wakeup.c b/micropython/modules/wakeup/wakeup.c index 69f2422c3..0b9d523e8 100644 --- a/micropython/modules/wakeup/wakeup.c +++ b/micropython/modules/wakeup/wakeup.c @@ -1,4 +1,25 @@ #include "wakeup.h" +#include "hardware/gpio.h" +#include "wakeup.config.hpp" +#include "pico/runtime_init.h" + + +uint32_t runtime_wakeup_gpio_state = 0; + +// Pins to toggle on wakeup +#ifndef PICO_WAKEUP_PIN_MASK +#define PICO_WAKEUP_PIN_MASK ((0b1 << 2) | (0b1 << 6)) +#endif + +// Direction +#ifndef PICO_WAKEUP_PIN_DIR +#define PICO_WAKEUP_PIN_DIR ((0b1 << 2) | (0b1 << 6)) +#endif + +// Value +#ifndef PICO_WAKEUP_PIN_VALUE +#define PICO_WAKEUP_PIN_VALUE ((0b1 << 2) | (0b1 << 6)) +#endif static MP_DEFINE_CONST_FUN_OBJ_0(Wakeup_get_gpio_state_obj, Wakeup_get_gpio_state); static MP_DEFINE_CONST_FUN_OBJ_0(Wakeup_reset_gpio_state_obj, Wakeup_reset_gpio_state); @@ -23,4 +44,17 @@ const mp_obj_module_t wakeup_user_cmodule = { MP_REGISTER_MODULE(MP_QSTR_wakeup, wakeup_user_cmodule, MODULE_WAKEUP_ENABLED); #else MP_REGISTER_MODULE(MP_QSTR_wakeup, wakeup_user_cmodule); -#endif \ No newline at end of file +#endif + +void runtime_init_latch(void) { + runtime_wakeup_gpio_state = gpio_get_all(); + + gpio_init_mask(PICO_WAKEUP_PIN_MASK); + gpio_set_dir_masked(PICO_WAKEUP_PIN_MASK, PICO_WAKEUP_PIN_DIR); + gpio_put_masked(PICO_WAKEUP_PIN_MASK, PICO_WAKEUP_PIN_VALUE); +}; + +// After runtime_init_early_resets, PICO_RUNTIME_INIT_EARLY_RESETS ? +PICO_RUNTIME_INIT_FUNC_HW(runtime_init_latch, "00101"); +// Too early? +// PICO_RUNTIME_INIT_FUNC_HW(runtime_init_latch, PICO_RUNTIME_INIT_EARLIEST); From 18991756308e3557fa5ff761d67abf23a37eb54e Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 7 Aug 2024 22:36:59 +0100 Subject: [PATCH 3/8] PicoGraphics: Support for Explorer. --- drivers/st7789/st7789.hpp | 3 ++- micropython/modules/picographics/picographics.c | 1 + micropython/modules/picographics/picographics.cpp | 14 +++++++++++++- micropython/modules/picographics/picographics.h | 3 ++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/st7789/st7789.hpp b/drivers/st7789/st7789.hpp index e4ad2ee6f..52369256f 100644 --- a/drivers/st7789/st7789.hpp +++ b/drivers/st7789/st7789.hpp @@ -58,9 +58,10 @@ namespace pimoroni { cs(pins.cs), dc(pins.dc), wr_sck(pins.wr_sck), rd_sck(pins.rd_sck), d0(pins.d0), bl(pins.bl) { parallel_pio = pio1; + pio_set_gpio_base(parallel_pio, d0 + 8 >= 32 ? 16 : 0); parallel_sm = pio_claim_unused_sm(parallel_pio, true); parallel_offset = pio_add_program(parallel_pio, &st7789_parallel_program); - + //gpio_init(wr_sck); //gpio_set_dir(wr_sck, GPIO_OUT); //gpio_set_function(wr_sck, GPIO_FUNC_SIO); diff --git a/micropython/modules/picographics/picographics.c b/micropython/modules/picographics/picographics.c index b1aeec19e..0074b55f0 100644 --- a/micropython/modules/picographics/picographics.c +++ b/micropython/modules/picographics/picographics.c @@ -157,6 +157,7 @@ static const mp_map_elem_t picographics_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_DISPLAY_UNICORN_PACK), MP_ROM_INT(DISPLAY_UNICORN_PACK) }, { MP_ROM_QSTR(MP_QSTR_DISPLAY_SCROLL_PACK), MP_ROM_INT(DISPLAY_SCROLL_PACK) }, { 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_PEN_1BIT), MP_ROM_INT(PEN_1BIT) }, { MP_ROM_QSTR(MP_QSTR_PEN_P4), MP_ROM_INT(PEN_P4) }, diff --git a/micropython/modules/picographics/picographics.cpp b/micropython/modules/picographics/picographics.cpp index 4fe1e2d04..a55e8ad41 100644 --- a/micropython/modules/picographics/picographics.cpp +++ b/micropython/modules/picographics/picographics.cpp @@ -44,6 +44,13 @@ typedef struct _ModPicoGraphics_obj_t { bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height, int &rotate, int &pen_type, PicoGraphicsBusType &bus_type) { switch(display) { + case DISPLAY_EXPLORER: + width = 320; + height = 240; + bus_type = BUS_PARALLEL; + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_RGB565; + break; case DISPLAY_PICO_DISPLAY: width = 240; height = 135; @@ -335,6 +342,10 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size } else if (display == DISPLAY_PICO_W_EXPLORER) { spi_bus = {PIMORONI_SPI_DEFAULT_INSTANCE, 17, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, SPI_DEFAULT_MISO, 9}; } + } else if (bus_type == BUS_PARALLEL) { + if (display == DISPLAY_EXPLORER) { + parallel_bus = {27, 28, 30, 31, 32, 26}; + } } } @@ -349,7 +360,8 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size // TODO grab BUSY and RESET from ARG_extra_pins self->display = m_new_class(Inky73, width, height, (Rotation)rotate, spi_bus); - } else if (display == DISPLAY_TUFTY_2040) { + } else if (display == DISPLAY_TUFTY_2040 + || display == DISPLAY_EXPLORER) { self->display = m_new_class(ST7789, width, height, (Rotation)rotate, parallel_bus); } else if (display == DISPLAY_LCD_160X80) { diff --git a/micropython/modules/picographics/picographics.h b/micropython/modules/picographics/picographics.h index 1812a875f..655d336b7 100644 --- a/micropython/modules/picographics/picographics.h +++ b/micropython/modules/picographics/picographics.h @@ -30,7 +30,8 @@ enum PicoGraphicsDisplay { DISPLAY_STELLAR_UNICORN, DISPLAY_UNICORN_PACK, DISPLAY_SCROLL_PACK, - DISPLAY_PICO_W_EXPLORER + DISPLAY_PICO_W_EXPLORER, + DISPLAY_EXPLORER }; enum PicoGraphicsPenType { From 491afaa58ebf4f4d1977bb56ca24bc8428890bfc Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 16 Sep 2024 12:14:41 +0100 Subject: [PATCH 4/8] CI: Retarget to our psram branch. --- .github/workflows/micropython.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/micropython.yml b/.github/workflows/micropython.yml index e4e09c67c..387db8352 100644 --- a/.github/workflows/micropython.yml +++ b/.github/workflows/micropython.yml @@ -7,8 +7,8 @@ on: types: [created] env: - MICROPYTHON_VERSION: rp2-add-rp2350 - MICROPYTHON_FLAVOUR: dpgeorge + MICROPYTHON_VERSION: feature/psram + MICROPYTHON_FLAVOUR: pimoroni jobs: build: From c8ffaa68134d2ffa6462860db2000d3579400362 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 18 Sep 2024 12:28:55 +0100 Subject: [PATCH 5/8] UC8151: Experimental: Do *not* power off display. --- drivers/uc8151/uc8151.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/uc8151/uc8151.cpp b/drivers/uc8151/uc8151.cpp index 76477141f..92d7a6203 100644 --- a/drivers/uc8151/uc8151.cpp +++ b/drivers/uc8151/uc8151.cpp @@ -389,7 +389,7 @@ namespace pimoroni { } void UC8151::power_off() { - command(POF); + //command(POF); } void UC8151::read(uint8_t reg, size_t len, uint8_t *data) { @@ -534,7 +534,7 @@ namespace pimoroni { void UC8151::off() { busy_wait(); - command(POF); // turn off + //command(POF); // turn off } } From edd79d8e910230b0787d40a27a5a21c366fb9f19 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 2 Oct 2024 14:02:11 +0100 Subject: [PATCH 6/8] CI: Update py_decl to 0.0.4. Gives more detailed reports about firmware/flash size and overlap. --- .github/workflows/micropython.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/micropython.yml b/.github/workflows/micropython.yml index 387db8352..55dd1d9bc 100644 --- a/.github/workflows/micropython.yml +++ b/.github/workflows/micropython.yml @@ -88,7 +88,7 @@ jobs: uses: actions/checkout@v4 with: repository: gadgetoid/py_decl - ref: v0.0.1 + ref: v0.0.4 path: py_decl - name: Build MPY Cross From 5e0682bc22c872673332c5617110d354d7399197 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 7 Oct 2024 14:36:06 +0100 Subject: [PATCH 7/8] PicoGraphics: Layers. Add preliminary support for multiple layered drawing surfaces. Allows, for example, static content to be loaded into one layer and remain unmodified while the above layer contains animations. Particularly useful for drawing PNG or JPEG UI elements which are then overdrawn with text or animated elements, without paying the cost of loading/decoding every frame. --- drivers/st7735/st7735.cpp | 22 +++---- drivers/st7789/st7789.cpp | 38 +++++------ libraries/pico_graphics/pico_graphics.cpp | 7 +++ libraries/pico_graphics/pico_graphics.hpp | 32 +++++++--- .../pico_graphics/pico_graphics_pen_1bit.cpp | 6 +- .../pico_graphics/pico_graphics_pen_1bitY.cpp | 4 +- .../pico_graphics/pico_graphics_pen_3bit.cpp | 4 +- .../pico_graphics/pico_graphics_pen_inky7.cpp | 4 +- .../pico_graphics/pico_graphics_pen_p4.cpp | 4 +- .../pico_graphics/pico_graphics_pen_p8.cpp | 4 +- .../pico_graphics_pen_rgb332.cpp | 28 ++++++--- .../pico_graphics_pen_rgb565.cpp | 32 +++++++++- .../pico_graphics_pen_rgb888.cpp | 4 +- .../modules/picographics/picographics.c | 5 ++ .../modules/picographics/picographics.cpp | 63 ++++++++++++------- .../modules/picographics/picographics.h | 3 + 16 files changed, 170 insertions(+), 90 deletions(-) diff --git a/drivers/st7735/st7735.cpp b/drivers/st7735/st7735.cpp index d2cabff7c..1ae5728eb 100644 --- a/drivers/st7735/st7735.cpp +++ b/drivers/st7735/st7735.cpp @@ -165,21 +165,17 @@ namespace pimoroni { // Native 16-bit framebuffer update void ST7735::update(PicoGraphics *graphics) { - if(graphics->pen_type == PicoGraphics::PEN_RGB565) { - command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer); - } else { - command(reg::RAMWR); - gpio_put(dc, 1); // data mode - gpio_put(cs, 0); + command(reg::RAMWR); + gpio_put(dc, 1); // data mode + gpio_put(cs, 0); - graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { - if (length > 0) { - spi_write_blocking(spi, (const uint8_t*)data, length); - } - }); + graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { + if (length > 0) { + spi_write_blocking(spi, (const uint8_t*)data, length); + } + }); - gpio_put(cs, 1); - } + gpio_put(cs, 1); } void ST7735::set_backlight(uint8_t brightness) { diff --git a/drivers/st7789/st7789.cpp b/drivers/st7789/st7789.cpp index ed34447c5..b76f9b078 100644 --- a/drivers/st7789/st7789.cpp +++ b/drivers/st7789/st7789.cpp @@ -282,30 +282,26 @@ namespace pimoroni { void ST7789::update(PicoGraphics *graphics) { uint8_t cmd = reg::RAMWR; - if(graphics->pen_type == PicoGraphics::PEN_RGB565) { // Display buffer is screen native - command(cmd, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer); - } else { - gpio_put(dc, 0); // command mode - gpio_put(cs, 0); - if(spi) { // SPI Bus - spi_write_blocking(spi, &cmd, 1); - } else { // Parallel Bus - write_blocking_parallel(&cmd, 1); - } + gpio_put(dc, 0); // command mode + gpio_put(cs, 0); + if(spi) { // SPI Bus + spi_write_blocking(spi, &cmd, 1); + } else { // Parallel Bus + write_blocking_parallel(&cmd, 1); + } - gpio_put(dc, 1); // data mode + gpio_put(dc, 1); // data mode - graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { - if (length > 0) { - write_blocking_dma((const uint8_t*)data, length); - } - else { - dma_channel_wait_for_finish_blocking(st_dma); - } - }); + graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { + if (length > 0) { + write_blocking_dma((const uint8_t*)data, length); + } + else { + dma_channel_wait_for_finish_blocking(st_dma); + } + }); - gpio_put(cs, 1); - } + gpio_put(cs, 1); } void ST7789::set_backlight(uint8_t brightness) { diff --git a/libraries/pico_graphics/pico_graphics.cpp b/libraries/pico_graphics/pico_graphics.cpp index 4e32e4ec3..58ae1ba70 100644 --- a/libraries/pico_graphics/pico_graphics.cpp +++ b/libraries/pico_graphics/pico_graphics.cpp @@ -19,6 +19,13 @@ namespace pimoroni { RGB* PicoGraphics::get_palette() {return nullptr;} bool PicoGraphics::supports_alpha_blend() {return false;} + void PicoGraphics::set_layer(uint l) { + this->layer = l; + }; + uint PicoGraphics::get_layer() { + return this->layer; + }; + void PicoGraphics::set_dimensions(int width, int height) { bounds = clip = {0, 0, width, height}; } diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index 7445c5342..df16b874f 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -226,6 +226,9 @@ namespace pimoroni { Rect clip; uint thickness = 1; + uint layers = 1; + uint layer = 0; + typedef std::function conversion_callback_func; typedef std::function next_pixel_func; typedef std::function next_pixel_func_rgb888; @@ -270,6 +273,12 @@ namespace pimoroni { PicoGraphics(uint16_t width, uint16_t height, void *frame_buffer) : frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height) { set_font(&font6); + layers = 1; + }; + + PicoGraphics(uint16_t width, uint16_t height, uint16_t layers, void *frame_buffer) + : frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height), layers(layers) { + set_font(&font6); }; virtual void set_pen(uint c) = 0; @@ -278,6 +287,9 @@ namespace pimoroni { virtual void set_pixel_span(const Point &p, uint l) = 0; void set_thickness(uint t); + void set_layer(uint l); + uint get_layer(); + virtual int get_palette_size(); virtual RGB* get_palette(); virtual bool supports_alpha_blend(); @@ -330,7 +342,7 @@ namespace pimoroni { public: uint8_t color; - PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -346,7 +358,7 @@ namespace pimoroni { public: uint8_t color; - PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -387,7 +399,7 @@ namespace pimoroni { bool cache_built = false; std::array candidates; - PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -420,7 +432,7 @@ namespace pimoroni { bool cache_built = false; std::array candidates; - PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override; @@ -453,7 +465,7 @@ namespace pimoroni { bool cache_built = false; std::array candidates; - PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override; @@ -478,7 +490,7 @@ namespace pimoroni { class PicoGraphics_PenRGB332 : public PicoGraphics { public: RGB332 color; - PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int create_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -503,7 +515,7 @@ namespace pimoroni { public: RGB src_color; RGB565 color; - PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int create_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -513,13 +525,15 @@ namespace pimoroni { static size_t buffer_size(uint w, uint h) { return w * h * sizeof(RGB565); } + + void frame_convert(PenType type, conversion_callback_func callback) override; }; class PicoGraphics_PenRGB888 : public PicoGraphics { public: RGB src_color; RGB888 color; - PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int create_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -597,7 +611,7 @@ namespace pimoroni { uint color; IDirectDisplayDriver &driver; - PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver &direct_display_driver); + PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver &direct_display_driver, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int create_pen(uint8_t r, uint8_t g, uint8_t b) override; diff --git a/libraries/pico_graphics/pico_graphics_pen_1bit.cpp b/libraries/pico_graphics/pico_graphics_pen_1bit.cpp index 30fb1e53b..e05b2a370 100644 --- a/libraries/pico_graphics/pico_graphics_pen_1bit.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_1bit.cpp @@ -2,11 +2,11 @@ namespace pimoroni { - PicoGraphics_Pen1Bit::PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_Pen1Bit::PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_1BIT; if(this->frame_buffer == nullptr) { - this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); + this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height) * layers]); } } diff --git a/libraries/pico_graphics/pico_graphics_pen_1bitY.cpp b/libraries/pico_graphics/pico_graphics_pen_1bitY.cpp index 6fc2cb8c2..6efdde920 100644 --- a/libraries/pico_graphics/pico_graphics_pen_1bitY.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_1bitY.cpp @@ -2,8 +2,8 @@ namespace pimoroni { - PicoGraphics_Pen1BitY::PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_Pen1BitY::PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_1BIT; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); diff --git a/libraries/pico_graphics/pico_graphics_pen_3bit.cpp b/libraries/pico_graphics/pico_graphics_pen_3bit.cpp index 5faba1cdc..dc67018b3 100644 --- a/libraries/pico_graphics/pico_graphics_pen_3bit.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_3bit.cpp @@ -2,8 +2,8 @@ namespace pimoroni { - PicoGraphics_Pen3Bit::PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_Pen3Bit::PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_3BIT; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); diff --git a/libraries/pico_graphics/pico_graphics_pen_inky7.cpp b/libraries/pico_graphics/pico_graphics_pen_inky7.cpp index 9a6ae5b0d..e41de871d 100644 --- a/libraries/pico_graphics/pico_graphics_pen_inky7.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_inky7.cpp @@ -1,8 +1,8 @@ #include "pico_graphics.hpp" namespace pimoroni { - PicoGraphics_PenInky7::PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver &direct_display_driver) - : PicoGraphics(width, height, nullptr), + PicoGraphics_PenInky7::PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver &direct_display_driver, uint16_t layers) + : PicoGraphics(width, height, layers, nullptr), driver(direct_display_driver) { this->pen_type = PEN_INKY7; } diff --git a/libraries/pico_graphics/pico_graphics_pen_p4.cpp b/libraries/pico_graphics/pico_graphics_pen_p4.cpp index 68b3d62c2..c69deec79 100644 --- a/libraries/pico_graphics/pico_graphics_pen_p4.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_p4.cpp @@ -2,8 +2,8 @@ namespace pimoroni { - PicoGraphics_PenP4::PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_PenP4::PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_P4; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); diff --git a/libraries/pico_graphics/pico_graphics_pen_p8.cpp b/libraries/pico_graphics/pico_graphics_pen_p8.cpp index f7d0be73d..b93f835bf 100644 --- a/libraries/pico_graphics/pico_graphics_pen_p8.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_p8.cpp @@ -1,8 +1,8 @@ #include "pico_graphics.hpp" namespace pimoroni { - PicoGraphics_PenP8::PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_PenP8::PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_P8; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp index 1bce808fc..58afcae8a 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp @@ -2,11 +2,11 @@ #include namespace pimoroni { - PicoGraphics_PenRGB332::PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_PenRGB332::PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_RGB332; if(this->frame_buffer == nullptr) { - this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); + this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height) * layers]); } } void PicoGraphics_PenRGB332::set_pen(uint c) { @@ -23,12 +23,14 @@ namespace pimoroni { } void PicoGraphics_PenRGB332::set_pixel(const Point &p) { uint8_t *buf = (uint8_t *)frame_buffer; + buf += buffer_size(this->bounds.w, this->bounds.h) * layer; buf[p.y * bounds.w + p.x] = color; } void PicoGraphics_PenRGB332::set_pixel_span(const Point &p, uint l) { // pointer to byte in framebuffer that contains this pixel uint8_t *buf = (uint8_t *)frame_buffer; - buf = &buf[p.y * bounds.w + p.x]; + buf += buffer_size(this->bounds.w, this->bounds.h) * layer; + buf += p.y * bounds.w + p.x; while(l--) { *buf++ = color; @@ -38,6 +40,7 @@ namespace pimoroni { if(!bounds.contains(p)) return; uint8_t *buf = (uint8_t *)frame_buffer; + buf += buffer_size(this->bounds.w, this->bounds.h) * layer; RGB332 blended = RGB(buf[p.y * bounds.w + p.x]).blend(RGB(color), a).to_rgb332(); @@ -96,9 +99,20 @@ namespace pimoroni { // Treat our void* frame_buffer as uint8_t uint8_t *src = (uint8_t *)frame_buffer; - frame_convert_rgb565(callback, [&]() { - return rgb332_to_rgb565_lut[*src++]; - }); + if(this->layers > 1) { + // Assume only two layers for now + uint8_t *src_layer2 = src + buffer_size(this->bounds.w, this->bounds.h); + + frame_convert_rgb565(callback, [&]() { + RGB565 c1 = rgb332_to_rgb565_lut[*src++]; + RGB565 c2 = rgb332_to_rgb565_lut[*src_layer2++]; + return c2 ? c2 : c1; + }); + } else { + frame_convert_rgb565(callback, [&]() { + return rgb332_to_rgb565_lut[*src++]; + }); + } } } void PicoGraphics_PenRGB332::sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) { diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp index f17c6be47..5151e029e 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp @@ -1,8 +1,8 @@ #include "pico_graphics.hpp" namespace pimoroni { - PicoGraphics_PenRGB565::PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_PenRGB565::PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_RGB565; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); @@ -23,15 +23,43 @@ namespace pimoroni { } void PicoGraphics_PenRGB565::set_pixel(const Point &p) { uint16_t *buf = (uint16_t *)frame_buffer; + // We can't use buffer_size because our pointer is uint16_t + buf += this->bounds.w * this->bounds.h * layer; buf[p.y * bounds.w + p.x] = color; } void PicoGraphics_PenRGB565::set_pixel_span(const Point &p, uint l) { // pointer to byte in framebuffer that contains this pixel uint16_t *buf = (uint16_t *)frame_buffer; + // We can't use buffer_size because our pointer is uint16_t + buf += this->bounds.w * this->bounds.h * layer; buf = &buf[p.y * bounds.w + p.x]; while(l--) { *buf++ = color; } } + void PicoGraphics_PenRGB565::frame_convert(PenType type, conversion_callback_func callback) { + if(type == PEN_RGB565) { + // Treat our void* frame_buffer as uint8_t + uint16_t *src = (uint16_t *)frame_buffer; + + if(layers > 1) { + // Assume only two layers for now + uint16_t *src_layer2 = src; + + // We can't use buffer_size because our pointer is uint16_t + src_layer2 += this->bounds.w * this->bounds.h * layer; + + frame_convert_rgb565(callback, [&]() { + RGB565 c1 = *src++; + RGB565 c2 = *src_layer2++; + return c2 ? c2 : c1; + }); + } else { + frame_convert_rgb565(callback, [&]() { + return *src++; + }); + } + } + } } \ No newline at end of file diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp index 6cc9d2adf..6f5360643 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp @@ -1,8 +1,8 @@ #include "pico_graphics.hpp" namespace pimoroni { - PicoGraphics_PenRGB888::PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_PenRGB888::PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_RGB888; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); diff --git a/micropython/modules/picographics/picographics.c b/micropython/modules/picographics/picographics.c index 0074b55f0..ad6d0dc57 100644 --- a/micropython/modules/picographics/picographics.c +++ b/micropython/modules/picographics/picographics.c @@ -24,6 +24,9 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_create_pen_obj, 4, 4, ModPic MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_create_pen_hsv_obj, 4, 4, ModPicoGraphics_create_pen_hsv); MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_thickness_obj, ModPicoGraphics_set_thickness); +// Layers +MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_layer_obj, ModPicoGraphics_set_layer); + // Primitives MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_set_clip_obj, 5, 5, ModPicoGraphics_set_clip); MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_remove_clip_obj, ModPicoGraphics_remove_clip); @@ -60,6 +63,8 @@ static const mp_rom_map_elem_t ModPicoGraphics_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_set_thickness), MP_ROM_PTR(&ModPicoGraphics_set_thickness_obj) }, { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&ModPicoGraphics_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_layer), MP_ROM_PTR(&ModPicoGraphics_set_layer_obj) }, + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&ModPicoGraphics_update_obj) }, { MP_ROM_QSTR(MP_QSTR_partial_update), MP_ROM_PTR(&ModPicoGraphics_partial_update_obj) }, { MP_ROM_QSTR(MP_QSTR_set_update_speed), MP_ROM_PTR(&ModPicoGraphics_set_update_speed_obj) }, diff --git a/micropython/modules/picographics/picographics.cpp b/micropython/modules/picographics/picographics.cpp index a55e8ad41..8ac300cf0 100644 --- a/micropython/modules/picographics/picographics.cpp +++ b/micropython/modules/picographics/picographics.cpp @@ -254,24 +254,24 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height, return true; } -size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint height) { +size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint height, uint layers) { switch(pen_type) { case PEN_1BIT: - return PicoGraphics_Pen1Bit::buffer_size(width, height); + return PicoGraphics_Pen1Bit::buffer_size(width, height) * layers; case PEN_3BIT: - return PicoGraphics_Pen3Bit::buffer_size(width, height); + return PicoGraphics_Pen3Bit::buffer_size(width, height) * layers; case PEN_P4: - return PicoGraphics_PenP4::buffer_size(width, height); + return PicoGraphics_PenP4::buffer_size(width, height) * layers; case PEN_P8: - return PicoGraphics_PenP8::buffer_size(width, height); + return PicoGraphics_PenP8::buffer_size(width, height) * layers; case PEN_RGB332: - return PicoGraphics_PenRGB332::buffer_size(width, height); + return PicoGraphics_PenRGB332::buffer_size(width, height) * layers; case PEN_RGB565: - return PicoGraphics_PenRGB565::buffer_size(width, height); + return PicoGraphics_PenRGB565::buffer_size(width, height) * layers; case PEN_RGB888: - return PicoGraphics_PenRGB888::buffer_size(width, height); + return PicoGraphics_PenRGB888::buffer_size(width, height) * layers; case PEN_INKY7: - return PicoGraphics_PenInky7::buffer_size(width, height); + return PicoGraphics_PenInky7::buffer_size(width, height) * layers; default: return 0; } @@ -280,7 +280,7 @@ size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint h mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { ModPicoGraphics_obj_t *self = nullptr; - enum { ARG_display, ARG_rotate, ARG_bus, ARG_buffer, ARG_pen_type, ARG_extra_pins, ARG_i2c_address }; + enum { ARG_display, ARG_rotate, ARG_bus, ARG_buffer, ARG_pen_type, ARG_extra_pins, ARG_i2c_address, ARG_layers }; static const mp_arg_t allowed_args[] = { { MP_QSTR_display, MP_ARG_INT | MP_ARG_REQUIRED }, { MP_QSTR_rotate, MP_ARG_INT, { .u_int = -1 } }, @@ -289,6 +289,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size { MP_QSTR_pen_type, MP_ARG_INT, { .u_int = -1 } }, { MP_QSTR_extra_pins, MP_ARG_OBJ, { .u_obj = mp_const_none } }, { MP_QSTR_i2c_address, MP_ARG_INT, { .u_int = -1 } }, + { MP_QSTR_layers, MP_ARG_INT, { .u_int = 1 } }, }; // Parse args. @@ -304,6 +305,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size int height = 0; int pen_type = args[ARG_pen_type].u_int; int rotate = args[ARG_rotate].u_int; + int layers = args[ARG_layers].u_int; PicoGraphicsBusType bus_type = BUS_SPI; if(!get_display_settings(display, width, height, rotate, pen_type, bus_type)) mp_raise_ValueError("Unsupported display!"); if(rotate == -1) rotate = (int)Rotation::ROTATE_0; @@ -395,7 +397,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size } // Create or fetch buffer - size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height); + size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height, layers); if(required_size == 0) mp_raise_ValueError("Unsupported pen type!"); if(pen_type == PEN_INKY7) { @@ -418,31 +420,31 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size switch((PicoGraphicsPenType)pen_type) { case PEN_1BIT: if (display == DISPLAY_INKY_PACK) { - self->graphics = m_new_class(PicoGraphics_Pen1BitY, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_Pen1BitY, self->display->width, self->display->height, self->buffer, layers); } else { - self->graphics = m_new_class(PicoGraphics_Pen1Bit, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_Pen1Bit, self->display->width, self->display->height, self->buffer, layers); } break; case PEN_3BIT: - self->graphics = m_new_class(PicoGraphics_Pen3Bit, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_Pen3Bit, self->display->width, self->display->height, self->buffer, layers); break; case PEN_P4: - self->graphics = m_new_class(PicoGraphics_PenP4, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_PenP4, self->display->width, self->display->height, self->buffer, layers); break; case PEN_P8: - self->graphics = m_new_class(PicoGraphics_PenP8, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_PenP8, self->display->width, self->display->height, self->buffer, layers); break; case PEN_RGB332: - self->graphics = m_new_class(PicoGraphics_PenRGB332, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_PenRGB332, self->display->width, self->display->height, self->buffer, layers); break; case PEN_RGB565: - self->graphics = m_new_class(PicoGraphics_PenRGB565, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_PenRGB565, self->display->width, self->display->height, self->buffer, layers); break; case PEN_RGB888: - self->graphics = m_new_class(PicoGraphics_PenRGB888, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_PenRGB888, self->display->width, self->display->height, self->buffer, layers); break; case PEN_INKY7: - self->graphics = m_new_class(PicoGraphics_PenInky7, self->display->width, self->display->height, *(IDirectDisplayDriver *)self->buffer); + self->graphics = m_new_class(PicoGraphics_PenInky7, self->display->width, self->display->height, *(IDirectDisplayDriver *)self->buffer, layers); break; default: break; @@ -453,8 +455,15 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size self->spritedata = nullptr; // Clear the buffer + self->graphics->set_layer(0); self->graphics->set_pen(0); self->graphics->clear(); + if(layers > 1) { + self->graphics->set_layer(1); + self->graphics->set_pen(0); + self->graphics->clear(); + self->graphics->set_layer(0); + } // Update the LCD from the graphics library if (display != DISPLAY_INKY_FRAME && display != DISPLAY_INKY_FRAME_4 && display != DISPLAY_INKY_PACK && display != DISPLAY_INKY_FRAME_7) { @@ -477,7 +486,7 @@ mp_obj_t ModPicoGraphics_set_spritesheet(mp_obj_t self_in, mp_obj_t spritedata) mp_buffer_info_t bufinfo; mp_get_buffer_raise(spritedata, &bufinfo, MP_BUFFER_RW); - int required_size = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, 128, 128); + int required_size = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, 128, 128, 1); if(bufinfo.len != (size_t)(required_size)) { mp_raise_ValueError("Spritesheet the wrong size!"); @@ -562,7 +571,7 @@ mp_int_t ModPicoGraphics_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *buf mp_raise_ValueError("No local framebuffer."); } bufinfo->buf = self->graphics->frame_buffer; - bufinfo->len = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, self->graphics->bounds.w, self->graphics->bounds.h); + bufinfo->len = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, self->graphics->bounds.w, self->graphics->bounds.h, 1); bufinfo->typecode = 'B'; return 0; } @@ -603,7 +612,7 @@ mp_obj_t ModPicoGraphics_get_required_buffer_size(mp_obj_t display_in, mp_obj_t int pen_type = mp_obj_get_int(pen_type_in); PicoGraphicsBusType bus_type = BUS_SPI; if(!get_display_settings(display, width, height, rotation, pen_type, bus_type)) mp_raise_ValueError("Unsupported display!"); - size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height); + size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height, 1); if(required_size == 0) mp_raise_ValueError("Unsupported pen type!"); return mp_obj_new_int(required_size); @@ -755,6 +764,14 @@ mp_obj_t ModPicoGraphics_set_pen(mp_obj_t self_in, mp_obj_t pen) { return mp_const_none; } +mp_obj_t ModPicoGraphics_set_layer(mp_obj_t self_in, mp_obj_t layer) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + + self->graphics->set_layer(mp_obj_get_int(layer)); + + return mp_const_none; +} + mp_obj_t ModPicoGraphics_reset_pen(mp_obj_t self_in, mp_obj_t pen) { ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); diff --git a/micropython/modules/picographics/picographics.h b/micropython/modules/picographics/picographics.h index 655d336b7..1238354d9 100644 --- a/micropython/modules/picographics/picographics.h +++ b/micropython/modules/picographics/picographics.h @@ -77,6 +77,9 @@ extern mp_obj_t ModPicoGraphics_reset_pen(mp_obj_t self_in, mp_obj_t pen); extern mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t ModPicoGraphics_hsv_to_rgb(size_t n_args, const mp_obj_t *args); +// Layers +extern mp_obj_t ModPicoGraphics_set_layer(mp_obj_t self_in, mp_obj_t layer); + // Pen extern mp_obj_t ModPicoGraphics_set_pen(mp_obj_t self_in, mp_obj_t pen); extern mp_obj_t ModPicoGraphics_create_pen(size_t n_args, const mp_obj_t *args); From be24eb5d4e6d72e75966cc3c704407a970711545 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 7 Oct 2024 15:51:02 +0100 Subject: [PATCH 8/8] PicoGraphics: Support multiple layers in more types. --- drivers/st7735/st7735.cpp | 22 ++--- drivers/st7789/st7789.cpp | 38 +++++---- libraries/pico_graphics/pico_graphics.cpp | 1 + libraries/pico_graphics/pico_graphics.hpp | 2 + .../pico_graphics/pico_graphics_pen_p4.cpp | 45 ++++++++--- .../pico_graphics/pico_graphics_pen_p8.cpp | 80 +++++++++++++++---- .../pico_graphics_pen_rgb332.cpp | 27 ++++--- .../pico_graphics_pen_rgb565.cpp | 8 +- .../pico_graphics_pen_rgb888.cpp | 2 + 9 files changed, 158 insertions(+), 67 deletions(-) diff --git a/drivers/st7735/st7735.cpp b/drivers/st7735/st7735.cpp index 1ae5728eb..b945537a8 100644 --- a/drivers/st7735/st7735.cpp +++ b/drivers/st7735/st7735.cpp @@ -165,17 +165,21 @@ namespace pimoroni { // Native 16-bit framebuffer update void ST7735::update(PicoGraphics *graphics) { - command(reg::RAMWR); - gpio_put(dc, 1); // data mode - gpio_put(cs, 0); + if(graphics->pen_type == PicoGraphics::PEN_RGB565 && graphics->layers == 1) { + command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer); + } else { + command(reg::RAMWR); + gpio_put(dc, 1); // data mode + gpio_put(cs, 0); - graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { - if (length > 0) { - spi_write_blocking(spi, (const uint8_t*)data, length); - } - }); + graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { + if (length > 0) { + spi_write_blocking(spi, (const uint8_t*)data, length); + } + }); - gpio_put(cs, 1); + gpio_put(cs, 1); + } } void ST7735::set_backlight(uint8_t brightness) { diff --git a/drivers/st7789/st7789.cpp b/drivers/st7789/st7789.cpp index b76f9b078..9f033adc5 100644 --- a/drivers/st7789/st7789.cpp +++ b/drivers/st7789/st7789.cpp @@ -282,26 +282,30 @@ namespace pimoroni { void ST7789::update(PicoGraphics *graphics) { uint8_t cmd = reg::RAMWR; - gpio_put(dc, 0); // command mode - gpio_put(cs, 0); - if(spi) { // SPI Bus - spi_write_blocking(spi, &cmd, 1); - } else { // Parallel Bus - write_blocking_parallel(&cmd, 1); - } + if(graphics->pen_type == PicoGraphics::PEN_RGB565 && graphics->layers == 1) { // Display buffer is screen native + command(cmd, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer); + } else { + gpio_put(dc, 0); // command mode + gpio_put(cs, 0); + if(spi) { // SPI Bus + spi_write_blocking(spi, &cmd, 1); + } else { // Parallel Bus + write_blocking_parallel(&cmd, 1); + } - gpio_put(dc, 1); // data mode + gpio_put(dc, 1); // data mode - graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { - if (length > 0) { - write_blocking_dma((const uint8_t*)data, length); - } - else { - dma_channel_wait_for_finish_blocking(st_dma); - } - }); + graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { + if (length > 0) { + write_blocking_dma((const uint8_t*)data, length); + } + else { + dma_channel_wait_for_finish_blocking(st_dma); + } + }); - gpio_put(cs, 1); + gpio_put(cs, 1); + } } void ST7789::set_backlight(uint8_t brightness) { diff --git a/libraries/pico_graphics/pico_graphics.cpp b/libraries/pico_graphics/pico_graphics.cpp index 58ae1ba70..c457ca99d 100644 --- a/libraries/pico_graphics/pico_graphics.cpp +++ b/libraries/pico_graphics/pico_graphics.cpp @@ -21,6 +21,7 @@ namespace pimoroni { void PicoGraphics::set_layer(uint l) { this->layer = l; + this->layer_offset = this->bounds.w * this->bounds.h * l; }; uint PicoGraphics::get_layer() { return this->layer; diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index df16b874f..cb0218457 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -79,6 +79,7 @@ namespace pimoroni { } } + constexpr operator bool() {return r || g || b;}; constexpr RGB operator+ (const RGB& c) const {return RGB(r + c.r, g + c.g, b + c.b);} constexpr RGB& operator+=(const RGB& c) {r += c.r; g += c.g; b += c.b; return *this;} constexpr RGB& operator-=(const RGB& c) {r -= c.r; g -= c.g; b -= c.b; return *this;} @@ -228,6 +229,7 @@ namespace pimoroni { uint layers = 1; uint layer = 0; + uint layer_offset = 0; typedef std::function conversion_callback_func; typedef std::function next_pixel_func; diff --git a/libraries/pico_graphics/pico_graphics_pen_p4.cpp b/libraries/pico_graphics/pico_graphics_pen_p4.cpp index c69deec79..bf5edc3fc 100644 --- a/libraries/pico_graphics/pico_graphics_pen_p4.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_p4.cpp @@ -59,6 +59,7 @@ namespace pimoroni { // pointer to byte in framebuffer that contains this pixel uint8_t *buf = (uint8_t *)frame_buffer; + buf += this->layer_offset / 2; uint8_t *f = &buf[i / 2]; uint8_t o = (~i & 0b1) * 4; // bit offset within byte @@ -74,6 +75,7 @@ namespace pimoroni { // pointer to byte in framebuffer that contains this pixel uint8_t *buf = (uint8_t *)frame_buffer; + buf += this->layer_offset / 2; uint8_t *f = &buf[i / 2]; // doubled up color value, so the color is stored in both nibbles @@ -144,16 +146,39 @@ namespace pimoroni { uint8_t *src = (uint8_t *)frame_buffer; uint8_t o = 4; - frame_convert_rgb565(callback, [&]() { - uint8_t c = *src; - uint8_t b = (c >> o) & 0xf; // bit value shifted to position - - // Increment to next 4-bit entry - o ^= 4; - if (o != 0) ++src; - - return cache[b]; - }); + if(this->layers > 1) { + + uint offset = this->bounds.w * this->bounds.h / 2; + + frame_convert_rgb565(callback, [&]() { + uint8_t b = 0; + + // Iterate through layers in reverse order + // Return the first nonzero (not transparent) pixel + for(auto layer = this->layers; layer > 0; layer--) { + uint8_t c = *(src + offset * (layer - 1)); + b = (c >> o) & 0xf; // bit value shifted to position + if (b) break; + } + + // Increment to next 4-bit entry + o ^= 4; + if (o != 0) src++; + + return cache[b]; + }); + } else { + frame_convert_rgb565(callback, [&]() { + uint8_t c = *src; + uint8_t b = (c >> o) & 0xf; // bit value shifted to position + + // Increment to next 4-bit entry + o ^= 4; + if (o != 0) ++src; + + return cache[b]; + }); + } } } } diff --git a/libraries/pico_graphics/pico_graphics_pen_p8.cpp b/libraries/pico_graphics/pico_graphics_pen_p8.cpp index b93f835bf..fd952690f 100644 --- a/libraries/pico_graphics/pico_graphics_pen_p8.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_p8.cpp @@ -51,12 +51,14 @@ namespace pimoroni { } void PicoGraphics_PenP8::set_pixel(const Point &p) { uint8_t *buf = (uint8_t *)frame_buffer; + buf += this->layer_offset; buf[p.y * bounds.w + p.x] = color; } void PicoGraphics_PenP8::set_pixel_span(const Point &p, uint l) { // pointer to byte in framebuffer that contains this pixel uint8_t *buf = (uint8_t *)frame_buffer; + buf += this->layer_offset; buf = &buf[p.y * bounds.w + p.x]; while(l--) { @@ -103,26 +105,70 @@ namespace pimoroni { } void PicoGraphics_PenP8::frame_convert(PenType type, conversion_callback_func callback) { - if(type == PEN_RGB565) { - // Cache the RGB888 palette as RGB565 - RGB565 cache[palette_size]; - for(auto i = 0u; i < palette_size; i++) { - cache[i] = palette[i].to_rgb565(); - } + // Treat our void* frame_buffer as uint8_t + uint8_t *src = (uint8_t *)frame_buffer; + + if(layers > 1) { + // The size of a single layer + uint offset = this->bounds.w * this->bounds.h; + + if(type == PEN_RGB565) { + // Cache the RGB888 palette as RGB565 + RGB565 cache[palette_size]; + for(auto i = 0u; i < palette_size; i++) { + cache[i] = palette[i].to_rgb565(); + } + + frame_convert_rgb565(callback, [&]() { + // Check the *palette* index, rather than the colour + // Thus palette entry 0 is *always* transparent + uint8_t c = 0; + + // Iterate through layers in reverse order + // Return the first nonzero (not transparent) pixel + for(auto layer = this->layers; layer > 0; layer--) { + c = *(src + offset * (layer - 1)); + if (c) break; + } - // Treat our void* frame_buffer as uint8_t - uint8_t *src = (uint8_t *)frame_buffer; + src++; - frame_convert_rgb565(callback, [&]() { - return cache[*src++]; - }); - } else if (type == PEN_RGB888) { - // Treat our void* frame_buffer as uint8_t - uint8_t *src = (uint8_t *)frame_buffer; + return cache[c]; + }); + } else if (type == PEN_RGB888) { + frame_convert_rgb888(callback, [&]() { + // Check the *palette* index, rather than the colour + // Thus palette entry 0 is *always* transparent + uint8_t c = 0; - frame_convert_rgb888(callback, [&]() { - return palette[*src++].to_rgb888(); - }); + // Iterate through layers in reverse order + // Return the first nonzero (not transparent) pixel + for(auto layer = this->layers; layer > 0; layer--) { + c = *(src + offset * (layer - 1)); + if (c) break; + } + + src++; + + return palette[c].to_rgb888(); + }); + } + } else { + if(type == PEN_RGB565) { + // Cache the RGB888 palette as RGB565 + RGB565 cache[palette_size]; + for(auto i = 0u; i < palette_size; i++) { + cache[i] = palette[i].to_rgb565(); + } + + frame_convert_rgb565(callback, [&]() { + return cache[*src++]; + }); + } else if (type == PEN_RGB888) { + frame_convert_rgb888(callback, [&]() { + return palette[*src++].to_rgb888(); + }); + } } } } diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp index 58afcae8a..3c8ca8521 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp @@ -23,13 +23,13 @@ namespace pimoroni { } void PicoGraphics_PenRGB332::set_pixel(const Point &p) { uint8_t *buf = (uint8_t *)frame_buffer; - buf += buffer_size(this->bounds.w, this->bounds.h) * layer; + buf += this->layer_offset; buf[p.y * bounds.w + p.x] = color; } void PicoGraphics_PenRGB332::set_pixel_span(const Point &p, uint l) { // pointer to byte in framebuffer that contains this pixel uint8_t *buf = (uint8_t *)frame_buffer; - buf += buffer_size(this->bounds.w, this->bounds.h) * layer; + buf += this->layer_offset; buf += p.y * bounds.w + p.x; while(l--) { @@ -40,7 +40,7 @@ namespace pimoroni { if(!bounds.contains(p)) return; uint8_t *buf = (uint8_t *)frame_buffer; - buf += buffer_size(this->bounds.w, this->bounds.h) * layer; + buf += this->layer_offset; RGB332 blended = RGB(buf[p.y * bounds.w + p.x]).blend(RGB(color), a).to_rgb332(); @@ -99,14 +99,23 @@ namespace pimoroni { // Treat our void* frame_buffer as uint8_t uint8_t *src = (uint8_t *)frame_buffer; - if(this->layers > 1) { - // Assume only two layers for now - uint8_t *src_layer2 = src + buffer_size(this->bounds.w, this->bounds.h); + if(this->layers > 1) { + // The size of a single layer + uint offset = this->bounds.w * this->bounds.h; frame_convert_rgb565(callback, [&]() { - RGB565 c1 = rgb332_to_rgb565_lut[*src++]; - RGB565 c2 = rgb332_to_rgb565_lut[*src_layer2++]; - return c2 ? c2 : c1; + uint8_t c = 0; + + // Iterate through layers in reverse order + // Return the first nonzero (not transparent) pixel + for(auto layer = this->layers; layer > 0; layer--) { + c = *(src + offset * (layer - 1)); + if (c) break; + } + + src++; + + return rgb332_to_rgb565_lut[c]; }); } else { frame_convert_rgb565(callback, [&]() { diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp index 5151e029e..ce0e42e92 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp @@ -24,14 +24,14 @@ namespace pimoroni { void PicoGraphics_PenRGB565::set_pixel(const Point &p) { uint16_t *buf = (uint16_t *)frame_buffer; // We can't use buffer_size because our pointer is uint16_t - buf += this->bounds.w * this->bounds.h * layer; + buf += this->layer_offset; buf[p.y * bounds.w + p.x] = color; } void PicoGraphics_PenRGB565::set_pixel_span(const Point &p, uint l) { // pointer to byte in framebuffer that contains this pixel uint16_t *buf = (uint16_t *)frame_buffer; // We can't use buffer_size because our pointer is uint16_t - buf += this->bounds.w * this->bounds.h * layer; + buf += this->layer_offset; buf = &buf[p.y * bounds.w + p.x]; while(l--) { @@ -45,10 +45,8 @@ namespace pimoroni { if(layers > 1) { // Assume only two layers for now - uint16_t *src_layer2 = src; - // We can't use buffer_size because our pointer is uint16_t - src_layer2 += this->bounds.w * this->bounds.h * layer; + uint16_t *src_layer2 = src + this->bounds.w * this->bounds.h; frame_convert_rgb565(callback, [&]() { RGB565 c1 = *src++; diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp index 6f5360643..0b145239b 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp @@ -23,11 +23,13 @@ namespace pimoroni { } void PicoGraphics_PenRGB888::set_pixel(const Point &p) { uint32_t *buf = (uint32_t *)frame_buffer; + buf += this->layer_offset; buf[p.y * bounds.w + p.x] = color; } void PicoGraphics_PenRGB888::set_pixel_span(const Point &p, uint l) { // pointer to byte in framebuffer that contains this pixel uint32_t *buf = (uint32_t *)frame_buffer; + buf += this->layer_offset; buf = &buf[p.y * bounds.w + p.x]; while(l--) {