summaryrefslogtreecommitdiffstatshomepage
path: root/extmod/modframebuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'extmod/modframebuf.c')
-rw-r--r--extmod/modframebuf.c282
1 files changed, 195 insertions, 87 deletions
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;
+ 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);