PicoGraphics: Tidy up dithering.

driver/sh1107
Phil Howard 2022-06-14 15:06:44 +01:00
rodzic b9ca8ec779
commit 27d571b473
6 zmienionych plików z 134 dodań i 50 usunięć

Wyświetl plik

@ -53,22 +53,7 @@ namespace pimoroni {
void PicoGraphics::remove_clip() {
clip = bounds;
}
void PicoGraphics::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::clear() {
rectangle(clip);
}

Wyświetl plik

@ -208,8 +208,6 @@ namespace pimoroni {
void set_clip(const Rect &r);
void remove_clip();
void get_dither_candidates(const RGB &col, const RGB *palette, size_t len, std::array<uint8_t, 16> &candidates);
void clear();
void pixel(const Point &p);
void pixel_span(const Point &p, int32_t l);
@ -226,15 +224,28 @@ namespace pimoroni {
class PicoGraphics_PenP4 : public PicoGraphics {
public:
static const uint palette_size = 16;
uint8_t color;
RGB palette[16];
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;
PicoGraphics_PenP4(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;
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 reset_pen(uint8_t i) override;
void set_pixel(const Point &p) 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 / 2;
@ -243,16 +254,28 @@ namespace pimoroni {
class PicoGraphics_PenP8 : public PicoGraphics {
public:
static const uint palette_size = 256;
uint8_t color;
RGB palette[256];
bool used[256];
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;
PicoGraphics_PenP8(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;
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 reset_pen(uint8_t i) override;
void set_pixel(const Point &p) 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;
@ -267,11 +290,14 @@ namespace pimoroni {
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
void set_pixel(const Point &p) override;
void set_pixel_dither(const Point &p, const RGB &c) override;
void set_pixel_dither(const Point &p, const RGB565 &c) override;
void scanline_convert(PenType type, conversion_callback_func callback) override;
void sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) override;
void scanline_convert(PenType type, conversion_callback_func callback) override;
static size_t buffer_size(uint w, uint h) {
return w * h;
}

Wyświetl plik

@ -7,19 +7,20 @@ namespace pimoroni {
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
}
for(auto i = 0u; i < 16; i++) {
for(auto i = 0u; i < palette_size; i++) {
palette[i] = {
uint8_t(i << 4),
uint8_t(i << 4),
uint8_t(i << 4)
};
used[i] = false;
}
}
void PicoGraphics_PenP4::set_pen(uint c) {
color = c & 0xf;
}
void PicoGraphics_PenP4::set_pen(uint8_t r, uint8_t g, uint8_t b) {
int pen = RGB(r, g, b).closest(palette, 16);
int pen = RGB(r, g, b).closest(palette, palette_size);
if(pen != -1) color = pen;
}
int PicoGraphics_PenP4::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {
@ -27,6 +28,22 @@ namespace pimoroni {
palette[i] = {r, g, b};
return i;
}
int PicoGraphics_PenP4::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;
return i;
}
}
return -1;
}
int PicoGraphics_PenP4::reset_pen(uint8_t i) {
palette[i] = {0, 0, 0};
used[i] = false;
return i;
}
void PicoGraphics_PenP4::set_pixel(const Point &p) {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
@ -39,26 +56,49 @@ namespace pimoroni {
*f &= m; // clear bits
*f |= b; // set value
}
void PicoGraphics_PenP4::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_PenP4::set_pixel_dither(const Point &p, const RGB &c) {
if(!bounds.contains(p)) return;
static uint pattern[16] = // dither pattern
{0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5};
static std::array<uint8_t, 16> candidates;
get_dither_candidates(c, palette, 256, candidates);
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 = candidates[pattern[pattern_index]];
color = candidate_cache[cache_key][pattern[pattern_index]];
set_pixel(p);
}
void PicoGraphics_PenP4::scanline_convert(PenType type, conversion_callback_func callback) {
if(type == PEN_RGB565) {
// Cache the RGB888 palette as RGB565
RGB565 cache[16];
for(auto i = 0u; i < 16; i++) {
RGB565 cache[palette_size];
for(auto i = 0u; i < palette_size; i++) {
cache[i] = palette[i].to_rgb565();
}

Wyświetl plik

@ -7,8 +7,8 @@ namespace pimoroni {
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
}
for(auto i = 0u; i < 256; i++) {
palette[i] = {0, 0, 0};
for(auto i = 0u; i < palette_size; i++) {
palette[i] = {uint8_t(i), uint8_t(i), uint8_t(i)};
used[i] = false;
}
}
@ -26,7 +26,7 @@ namespace pimoroni {
}
int PicoGraphics_PenP8::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 < 256u; i++) {
for(auto i = 0u; i < palette_size; i++) {
if(!used[i]) {
palette[i] = {r, g, b};
used[i] = true;
@ -44,11 +44,50 @@ namespace pimoroni {
uint8_t *buf = (uint8_t *)frame_buffer;
buf[p.y * bounds.w + p.x] = color;
}
void PicoGraphics_PenP8::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_PenP8::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][pattern[pattern_index]];
set_pixel(p);
}
void PicoGraphics_PenP8::scanline_convert(PenType type, conversion_callback_func callback) {
if(type == PEN_RGB565) {
// Cache the RGB888 palette as RGB565
RGB565 cache[256];
for(auto i = 0u; i < 256; i++) {
RGB565 cache[palette_size];
for(auto i = 0u; i < palette_size; i++) {
cache[i] = palette[i].to_rgb565();
}

Wyświetl plik

@ -32,6 +32,11 @@ const mp_obj_type_t JPEG_type = {
STATIC const mp_map_elem_t JPEG_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_jpegdec) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_JPEG), (mp_obj_t)&JPEG_type },
{ MP_ROM_QSTR(MP_QSTR_JPEG_SCALE_FULL), MP_ROM_INT(0) },
{ MP_ROM_QSTR(MP_QSTR_JPEG_SCALE_HALF), MP_ROM_INT(2) },
{ MP_ROM_QSTR(MP_QSTR_JPEG_SCALE_QUARTER), MP_ROM_INT(4) },
{ MP_ROM_QSTR(MP_QSTR_JPEG_SCALE_EIGHTH), MP_ROM_INT(8) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_JPEG_globals, JPEG_globals_table);

Wyświetl plik

@ -47,15 +47,6 @@ MICROPY_EVENT_POLL_HOOK
current_graphics->pixel({pDraw->x + x, pDraw->y + y});
}
}
} else if(pDraw->iBpp == 8) {
uint8_t *pixels = (uint8_t *)pDraw->pPixels;
for(int y = 0; y < pDraw->iHeight; y++) {
for(int x = 0; x < pDraw->iWidth; x++) {
int i = y * pDraw->iWidth + x;
current_graphics->set_pen(pixels[i] >> (current_graphics->pen_type == PicoGraphics::PEN_P4 ? 4 : 0));
current_graphics->pixel({pDraw->x + x, pDraw->y + y});
}
}
} else {
for(int y = 0; y < pDraw->iHeight; y++) {
for(int x = 0; x < pDraw->iWidth; x++) {
@ -65,6 +56,8 @@ 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) {
current_graphics->set_pixel_dither({pDraw->x + x, pDraw->y + y}, RGB((RGB565)pDraw->pPixels[i]));
} else {
current_graphics->set_pen(pDraw->pPixels[i]);
current_graphics->pixel({pDraw->x + x, pDraw->y + y});
@ -107,13 +100,9 @@ static int _open(_JPEG_obj_t *self, void *buf, size_t len) {
switch(self->graphics->graphics->pen_type) {
case PicoGraphics::PEN_RGB332:
case PicoGraphics::PEN_RGB565:
self->jpeg->setPixelType(RGB565_BIG_ENDIAN);
break;
case PicoGraphics::PEN_P8:
self->jpeg->setPixelType(EIGHT_BIT_GRAYSCALE);
break;
case PicoGraphics::PEN_P4:
self->jpeg->setPixelType(FOUR_BIT_DITHERED);
self->jpeg->setPixelType(RGB565_BIG_ENDIAN);
break;
// TODO 2-bit is currently unsupported
case PicoGraphics::PEN_P2: