kopia lustrzana https://github.com/pimoroni/pimoroni-pico
GPIO High and palette mode support
rodzic
5a6aa0186c
commit
a7435c6a5e
|
@ -57,11 +57,15 @@ namespace pimoroni {
|
|||
swd_load_program(section_addresses, section_data, section_data_len, sizeof(section_addresses) / sizeof(section_addresses[0]), 0x20000001, 0x15004000, true);
|
||||
|
||||
ram.init();
|
||||
write_header(0);
|
||||
bank = 0;
|
||||
write_header();
|
||||
sleep_us(100);
|
||||
|
||||
gpio_put(RAM_SEL, 1);
|
||||
ram.init();
|
||||
write_header(1);
|
||||
bank = 1;
|
||||
write_header();
|
||||
sleep_us(100);
|
||||
|
||||
bank = 0;
|
||||
gpio_put(RAM_SEL, 0);
|
||||
|
@ -78,24 +82,74 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
void DVDisplay::flip() {
|
||||
if (pixel_buffer_location.y != -1) {
|
||||
ram.write(pointToAddress(pixel_buffer_location), pixel_buffer, pixel_buffer_x << 1);
|
||||
if (use_palette_mode) {
|
||||
write_palette();
|
||||
if (pixel_buffer_location.y != -1) {
|
||||
ram.write(point_to_address_palette(pixel_buffer_location), pixel_buffer, pixel_buffer_x);
|
||||
pixel_buffer_location.y = -1;
|
||||
}
|
||||
}
|
||||
else if (pixel_buffer_location.y != -1) {
|
||||
ram.write(point_to_address(pixel_buffer_location), pixel_buffer, pixel_buffer_x << 1);
|
||||
pixel_buffer_location.y = -1;
|
||||
}
|
||||
bank ^= 1;
|
||||
ram.wait_for_finish_blocking();
|
||||
while (gpio_get(VSYNC) == 0);
|
||||
gpio_put(RAM_SEL, bank);
|
||||
if (rewrite_header) {
|
||||
write_header();
|
||||
rewrite_header = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t DVDisplay::get_driver_gpio() {
|
||||
uint8_t DVDisplay::get_gpio() {
|
||||
return i2c.reg_read_uint8(I2C_ADDR, I2C_REG_GPIO);
|
||||
}
|
||||
|
||||
uint8_t DVDisplay::get_driver_gpio_hi() {
|
||||
uint8_t DVDisplay::get_gpio_hi() {
|
||||
return i2c.reg_read_uint8(I2C_ADDR, I2C_REG_GPIO_HI);
|
||||
}
|
||||
|
||||
void DVDisplay::i2c_modify_bit(uint8_t reg, uint bit, bool enable) {
|
||||
uint8_t val = i2c.reg_read_uint8(I2C_ADDR, reg);
|
||||
if (enable) val |= 1u << bit;
|
||||
else val &= ~(1u << bit);
|
||||
i2c.reg_write_uint8(I2C_ADDR, reg, val);
|
||||
}
|
||||
|
||||
void DVDisplay::set_gpio_hi_dir(uint pin, bool output) {
|
||||
i2c_modify_bit(I2C_REG_GPIO_HI_OE, pin, output);
|
||||
}
|
||||
|
||||
void DVDisplay::set_gpio_hi_dir_all(uint8_t val) {
|
||||
i2c.reg_write_uint8(I2C_ADDR, I2C_REG_GPIO_HI_OE, val);
|
||||
}
|
||||
|
||||
void DVDisplay::set_gpio_hi(uint pin, bool on) {
|
||||
i2c_modify_bit(I2C_REG_GPIO_HI_OUT, pin, on);
|
||||
}
|
||||
|
||||
void DVDisplay::set_gpio_hi_all(uint8_t val) {
|
||||
i2c.reg_write_uint8(I2C_ADDR, I2C_REG_GPIO_HI_OUT, val);
|
||||
}
|
||||
|
||||
void DVDisplay::set_gpio_hi_pull_up(uint pin, bool on) {
|
||||
i2c_modify_bit(I2C_REG_GPIO_HI_PULL_UP, pin, on);
|
||||
}
|
||||
|
||||
void DVDisplay::set_gpio_hi_pull_up_all(uint8_t val) {
|
||||
i2c.reg_write_uint8(I2C_ADDR, I2C_REG_GPIO_HI_PULL_UP, val);
|
||||
}
|
||||
|
||||
void DVDisplay::set_gpio_hi_pull_down(uint pin, bool on) {
|
||||
i2c_modify_bit(I2C_REG_GPIO_HI_PULL_DOWN, pin, on);
|
||||
}
|
||||
|
||||
void DVDisplay::set_gpio_hi_pull_down_all(uint8_t val) {
|
||||
i2c.reg_write_uint8(I2C_ADDR, I2C_REG_GPIO_HI_PULL_DOWN, val);
|
||||
}
|
||||
|
||||
void DVDisplay::set_led_level(uint8_t level) {
|
||||
i2c.reg_write_uint8(I2C_ADDR, I2C_REG_LED, level | 0x80);
|
||||
}
|
||||
|
@ -120,19 +174,32 @@ namespace pimoroni {
|
|||
ram.read_blocking(address, (uint32_t*)data, (len + 1) >> 1);
|
||||
}
|
||||
|
||||
void DVDisplay::write(uint32_t address, size_t len, const uint8_t colour)
|
||||
{
|
||||
uint32_t val = colour | ((uint32_t)colour << 16);
|
||||
val |= val << 8;
|
||||
|
||||
ram.write_repeat(address, val, len);
|
||||
}
|
||||
|
||||
void DVDisplay::read(uint32_t address, size_t len, uint8_t *data)
|
||||
{
|
||||
ram.read_blocking(address, (uint32_t*)data, len);
|
||||
}
|
||||
|
||||
void DVDisplay::write_pixel(const Point &p, uint16_t colour)
|
||||
{
|
||||
if (pixel_buffer_location.y == p.y && pixel_buffer_location.x + pixel_buffer_x == p.x) {
|
||||
if (pixel_buffer_x & 1) pixel_buffer[pixel_buffer_x >> 1] |= (uint32_t)colour << 16;
|
||||
else pixel_buffer[pixel_buffer_x >> 1] = colour;
|
||||
if (++pixel_buffer_x == PIXEL_BUFFER_LEN_IN_WORDS * 2) {
|
||||
ram.write(pointToAddress(pixel_buffer_location), pixel_buffer, PIXEL_BUFFER_LEN_IN_WORDS * 4);
|
||||
ram.write(point_to_address(pixel_buffer_location), pixel_buffer, PIXEL_BUFFER_LEN_IN_WORDS * 4);
|
||||
pixel_buffer_location.y = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (pixel_buffer_location.y != -1) {
|
||||
ram.write(pointToAddress(pixel_buffer_location), pixel_buffer, pixel_buffer_x << 1);
|
||||
ram.write(point_to_address(pixel_buffer_location), pixel_buffer, pixel_buffer_x << 1);
|
||||
}
|
||||
pixel_buffer_location = p;
|
||||
pixel_buffer_x = 1;
|
||||
|
@ -141,7 +208,7 @@ namespace pimoroni {
|
|||
|
||||
void DVDisplay::write_pixel_span(const Point &p, uint l, uint16_t colour)
|
||||
{
|
||||
write(pointToAddress(p), l, colour);
|
||||
write(point_to_address(p), l, colour);
|
||||
}
|
||||
|
||||
void DVDisplay::write_pixel_span(const Point &p, uint l, uint16_t *data)
|
||||
|
@ -149,21 +216,92 @@ namespace pimoroni {
|
|||
uint32_t offset = 0;
|
||||
if (((uintptr_t)data & 0x2) != 0) {
|
||||
uint32_t val = *data++;
|
||||
ram.write(pointToAddress(p), &val, 2);
|
||||
ram.write(point_to_address(p), &val, 2);
|
||||
--l;
|
||||
offset = 2;
|
||||
}
|
||||
if (l > 0) {
|
||||
ram.write(pointToAddress(p) + offset, (uint32_t*)data, l << 1);
|
||||
ram.write(point_to_address(p) + offset, (uint32_t*)data, l << 1);
|
||||
}
|
||||
}
|
||||
|
||||
void DVDisplay::read_pixel_span(const Point &p, uint l, uint16_t *data)
|
||||
{
|
||||
read(pointToAddress(p), l, data);
|
||||
read(point_to_address(p), l, data);
|
||||
}
|
||||
|
||||
void DVDisplay::write_header(uint bank)
|
||||
void DVDisplay::enable_palette(bool enable)
|
||||
{
|
||||
use_palette_mode = enable;
|
||||
rewrite_header = true;
|
||||
write_header();
|
||||
write_palette();
|
||||
}
|
||||
|
||||
void DVDisplay::set_palette(RGB888 new_palette[PALETTE_SIZE])
|
||||
{
|
||||
for (int i = 0; i < PALETTE_SIZE; ++i) {
|
||||
set_palette_colour(i, new_palette[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void DVDisplay::set_palette_colour(uint8_t entry, RGB888 colour)
|
||||
{
|
||||
palette[entry * 3] = (colour >> 16) & 0xFF;
|
||||
palette[entry * 3 + 1] = (colour >> 8) & 0xFF;
|
||||
palette[entry * 3 + 2] = colour & 0xFF;
|
||||
}
|
||||
|
||||
void DVDisplay::write_palette()
|
||||
{
|
||||
uint addr = (height + 7) * 4;
|
||||
ram.write(addr, (uint32_t*)palette, PALETTE_SIZE * 3);
|
||||
}
|
||||
|
||||
void DVDisplay::write_palette_pixel(const Point &p, uint8_t colour)
|
||||
{
|
||||
if (pixel_buffer_location.y == p.y && pixel_buffer_location.x + pixel_buffer_x == p.x) {
|
||||
if (pixel_buffer_x & 3) pixel_buffer[pixel_buffer_x >> 2] |= (uint32_t)colour << ((pixel_buffer_x & 3) << 3);
|
||||
else pixel_buffer[pixel_buffer_x >> 2] = colour;
|
||||
if (++pixel_buffer_x == PIXEL_BUFFER_LEN_IN_WORDS * 4) {
|
||||
ram.write(point_to_address_palette(pixel_buffer_location), pixel_buffer, PIXEL_BUFFER_LEN_IN_WORDS * 4);
|
||||
pixel_buffer_location.y = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (pixel_buffer_location.y != -1) {
|
||||
ram.write(point_to_address_palette(pixel_buffer_location), pixel_buffer, pixel_buffer_x);
|
||||
}
|
||||
pixel_buffer_location = p;
|
||||
pixel_buffer_x = 1;
|
||||
pixel_buffer[0] = colour;
|
||||
}
|
||||
|
||||
void DVDisplay::write_palette_pixel_span(const Point &p, uint l, uint8_t colour)
|
||||
{
|
||||
write(point_to_address_palette(p), l, colour);
|
||||
}
|
||||
|
||||
void DVDisplay::write_palette_pixel_span(const Point &p, uint l, uint8_t* data)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
while (((uintptr_t)data & 0x3) != 0 && l > 0) {
|
||||
uint32_t val = *data++;
|
||||
ram.write(point_to_address_palette(p), &val, 1);
|
||||
--l;
|
||||
offset++;
|
||||
}
|
||||
if (l > 0) {
|
||||
ram.write(point_to_address_palette(p) + offset, (uint32_t*)data, l);
|
||||
}
|
||||
}
|
||||
|
||||
void DVDisplay::read_palette_pixel_span(const Point &p, uint l, uint8_t *data)
|
||||
{
|
||||
read(point_to_address_palette(p), l, data);
|
||||
}
|
||||
|
||||
void DVDisplay::write_header()
|
||||
{
|
||||
uint32_t buf[8];
|
||||
uint32_t full_width = width * h_repeat;
|
||||
|
@ -173,20 +311,20 @@ namespace pimoroni {
|
|||
buf[3] = (uint32_t)height << 16;
|
||||
buf[4] = 0x00000001;
|
||||
buf[5] = 0x00010000 + height + (bank << 24);
|
||||
buf[6] = 0x00000000;
|
||||
buf[6] = 0x00000001;
|
||||
ram.write(0, buf, 7 * 4);
|
||||
ram.wait_for_finish_blocking();
|
||||
|
||||
uint addr = 4 * 7;
|
||||
uint line_type = 0x90000000u;
|
||||
if (use_palette_mode) line_type = 0xa0000000u;
|
||||
for (int i = 0; i < height; i += 8) {
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
buf[j] = 0x90000000 + ((uint32_t)h_repeat << 24) + ((i + j) * width * 2) + base_address;
|
||||
buf[j] = line_type + ((uint32_t)h_repeat << 24) + ((i + j) * width * 2) + base_address;
|
||||
}
|
||||
ram.write(addr, buf, 8 * 4);
|
||||
ram.wait_for_finish_blocking();
|
||||
addr += 4 * 8;
|
||||
}
|
||||
|
||||
sleep_us(100);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
namespace pimoroni {
|
||||
|
||||
// This is ARGB1555 only for now
|
||||
class DVDisplay : public IDirectDisplayDriver<uint16_t> {
|
||||
class DVDisplay : public IDirectDisplayDriver<uint16_t>, public IPaletteDisplayDriver {
|
||||
//--------------------------------------------------
|
||||
// Variables
|
||||
//--------------------------------------------------
|
||||
|
@ -41,6 +41,10 @@ namespace pimoroni {
|
|||
static constexpr uint I2C_REG_GPIO = 0xC0;
|
||||
static constexpr uint I2C_REG_LED = 0xC1;
|
||||
static constexpr uint I2C_REG_GPIO_HI = 0xC8;
|
||||
static constexpr uint I2C_REG_GPIO_HI_OUT = 0xC9;
|
||||
static constexpr uint I2C_REG_GPIO_HI_OE = 0xCA;
|
||||
static constexpr uint I2C_REG_GPIO_HI_PULL_UP = 0xCB;
|
||||
static constexpr uint I2C_REG_GPIO_HI_PULL_DOWN = 0xCC;
|
||||
static constexpr uint I2C_REG_EDID = 0xED;
|
||||
|
||||
static constexpr uint32_t base_address = 0x10000;
|
||||
|
@ -51,6 +55,8 @@ namespace pimoroni {
|
|||
uint8_t v_repeat = 1;
|
||||
|
||||
public:
|
||||
static constexpr int PALETTE_SIZE = 32;
|
||||
|
||||
// Valid resolutions are:
|
||||
// 640x480 (60Hz), 720x480 (60Hz), 720x400 (70Hz), 720x576 (50Hz)
|
||||
// 800x600 (60Hz), 800x480 (60Hz), 800x450 (60Hz), 960x540 (50Hz), 1280x720 (30Hz)
|
||||
|
@ -104,11 +110,31 @@ namespace pimoroni {
|
|||
void init();
|
||||
void flip();
|
||||
|
||||
uint8_t get_driver_gpio();
|
||||
uint8_t get_driver_gpio_hi();
|
||||
// 32 colour palette mode. Note that palette entries range from 0-31,
|
||||
// but when writing colour values the palette entry is in bits 6-2, so the
|
||||
// entry value is effectively multiplied by 4.
|
||||
void enable_palette(bool enable);
|
||||
void set_palette(RGB888 palette[PALETTE_SIZE]);
|
||||
void set_palette_colour(uint8_t entry, RGB888 colour);
|
||||
|
||||
void write_palette_pixel(const Point &p, uint8_t colour);
|
||||
void write_palette_pixel_span(const Point &p, uint l, uint8_t colour);
|
||||
void write_palette_pixel_span(const Point &p, uint l, uint8_t* data);
|
||||
void read_palette_pixel_span(const Point &p, uint l, uint8_t *data);
|
||||
|
||||
bool is_button_b_pressed() { return (get_driver_gpio() & 0x1) != 0x1; }
|
||||
bool is_button_c_pressed() { return (get_driver_gpio() & 0x2) != 0x2; }
|
||||
uint8_t get_gpio();
|
||||
uint8_t get_gpio_hi();
|
||||
void set_gpio_hi_dir(uint pin, bool output);
|
||||
void set_gpio_hi_dir_all(uint8_t output_enables);
|
||||
void set_gpio_hi(uint pin, bool on);
|
||||
void set_gpio_hi_all(uint8_t vals);
|
||||
void set_gpio_hi_pull_up(uint pin, bool on);
|
||||
void set_gpio_hi_pull_up_all(uint8_t vals);
|
||||
void set_gpio_hi_pull_down(uint pin, bool on);
|
||||
void set_gpio_hi_pull_down_all(uint8_t vals);
|
||||
|
||||
bool is_button_b_pressed() { return (get_gpio() & 0x1) != 0x1; }
|
||||
bool is_button_c_pressed() { return (get_gpio() & 0x2) != 0x2; }
|
||||
|
||||
// Valid LED levels are from 0-127.
|
||||
void set_led_level(uint8_t level);
|
||||
|
@ -119,6 +145,10 @@ namespace pimoroni {
|
|||
void get_edid(uint8_t* edid);
|
||||
|
||||
private:
|
||||
uint8_t palette[PALETTE_SIZE * 3] alignas(4);
|
||||
bool use_palette_mode = false;
|
||||
bool rewrite_header = false;
|
||||
|
||||
static constexpr int PIXEL_BUFFER_LEN_IN_WORDS = 16;
|
||||
uint32_t pixel_buffer[PIXEL_BUFFER_LEN_IN_WORDS];
|
||||
Point pixel_buffer_location;
|
||||
|
@ -126,12 +156,20 @@ namespace pimoroni {
|
|||
|
||||
void write(uint32_t address, size_t len, const uint16_t colour);
|
||||
void read(uint32_t address, size_t len, uint16_t *data);
|
||||
void write(uint32_t address, size_t len, const uint8_t colour);
|
||||
void read(uint32_t address, size_t len, uint8_t *data);
|
||||
|
||||
void write_header(uint bank);
|
||||
void write_palette();
|
||||
void write_header();
|
||||
|
||||
uint32_t pointToAddress(const Point &p)
|
||||
{
|
||||
void i2c_modify_bit(uint8_t reg, uint bit, bool enable);
|
||||
|
||||
uint32_t point_to_address(const Point &p) {
|
||||
return base_address + ((p.y * (uint32_t)width) + p.x) * 2;
|
||||
}
|
||||
|
||||
uint32_t point_to_address_palette(const Point &p) {
|
||||
return base_address + (p.y * (uint32_t)width * 2) + p.x;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
|
@ -61,13 +61,21 @@ int main() {
|
|||
}
|
||||
#endif
|
||||
|
||||
PicoGraphics_PenDV_RGB555 graphics(FRAME_WIDTH, FRAME_HEIGHT, display);
|
||||
display.enable_palette(true);
|
||||
PicoGraphics_PenDV_P5 graphics(FRAME_WIDTH, FRAME_HEIGHT, display);
|
||||
|
||||
graphics.set_pen(0x001F);
|
||||
graphics.create_pen(0, 0, 0);
|
||||
graphics.create_pen(0xFF, 0xFF, 0xFF);
|
||||
|
||||
for (int i = 0; i < 25; ++i) {
|
||||
graphics.create_pen_hsv(i * 0.04f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
graphics.set_pen(0xFF, 0, 0);
|
||||
graphics.clear();
|
||||
display.flip();
|
||||
sleep_ms(2000);
|
||||
graphics.set_pen(0x7C00);
|
||||
graphics.set_pen(0, 0, 0xFF);
|
||||
graphics.clear();
|
||||
display.flip();
|
||||
|
||||
|
@ -76,7 +84,7 @@ int main() {
|
|||
|
||||
constexpr int NUM_CIRCLES = 50;
|
||||
struct Circle {
|
||||
uint16_t x, y, size, grow;
|
||||
uint16_t x, y, size, grow, pen;
|
||||
} circles[NUM_CIRCLES];
|
||||
|
||||
for(int i =0 ; i < 50 ; i++)
|
||||
|
@ -85,6 +93,7 @@ int main() {
|
|||
circles[i].grow = std::max(0, (rand() % 50) - 25);
|
||||
circles[i].x = rand() % graphics.bounds.w;
|
||||
circles[i].y = rand() % graphics.bounds.h;
|
||||
circles[i].pen = 2 + (i >> 1);
|
||||
}
|
||||
|
||||
int frames = 0;
|
||||
|
@ -94,17 +103,18 @@ int main() {
|
|||
//}
|
||||
uint32_t render_start_time = time_us_32();
|
||||
|
||||
graphics.set_pen(0xFFFF);
|
||||
graphics.set_pen(0xFF, 0xFF, 0xFF);
|
||||
graphics.clear();
|
||||
|
||||
#if 0
|
||||
for (uint i = 0; i < 128; i++) {
|
||||
for (uint j = 0; j < 256; j++) {
|
||||
graphics.set_pen((j << 7) | i);
|
||||
RGB555 col = (j << 7) | i;
|
||||
graphics.set_pen((col << 3) & 0xF8, (col >> 2) & 0xF8, (col >> 7) & 0xF8);
|
||||
graphics.pixel(Point(j, i));
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
for (uint i = 0; i < 128; i++) {
|
||||
for (uint j = 0; j < 256; j++) {
|
||||
graphics.set_pen((j << 7) | i);
|
||||
|
@ -115,10 +125,12 @@ int main() {
|
|||
|
||||
for(int i =0 ; i < NUM_CIRCLES ; i++)
|
||||
{
|
||||
graphics.set_pen(0);
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.circle(Point(circles[i].x, circles[i].y), circles[i].size);
|
||||
|
||||
graphics.set_pen(RGB::from_hsv(i * 0.02f, 1.0f, 1.0f).to_rgb555());
|
||||
//RGB col = RGB::from_hsv(i * 0.02f, 1.0f, 1.0f);
|
||||
//graphics.set_pen(col.r, col.g, col.b);
|
||||
graphics.set_pen(circles[i].pen);
|
||||
graphics.circle(Point(circles[i].x, circles[i].y), circles[i].size-2);
|
||||
if (circles[i].grow) {
|
||||
circles[i].size++;
|
||||
|
@ -149,15 +161,19 @@ int main() {
|
|||
gpio_get(BUTTON_A) == 0 ? "A" : " ",
|
||||
display.is_button_b_pressed() ? "B" : " ",
|
||||
display.is_button_c_pressed() ? "C" : " ");
|
||||
graphics.set_pen(0);
|
||||
graphics.set_pen(0, 0, 0);
|
||||
graphics.text(buffer, {500,10}, FRAME_WIDTH - 500, 3);
|
||||
|
||||
uint32_t flip_start_time = time_us_32();
|
||||
display.flip();
|
||||
uint32_t flip_time = time_us_32() - flip_start_time;
|
||||
printf("Render: %.3f, flip: %.3f\n", render_time / 1000.f, flip_time / 1000.f);
|
||||
if (false) printf("Render: %.3f, flip: %.3f\n", render_time / 1000.f, flip_time / 1000.f);
|
||||
|
||||
//printf("%02x %02x\n", display.get_gpio(), display.get_gpio_hi());
|
||||
|
||||
++frames;
|
||||
display.set_gpio_hi_pull_up_all(frames & 0x3F);
|
||||
display.set_gpio_hi_pull_down_all(~(frames & 0x3F));
|
||||
if (gpio_get(BUTTON_A) == 0) display.set_led_level((uint8_t)frames);
|
||||
else display.set_led_heartbeat();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ add_library(pico_graphics
|
|||
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb888.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_inky7.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_dv_rgb555.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_dv_p5.cpp
|
||||
)
|
||||
|
||||
target_include_directories(pico_graphics INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
|
|
@ -202,7 +202,8 @@ namespace pimoroni {
|
|||
PEN_RGB565,
|
||||
PEN_RGB888,
|
||||
PEN_INKY7,
|
||||
PEN_DV_RGB555
|
||||
PEN_DV_RGB555,
|
||||
PEN_DV_P5
|
||||
};
|
||||
|
||||
void *frame_buffer;
|
||||
|
@ -538,6 +539,12 @@ namespace pimoroni {
|
|||
virtual void read_pixel_span(const Point &p, uint l, T *data) {};
|
||||
};
|
||||
|
||||
class IPaletteDisplayDriver {
|
||||
public:
|
||||
virtual void write_palette_pixel(const Point &p, uint8_t colour) = 0;
|
||||
virtual void write_palette_pixel_span(const Point &p, uint l, uint8_t colour) = 0;
|
||||
virtual void set_palette_colour(uint8_t entry, RGB888 colour) = 0;
|
||||
};
|
||||
|
||||
class PicoGraphics_PenInky7 : public PicoGraphics {
|
||||
public:
|
||||
|
@ -607,4 +614,37 @@ namespace pimoroni {
|
|||
return w * h * sizeof(RGB555);
|
||||
}
|
||||
};
|
||||
|
||||
class PicoGraphics_PenDV_P5 : public PicoGraphics {
|
||||
public:
|
||||
static const uint16_t palette_size = 32;
|
||||
uint8_t color;
|
||||
IPaletteDisplayDriver &driver;
|
||||
RGB palette[palette_size];
|
||||
bool used[palette_size];
|
||||
|
||||
std::array<std::array<uint8_t, 16>, 512> candidate_cache;
|
||||
bool cache_built = false;
|
||||
std::array<uint8_t, 16> candidates;
|
||||
|
||||
PicoGraphics_PenDV_P5(uint16_t width, uint16_t height, IPaletteDisplayDriver &dv_display);
|
||||
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;
|
||||
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
|
||||
int create_pen_hsv(float h, float s, float v) override;
|
||||
int reset_pen(uint8_t i) override;
|
||||
|
||||
int get_palette_size() override {return palette_size;};
|
||||
RGB* get_palette() override {return palette;};
|
||||
|
||||
void set_pixel(const Point &p) override;
|
||||
void set_pixel_span(const Point &p, uint l) override;
|
||||
void get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array<uint8_t, 16> &candidates);
|
||||
void set_pixel_dither(const Point &p, const RGB &c) override;
|
||||
|
||||
static size_t buffer_size(uint w, uint h) {
|
||||
return w * h;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
#include "pico_graphics.hpp"
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
PicoGraphics_PenDV_P5::PicoGraphics_PenDV_P5(uint16_t width, uint16_t height, IPaletteDisplayDriver &palette_display_driver)
|
||||
: PicoGraphics(width, height, nullptr),
|
||||
driver(palette_display_driver)
|
||||
{
|
||||
this->pen_type = PEN_DV_P5;
|
||||
for(auto i = 0u; i < palette_size; i++) {
|
||||
palette[i] = {
|
||||
uint8_t(i << 3),
|
||||
uint8_t(i << 3),
|
||||
uint8_t(i << 3)
|
||||
};
|
||||
driver.set_palette_colour(i, palette[i].to_rgb888());
|
||||
used[i] = false;
|
||||
}
|
||||
cache_built = false;
|
||||
}
|
||||
void PicoGraphics_PenDV_P5::set_pen(uint c) {
|
||||
color = c & 0x1f;
|
||||
}
|
||||
void PicoGraphics_PenDV_P5::set_pen(uint8_t r, uint8_t g, uint8_t b) {
|
||||
int pen = RGB(r, g, b).closest(palette, palette_size);
|
||||
if(pen != -1) color = pen;
|
||||
}
|
||||
int PicoGraphics_PenDV_P5::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {
|
||||
i &= 0x1f;
|
||||
used[i] = true;
|
||||
palette[i] = {r, g, b};
|
||||
cache_built = false;
|
||||
driver.set_palette_colour(i, palette[i].to_rgb888());
|
||||
return i;
|
||||
}
|
||||
int PicoGraphics_PenDV_P5::create_pen(uint8_t r, uint8_t g, uint8_t b) {
|
||||
// Create a colour and place it in the palette if there's space
|
||||
for(auto i = 0u; i < palette_size; i++) {
|
||||
if(!used[i]) {
|
||||
palette[i] = {r, g, b};
|
||||
used[i] = true;
|
||||
cache_built = false;
|
||||
driver.set_palette_colour(i, palette[i].to_rgb888());
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int PicoGraphics_PenDV_P5::create_pen_hsv(float h, float s, float v) {
|
||||
RGB p = RGB::from_hsv(h, s, v);
|
||||
return create_pen(p.r, p.g, p.b);
|
||||
}
|
||||
int PicoGraphics_PenDV_P5::reset_pen(uint8_t i) {
|
||||
palette[i] = {0, 0, 0};
|
||||
used[i] = false;
|
||||
cache_built = false;
|
||||
return i;
|
||||
}
|
||||
void PicoGraphics_PenDV_P5::set_pixel(const Point &p) {
|
||||
driver.write_palette_pixel(p, color << 2);
|
||||
}
|
||||
|
||||
void PicoGraphics_PenDV_P5::set_pixel_span(const Point &p, uint l) {
|
||||
driver.write_palette_pixel_span(p, l, color << 2);
|
||||
}
|
||||
|
||||
void PicoGraphics_PenDV_P5::get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array<uint8_t, 16> &candidates) {
|
||||
RGB error;
|
||||
for(size_t i = 0; i < candidates.size(); i++) {
|
||||
candidates[i] = (col + error).closest(palette, len);
|
||||
error += (col - palette[candidates[i]]);
|
||||
}
|
||||
|
||||
// sort by a rough approximation of luminance, this ensures that neighbouring
|
||||
// pixels in the dither matrix are at extreme opposites of luminence
|
||||
// giving a more balanced output
|
||||
std::sort(candidates.begin(), candidates.end(), [palette](int a, int b) {
|
||||
return palette[a].luminance() > palette[b].luminance();
|
||||
});
|
||||
}
|
||||
|
||||
void PicoGraphics_PenDV_P5::set_pixel_dither(const Point &p, const RGB &c) {
|
||||
if(!bounds.contains(p)) return;
|
||||
|
||||
uint used_palette_entries = 0;
|
||||
for(auto i = 0u; i < palette_size; i++) {
|
||||
if(!used[i]) break;
|
||||
used_palette_entries++;
|
||||
}
|
||||
|
||||
if(!cache_built) {
|
||||
for(uint i = 0; i < 512; i++) {
|
||||
RGB cache_col((i & 0x1C0) >> 1, (i & 0x38) << 2, (i & 0x7) << 5);
|
||||
get_dither_candidates(cache_col, palette, used_palette_entries, candidate_cache[i]);
|
||||
}
|
||||
cache_built = true;
|
||||
}
|
||||
|
||||
uint cache_key = ((c.r & 0xE0) << 1) | ((c.g & 0xE0) >> 2) | ((c.b & 0xE0) >> 5);
|
||||
//get_dither_candidates(c, palette, 256, candidates);
|
||||
|
||||
// find the pattern coordinate offset
|
||||
uint pattern_index = (p.x & 0b11) | ((p.y & 0b11) << 2);
|
||||
|
||||
// set the pixel
|
||||
//color = candidates[pattern[pattern_index]];
|
||||
color = candidate_cache[cache_key][dither16_pattern[pattern_index]];
|
||||
set_pixel(p);
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue