From 225e22b2877c7731b7428db0e28d3ad7e6751e35 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Sun, 28 Aug 2016 17:44:36 +0200 Subject: [PATCH] extmod/modframebuf: Make FrameBuffer handle 16bit depth. Rename FrameBuffer1 into FrameBuffer and make it handle different bit depths via a method table that has getpixel and setpixel. Currently supported formats are MVLSB (monochrome, vertical, LSB) and RGB565. Also add blit() and fill_rect() methods. --- extmod/modframebuf.c | 286 +++++++++++++++++++++++---------- tests/extmod/framebuf1.py | 2 +- tests/extmod/framebuf16.py | 59 +++++++ tests/extmod/framebuf16.py.exp | 57 +++++++ 4 files changed, 314 insertions(+), 90 deletions(-) create mode 100644 tests/extmod/framebuf16.py create mode 100644 tests/extmod/framebuf16.py.exp diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index cd7f1c5e4b..2ca5f2df3e 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -35,17 +35,64 @@ #include "stmhal/font_petme128_8x8.h" -// 1-bit frame buffer, each byte is a column of 8 pixels -typedef struct _mp_obj_framebuf1_t { +typedef struct _mp_obj_framebuf_t { mp_obj_base_t base; - uint8_t *buf; + void *buf; uint16_t width, height, stride; -} mp_obj_framebuf1_t; + uint8_t format; +} mp_obj_framebuf_t; -STATIC mp_obj_t framebuf1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 3, 4, false); +typedef void (*setpixel_t)(const mp_obj_framebuf_t*, int, int, uint32_t); +typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t*, int, int); - mp_obj_framebuf1_t *o = m_new_obj(mp_obj_framebuf1_t); +typedef struct _mp_framebuf_p_t { + setpixel_t setpixel; + getpixel_t getpixel; +} mp_framebuf_p_t; + +// Functions for MVLSB format + +STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) { + size_t index = (y >> 3) * fb->stride + x; + uint8_t offset = y & 0x07; + ((uint8_t*)fb->buf)[index] = (((uint8_t*)fb->buf)[index] & ~(0x01 << offset)) | ((color != 0) << offset); +} + +STATIC uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { + return (((uint8_t*)fb->buf)[(y >> 3) * fb->stride + x] >> (y & 0x07)) & 0x01; +} + +// Functions for RGB565 format + +STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) { + ((uint16_t*)fb->buf)[x + y * fb->stride] = color; +} + +STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { + return ((uint16_t*)fb->buf)[x + y * fb->stride]; +} + +// constants for formats +#define FRAMEBUF_MVLSB (0) +#define FRAMEBUF_RGB565 (1) + +STATIC mp_framebuf_p_t formats[] = { + [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel}, + [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel}, +}; + +static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) { + formats[fb->format].setpixel(fb, x, y, color); +} + +static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, int x, int y) { + return formats[fb->format].getpixel(fb, x, y); +} + +STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 4, 5, false); + + mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t); o->base.type = type; mp_buffer_info_t bufinfo; @@ -54,98 +101,163 @@ STATIC mp_obj_t framebuf1_make_new(const mp_obj_type_t *type, size_t n_args, siz o->width = mp_obj_get_int(args[1]); o->height = mp_obj_get_int(args[2]); - o->stride = o->width; - if (n_args >= 4) { - o->stride = mp_obj_get_int(args[3]); + o->format = mp_obj_get_int(args[3]); + if (n_args >= 5) { + o->stride = mp_obj_get_int(args[4]); + } else { + o->stride = o->width; + } + + switch (o->format) { + case FRAMEBUF_MVLSB: + case FRAMEBUF_RGB565: + break; + default: + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "invalid format")); } return MP_OBJ_FROM_PTR(o); } -STATIC mp_obj_t framebuf1_fill(mp_obj_t self_in, mp_obj_t col_in) { - mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(self_in); +STATIC mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) { + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t col = mp_obj_get_int(col_in); - if (col) { - col = 0xff; - } - int end = (self->height + 7) >> 3; - for (int y = 0; y < end; ++y) { - memset(self->buf + y * self->stride, col, self->width); + for (int y = 0; y < self->height; ++y) { + for (int x = 0; x < self->width; ++x) { + setpixel(self, x, y, col); + } } return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf1_fill_obj, framebuf1_fill); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill); -STATIC mp_obj_t framebuf1_pixel(size_t n_args, const mp_obj_t *args) { - mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(args[0]); +STATIC mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args) { + (void)n_args; + + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); + mp_int_t x = mp_obj_get_int(args[1]); + mp_int_t y = mp_obj_get_int(args[2]); + mp_int_t width = mp_obj_get_int(args[3]); + mp_int_t height = mp_obj_get_int(args[4]); + mp_int_t color = mp_obj_get_int(args[5]); + + if (x + width <= 0 || y + height <= 0 || y >= self->height || x >= self->width) { + // No operation needed. + return mp_const_none; + } + + // clip to the framebuffer + int xend = MIN(self->width, x + width); + int yend = MIN(self->height, y + height); + x = MAX(MIN(x, self->width), 0); + y = MAX(MIN(y, self->height), 0); + + for (; y < yend; ++y) { + for (int xc = x; xc < xend; ++xc) { + setpixel(self, xc, y, color); + } + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect); + +STATIC mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args) { + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); mp_int_t x = mp_obj_get_int(args[1]); mp_int_t y = mp_obj_get_int(args[2]); if (0 <= x && x < self->width && 0 <= y && y < self->height) { - int index = (y / 8) * self->stride + x; if (n_args == 3) { // get - return MP_OBJ_NEW_SMALL_INT((self->buf[index] >> (y & 7)) & 1); + return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y)); } else { // set - if (mp_obj_get_int(args[3])) { - self->buf[index] |= (1 << (y & 7)); - } else { - self->buf[index] &= ~(1 << (y & 7)); - } + setpixel(self, x, y, mp_obj_get_int(args[3])); } } return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf1_pixel_obj, 3, 4, framebuf1_pixel); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel); -STATIC mp_obj_t framebuf1_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) { - mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(self_in); +STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args) { + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); + mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(args[1]); + mp_int_t x = mp_obj_get_int(args[2]); + mp_int_t y = mp_obj_get_int(args[3]); + mp_int_t key = -1; + if (n_args > 4) { + key = mp_obj_get_int(args[4]); + } + + if ( + (x >= self->width) || + (y >= self->height) || + (-x >= source->width) || + (-y >= source->height) + ) { + // Out of bounds, no-op. + return mp_const_none; + } + + // Clip. + int x0 = MAX(0, x); + int y0 = MAX(0, y); + int x1 = MAX(0, -x); + int y1 = MAX(0, -y); + int x0end = MIN(self->width, x + source->width); + int y0end = MIN(self->height, y + source->height); + uint32_t color; + + for (; y0 < y0end; ++y0) { + int cx1 = x1; + for (int cx0 = x0; cx0 < x0end; ++cx0) { + color = getpixel(source, cx1, y1); + if (color != key) { + setpixel(self, cx0, y0, color); + } + ++cx1; + } + ++y1; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 5, framebuf_blit); + +STATIC mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) { + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); mp_int_t xstep = mp_obj_get_int(xstep_in); mp_int_t ystep = mp_obj_get_int(ystep_in); - int end = (self->height + 7) >> 3; - if (ystep > 0) { - for (int y = end; y > 0;) { - --y; - for (int x = 0; x < self->width; ++x) { - int prev = 0; - if (y > 0) { - prev = (self->buf[(y - 1) * self->stride + x] >> (8 - ystep)) & ((1 << ystep) - 1); - } - self->buf[y * self->stride + x] = (self->buf[y * self->stride + x] << ystep) | prev; - } - } - } else if (ystep < 0) { - for (int y = 0; y < end; ++y) { - for (int x = 0; x < self->width; ++x) { - int prev = 0; - if (y + 1 < end) { - prev = self->buf[(y + 1) * self->stride + x] << (8 + ystep); - } - self->buf[y * self->stride + x] = (self->buf[y * self->stride + x] >> -ystep) | prev; - } - } - } + int sx, y, xend, yend, dx, dy; if (xstep < 0) { - for (int y = 0; y < end; ++y) { - for (int x = 0; x < self->width + xstep; ++x) { - self->buf[y * self->stride + x] = self->buf[y * self->stride + x - xstep]; - } - } - } else if (xstep > 0) { - for (int y = 0; y < end; ++y) { - for (int x = self->width - 1; x >= xstep; --x) { - self->buf[y * self->stride + x] = self->buf[y * self->stride + x - xstep]; - } + sx = 0; + xend = self->width + xstep; + dx = 1; + } else { + sx = self->width - 1; + xend = xstep - 1; + dx = -1; + } + if (ystep < 0) { + y = 0; + yend = self->height + ystep; + dy = 1; + } else { + y = self->height - 1; + yend = ystep - 1; + dy = -1; + } + for (; y != yend; y += dy) { + for (int x = sx; x != xend; x += dx) { + setpixel(self, x, y, getpixel(self, x - xstep, y - ystep)); } } - // TODO: Should we clear the margin created by scrolling? return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf1_scroll_obj, framebuf1_scroll); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf_scroll_obj, framebuf_scroll); -STATIC mp_obj_t framebuf1_text(size_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args) { // extract arguments - mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(args[0]); + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]); const char *str = mp_obj_str_get_str(args[1]); mp_int_t x0 = mp_obj_get_int(args[2]); mp_int_t y0 = mp_obj_get_int(args[3]); @@ -170,43 +282,39 @@ STATIC mp_obj_t framebuf1_text(size_t n_args, const mp_obj_t *args) { for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column if (vline_data & 1) { // only draw if pixel set if (0 <= y && y < self->height) { // clip y - uint byte_pos = x0 + self->stride * ((uint)y >> 3); - if (col == 0) { - // clear pixel - self->buf[byte_pos] &= ~(1 << (y & 7)); - } else { - // set pixel - self->buf[byte_pos] |= 1 << (y & 7); - } + setpixel(self, x0, y, col); } } } } } } - return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf1_text_obj, 4, 5, framebuf1_text); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text); -STATIC const mp_rom_map_elem_t framebuf1_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf1_fill_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf1_pixel_obj) }, - { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf1_scroll_obj) }, - { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf1_text_obj) }, +STATIC const mp_rom_map_elem_t framebuf_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf_fill_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&framebuf_fill_rect_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf_pixel_obj) }, + { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) }, + { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) }, + { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) }, }; -STATIC MP_DEFINE_CONST_DICT(framebuf1_locals_dict, framebuf1_locals_dict_table); +STATIC MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table); -STATIC const mp_obj_type_t mp_type_framebuf1 = { +STATIC const mp_obj_type_t mp_type_framebuf = { { &mp_type_type }, - .name = MP_QSTR_FrameBuffer1, - .make_new = framebuf1_make_new, - .locals_dict = (mp_obj_t)&framebuf1_locals_dict, + .name = MP_QSTR_FrameBuffer, + .make_new = framebuf_make_new, + .locals_dict = (mp_obj_t)&framebuf_locals_dict, }; STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebuf) }, - { MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&mp_type_framebuf1) }, + { MP_ROM_QSTR(MP_QSTR_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) }, + { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB) }, + { MP_ROM_QSTR(MP_QSTR_RGB565), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565) }, }; STATIC MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table); diff --git a/tests/extmod/framebuf1.py b/tests/extmod/framebuf1.py index 52899028c7..836b1a9de1 100644 --- a/tests/extmod/framebuf1.py +++ b/tests/extmod/framebuf1.py @@ -8,7 +8,7 @@ except ImportError: w = 5 h = 16 buf = bytearray(w * h // 8) -fbuf = framebuf.FrameBuffer1(buf, w, h, w) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.MVLSB) # fill fbuf.fill(1) diff --git a/tests/extmod/framebuf16.py b/tests/extmod/framebuf16.py new file mode 100644 index 0000000000..754b28fdf1 --- /dev/null +++ b/tests/extmod/framebuf16.py @@ -0,0 +1,59 @@ +try: + import framebuf +except ImportError: + print("SKIP") + import sys + sys.exit() + +def printbuf(): + print("--8<--") + for y in range(h): + print(buf[y * w * 2:(y + 1) * w * 2]) + print("-->8--") + +w = 4 +h = 5 +buf = bytearray(w * h * 2) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.RGB565) + +# fill +fbuf.fill(0xffff) +printbuf() +fbuf.fill(0x0000) +printbuf() + +# put pixel +fbuf.pixel(0, 0, 0xeeee) +fbuf.pixel(3, 0, 0xee00) +fbuf.pixel(0, 4, 0x00ee) +fbuf.pixel(3, 4, 0x0ee0) +printbuf() + +# get pixel +print(fbuf.pixel(0, 4), fbuf.pixel(1, 1)) + +# scroll +fbuf.fill(0x0000) +fbuf.pixel(2, 2, 0xffff) +printbuf() +fbuf.scroll(0, 1) +printbuf() +fbuf.scroll(1, 0) +printbuf() +fbuf.scroll(-1, -2) +printbuf() + +w2 = 2 +h2 = 3 +buf2 = bytearray(w2 * h2 * 2) +fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.RGB565) + +fbuf2.fill(0x0000) +fbuf2.pixel(0, 0, 0x0ee0) +fbuf2.pixel(0, 2, 0xee00) +fbuf2.pixel(1, 0, 0x00ee) +fbuf2.pixel(1, 2, 0xe00e) +fbuf.fill(0xffff) +fbuf.blit(fbuf2, 3, 3, 0x0000) +fbuf.blit(fbuf2, -1, -1, 0x0000) +printbuf() diff --git a/tests/extmod/framebuf16.py.exp b/tests/extmod/framebuf16.py.exp new file mode 100644 index 0000000000..c41dc19d07 --- /dev/null +++ b/tests/extmod/framebuf16.py.exp @@ -0,0 +1,57 @@ +--8<-- +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\xee\xee\x00\x00\x00\x00\x00\xee') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\xee\x00\x00\x00\x00\x00\xe0\x0e') +-->8-- +238 0 +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\xff\xff\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +bytearray(b'\x00\x00\x00\x00\x00\x00\xff\xff') +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') +-->8-- +--8<-- +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\x0e\xe0\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +bytearray(b'\xff\xff\xff\xff\xff\xff\xe0\x0e') +bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff') +-->8--