Skip to content

Commit

Permalink
extmod/modframebuf: Make FrameBuffer handle 16bit depth.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
deshipu authored and dpgeorge committed Dec 1, 2016
1 parent 8b82429 commit 225e22b
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 88 deletions.
282 changes: 195 additions & 87 deletions extmod/modframebuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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]);
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion tests/extmod/framebuf1.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
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)
Expand Down
Loading

0 comments on commit 225e22b

Please sign in to comment.