summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--extmod/modframebuf.c282
-rw-r--r--tests/extmod/framebuf1.py2
-rw-r--r--tests/extmod/framebuf16.py59
-rw-r--r--tests/extmod/framebuf16.py.exp57
4 files changed, 312 insertions, 88 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);
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--