Merge pull request #419 from pimoroni/patch-uc8159-3bpp

PicoGraphics/UC8159: 3bit bitplane pen mode.
pull/426/head v1.19.1
Philip Howard 2022-07-04 11:36:20 +01:00 zatwierdzone przez GitHub
commit 9c079bb1e5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
13 zmienionych plików z 207 dodań i 42 usunięć

Wyświetl plik

@ -104,6 +104,10 @@ namespace pimoroni {
command(0x50, {0x37});
}
void UC8159::set_blocking(bool blocking) {
this->blocking = blocking;
}
void UC8159::power_off() {
busy_wait();
command(POF); // turn off
@ -134,14 +138,28 @@ namespace pimoroni {
command(reg, values.size(), (uint8_t *)values.begin());
}
void UC8159::update(const void *data, bool blocking) {
void UC8159::update(PicoGraphics *graphics) {
if(graphics->pen_type != PicoGraphics::PEN_3BIT) return; // Incompatible buffer
if(blocking) {
busy_wait();
}
setup();
command(DTM1, (width * height) / 2, (uint8_t *)data); // transmit framebuffer
gpio_put(CS, 0);
uint8_t reg = DTM1;
gpio_put(DC, 0); // command mode
spi_write_blocking(spi, &reg, 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);
});
gpio_put(CS, 1);
busy_wait();
command(PON); // turn on
@ -159,9 +177,4 @@ namespace pimoroni {
}
}
void UC8159::update(PicoGraphics *graphics) {
if(graphics->pen_type != PicoGraphics::PEN_P4) return; // Incompatible buffer
update(graphics->frame_buffer, false);
}
}

Wyświetl plik

@ -16,12 +16,6 @@ namespace pimoroni {
// Variables
//--------------------------------------------------
private:
// highest possible resolution is 160x296 which at 1 bit per pixel
// requires 5920 bytes of frame buffer
//uint8_t frame_buffer[5920] = {0};
uint8_t *frame_buffer;
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;
// interface pins with our standard defaults where appropriate
@ -34,6 +28,8 @@ namespace pimoroni {
absolute_time_t timeout;
bool blocking = false;
public:
enum colour : uint8_t {
BLACK = 0,
@ -67,10 +63,11 @@ namespace pimoroni {
bool is_busy() override;
void update(PicoGraphics *graphics) override;
void set_blocking(bool blocking);
private:
void init();
void setup();
void update(const void *data, bool blocking = true);
void command(uint8_t reg, size_t len, const uint8_t *data);
void command(uint8_t reg, std::initializer_list<uint8_t> values);
void command(uint8_t reg, const uint8_t data) {command(reg, 0, &data);};

Wyświetl plik

@ -3,6 +3,7 @@ add_library(pico_graphics
${CMAKE_CURRENT_LIST_DIR}/pico_graphics.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_1bit.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_1bitY.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_3bit.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_p4.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_p8.cpp
${CMAKE_CURRENT_LIST_DIR}/pico_graphics_pen_rgb332.cpp

Wyświetl plik

@ -2,6 +2,8 @@
namespace pimoroni {
const uint8_t dither16_pattern[16] = {0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5};
int PicoGraphics::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {return -1;};
int PicoGraphics::reset_pen(uint8_t i) {return -1;};
int PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b) {return -1;};

Wyświetl plik

@ -144,10 +144,13 @@ namespace pimoroni {
0x00e4, 0x08e4, 0x10e4, 0x18e4, 0x00e5, 0x08e5, 0x10e5, 0x18e5, 0x00e6, 0x08e6, 0x10e6, 0x18e6, 0x00e7, 0x08e7, 0x10e7, 0x18e7,
};
extern const uint8_t dither16_pattern[16];
class PicoGraphics {
public:
enum PenType {
PEN_1BIT,
PEN_3BIT,
PEN_P2,
PEN_P4,
PEN_P8,
@ -275,15 +278,58 @@ namespace pimoroni {
}
};
class PicoGraphics_Pen3Bit : public PicoGraphics {
public:
static const uint16_t palette_size = 8;
uint8_t color;
RGB palette[8] = {
/*
{0x2b, 0x2a, 0x37},
{0xdc, 0xcb, 0xba},
{0x35, 0x56, 0x33},
{0x33, 0x31, 0x47},
{0x9c, 0x3b, 0x2e},
{0xd3, 0xa9, 0x34},
{0xab, 0x58, 0x37},
{0xb2, 0x8e, 0x67}
*/
{ 0, 0, 0}, // black
{255, 255, 255}, // white
{ 0, 255, 0}, // green
{ 0, 0, 255}, // blue
{255, 0, 0}, // red
{255, 255, 0}, // yellow
{255, 128, 0}, // orange
{220, 180, 200} // clean / taupe?!
};
std::array<std::array<uint8_t, 16>, 512> candidate_cache;
bool cache_built = false;
std::array<uint8_t, 16> candidates;
PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
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;
void scanline_convert(PenType type, conversion_callback_func callback) override;
static size_t buffer_size(uint w, uint h) {
return (w * h / 8) * 3;
}
};
class PicoGraphics_PenP4 : public PicoGraphics {
public:
static const uint palette_size = 16;
static const uint16_t palette_size = 16;
uint8_t color;
RGB palette[palette_size];
bool used[palette_size];
const uint pattern[16] = // dither pattern
{0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5};
std::array<std::array<uint8_t, 16>, 512> candidate_cache;
bool cache_built = false;
std::array<uint8_t, 16> candidates;
@ -308,13 +354,11 @@ namespace pimoroni {
class PicoGraphics_PenP8 : public PicoGraphics {
public:
static const uint palette_size = 256;
static const uint16_t palette_size = 256;
uint8_t color;
RGB palette[palette_size];
bool used[palette_size];
const uint pattern[16] = // dither pattern
{0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5};
std::array<std::array<uint8_t, 16>, 512> candidate_cache;
bool cache_built = false;
std::array<uint8_t, 16> candidates;

Wyświetl plik

@ -0,0 +1,114 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_Pen3Bit::PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
this->pen_type = PEN_3BIT;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
}
cache_built = false;
}
void PicoGraphics_Pen3Bit::set_pen(uint c) {
color = c & 0xf;
}
void PicoGraphics_Pen3Bit::set_pen(uint8_t r, uint8_t g, uint8_t b) {
}
void PicoGraphics_Pen3Bit::set_pixel(const Point &p) {
uint offset = (bounds.w * bounds.h) / 8;
uint8_t *buf = (uint8_t *)frame_buffer;
uint bo = 7 - (p.x & 0b111);
uint8_t *bufA = &buf[(p.x / 8) + (p.y * bounds.w / 8)];
uint8_t *bufB = bufA + offset;
uint8_t *bufC = bufA + offset + offset;
uint8_t cA = (color & 0b100) >> 2;
*bufA &= ~(1U << bo);
*bufA |= (cA << bo);
uint8_t cB = (color & 0b010) >> 1;
*bufB &= ~(1U << bo);
*bufB |= (cB << bo);
uint8_t cC = (color & 0b001);
*bufC &= ~(1U << bo);
*bufC |= (cC << bo);
}
void PicoGraphics_Pen3Bit::set_pixel_span(const Point &p, uint l) {
Point lp = p;
while(l--) {
set_pixel(lp);
lp.x++;
}
}
void PicoGraphics_Pen3Bit::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_Pen3Bit::set_pixel_dither(const Point &p, const RGB &c) {
if(!bounds.contains(p)) return;
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, palette_size, 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);
}
void PicoGraphics_Pen3Bit::scanline_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;
uint8_t *buf = (uint8_t *)frame_buffer;
for(auto y = 0; y < bounds.h; y++) {
for(auto x = 0; x < bounds.w; x++) {
uint bo = 7 - (x & 0b111);
uint8_t *bufA = &buf[(x / 8) + (y * bounds.w / 8)];
uint8_t *bufB = bufA + offset;
uint8_t *bufC = bufA + offset + offset;
uint8_t nibble = (*bufA >> bo) & 1U;
nibble <<= 1;
nibble |= (*bufB >> bo) & 1U;
nibble <<= 1;
nibble |= (*bufC >> bo) & 1U;
nibble <<= (x & 0b1) ? 0 : 4;
row_buf[x / 2] &= (x & 0b1) ? 0b11110000 : 0b00001111;
row_buf[x / 2] |= nibble;
}
callback(row_buf, bounds.w / 2);
}
}
}
}

Wyświetl plik

@ -121,7 +121,7 @@ namespace pimoroni {
// set the pixel
//color = candidates[pattern[pattern_index]];
color = candidate_cache[cache_key][pattern[pattern_index]];
color = candidate_cache[cache_key][dither16_pattern[pattern_index]];
set_pixel(p);
}
void PicoGraphics_PenP4::scanline_convert(PenType type, conversion_callback_func callback) {

Wyświetl plik

@ -94,7 +94,7 @@ namespace pimoroni {
// set the pixel
//color = candidates[pattern[pattern_index]];
color = candidate_cache[cache_key][pattern[pattern_index]];
color = candidate_cache[cache_key][dither16_pattern[pattern_index]];
set_pixel(p);
}

Wyświetl plik

@ -33,14 +33,7 @@ namespace pimoroni {
}
void PicoGraphics_PenRGB332::set_pixel_dither(const Point &p, const RGB &c) {
if(!bounds.contains(p)) return;
static uint8_t _odm[16] = {
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5
};
uint8_t _dmv = _odm[(p.x & 0b11) | ((p.y & 0b11) << 2)];
uint8_t _dmv = dither16_pattern[(p.x & 0b11) | ((p.y & 0b11) << 2)];
uint8_t red = c.r & 0b11000000; // Two bits red
uint8_t red_r = c.r & 0b111111; // Remaining six bits red
@ -64,14 +57,8 @@ namespace pimoroni {
void PicoGraphics_PenRGB332::set_pixel_dither(const Point &p, const RGB565 &c) {
if(!bounds.contains(p)) return;
RGB565 cs = __builtin_bswap16(c);
static uint8_t _odm[16] = {
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5
};
uint8_t _dmv = _odm[(p.x & 0b11) | ((p.y & 0b11) << 2)];
uint8_t _dmv = dither16_pattern[(p.x & 0b11) | ((p.y & 0b11) << 2)];
// RRRRRGGGGGGBBBBB
uint8_t red = (cs & 0b1100000000000000) >> 8; // Two bits grn

Wyświetl plik

@ -118,7 +118,7 @@ MICROPY_EVENT_POLL_HOOK
//current_graphics->pixel({pDraw->x + x, pDraw->y + y});
// TODO make dither optional
current_graphics->set_pixel_dither({pDraw->x + x, pDraw->y + y}, (RGB565)(pDraw->pPixels[i]));
} else if (current_graphics->pen_type == PicoGraphics::PEN_P8 || current_graphics->pen_type == PicoGraphics::PEN_P4) {
} else if (current_graphics->pen_type == PicoGraphics::PEN_P8 || current_graphics->pen_type == PicoGraphics::PEN_P4 || current_graphics->pen_type == PicoGraphics::PEN_3BIT) {
current_graphics->set_pixel_dither({pDraw->x + x, pDraw->y + y}, RGB((RGB565)pDraw->pPixels[i]));
} else {
current_graphics->set_pen(pDraw->pPixels[i]);
@ -229,6 +229,7 @@ mp_obj_t _JPEG_decode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args
case PicoGraphics::PEN_RGB565:
case PicoGraphics::PEN_P8:
case PicoGraphics::PEN_P4:
case PicoGraphics::PEN_3BIT:
self->jpeg->setPixelType(RGB565_BIG_ENDIAN);
break;
// TODO 2-bit is currently unsupported

Wyświetl plik

@ -13,6 +13,7 @@ target_sources(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_1bit.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_1bitY.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_3bit.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_p4.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_p8.cpp
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/pico_graphics_pen_rgb332.cpp

Wyświetl plik

@ -115,6 +115,8 @@ size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint h
switch(pen_type) {
case PEN_1BIT:
return PicoGraphics_Pen1Bit::buffer_size(width, height);
case PEN_3BIT:
return PicoGraphics_Pen3Bit::buffer_size(width, height);
case PEN_P4:
return PicoGraphics_PenP4::buffer_size(width, height);
case PEN_P8:
@ -195,12 +197,11 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
// Try to create an appropriate display driver
if (display == DISPLAY_INKY_FRAME) {
pen_type = PEN_P4; // FORCE to P4 since it's the only supported mode
pen_type = PEN_3BIT; // FORCE to 3BIT
// TODO grab BUSY and RESET from ARG_extra_pins
self->display = m_new_class(UC8159, width, height, spi_bus);
}
else if (display == DISPLAY_TUFTY_2040) {
} else if (display == DISPLAY_TUFTY_2040) {
self->display = m_new_class(ST7789, width, height, (Rotation)rotate, parallel_bus);
} else if (display == DISPLAY_LCD_160X80) {
@ -244,6 +245,9 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
self->graphics = m_new_class(PicoGraphics_Pen1Bit, self->display->width, self->display->height, self->buffer);
}
break;
case PEN_3BIT:
self->graphics = m_new_class(PicoGraphics_Pen3Bit, self->display->width, self->display->height, self->buffer);
break;
case PEN_P4:
self->graphics = m_new_class(PicoGraphics_PenP4, self->display->width, self->display->height, self->buffer);
break;

Wyświetl plik

@ -17,6 +17,7 @@ enum PicoGraphicsDisplay {
enum PicoGraphicsPenType {
PEN_1BIT = 0,
PEN_3BIT,
PEN_P2,
PEN_P4,
PEN_P8,