kopia lustrzana https://github.com/pimoroni/pimoroni-pico
PicoGraphics: Tidy up dithering.
rodzic
b9ca8ec779
commit
27d571b473
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
Ładowanie…
Reference in New Issue