From 7e4725d1cdc77e8f9ce72a2dea20a0c8a591cc59 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 6 Jun 2022 17:54:15 +0100 Subject: [PATCH] ST7789/PicoGraphics: Templated framebuffer formats. --- drivers/st7789/st7789.cmake | 2 +- drivers/st7789/st7789.cpp | 96 ++++++ drivers/st7789/st7789.hpp | 19 +- libraries/pico_graphics/pico_graphics.cpp | 184 +++-------- libraries/pico_graphics/pico_graphics.hpp | 300 ++++++++++++++---- .../picographics_st7789.cpp | 6 - .../picographics_st7789.hpp | 30 +- micropython/modules/st7789/st7789.c | 10 +- micropython/modules/st7789/st7789.cpp | 64 ++-- micropython/modules/st7789/st7789.h | 5 +- 10 files changed, 460 insertions(+), 256 deletions(-) diff --git a/drivers/st7789/st7789.cmake b/drivers/st7789/st7789.cmake index 80420fef..a08f111c 100644 --- a/drivers/st7789/st7789.cmake +++ b/drivers/st7789/st7789.cmake @@ -9,4 +9,4 @@ target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) target_include_directories(st7789 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) # Pull in pico libraries that we need -target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib pimoroni_bus hardware_spi hardware_pwm hardware_dma) +target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib pimoroni_bus hardware_spi hardware_pwm hardware_dma pico_graphics) diff --git a/drivers/st7789/st7789.cpp b/drivers/st7789/st7789.cpp index 32068544..251d98d4 100644 --- a/drivers/st7789/st7789.cpp +++ b/drivers/st7789/st7789.cpp @@ -200,7 +200,102 @@ namespace pimoroni { gpio_put(cs, 1); } + + void ST7789::update(PicoGraphics *graphics) { + command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)graphics->get_data()); + } + void ST7789::update(PicoGraphics *graphics) { + uint8_t command = reg::RAMWR; + + gpio_put(dc, 0); // command mode + + gpio_put(cs, 0); + + if(spi) { + spi_write_blocking(spi, &command, 1); + } else { + write_blocking_parallel(&command, 1); + } + + gpio_put(dc, 1); // data mode + + uint16_t row_buf[width]; + + for(auto y = 0u; y < height; y++) { + graphics->get_data(y, &row_buf); + // TODO: Add DMA->SPI / PIO while we prep the next row + if(spi) { + spi_write_blocking(spi, (const uint8_t*)row_buf, width * sizeof(uint16_t)); + } else { + write_blocking_parallel((const uint8_t*)row_buf, width * sizeof(uint16_t)); + } + } + + gpio_put(cs, 1); + } + + void ST7789::update(PicoGraphics *graphics) { + uint8_t command = reg::RAMWR; + + gpio_put(dc, 0); // command mode + + gpio_put(cs, 0); + + if(spi) { + spi_write_blocking(spi, &command, 1); + } else { + write_blocking_parallel(&command, 1); + } + + gpio_put(dc, 1); // data mode + + uint16_t row_buf[width]; + + for(auto y = 0u; y < height; y++) { + graphics->get_data(y, &row_buf); + // TODO: Add DMA->SPI / PIO while we prep the next row + if(spi) { + spi_write_blocking(spi, (const uint8_t*)row_buf, width * sizeof(uint16_t)); + } else { + write_blocking_parallel((const uint8_t*)row_buf, width * sizeof(uint16_t)); + } + } + + gpio_put(cs, 1); + } + + void ST7789::update(PicoGraphics *graphics) { + uint8_t command = reg::RAMWR; + + gpio_put(dc, 0); // command mode + + gpio_put(cs, 0); + + if(spi) { + spi_write_blocking(spi, &command, 1); + } else { + write_blocking_parallel(&command, 1); + } + + gpio_put(dc, 1); // data mode + + uint16_t row_buf[width]; + + for(auto y = 0u; y < height; y++) { + graphics->get_data(y, &row_buf); + // TODO: Add DMA->SPI / PIO while we prep the next row + if(spi) { + spi_write_blocking(spi, (const uint8_t*)row_buf, width * sizeof(uint16_t)); + } else { + write_blocking_parallel((const uint8_t*)row_buf, width * sizeof(uint16_t)); + } + } + + gpio_put(cs, 1); + } + +/* // Native 16-bit framebuffer update void ST7789::update() { command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)frame_buffer); @@ -238,6 +333,7 @@ namespace pimoroni { gpio_put(cs, 1); } + */ void ST7789::set_backlight(uint8_t brightness) { // gamma correct the provided 0-255 brightness value onto a diff --git a/drivers/st7789/st7789.hpp b/drivers/st7789/st7789.hpp index 228dc1f1..48f7ef61 100644 --- a/drivers/st7789/st7789.hpp +++ b/drivers/st7789/st7789.hpp @@ -5,6 +5,7 @@ #include "hardware/pwm.h" #include "common/pimoroni_common.hpp" #include "common/pimoroni_bus.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" #include @@ -41,15 +42,11 @@ namespace pimoroni { public: - - // frame buffer where pixel data is stored - void *frame_buffer; - // Parallel init ST7789(uint16_t width, uint16_t height, Rotation rotation, void *frame_buffer, ParallelPins pins) : spi(nullptr), width(width), height(height), rotation(rotation), round(false), - cs(pins.cs), dc(pins.dc), wr_sck(pins.wr_sck), rd_sck(pins.rd_sck), d0(pins.d0), bl(pins.bl), frame_buffer(frame_buffer) { + cs(pins.cs), dc(pins.dc), wr_sck(pins.wr_sck), rd_sck(pins.rd_sck), d0(pins.d0), bl(pins.bl) { gpio_set_function(wr_sck, GPIO_FUNC_SIO); gpio_set_dir(wr_sck, GPIO_OUT); @@ -71,7 +68,7 @@ namespace pimoroni { ST7789(uint16_t width, uint16_t height, Rotation rotation, bool round, void *frame_buffer, SPIPins pins) : spi(pins.spi), width(width), height(height), rotation(rotation), round(round), - cs(pins.cs), dc(pins.dc), wr_sck(pins.sck), d0(pins.mosi), bl(pins.bl), frame_buffer(frame_buffer) { + cs(pins.cs), dc(pins.dc), wr_sck(pins.sck), d0(pins.mosi), bl(pins.bl) { // configure spi interface and pins spi_init(spi, SPI_BAUD); @@ -86,17 +83,15 @@ namespace pimoroni { void command(uint8_t command, size_t len = 0, const char *data = NULL); void set_backlight(uint8_t brightness); - void update(); - void update(uint16_t *palette); + void update(PicoGraphics *graphics); + void update(PicoGraphics *graphics); + void update(PicoGraphics *graphics); + void update(PicoGraphics *graphics); private: void configure_display(Rotation rotate); void write_blocking_parallel(const uint8_t *src, size_t len); void common_init() { - if(!this->frame_buffer) { - this->frame_buffer = new uint8_t[width * height]; - } - gpio_set_function(dc, GPIO_FUNC_SIO); gpio_set_dir(dc, GPIO_OUT); diff --git a/libraries/pico_graphics/pico_graphics.cpp b/libraries/pico_graphics/pico_graphics.cpp index 3ff3b84e..098a16aa 100644 --- a/libraries/pico_graphics/pico_graphics.cpp +++ b/libraries/pico_graphics/pico_graphics.cpp @@ -1,135 +1,40 @@ #include "pico_graphics.hpp" namespace pimoroni { - PicoGraphics::PicoGraphics(uint16_t width, uint16_t height, void *frame_buffer) - : frame_buffer((Pen *)frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height) { - set_font(&font6); - set_palette_mode(PaletteModeRGB332); - }; - void PicoGraphics::set_font(const bitmap::font_t *font){ + template + void PicoGraphics::set_font(const bitmap::font_t *font){ this->font = font; } - void PicoGraphics::set_pen(Pen p) { - pen = p; + template + void PicoGraphics::set_pen(uint16_t p) { + pen.set_color(p); } - void PicoGraphics::set_palette_mode(PaletteMode mode) { - palette_mode = mode; - if(mode == PaletteModeRGB332) { - rgb332_palette(); - } else { - empty_palette(); - } - } - - int PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b) { - if (palette_mode == PaletteModeRGB332) { - return rgb_to_rgb332_index(r, g, b); // Fast pack RGB into palette index - } else { - RGB565 c = create_pen_rgb565(r, g, b); - int result = search_palette(c); - - if (result == -1) { - result = put_palette(create_pen_rgb565(r, g, b)); - } - - return result; - } - } - - void PicoGraphics::set_pen(uint8_t r, uint8_t g, uint8_t b) { - int result = create_pen(r, g, b); - (void)result; - } - - int PicoGraphics::search_palette(RGB565 c) { - for(auto i = 0u; i < 256; i++) { - if((palette_status[i] & PaletteStatusUsed) && palette[i] == c) return i; - } - return -1; - } - - int PicoGraphics::put_palette(RGB565 c) { - if(palette_mode != PaletteModeUSER) return -1; - - for(auto i = 0u; i < 256; i++) { - if(!(palette_status[i] & (PaletteStatusUsed | PaletteStatusReserved))) { - palette[i] = c; - palette_status[i] = PaletteStatusUsed; - return i; - } - } - return -1; - } - - void PicoGraphics::set_palette(uint8_t i, RGB565 c) { - if(palette_mode != PaletteModeUSER) return; - - palette[i] = c; - palette_status[i] |= PaletteStatusUsed; - } - - int PicoGraphics::reserve_palette() { - if(palette_mode != PaletteModeUSER) return - 1; - - for (auto i = 0u; i < 256; i++) { - if (!palette_status[i]) { - palette_status[i] = PaletteStatusReserved; - return i; - } - } - return -1; - } - - void PicoGraphics::empty_palette() { - for (auto i = 0u; i < 256; i++) { - palette[i] = 0; - palette_status[i] = 0; - } - } - - void PicoGraphics::rgb332_palette() { - for (auto i = 0u; i < 256; i++) { - // Convert the implicit RGB332 (i) into RGB565 - // 0b11100 000 0b00011100 0b00000011 - palette[i] = ((i & 0b11100000) << 8) | ((i & 0b00011100) << 6) | ((i & 0b00000011) << 3); - palette[i] = __builtin_bswap16(palette[i]); - palette_status[i] = PaletteStatusUsed; - } - } - - void PicoGraphics::set_clip(const Rect &r) { + template + void PicoGraphics::set_clip(const Rect &r) { clip = bounds.intersection(r); } - void PicoGraphics::remove_clip() { + template + void PicoGraphics::remove_clip() { clip = bounds; } - Pen* PicoGraphics::ptr(const Rect &r) { - return frame_buffer + r.x + r.y * bounds.w; - } - - Pen* PicoGraphics::ptr(const Point &p) { - return frame_buffer + p.x + p.y * bounds.w; - } - - Pen* PicoGraphics::ptr(int32_t x, int32_t y) { - return frame_buffer + x + y * bounds.w; - } - - void PicoGraphics::clear() { + template + void PicoGraphics::clear() { rectangle(clip); } - void PicoGraphics::pixel(const Point &p) { + template + void PicoGraphics::pixel(const Point &p) { if(!clip.contains(p)) return; - *ptr(p) = pen; + pen.set_pixel(frame_buffer, p.x, p.y, bounds.w); } - void PicoGraphics::pixel_span(const Point &p, int32_t l) { + template + void PicoGraphics::pixel_span(const Point &p, int32_t l) { // check if span in bounds if( p.x + l < clip.x || p.x >= clip.x + clip.w || p.y < clip.y || p.y >= clip.y + clip.h) return; @@ -139,31 +44,36 @@ namespace pimoroni { if(clipped.x < clip.x) {l += clipped.x - clip.x; clipped.x = clip.x;} if(clipped.x + l >= clip.x + clip.w) {l = clip.x + clip.w - clipped.x;} - Pen *dest = ptr(clipped); + Point dest(clipped.x, clipped.y); while(l--) { - *dest++ = pen; + pen.set_pixel(frame_buffer, dest.x, dest.y, bounds.w); + dest.x++; } } - void PicoGraphics::rectangle(const Rect &r) { + template + void PicoGraphics::rectangle(const Rect &r) { // clip and/or discard depending on rectangle visibility Rect clipped = r.intersection(clip); if(clipped.empty()) return; - Pen *dest = ptr(clipped); + Point dest(clipped.x, clipped.y); while(clipped.h--) { // draw span of pixels for this row - for(int32_t i = 0; i < clipped.w; i++) { - *dest++ = pen; - } + pixel_span(dest, clipped.w); + /*for(int32_t i = 0; i < clipped.w; i++) { + pen.set_pixel(frame_buffer, dest.x, dest.y, bounds.w); + dest.x++; + }*/ // move to next scanline - dest += bounds.w - clipped.w; + dest.y++; } } - void PicoGraphics::circle(const Point &p, int32_t radius) { + template + void PicoGraphics::circle(const Point &p, int32_t radius) { // circle in screen bounds? Rect bounds = Rect(p.x - radius, p.y - radius, radius * 2, radius * 2); if(!bounds.intersects(clip)) return; @@ -191,19 +101,22 @@ namespace pimoroni { } } - void PicoGraphics::character(const char c, const Point &p, uint8_t scale) { + template + void PicoGraphics::character(const char c, const Point &p, uint8_t scale) { bitmap::character(font, [this](int32_t x, int32_t y, int32_t w, int32_t h){ rectangle(Rect(x, y, w, h)); }, c, p.x, p.y, scale); } - void PicoGraphics::text(const std::string &t, const Point &p, int32_t wrap, uint8_t scale) { + template + void PicoGraphics::text(const std::string &t, const Point &p, int32_t wrap, uint8_t scale) { bitmap::text(font, [this](int32_t x, int32_t y, int32_t w, int32_t h){ rectangle(Rect(x, y, w, h)); }, t, p.x, p.y, wrap, scale); } - int32_t PicoGraphics::measure_text(const std::string &t, uint8_t scale) { + template + int32_t PicoGraphics::measure_text(const std::string &t, uint8_t scale) { return bitmap::measure_text(font, t, scale); } @@ -215,7 +128,8 @@ namespace pimoroni { return (p1.y == p2.y && p1.x > p2.x) || (p1.y < p2.y); } - void PicoGraphics::triangle(Point p1, Point p2, Point p3) { + template + void PicoGraphics::triangle(Point p1, Point p2, Point p3) { Rect triangle_bounds( Point(std::min(p1.x, std::min(p2.x, p3.x)), std::min(p1.y, std::min(p2.y, p3.y))), Point(std::max(p1.x, std::max(p2.x, p3.x)), std::max(p1.y, std::max(p2.y, p3.y)))); @@ -257,13 +171,13 @@ namespace pimoroni { int32_t w1 = w1row; int32_t w2 = w2row; - Pen *dest = ptr(triangle_bounds.x, triangle_bounds.y + y); + Point dest = Point(triangle_bounds.x, triangle_bounds.y + y); for (int32_t x = 0; x < triangle_bounds.w; x++) { if ((w0 | w1 | w2) >= 0) { - *dest = pen; + pen.set_pixel(frame_buffer, dest.x, dest.y, bounds.w); } - dest++; + dest.x++; w0 += a12; w1 += a20; @@ -276,7 +190,8 @@ namespace pimoroni { } } - void PicoGraphics::polygon(const std::vector &points) { + template + void PicoGraphics::polygon(const std::vector &points) { static int32_t nodes[64]; // maximum allowed number of nodes per scanline for polygon rendering int32_t miny = points[0].y, maxy = points[0].y; @@ -322,7 +237,8 @@ namespace pimoroni { } } - void PicoGraphics::line(Point p1, Point p2) { + template + void PicoGraphics::line(Point p1, Point p2) { // fast horizontal line if(p1.y == p2.y) { int32_t start = std::max(clip.x, std::min(p1.x, p2.x)); @@ -335,10 +251,10 @@ namespace pimoroni { if(p1.x == p2.x) { int32_t start = std::max(clip.y, std::min(p1.y, p2.y)); int32_t length = std::min(clip.y + clip.h, std::max(p1.y, p2.y)) - start; - Pen *dest = ptr(p1.x, start); + Point dest(p1.x, start); while(length--) { - *dest = pen; - dest += bounds.w; + pen.set_pixel(frame_buffer, dest.x, dest.y, bounds.w); + dest.y++; } return; } @@ -357,7 +273,7 @@ namespace pimoroni { int32_t x = p1.x; int32_t y = p1.y << 16; while(s--) { - pixel(Point(x, y >> 16)); + pen.set_pixel(frame_buffer, x, y >> 16, bounds.w); y += sy; x += sx; } @@ -369,7 +285,7 @@ namespace pimoroni { int32_t y = p1.y; int32_t x = p1.x << 16; while(s--) { - pixel(Point(x >> 16, y)); + pen.set_pixel(frame_buffer, x >> 16, y, bounds.w); y += sy; x += sx; } diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index f0258435..d8b4d8d4 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -9,9 +9,9 @@ // a tiny little graphics library for our Pico products // supports only 16-bit (565) RGB framebuffers namespace pimoroni { - - typedef uint8_t Pen; + typedef uint8_t RGB332; typedef uint16_t RGB565; + typedef int Pen; struct Rect; @@ -44,59 +44,255 @@ namespace pimoroni { void deflate(int32_t v); }; + class PicoGraphicsPenType { + public: + + struct PaletteEntry { + RGB565 color; + bool used; + }; + + static constexpr RGB332 rgb_to_rgb332(uint8_t r, uint8_t g, uint8_t b) { + return (r & 0b11100000) | ((g & 0b11100000) >> 3) | ((b & 0b11000000) >> 6); + } + + static constexpr RGB565 rgb332_to_rgb565(RGB332 c) { + uint16_t p = ((c & 0b11100000) << 8) | + ((c & 0b00011100) << 6) | + ((c & 0b00000011) << 3); + return __builtin_bswap16(p); + } + + static constexpr RGB565 rgb_to_rgb565(uint8_t r, uint8_t g, uint8_t b) { + uint16_t p = ((r & 0b11111000) << 8) | + ((g & 0b11111100) << 3) | + ((b & 0b11111000) >> 3); + + return __builtin_bswap16(p); + } + + virtual void set_pixel(void *frame_buffer, uint x, uint y, uint stride); + virtual int create(uint8_t r, uint8_t g, uint8_t b); + virtual void set_color(uint c); + virtual void set_color(uint8_t r, uint8_t g, uint8_t b); + virtual void update_color(uint8_t i, uint8_t r, uint8_t g, uint8_t b); + + virtual void palette_lookup(void *frame_buffer, void *result, uint offset, uint length); + static size_t buffer_size(uint w, uint h); // Must be static, since user must know required size to alloc framebuffer + }; + + class PenP4 : public PicoGraphicsPenType { + public: + uint8_t color; + PaletteEntry palette[8]; + PenP4() { + palette[0].color = rgb_to_rgb565(57, 48, 57); // Black + palette[1].color = rgb_to_rgb565(255, 255, 255); // White + palette[2].color = rgb_to_rgb565(58, 91, 70); // Green + palette[3].color = rgb_to_rgb565(61, 59, 94); // Blue + palette[4].color = rgb_to_rgb565(156, 72, 75); // Red + palette[5].color = rgb_to_rgb565(208, 190, 71); // Yellow + palette[6].color = rgb_to_rgb565(177, 106, 73); // Orange + palette[7].color = rgb_to_rgb565(255, 255, 255); // Clear + } + void set_color(uint c) { + color = c & 0xf; + } + void set_color(uint8_t r, uint8_t g, uint8_t b) override { + // TODO look up closest palette colour, or just NOOP? + } + void update_color(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {} + void reset_color(uint8_t i) {} + int create(uint8_t r, uint8_t g, uint8_t b) override { + return -1; // Can never create new colours, fixed palette! + } + void set_pixel(void *frame_buffer, uint x, uint y, uint stride) override { + // pointer to byte in framebuffer that contains this pixel + uint8_t *buf = (uint8_t *)frame_buffer; + uint8_t *p = &buf[(x / 2) + (y * stride / 2)]; + + uint8_t o = (~x & 0b1) * 4; // bit offset within byte + uint8_t m = ~(0b1111 << o); // bit mask for byte + uint8_t b = color << o; // bit value shifted to position + + *p &= m; // clear bits + *p |= b; // set value + } + void palette_lookup(void *frame_buffer, void *result, uint offset, uint length) override { + uint8_t *src = (uint8_t *)frame_buffer; + uint16_t *dst = (uint16_t *)result; + for(auto x = 0u; x < length; x++) { + uint8_t c = src[(offset / 2) + (x / 2)]; + uint8_t o = (~x & 0b1) * 4; // bit offset within byte + uint8_t b = (c >> o) & 0xf; // bit value shifted to position + dst[x] = palette[b].color; + } + } + static size_t buffer_size(uint w, uint h) { + return w * h / 2; + } + }; + + class PenP8 : public PicoGraphicsPenType { + public: + uint8_t color; + PaletteEntry palette[256]; + PenP8() { + for(auto i = 0u; i < 256; i++) { + reset_color(i); + } + } + void set_color(uint c) override { + color = c; + } + void set_color(uint8_t r, uint8_t g, uint8_t b) override { + // TODO look up closest palette colour, or just NOOP? + } + void update_color(uint8_t i, uint8_t r, uint8_t g, uint8_t b) { + palette[i].color = rgb_to_rgb565(r, g, b); + palette[i].used = true; + } + void reset_color(uint8_t i) { + palette[i].color = 0; + palette[i].used = false; + } + int create(uint8_t r, uint8_t g, uint8_t b) override { + // Create a colour and place it in the palette if there's space + RGB565 c = rgb_to_rgb565(r, g, b); + for(auto i = 0u; i < 256u; i++) { + if(!palette[i].used) { + palette[i].color = c; + palette[i].used = true; + return i; + } + } + return -1; + } + void set_pixel(void *frame_buffer, uint x, uint y, uint stride) override { + uint8_t *buf = (uint8_t *)frame_buffer; + buf[y * stride + x] = color; + } + void palette_lookup(void *frame_buffer, void *result, uint offset, uint length) override { + uint8_t *src = (uint8_t *)frame_buffer; + uint16_t *dst = (uint16_t *)result; + for(auto x = 0u; x < length; x++) { + dst[x] = palette[src[offset + x]].color; + } + } + static size_t buffer_size(uint w, uint h) { + return w * h; + } + }; + + class PenRGB332 : public PicoGraphicsPenType { + public: + RGB332 color; + PaletteEntry palette[256]; + PenRGB332() { + for(auto i = 0u; i < 256; i++) { + reset_color(i); + } + } + void set_color(uint c) { + color = c; + } + void set_color(uint8_t r, uint8_t g, uint8_t b) override { + color = rgb_to_rgb332(r, g, b); + } + void update_color(uint8_t i, uint8_t r, uint8_t g, uint8_t b) { + palette[i].color = rgb_to_rgb565(r, g, b); + palette[i].used = true; + } + void reset_color(uint8_t i) { + palette[i].color = rgb332_to_rgb565(i); + palette[i].used = true; + } + int create(uint8_t r, uint8_t g, uint8_t b) override { + return rgb_to_rgb332(r, g, b); + } + void set_pixel(void *frame_buffer, uint x, uint y, uint stride) override { + uint8_t *buf = (uint8_t *)frame_buffer; + buf[y * stride + x] = color; + } + void palette_lookup(void *frame_buffer, void *result, uint offset, uint length) override { + uint8_t *src = (uint8_t *)frame_buffer; + uint16_t *dst = (uint16_t *)result; + for(auto x = 0u; x < length; x++) { + dst[x] = palette[src[offset + x]].color; + } + } + static size_t buffer_size(uint w, uint h) { + return w * h; + } + }; + + class PenRGB565 : public PicoGraphicsPenType { + public: + uint16_t color; + void set_color(uint c) override { + color = c; + } + void update_color(uint8_t i, uint8_t r, uint8_t g, uint8_t b) { + // Palette only. Does nothing. + } + void reset_color(uint8_t i) { + // Palette only. Does nothing. + } + void set_color(uint8_t r, uint8_t g, uint8_t b) override { + color = rgb_to_rgb565(r, g, b); + } + int create(uint8_t r, uint8_t g, uint8_t b) override { + return rgb_to_rgb565(r, g, b); + } + void set_pixel(void *frame_buffer, uint x, uint y, uint stride) override { + uint16_t *buf = (uint16_t *)frame_buffer; + buf[y * stride + x] = color; + } + void palette_lookup(void *frame_buffer, void *result, uint offset, uint length) override { + } + static size_t buffer_size(uint w, uint h) { + return w * h * sizeof(RGB565); + } + }; + + template class PicoGraphics { public: - Pen *frame_buffer; + void *frame_buffer; Rect bounds; Rect clip; - Pen pen; - const bitmap::font_t *font; - RGB565 palette[256]; - uint8_t palette_status[256]; - - enum PaletteMode { - PaletteModeRGB332 = 0, - PaletteModeUSER = 1 - }; - - enum PaletteStatus : uint8_t { - PaletteStatusReserved = 1, - PaletteStatusUsed = 2 - }; - - PaletteMode palette_mode = PaletteModeRGB332; + T pen; public: - PicoGraphics(uint16_t width, uint16_t height, void *frame_buffer); + 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); + if(frame_buffer == nullptr) { + frame_buffer = (void *)(new uint8_t[pen.buffer_size(width, height)]); + } + }; + void set_font(const bitmap::font_t *font); - void set_pen(Pen p); - void set_palette_mode(PaletteMode mode); + void set_pen(uint16_t p); - [[deprecated("Use uint8_t create_pen(uint8_t, uint8_t, uint8_t).")]] - void set_pen(uint8_t r, uint8_t g, uint8_t b); - - static constexpr Pen rgb_to_rgb332_index(uint8_t r, uint8_t g, uint8_t b) { - return (r & 0b11100000) | ((g & 0b11100000) >> 3) | ((b & 0b11000000) >> 6); + int create_pen(uint8_t r, uint8_t g, uint8_t b) { + return pen.create(r, g, b); } - static constexpr RGB565 create_pen_rgb565(uint8_t r, uint8_t g, uint8_t b) { - uint16_t p = ((r & 0b11111000) << 8) | - ((g & 0b11111100) << 3) | - ((b & 0b11111000) >> 3); - - return __builtin_bswap16(p); + void set_pen(uint8_t r, uint8_t g, uint8_t b) { + pen.set_color(r, g, b); } - - static constexpr RGB565 create_pen_rgb332(uint8_t r, uint8_t g, uint8_t b) { - uint16_t p = ((r & 0b11100000) << 8) | - ((g & 0b11100000) << 3) | - ((b & 0b11000000) >> 3); - return __builtin_bswap16(p); + void update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) { + pen.update_color(i, r, g, b); + } + + void reset_pen(uint8_t i) { + pen.reset_color(i); } void set_dimensions(int width, int height) { @@ -106,23 +302,16 @@ namespace pimoroni { clip.h = height; } - int create_pen(uint8_t r, uint8_t g, uint8_t b); - - int reserve_palette(); - void empty_palette(); - void rgb332_palette(); - - int search_palette(RGB565 c); - int get_palette(uint8_t i); - void set_palette(uint8_t i, RGB565 c); - int put_palette(RGB565 c); - void set_clip(const Rect &r); void remove_clip(); - Pen* ptr(const Point &p); - Pen* ptr(const Rect &r); - Pen* ptr(int32_t x, int32_t y); + void* get_data() { + return frame_buffer; + } + + void get_data(uint y, void *row_buf) { + pen.palette_lookup(frame_buffer, row_buf, y * bounds.w, bounds.w); + } void clear(); void pixel(const Point &p); @@ -137,4 +326,9 @@ namespace pimoroni { void line(Point p1, Point p2); }; + template class PicoGraphics; + template class PicoGraphics; + template class PicoGraphics; + template class PicoGraphics; + } diff --git a/libraries/picographics_st7789/picographics_st7789.cpp b/libraries/picographics_st7789/picographics_st7789.cpp index 328755f8..5bd9bd69 100644 --- a/libraries/picographics_st7789/picographics_st7789.cpp +++ b/libraries/picographics_st7789/picographics_st7789.cpp @@ -5,11 +5,5 @@ namespace pimoroni { - void PicoGraphicsST7789::update() { - st7789.update(palette); - } - void PicoGraphicsST7789::set_backlight(uint8_t brightness) { - st7789.set_backlight(brightness); - } } diff --git a/libraries/picographics_st7789/picographics_st7789.hpp b/libraries/picographics_st7789/picographics_st7789.hpp index b0e87307..a4428d2c 100644 --- a/libraries/picographics_st7789/picographics_st7789.hpp +++ b/libraries/picographics_st7789/picographics_st7789.hpp @@ -6,42 +6,46 @@ namespace pimoroni { - class PicoGraphicsST7789 : public PicoGraphics { + template + class PicoGraphicsST7789 : public PicoGraphics { private: ST7789 st7789; public: PicoGraphicsST7789(uint16_t width, uint16_t height, Rotation rotation, bool round=false, void *frame_buffer=nullptr) : - PicoGraphics(width, height, frame_buffer), + PicoGraphics(width, height, frame_buffer), st7789(width, height, rotation, round, frame_buffer, get_spi_pins(BG_SPI_FRONT)) { common_init(); }; PicoGraphicsST7789(uint16_t width, uint16_t height, Rotation rotation, bool round, void *frame_buffer, SPIPins bus_pins) : - PicoGraphics(width, height, frame_buffer), + PicoGraphics(width, height, frame_buffer), st7789(width, height, rotation, round, frame_buffer, bus_pins) { common_init(); }; PicoGraphicsST7789(uint16_t width, uint16_t height, Rotation rotation, void *frame_buffer, ParallelPins bus_pins) : - PicoGraphics(width, height, frame_buffer), + PicoGraphics(width, height, frame_buffer), st7789(width, height, rotation, frame_buffer, bus_pins) { common_init(); }; void common_init() { - this->frame_buffer = (Pen *)st7789.frame_buffer; - this->st7789.init(); - this->set_dimensions(this->st7789.width, this->st7789.height); - this->st7789.update(palette); + st7789.init(); + this->set_dimensions(st7789.width, st7789.height); + st7789.update(this); + } + + void update() { + st7789.update(this); + } + + void set_backlight(uint8_t brightness) { + st7789.set_backlight(brightness); } - void update(); - void set_backlight(uint8_t brightness); - void configure_display(bool rotate180); void set_framebuffer(void* frame_buffer) { - this->frame_buffer = (Pen *)frame_buffer; - st7789.frame_buffer = frame_buffer; + this->frame_buffer = frame_buffer; } }; diff --git a/micropython/modules/st7789/st7789.c b/micropython/modules/st7789/st7789.c index f79e3fc4..af4e1eb5 100644 --- a/micropython/modules/st7789/st7789.c +++ b/micropython/modules/st7789/st7789.c @@ -10,9 +10,8 @@ MP_DEFINE_CONST_FUN_OBJ_2(GenericST7789_set_backlight_obj, GenericST7789_set_bac MP_DEFINE_CONST_FUN_OBJ_2(GenericST7789_set_framebuffer_obj, GenericST7789_set_framebuffer); // Palette management -MP_DEFINE_CONST_FUN_OBJ_2(GenericST7789_set_palette_mode_obj, GenericST7789_set_palette_mode); -MP_DEFINE_CONST_FUN_OBJ_3(GenericST7789_set_palette_obj, GenericST7789_set_palette); -MP_DEFINE_CONST_FUN_OBJ_1(GenericST7789_reserve_palette_obj, GenericST7789_reserve_palette); +MP_DEFINE_CONST_FUN_OBJ_KW(GenericST7789_update_pen_obj, 4, GenericST7789_update_pen); +MP_DEFINE_CONST_FUN_OBJ_2(GenericST7789_reset_pen_obj, GenericST7789_reset_pen); // Pen MP_DEFINE_CONST_FUN_OBJ_2(GenericST7789_set_pen_obj, GenericST7789_set_pen); @@ -41,9 +40,8 @@ STATIC const mp_rom_map_elem_t GenericST7789_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_set_pen), MP_ROM_PTR(&GenericST7789_set_pen_obj) }, { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&GenericST7789_update_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_palette_mode), MP_ROM_PTR(&GenericST7789_set_palette_mode_obj) }, - { MP_ROM_QSTR(MP_QSTR_set_palette), MP_ROM_PTR(&GenericST7789_set_palette_obj) }, - { MP_ROM_QSTR(MP_QSTR_reserve_palette), MP_ROM_PTR(&GenericST7789_reserve_palette_obj) }, + { MP_ROM_QSTR(MP_QSTR_update_pen), MP_ROM_PTR(&GenericST7789_update_pen_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset_pen), MP_ROM_PTR(&GenericST7789_reset_pen_obj) }, { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&GenericST7789_set_backlight_obj) }, { MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&GenericST7789_create_pen_obj) }, diff --git a/micropython/modules/st7789/st7789.cpp b/micropython/modules/st7789/st7789.cpp index 03cff7f0..8099f1e3 100644 --- a/micropython/modules/st7789/st7789.cpp +++ b/micropython/modules/st7789/st7789.cpp @@ -4,7 +4,9 @@ #include "micropython/modules/util.hpp" - +#ifndef PICO_GRAPHICS_PEN_TYPE +#define PICO_GRAPHICS_PEN_TYPE PenP4 +#endif using namespace pimoroni; @@ -14,7 +16,7 @@ extern "C" { typedef struct _GenericST7789_obj_t { mp_obj_base_t base; - PicoGraphicsST7789 *st7789; + PicoGraphicsST7789 *st7789; void *buffer; } GenericST7789_obj_t; @@ -66,32 +68,34 @@ mp_obj_t GenericST7789_make_new(const mp_obj_type_t *type, size_t n_args, size_t break; } + size_t required_size = PICO_GRAPHICS_PEN_TYPE::buffer_size(width, height); + if (args[ARG_buffer].u_obj != mp_const_none) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW); self->buffer = bufinfo.buf; - if(bufinfo.len < (size_t)(width * height * sizeof(Pen))) { + if(bufinfo.len < (size_t)(required_size)) { mp_raise_ValueError("Supplied buffer is too small!"); } } else { - self->buffer = m_new(uint8_t, width * height * sizeof(Pen)); + self->buffer = m_new(uint8_t, required_size); } if (display == DISPLAY_TUFTY_2040) { if (args[ARG_bus].u_obj == mp_const_none) { - self->st7789 = m_new_class(PicoGraphicsST7789, width, height, rotate, self->buffer, {10, 11, 12, 13, 14, 2}); + self->st7789 = m_new_class(PicoGraphicsST7789, width, height, rotate, self->buffer, {10, 11, 12, 13, 14, 2}); } else if (mp_obj_is_type(args[ARG_bus].u_obj, &ParallelPins_type)) { _PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj); - self->st7789 = m_new_class(PicoGraphicsST7789, width, height, rotate, self->buffer, *(ParallelPins *)(bus->pins)); + self->st7789 = m_new_class(PicoGraphicsST7789, width, height, rotate, self->buffer, *(ParallelPins *)(bus->pins)); } else { mp_raise_ValueError("ParallelBus expected!"); } } else { if (args[ARG_bus].u_obj == mp_const_none) { - self->st7789 = m_new_class(PicoGraphicsST7789, width, height, rotate, round, self->buffer, get_spi_pins(BG_SPI_FRONT)); + self->st7789 = m_new_class(PicoGraphicsST7789, width, height, rotate, round, self->buffer, get_spi_pins(BG_SPI_FRONT)); } else if (mp_obj_is_type(args[ARG_bus].u_obj, &SPIPins_type)) { _PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj); - self->st7789 = m_new_class(PicoGraphicsST7789, width, height, rotate, round, self->buffer, *(SPIPins *)(bus->pins)); + self->st7789 = m_new_class(PicoGraphicsST7789, width, height, rotate, round, self->buffer, *(SPIPins *)(bus->pins)); } else { mp_raise_ValueError("SPIBus expected!"); } @@ -146,7 +150,7 @@ mp_obj_t GenericST7789_set_backlight(mp_obj_t self_in, mp_obj_t brightness) { } mp_obj_t GenericST7789_module_RGB332(mp_obj_t r, mp_obj_t g, mp_obj_t b) { - return mp_obj_new_int(PicoGraphicsST7789::create_pen_rgb332( + return mp_obj_new_int(PicoGraphicsPenType::rgb_to_rgb332( mp_obj_get_int(r), mp_obj_get_int(g), mp_obj_get_int(b) @@ -154,7 +158,7 @@ mp_obj_t GenericST7789_module_RGB332(mp_obj_t r, mp_obj_t g, mp_obj_t b) { } mp_obj_t GenericST7789_module_RGB565(mp_obj_t r, mp_obj_t g, mp_obj_t b) { - return mp_obj_new_int(PicoGraphicsST7789::create_pen_rgb565( + return mp_obj_new_int(PicoGraphicsPenType::rgb_to_rgb565( mp_obj_get_int(r), mp_obj_get_int(g), mp_obj_get_int(b) @@ -164,40 +168,44 @@ mp_obj_t GenericST7789_module_RGB565(mp_obj_t r, mp_obj_t g, mp_obj_t b) { mp_obj_t GenericST7789_set_pen(mp_obj_t self_in, mp_obj_t pen) { GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t); - self->st7789->set_pen(mp_obj_get_int(pen) & 0xff); + self->st7789->set_pen(mp_obj_get_int(pen)); return mp_const_none; } -mp_obj_t GenericST7789_set_palette_mode(mp_obj_t self_in, mp_obj_t mode) { +mp_obj_t GenericST7789_reset_pen(mp_obj_t self_in, mp_obj_t pen) { GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t); - self->st7789->set_palette_mode((PicoGraphicsST7789::PaletteMode)mp_obj_get_int(mode)); + self->st7789->reset_pen(mp_obj_get_int(pen)); return mp_const_none; } -mp_obj_t GenericST7789_set_palette(mp_obj_t self_in, mp_obj_t index, mp_obj_t colour) { - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t); +mp_obj_t GenericST7789_update_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_i, ARG_r, ARG_g, ARG_b }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_i, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_r, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_g, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_b, MP_ARG_REQUIRED | MP_ARG_INT } + }; - self->st7789->set_palette( - mp_obj_get_int(index) & 0xff, - mp_obj_get_int(colour) & 0xffff + 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); + + GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, GenericST7789_obj_t); + + self->st7789->update_pen( + args[ARG_i].u_int & 0xff, + args[ARG_r].u_int & 0xff, + args[ARG_g].u_int & 0xff, + args[ARG_b].u_int & 0xff ); return mp_const_none; } -mp_obj_t GenericST7789_reserve_palette(mp_obj_t self_in) { - GenericST7789_obj_t *self = MP_OBJ_TO_PTR2(self_in, GenericST7789_obj_t); - - int result = self->st7789->reserve_palette(); - - if (result == -1) mp_raise_ValueError("reserve_palette failed. No space in palette!"); - - return mp_obj_new_int(result); -} - mp_obj_t GenericST7789_create_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_self, ARG_r, ARG_g, ARG_b }; static const mp_arg_t allowed_args[] = { diff --git a/micropython/modules/st7789/st7789.h b/micropython/modules/st7789/st7789.h index a9eb4b62..b65b6fd1 100644 --- a/micropython/modules/st7789/st7789.h +++ b/micropython/modules/st7789/st7789.h @@ -27,9 +27,8 @@ extern mp_obj_t GenericST7789_update(mp_obj_t self_in); extern mp_obj_t GenericST7789_set_backlight(mp_obj_t self_in, mp_obj_t brightness); // Palette management -extern mp_obj_t GenericST7789_set_palette_mode(mp_obj_t self_in, mp_obj_t mode); -extern mp_obj_t GenericST7789_set_palette(mp_obj_t self_in, mp_obj_t index, mp_obj_t colour); -extern mp_obj_t GenericST7789_reserve_palette(mp_obj_t self_in); +extern mp_obj_t GenericST7789_update_pen(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t GenericST7789_reset_pen(mp_obj_t self_in, mp_obj_t pen); // Pen extern mp_obj_t GenericST7789_set_pen(mp_obj_t self_in, mp_obj_t pen);