2021-01-16 12:26:03 +00:00
|
|
|
#pragma once
|
|
|
|
|
2021-01-20 15:59:48 +00:00
|
|
|
#include <string>
|
2021-01-16 12:26:03 +00:00
|
|
|
#include <cstdint>
|
2021-01-18 07:58:19 +00:00
|
|
|
#include <algorithm>
|
2021-01-23 23:41:09 +00:00
|
|
|
#include <vector>
|
2022-03-29 15:13:14 +01:00
|
|
|
#include "libraries/bitmap_fonts/font6_data.hpp"
|
2022-06-07 20:19:16 +01:00
|
|
|
#include "libraries/bitmap_fonts/font8_data.hpp"
|
|
|
|
#include "libraries/bitmap_fonts/font14_outline_data.hpp"
|
2022-06-07 16:37:06 +01:00
|
|
|
#include "common/pimoroni_common.hpp"
|
|
|
|
|
|
|
|
// A tiny graphics library for our Pico products
|
|
|
|
// supports:
|
|
|
|
// - 16-bit (565) RGB
|
|
|
|
// - 8-bit (332) RGB
|
|
|
|
// - 8-bit with 16-bit 256 entry palette
|
|
|
|
// - 4-bit with 16-bit 8 entry palette
|
2021-01-16 12:26:03 +00:00
|
|
|
namespace pimoroni {
|
2022-06-06 17:54:15 +01:00
|
|
|
typedef uint8_t RGB332;
|
2022-05-27 16:52:43 +01:00
|
|
|
typedef uint16_t RGB565;
|
2022-06-06 17:54:15 +01:00
|
|
|
typedef int Pen;
|
2021-01-18 07:58:19 +00:00
|
|
|
|
2021-01-23 23:41:09 +00:00
|
|
|
struct Rect;
|
|
|
|
|
|
|
|
struct Point {
|
2021-01-18 07:58:19 +00:00
|
|
|
int32_t x = 0, y = 0;
|
|
|
|
|
2021-01-23 23:41:09 +00:00
|
|
|
Point() = default;
|
|
|
|
Point(int32_t x, int32_t y) : x(x), y(y) {}
|
2021-01-18 07:58:19 +00:00
|
|
|
|
2021-01-23 23:41:09 +00:00
|
|
|
inline Point& operator-= (const Point &a) { x -= a.x; y -= a.y; return *this; }
|
|
|
|
inline Point& operator+= (const Point &a) { x += a.x; y += a.y; return *this; }
|
2021-01-18 07:58:19 +00:00
|
|
|
|
2021-01-23 23:41:09 +00:00
|
|
|
Point clamp(const Rect &r) const;
|
2021-01-18 07:58:19 +00:00
|
|
|
};
|
|
|
|
|
2021-01-23 23:41:09 +00:00
|
|
|
struct Rect {
|
2021-01-18 07:58:19 +00:00
|
|
|
int32_t x = 0, y = 0, w = 0, h = 0;
|
|
|
|
|
2021-01-23 23:41:09 +00:00
|
|
|
Rect() = default;
|
|
|
|
Rect(int32_t x, int32_t y, int32_t w, int32_t h) : x(x), y(y), w(w), h(h) {}
|
|
|
|
Rect(const Point &tl, const Point &br) : x(tl.x), y(tl.y), w(br.x - tl.x), h(br.y - tl.y) {}
|
2021-01-18 07:58:19 +00:00
|
|
|
|
|
|
|
bool empty() const;
|
2021-01-23 23:41:09 +00:00
|
|
|
bool contains(const Point &p) const;
|
|
|
|
bool contains(const Rect &p) const;
|
|
|
|
bool intersects(const Rect &r) const;
|
|
|
|
Rect intersection(const Rect &r) const;
|
2021-01-18 07:58:19 +00:00
|
|
|
|
|
|
|
void inflate(int32_t v);
|
|
|
|
void deflate(int32_t v);
|
|
|
|
};
|
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
class PicoGraphics {
|
|
|
|
public:
|
|
|
|
struct PaletteEntry {
|
|
|
|
RGB565 color;
|
|
|
|
bool used;
|
|
|
|
};
|
2022-06-06 17:54:15 +01:00
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
enum PenType {
|
|
|
|
PEN_P2 = 0,
|
|
|
|
PEN_P4,
|
|
|
|
PEN_P8,
|
|
|
|
PEN_RGB332,
|
|
|
|
PEN_RGB565
|
|
|
|
};
|
2022-06-06 17:54:15 +01:00
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
void *frame_buffer;
|
2022-06-06 17:54:15 +01:00
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
PenType pen_type;
|
|
|
|
Rect bounds;
|
|
|
|
Rect clip;
|
2022-06-06 17:54:15 +01:00
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
const bitmap::font_t *font;
|
2022-06-06 17:54:15 +01:00
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
|
|
|
virtual void set_pen(uint c);
|
|
|
|
virtual void set_pen(uint8_t r, uint8_t g, uint8_t b);
|
|
|
|
virtual void update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b);
|
|
|
|
virtual void reset_pen(uint8_t i);
|
|
|
|
virtual int create_pen(uint8_t r, uint8_t g, uint8_t b);
|
|
|
|
virtual void set_pixel(void *frame_buffer, uint x, uint y, uint stride);
|
|
|
|
virtual void palette_lookup(void *frame_buffer, void *result, uint offset, uint length);
|
|
|
|
|
|
|
|
void set_font(const bitmap::font_t *font);
|
2022-06-07 20:19:16 +01:00
|
|
|
void set_font(std::string font);
|
2022-06-07 16:37:06 +01:00
|
|
|
|
|
|
|
void set_dimensions(int width, int height);
|
2022-06-06 17:54:15 +01:00
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
void *get_data();
|
|
|
|
void get_data(uint y, void *row_buf);
|
2022-06-06 17:54:15 +01:00
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
void set_clip(const Rect &r);
|
|
|
|
void remove_clip();
|
|
|
|
|
|
|
|
void clear();
|
|
|
|
void pixel(const Point &p);
|
|
|
|
void pixel_span(const Point &p, int32_t l);
|
|
|
|
void rectangle(const Rect &r);
|
|
|
|
void circle(const Point &p, int32_t r);
|
|
|
|
void character(const char c, const Point &p, uint8_t scale = 2);
|
|
|
|
void text(const std::string &t, const Point &p, int32_t wrap, uint8_t scale = 2);
|
|
|
|
int32_t measure_text(const std::string &t, uint8_t scale = 2);
|
|
|
|
void polygon(const std::vector<Point> &points);
|
|
|
|
void triangle(Point p1, Point p2, Point p3);
|
|
|
|
void line(Point p1, Point p2);
|
2022-06-06 17:54:15 +01:00
|
|
|
};
|
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
|
|
|
|
class PicoGraphics_PenP4 : public PicoGraphics {
|
2022-06-06 17:54:15 +01:00
|
|
|
public:
|
|
|
|
uint8_t color;
|
|
|
|
PaletteEntry palette[8];
|
2022-06-07 20:19:16 +01:00
|
|
|
const RGB565 default_palette[8] = {
|
|
|
|
rgb_to_rgb565(57, 48, 57), // Black
|
|
|
|
rgb_to_rgb565(255, 255, 255), // White
|
|
|
|
rgb_to_rgb565(58, 91, 70), // Green
|
|
|
|
rgb_to_rgb565(61, 59, 94), // Blue
|
|
|
|
rgb_to_rgb565(156, 72, 75), // Red
|
|
|
|
rgb_to_rgb565(208, 190, 71), // Yellow
|
|
|
|
rgb_to_rgb565(177, 106, 73), // Orange
|
|
|
|
rgb_to_rgb565(255, 255, 255) // Clear
|
|
|
|
};
|
2022-06-07 16:37:06 +01:00
|
|
|
PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer)
|
|
|
|
: PicoGraphics(width, height, frame_buffer) {
|
|
|
|
this->pen_type = PEN_P4;
|
|
|
|
if(this->frame_buffer == nullptr) {
|
|
|
|
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
|
|
|
|
}
|
2022-06-07 20:19:16 +01:00
|
|
|
for(auto i = 0u; i < 8; i++) {
|
|
|
|
palette[i].color = default_palette[i];
|
|
|
|
palette[i].used = true;
|
|
|
|
}
|
2022-06-06 17:54:15 +01:00
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
void set_pen(uint c) {
|
2022-06-06 17:54:15 +01:00
|
|
|
color = c & 0xf;
|
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
void set_pen(uint8_t r, uint8_t g, uint8_t b) override {
|
2022-06-06 17:54:15 +01:00
|
|
|
// TODO look up closest palette colour, or just NOOP?
|
|
|
|
}
|
2022-06-07 20:19:16 +01:00
|
|
|
void update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override {
|
|
|
|
i &= 0xf;
|
|
|
|
palette[i].color = rgb_to_rgb565(r, g, b);
|
|
|
|
palette[i].used = true;
|
|
|
|
}
|
|
|
|
void reset_pen(uint8_t i) override {
|
|
|
|
i &= 0xf;
|
|
|
|
palette[i].color = default_palette[i];
|
|
|
|
}
|
2022-06-06 17:54:15 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
class PicoGraphics_PenP8 : public PicoGraphics {
|
2022-06-06 17:54:15 +01:00
|
|
|
public:
|
|
|
|
uint8_t color;
|
|
|
|
PaletteEntry palette[256];
|
2022-06-07 16:37:06 +01:00
|
|
|
PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer)
|
|
|
|
: PicoGraphics(width, height, frame_buffer) {
|
|
|
|
this->pen_type = PEN_P8;
|
|
|
|
if(this->frame_buffer == nullptr) {
|
|
|
|
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
|
|
|
|
}
|
2022-06-06 17:54:15 +01:00
|
|
|
for(auto i = 0u; i < 256; i++) {
|
2022-06-07 16:37:06 +01:00
|
|
|
reset_pen(i);
|
2022-06-06 17:54:15 +01:00
|
|
|
}
|
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
void set_pen(uint c) override {
|
2022-06-06 17:54:15 +01:00
|
|
|
color = c;
|
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
void set_pen(uint8_t r, uint8_t g, uint8_t b) override {
|
2022-06-06 17:54:15 +01:00
|
|
|
// TODO look up closest palette colour, or just NOOP?
|
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
void update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override {
|
2022-06-07 20:19:16 +01:00
|
|
|
i &= 0xff;
|
2022-06-06 17:54:15 +01:00
|
|
|
palette[i].color = rgb_to_rgb565(r, g, b);
|
|
|
|
palette[i].used = true;
|
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
void reset_pen(uint8_t i) override {
|
2022-06-07 20:19:16 +01:00
|
|
|
i &= 0xff;
|
2022-06-06 17:54:15 +01:00
|
|
|
palette[i].color = 0;
|
|
|
|
palette[i].used = false;
|
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
int create_pen(uint8_t r, uint8_t g, uint8_t b) override {
|
2022-06-06 17:54:15 +01:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
class PicoGraphics_PenRGB332 : public PicoGraphics {
|
2022-06-06 17:54:15 +01:00
|
|
|
public:
|
|
|
|
RGB332 color;
|
|
|
|
PaletteEntry palette[256];
|
2022-06-07 16:37:06 +01:00
|
|
|
PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer)
|
|
|
|
: PicoGraphics(width, height, frame_buffer) {
|
|
|
|
this->pen_type = PEN_RGB332;
|
|
|
|
if(this->frame_buffer == nullptr) {
|
|
|
|
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
|
|
|
|
}
|
2022-06-06 17:54:15 +01:00
|
|
|
for(auto i = 0u; i < 256; i++) {
|
2022-06-07 20:19:16 +01:00
|
|
|
palette[i].color = rgb332_to_rgb565(i);
|
|
|
|
palette[i].used = true;
|
2022-06-06 17:54:15 +01:00
|
|
|
}
|
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
void set_pen(uint c) override {
|
2022-06-06 17:54:15 +01:00
|
|
|
color = c;
|
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
void set_pen(uint8_t r, uint8_t g, uint8_t b) override {
|
2022-06-06 17:54:15 +01:00
|
|
|
color = rgb_to_rgb332(r, g, b);
|
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
int create_pen(uint8_t r, uint8_t g, uint8_t b) override {
|
2022-06-06 17:54:15 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
class PicoGraphics_PenRGB565 : public PicoGraphics {
|
2022-06-06 17:54:15 +01:00
|
|
|
public:
|
2022-06-07 16:37:06 +01:00
|
|
|
RGB565 color;
|
|
|
|
PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer)
|
|
|
|
: PicoGraphics(width, height, frame_buffer) {
|
|
|
|
this->pen_type = PEN_RGB565;
|
|
|
|
if(this->frame_buffer == nullptr) {
|
|
|
|
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
|
|
|
|
}
|
2022-06-06 17:54:15 +01:00
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
void set_pen(uint c) override {
|
|
|
|
color = c;
|
2022-06-06 17:54:15 +01:00
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
void set_pen(uint8_t r, uint8_t g, uint8_t b) override {
|
2022-06-06 17:54:15 +01:00
|
|
|
color = rgb_to_rgb565(r, g, b);
|
|
|
|
}
|
2022-06-07 16:37:06 +01:00
|
|
|
int create_pen(uint8_t r, uint8_t g, uint8_t b) override {
|
2022-06-06 17:54:15 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
static size_t buffer_size(uint w, uint h) {
|
|
|
|
return w * h * sizeof(RGB565);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
class DisplayDriver {
|
|
|
|
public:
|
|
|
|
uint16_t width;
|
|
|
|
uint16_t height;
|
|
|
|
Rotation rotation;
|
2022-06-06 17:54:15 +01:00
|
|
|
|
2022-06-07 16:37:06 +01:00
|
|
|
DisplayDriver(uint16_t width, uint16_t height, Rotation rotation)
|
|
|
|
: width(width), height(height), rotation(rotation) {};
|
2021-01-16 12:26:03 +00:00
|
|
|
|
2022-06-07 19:02:30 +01:00
|
|
|
virtual void update(PicoGraphics *display) {};
|
|
|
|
virtual void set_backlight(uint8_t brightness) {};
|
2021-01-16 12:26:03 +00:00
|
|
|
};
|
|
|
|
|
2022-04-18 19:40:05 +01:00
|
|
|
}
|