diff --git a/drivers/st7735/st7735.cpp b/drivers/st7735/st7735.cpp index aac94901..d2cabff7 100644 --- a/drivers/st7735/st7735.cpp +++ b/drivers/st7735/st7735.cpp @@ -172,8 +172,10 @@ namespace pimoroni { gpio_put(dc, 1); // data mode gpio_put(cs, 0); - graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { - 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); diff --git a/drivers/st7789/st7789.cpp b/drivers/st7789/st7789.cpp index e3243122..6f838ecb 100644 --- a/drivers/st7789/st7789.cpp +++ b/drivers/st7789/st7789.cpp @@ -290,8 +290,10 @@ namespace pimoroni { spi_write_blocking(spi, &cmd, 1); gpio_put(dc, 1); // data mode - graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { - 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); @@ -301,16 +303,12 @@ namespace pimoroni { write_blocking_parallel(&cmd, 1); gpio_put(dc, 1); // data mode - int scanline = 0; - - graphics->scanline_convert(PicoGraphics::PEN_RGB565, [this, scanline](void *data, size_t length) mutable { - write_blocking_parallel_dma((const uint8_t*)data, length); - - // Stall on the last scanline since "data" goes out of scope and is lost - scanline++; - if(scanline == height) { - while (dma_channel_is_busy(parallel_dma)) - ; + graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { + if (length > 0) { + write_blocking_parallel_dma((const uint8_t*)data, length); + } + else { + dma_channel_wait_for_finish_blocking(parallel_dma); } }); diff --git a/drivers/uc8159/uc8159.cpp b/drivers/uc8159/uc8159.cpp index 5539de1f..f8572f93 100644 --- a/drivers/uc8159/uc8159.cpp +++ b/drivers/uc8159/uc8159.cpp @@ -154,8 +154,10 @@ namespace pimoroni { spi_write_blocking(spi, ®, 1); gpio_put(DC, 1); // data mode - graphics->scanline_convert(PicoGraphics::PEN_P4, [this](void *buf, size_t length) { - spi_write_blocking(spi, (const uint8_t*)buf, length); + graphics->frame_convert(PicoGraphics::PEN_P4, [this](void *buf, size_t length) { + if (length > 0) { + spi_write_blocking(spi, (const uint8_t*)buf, length); + } }); gpio_put(CS, 1); diff --git a/libraries/pico_graphics/pico_graphics.cpp b/libraries/pico_graphics/pico_graphics.cpp index 0fc773dd..cf60178d 100644 --- a/libraries/pico_graphics/pico_graphics.cpp +++ b/libraries/pico_graphics/pico_graphics.cpp @@ -10,7 +10,7 @@ namespace pimoroni { void PicoGraphics::set_pixel_dither(const Point &p, const RGB &c) {}; void PicoGraphics::set_pixel_dither(const Point &p, const RGB565 &c) {}; void PicoGraphics::set_pixel_dither(const Point &p, const uint8_t &c) {}; - void PicoGraphics::scanline_convert(PenType type, conversion_callback_func callback) {}; + void PicoGraphics::frame_convert(PenType type, conversion_callback_func callback) {}; void PicoGraphics::sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) {}; void PicoGraphics::set_dimensions(int width, int height) { @@ -333,4 +333,34 @@ namespace pimoroni { } } } + + // Common function for frame buffer conversion to 565 pixel format + void PicoGraphics::frame_convert_rgb565(conversion_callback_func callback, next_pixel_func get_next_pixel) + { + // Allocate two temporary buffers, as the callback may transfer by DMA + // while we're preparing the next part of the row + const int BUF_LEN = 64; + uint16_t row_buf[2][BUF_LEN]; + int buf_idx = 0; + for(auto y = 0; y < bounds.h; y++) { + for(auto x = 0; x < bounds.w; x++) { + int buf_entry = x & (BUF_LEN - 1); + row_buf[buf_idx][buf_entry] = get_next_pixel(); + + if (buf_entry == BUF_LEN - 1) { + callback(row_buf[buf_idx], BUF_LEN * sizeof(RGB565)); + buf_idx ^= 1; + } + } + + if ((bounds.w & (BUF_LEN - 1)) != 0) { + // Callback to the driver with the remaining row data + callback(row_buf[buf_idx], (bounds.w & (BUF_LEN - 1)) * sizeof(RGB565)); + buf_idx ^= 1; + } + } + + // Callback with zero length to ensure previous buffer is fully written + callback(row_buf[buf_idx], 0); + } } diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index 25c59e67..98aed194 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -172,6 +172,7 @@ namespace pimoroni { Rect clip; typedef std::function conversion_callback_func; + typedef std::function next_pixel_func; //typedef std::function scanline_interrupt_func; //scanline_interrupt_func scanline_interrupt = nullptr; @@ -225,7 +226,7 @@ namespace pimoroni { virtual void set_pixel_dither(const Point &p, const RGB &c); virtual void set_pixel_dither(const Point &p, const RGB565 &c); virtual void set_pixel_dither(const Point &p, const uint8_t &c); - virtual void scanline_convert(PenType type, conversion_callback_func callback); + virtual void frame_convert(PenType type, conversion_callback_func callback); virtual void sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent); void set_font(const bitmap::font_t *font); @@ -252,6 +253,9 @@ namespace pimoroni { void polygon(const std::vector &points); void triangle(Point p1, Point p2, Point p3); void line(Point p1, Point p2); + + protected: + void frame_convert_rgb565(conversion_callback_func callback, next_pixel_func get_next_pixel); }; class PicoGraphics_Pen1Bit : public PicoGraphics { @@ -325,7 +329,7 @@ namespace pimoroni { void get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array &candidates); void set_pixel_dither(const Point &p, const RGB &c) override; - void scanline_convert(PenType type, conversion_callback_func callback) override; + void frame_convert(PenType type, conversion_callback_func callback) override; static size_t buffer_size(uint w, uint h) { return (w * h / 8) * 3; } @@ -354,7 +358,7 @@ namespace pimoroni { void get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array &candidates); void set_pixel_dither(const Point &p, const RGB &c) override; - void scanline_convert(PenType type, conversion_callback_func callback) override; + void frame_convert(PenType type, conversion_callback_func callback) override; static size_t buffer_size(uint w, uint h) { return w * h / 2; } @@ -383,7 +387,7 @@ namespace pimoroni { void get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array &candidates); void set_pixel_dither(const Point &p, const RGB &c) override; - void scanline_convert(PenType type, conversion_callback_func callback) override; + void frame_convert(PenType type, conversion_callback_func callback) override; static size_t buffer_size(uint w, uint h) { return w * h; } @@ -404,7 +408,7 @@ namespace pimoroni { void sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) override; - void scanline_convert(PenType type, conversion_callback_func callback) override; + void frame_convert(PenType type, conversion_callback_func callback) override; static size_t buffer_size(uint w, uint h) { return w * h; } diff --git a/libraries/pico_graphics/pico_graphics_pen_3bit.cpp b/libraries/pico_graphics/pico_graphics_pen_3bit.cpp index c20f0de7..e9d5ddea 100644 --- a/libraries/pico_graphics/pico_graphics_pen_3bit.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_3bit.cpp @@ -90,7 +90,7 @@ namespace pimoroni { color = candidate_cache[cache_key][dither16_pattern[pattern_index]]; set_pixel(p); } - void PicoGraphics_Pen3Bit::scanline_convert(PenType type, conversion_callback_func callback) { + void PicoGraphics_Pen3Bit::frame_convert(PenType type, conversion_callback_func callback) { if(type == PEN_P4) { uint8_t row_buf[bounds.w / 2]; uint offset = (bounds.w * bounds.h) / 8; diff --git a/libraries/pico_graphics/pico_graphics_pen_p4.cpp b/libraries/pico_graphics/pico_graphics_pen_p4.cpp index 6bf0ef66..ac346977 100644 --- a/libraries/pico_graphics/pico_graphics_pen_p4.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_p4.cpp @@ -124,7 +124,7 @@ namespace pimoroni { color = candidate_cache[cache_key][dither16_pattern[pattern_index]]; set_pixel(p); } - void PicoGraphics_PenP4::scanline_convert(PenType type, conversion_callback_func callback) { + void PicoGraphics_PenP4::frame_convert(PenType type, conversion_callback_func callback) { if(type == PEN_RGB565) { // Cache the RGB888 palette as RGB565 RGB565 cache[palette_size]; @@ -134,28 +134,18 @@ namespace pimoroni { // Treat our void* frame_buffer as uint8_t uint8_t *src = (uint8_t *)frame_buffer; + uint8_t o = 4; - // Allocate two per-row temporary buffers, as the callback may transfer by DMA - // while we're preparing the next row - uint16_t row_buf[2][bounds.w]; - for(auto y = 0; y < bounds.h; y++) { - /*if(scanline_interrupt != nullptr) { - scanline_interrupt(y); - // Cache the RGB888 palette as RGB565 - for(auto i = 0u; i < 16; i++) { - cache[i] = palette[i].to_rgb565(); - } - }*/ + 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; - for(auto x = 0; x < bounds.w; x++) { - uint8_t c = src[(bounds.w * y / 2) + (x / 2)]; - uint8_t o = (~x & 0b1) * 4; // bit offset within byte - uint8_t b = (c >> o) & 0xf; // bit value shifted to position - row_buf[y & 1][x] = cache[b]; - } - // Callback to the driver with the row data - callback(row_buf[y & 1], bounds.w * sizeof(RGB565)); - } + return cache[b]; + }); } } } diff --git a/libraries/pico_graphics/pico_graphics_pen_p8.cpp b/libraries/pico_graphics/pico_graphics_pen_p8.cpp index a8d7112b..8fd89c0f 100644 --- a/libraries/pico_graphics/pico_graphics_pen_p8.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_p8.cpp @@ -98,7 +98,7 @@ namespace pimoroni { set_pixel(p); } - void PicoGraphics_PenP8::scanline_convert(PenType type, conversion_callback_func callback) { + 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]; @@ -109,16 +109,9 @@ namespace pimoroni { // Treat our void* frame_buffer as uint8_t uint8_t *src = (uint8_t *)frame_buffer; - // Allocate two per-row temporary buffers, as the callback may transfer by DMA - // while we're preparing the next row - uint16_t row_buf[2][bounds.w]; - for(auto y = 0; y < bounds.h; y++) { - for(auto x = 0; x < bounds.w; x++) { - row_buf[y & 1][x] = cache[src[bounds.w * y + x]]; - } - // Callback to the driver with the row data - callback(row_buf[y & 1], bounds.w * sizeof(RGB565)); - } + frame_convert_rgb565(callback, [&]() { + return cache[*src++]; + }); } } } diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp index 361bd256..f2a8192c 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp @@ -78,24 +78,15 @@ namespace pimoroni { set_pixel(p); } - void PicoGraphics_PenRGB332::scanline_convert(PenType type, conversion_callback_func callback) { + void PicoGraphics_PenRGB332::frame_convert(PenType type, conversion_callback_func callback) { if(type == PEN_RGB565) { // Treat our void* frame_buffer as uint8_t uint8_t *src = (uint8_t *)frame_buffer; - // Allocate two per-row temporary buffers, as the callback may transfer by DMA - // while we're preparing the next row - uint16_t row_buf[2][bounds.w]; - for(auto y = 0; y < bounds.h; y++) { - for(auto x = 0; x < bounds.w; x++) { - row_buf[y & 1][x] = rgb332_to_rgb565_lut[*src]; - - src++; - } - // Callback to the driver with the row data - callback(row_buf[y & 1], bounds.w * sizeof(RGB565)); - } + 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) {