From be24eb5d4e6d72e75966cc3c704407a970711545 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 7 Oct 2024 15:51:02 +0100 Subject: [PATCH] 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--) {