From 225e22b2877c7731b7428db0e28d3ad7e6751e35 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Sun, 28 Aug 2016 17:44:36 +0200 Subject: 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 | 282 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 195 insertions(+), 87 deletions(-) (limited to 'extmod/modframebuf.c') diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index cd7f1c5e4..2ca5f2df3 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; + for (int y = 0; y < self->height; ++y) { + for (int x = 0; x < self->width; ++x) { + setpixel(self, x, y, col); + } } - int end = (self->height + 7) >> 3; - for (int y = 0; y < end; ++y) { - memset(self->buf + y * self->stride, col, self->width); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill); + +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_2(framebuf1_fill_obj, framebuf1_fill); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect); -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_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); - 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; +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 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); -- cgit v1.2.3 From 81e171b7bb38cff377ca389cd6fb179aea325998 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Dec 2016 15:51:16 +1100 Subject: extmod/modframebuf: Add back legacy FrameBuffer1 "class". For backwards compatibility. It simple creates a frame buffer with the MVLSB format. --- extmod/modframebuf.c | 23 +++++++++++++++++++++++ tests/extmod/framebuf1.py | 4 ++++ 2 files changed, 27 insertions(+) (limited to 'extmod/modframebuf.c') diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 2ca5f2df3..e1bfe8310 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -310,9 +310,32 @@ STATIC const mp_obj_type_t mp_type_framebuf = { .locals_dict = (mp_obj_t)&framebuf_locals_dict, }; +// this factory function is provided for backwards compatibility with old FrameBuffer1 class +STATIC mp_obj_t legacy_framebuffer1(size_t n_args, const mp_obj_t *args) { + mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t); + o->base.type = &mp_type_framebuf; + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE); + o->buf = bufinfo.buf; + + o->width = mp_obj_get_int(args[1]); + o->height = mp_obj_get_int(args[2]); + o->format = FRAMEBUF_MVLSB; + if (n_args >= 4) { + o->stride = mp_obj_get_int(args[3]); + } else { + o->stride = o->width; + } + + return MP_OBJ_FROM_PTR(o); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(legacy_framebuffer1_obj, 3, 4, legacy_framebuffer1); + 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_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) }, + { MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&legacy_framebuffer1_obj) }, { 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) }, }; diff --git a/tests/extmod/framebuf1.py b/tests/extmod/framebuf1.py index 836b1a9de..7faad7181 100644 --- a/tests/extmod/framebuf1.py +++ b/tests/extmod/framebuf1.py @@ -54,3 +54,7 @@ print(buf) # char out of font range set to chr(127) fbuf.text(str(chr(31)), 0, 0) print(buf) + +# test legacy constructor +fbuf = framebuf.FrameBuffer1(buf, w, h) +fbuf = framebuf.FrameBuffer1(buf, w, h, w) -- cgit v1.2.3 From a081b49d55ef409d612666d79a97e79259c8adba Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Dec 2016 16:51:31 +1100 Subject: extmod/modframebuf: Optimise fill and fill_rect methods. Fill is a very common operation (eg to clear the screen) and it is worth optimising it, by providing a specialised fill_rect function for each framebuffer format. This patch improved the speed of fill by 10 times for a 16-bit display with 160*128 pixels. --- extmod/modframebuf.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) (limited to 'extmod/modframebuf.c') diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index e1bfe8310..840f331f3 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -44,10 +44,12 @@ typedef struct _mp_obj_framebuf_t { 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); +typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, int, int, int, int, uint32_t); typedef struct _mp_framebuf_p_t { setpixel_t setpixel; getpixel_t getpixel; + fill_rect_t fill_rect; } mp_framebuf_p_t; // Functions for MVLSB format @@ -62,6 +64,18 @@ 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; } +STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { + while (h--) { + uint8_t *b = &((uint8_t*)fb->buf)[(y >> 3) * fb->stride + x]; + uint8_t offset = y & 0x07; + for (int ww = w; ww; --ww) { + *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset); + ++b; + } + ++y; + } +} + // Functions for RGB565 format STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) { @@ -72,13 +86,23 @@ STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { return ((uint16_t*)fb->buf)[x + y * fb->stride]; } +STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t colour) { + uint16_t *b = &((uint16_t*)fb->buf)[x + y * fb->stride]; + while (h--) { + for (int ww = w; ww; --ww) { + *b++ = colour; + } + b += fb->stride - w; + } +} + // 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}, + [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, + [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, }; static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) { @@ -123,11 +147,7 @@ STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size 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); - for (int y = 0; y < self->height; ++y) { - for (int x = 0; x < self->width; ++x) { - setpixel(self, x, y, col); - } - } + formats[self->format].fill_rect(self, 0, 0, self->width, self->height, col); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill); @@ -153,11 +173,8 @@ STATIC mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args) { 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); - } - } + formats[self->format].fill_rect(self, x, y, xend - x, yend - y, color); + return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect); -- cgit v1.2.3 From 374ed317c587dfc5ffcd9bdbe21aaa39d81c246b Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 5 Dec 2016 11:52:51 +1100 Subject: extmod/modframebuf: Add hline, vline, rect and line methods. These are basic drawing primitives. They work in a generic way on all framebuf formats by calling the underlying setpixel or fill_rect C-level primitives. --- extmod/modframebuf.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 129 insertions(+), 12 deletions(-) (limited to 'extmod/modframebuf.c') diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 840f331f3..b055a8938 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -113,6 +113,21 @@ static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, int x, int y) { return formats[fb->format].getpixel(fb, x, y); } +STATIC void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { + if (x + w <= 0 || y + h <= 0 || y >= fb->height || x >= fb->width) { + // No operation needed. + return; + } + + // clip to the framebuffer + int xend = MIN(fb->width, x + w); + int yend = MIN(fb->height, y + h); + x = MAX(x, 0); + y = MAX(y, 0); + + formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col); +} + 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); @@ -162,18 +177,7 @@ STATIC mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args) { 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); - - formats[self->format].fill_rect(self, x, y, xend - x, yend - y, color); + fill_rect(self, x, y, width, height, color); return mp_const_none; } @@ -196,6 +200,115 @@ STATIC mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel); +STATIC mp_obj_t framebuf_hline(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 w = mp_obj_get_int(args[3]); + mp_int_t col = mp_obj_get_int(args[4]); + + fill_rect(self, x, y, w, 1, col); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_hline_obj, 5, 5, framebuf_hline); + +STATIC mp_obj_t framebuf_vline(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 h = mp_obj_get_int(args[3]); + mp_int_t col = mp_obj_get_int(args[4]); + + fill_rect(self, x, y, 1, h, col); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_vline_obj, 5, 5, framebuf_vline); + +STATIC mp_obj_t framebuf_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 w = mp_obj_get_int(args[3]); + mp_int_t h = mp_obj_get_int(args[4]); + mp_int_t col = mp_obj_get_int(args[5]); + + fill_rect(self, x, y, w, 1, col); + fill_rect(self, x, y + h- 1, w, 1, col); + fill_rect(self, x, y, 1, h, col); + fill_rect(self, x + w- 1, y, 1, h, col); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_rect_obj, 6, 6, framebuf_rect); + +STATIC mp_obj_t framebuf_line(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 x1 = mp_obj_get_int(args[1]); + mp_int_t y1 = mp_obj_get_int(args[2]); + mp_int_t x2 = mp_obj_get_int(args[3]); + mp_int_t y2 = mp_obj_get_int(args[4]); + mp_int_t col = mp_obj_get_int(args[5]); + + mp_int_t dx = x2 - x1; + mp_int_t sx; + if (dx > 0) { + sx = 1; + } else { + dx = -dx; + sx = -1; + } + + mp_int_t dy = y2 - y1; + mp_int_t sy; + if (dy > 0) { + sy = 1; + } else { + dy = -dy; + sy = -1; + } + + bool steep; + if (dy > dx) { + mp_int_t temp; + temp = x1; x1 = y1; y1 = temp; + temp = dx; dx = dy; dy = temp; + temp = sx; sx = sy; sy = temp; + steep = true; + } else { + steep = false; + } + + mp_int_t e = 2 * dy - dx; + for (mp_int_t i = 0; i < dx; ++i) { + if (steep) { + setpixel(self, y1, x1, col); + } else { + setpixel(self, x1, y1, col); + } + while (e >= 0) { + y1 += sy; + e -= 2 * dx; + } + x1 += sx; + e += 2 * dy; + } + + setpixel(self, x2, y2, col); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_line_obj, 6, 6, framebuf_line); + 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]); @@ -314,6 +427,10 @@ 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_hline), MP_ROM_PTR(&framebuf_hline_obj) }, + { MP_ROM_QSTR(MP_QSTR_vline), MP_ROM_PTR(&framebuf_vline_obj) }, + { MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&framebuf_rect_obj) }, + { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&framebuf_line_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) }, -- cgit v1.2.3 From 898d4c1217e69497c67ca2ddb66641406a03700b Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 8 Dec 2016 15:17:47 +1100 Subject: extmod/modframebuf: Make framebuf implement the buffer protocol. So that one can easily access the underlying data of the frame buffer, eg to write the data out to a display. --- extmod/modframebuf.c | 10 ++++++++++ tests/extmod/framebuf1.py | 3 +++ tests/extmod/framebuf1.py.exp | 1 + 3 files changed, 14 insertions(+) (limited to 'extmod/modframebuf.c') diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index b055a8938..eb639c172 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -159,6 +159,15 @@ STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size return MP_OBJ_FROM_PTR(o); } +STATIC mp_int_t framebuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + (void)flags; + mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in); + bufinfo->buf = self->buf; + bufinfo->len = self->stride * self->height * (self->format == FRAMEBUF_RGB565 ? 2 : 1); + bufinfo->typecode = 'B'; // view framebuf as bytes + return 0; +} + 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); @@ -441,6 +450,7 @@ STATIC const mp_obj_type_t mp_type_framebuf = { { &mp_type_type }, .name = MP_QSTR_FrameBuffer, .make_new = framebuf_make_new, + .buffer_p = { .get_buffer = framebuf_get_buffer }, .locals_dict = (mp_obj_t)&framebuf_locals_dict, }; diff --git a/tests/extmod/framebuf1.py b/tests/extmod/framebuf1.py index 7be365528..7f09050b0 100644 --- a/tests/extmod/framebuf1.py +++ b/tests/extmod/framebuf1.py @@ -10,6 +10,9 @@ h = 16 buf = bytearray(w * h // 8) fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.MVLSB) +# access as buffer +print(memoryview(fbuf)[0]) + # fill fbuf.fill(1) print(buf) diff --git a/tests/extmod/framebuf1.py.exp b/tests/extmod/framebuf1.py.exp index dae02ad86..4a83a2e9c 100644 --- a/tests/extmod/framebuf1.py.exp +++ b/tests/extmod/framebuf1.py.exp @@ -1,3 +1,4 @@ +0 bytearray(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff') bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') bytearray(b'\x01\x00\x00\x00\x01\x80\x00\x00\x00\x80') -- cgit v1.2.3 From adf3cb50818636fdfb75e1a6a71b596565dcc90e Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 12:58:45 +1100 Subject: extmod/modframebuf: Store underlying buffer object to prevent GC free. --- extmod/modframebuf.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'extmod/modframebuf.c') diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index eb639c172..d6e686e07 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -37,6 +37,7 @@ typedef struct _mp_obj_framebuf_t { mp_obj_base_t base; + mp_obj_t buf_obj; // need to store this to prevent GC from reclaiming buf void *buf; uint16_t width, height, stride; uint8_t format; @@ -133,6 +134,7 @@ STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t); o->base.type = type; + o->buf_obj = args[0]; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_WRITE); -- cgit v1.2.3