kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Merge pull request #419 from pimoroni/patch-uc8159-3bpp
PicoGraphics/UC8159: 3bit bitplane pen mode.pull/426/head v1.19.1
commit
9c079bb1e5
|
@ -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, ®, 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -17,6 +17,7 @@ enum PicoGraphicsDisplay {
|
|||
|
||||
enum PicoGraphicsPenType {
|
||||
PEN_1BIT = 0,
|
||||
PEN_3BIT,
|
||||
PEN_P2,
|
||||
PEN_P4,
|
||||
PEN_P8,
|
||||
|
|
Ładowanie…
Reference in New Issue