From 53ad681ed1786601ca5eccb60f8fd950b8bc47d7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 8 Apr 2016 11:08:37 +0100 Subject: [PATCH] extmod: Add initial framebuf module. --- extmod/modframebuf.c | 205 +++++++++++++++++++++++++++++++++++++ py/builtin.h | 1 + py/mpconfig.h | 4 + py/objmodule.c | 3 + py/py.mk | 1 + py/qstrdefs.h | 9 ++ stmhal/font_petme128_8x8.h | 2 +- 7 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 extmod/modframebuf.c diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c new file mode 100644 index 0000000000..d0ef238075 --- /dev/null +++ b/extmod/modframebuf.c @@ -0,0 +1,205 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "py/nlr.h" +#include "py/obj.h" +#include "py/runtime.h" + +#if MICROPY_PY_FRAMEBUF + +#include "font_petme128_8x8.h" + +// 1-bit frame buffer, each byte is a column of 8 pixels +typedef struct _mp_obj_framebuf1_t { + mp_obj_base_t base; + uint8_t *buf; + uint16_t width, height, stride; +} mp_obj_framebuf1_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); + + mp_obj_framebuf1_t *o = m_new_obj(mp_obj_framebuf1_t); + o->base.type = type; + + 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->stride = o->width; + if (n_args >= 4) { + o->stride = mp_obj_get_int(args[3]); + } + + 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); + mp_int_t col = mp_obj_get_int(col_in); + if (col) { + col = 0xff; + } + for (int y = 0; y < self->height / 8; ++y) { + memset(self->buf + y * self->stride, col, self->width); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf1_fill_obj, framebuf1_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]); + 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)); + } else { + // set + if (mp_obj_get_int(args[3])) { + self->buf[index] |= (1 << (y & 7)); + } else { + self->buf[index] &= ~(1 << (y & 7)); + } + } + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf1_pixel_obj, 3, 4, framebuf1_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); + if (xstep == 0 && ystep > 0) { + for (int y = self->height / 8; 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 (xstep == 0 && ystep < 0) { + for (int y = 0; y < self->height / 8; ++y) { + for (int x = 0; x < self->width; ++x) { + int prev = 0; + if (y + 1 < self->height / 8) { + prev = self->buf[(y + 1) * self->stride + x] << (8 + ystep); + } + self->buf[y * self->stride + x] = (self->buf[y * self->stride + x] >> -ystep) | prev; + } + } + } + // TODO xstep!=0 + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf1_scroll_obj, framebuf1_scroll); + +STATIC mp_obj_t framebuf1_text(size_t n_args, const mp_obj_t *args) { + // extract arguments + mp_obj_framebuf1_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]); + mp_int_t col = 1; + if (n_args >= 5) { + col = mp_obj_get_int(args[4]); + } + + // loop over chars + for (; *str; ++str) { + // get char and make sure its in range of font + int chr = *(uint8_t*)str; + if (chr < 32 || chr > 127) { + chr = 127; + } + // get char data + const uint8_t *chr_data = &font_petme128_8x8[(chr - 32) * 8]; + // loop over char data + for (int j = 0; j < 8; j++, x0++) { + if (0 <= x0 && x0 < self->width) { // clip x + uint vline_data = chr_data[j]; // each byte is a column of 8 pixels, LSB at top + 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); + } + } + } + } + } + } + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf1_text_obj, 4, 5, framebuf1_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 MP_DEFINE_CONST_DICT(framebuf1_locals_dict, framebuf1_locals_dict_table); + +STATIC const mp_obj_type_t mp_type_framebuf1 = { + { &mp_type_type }, + .name = MP_QSTR_FrameBuffer1, + .make_new = framebuf1_make_new, + .locals_dict = (mp_obj_t)&framebuf1_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) }, +}; + +STATIC MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table); + +const mp_obj_module_t mp_module_framebuf = { + .base = { &mp_type_module }, + .name = MP_QSTR_framebuf, + .globals = (mp_obj_dict_t*)&framebuf_module_globals, +}; + +#endif // MICROPY_PY_FRAMEBUF diff --git a/py/builtin.h b/py/builtin.h index e3e68e1519..35d8910bd8 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -106,6 +106,7 @@ extern const mp_obj_module_t mp_module_ussl; extern const mp_obj_module_t mp_module_machine; extern const mp_obj_module_t mp_module_lwip; extern const mp_obj_module_t mp_module_websocket; +extern const mp_obj_module_t mp_module_framebuf; // extmod functions MP_DECLARE_CONST_FUN_OBJ(pyb_mount_obj); diff --git a/py/mpconfig.h b/py/mpconfig.h index 6c2db2f1fc..bbf0555137 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -845,6 +845,10 @@ typedef double mp_float_t; #define MICROPY_PY_WEBSOCKET (0) #endif +#ifndef MICROPY_PY_FRAMEBUF +#define MICROPY_PY_FRAMEBUF (0) +#endif + /*****************************************************************************/ /* Hooks for a port to add builtins */ diff --git a/py/objmodule.c b/py/objmodule.c index 5fd7b82c5b..d2a4d893c9 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -196,6 +196,9 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { #if MICROPY_PY_WEBSOCKET { MP_ROM_QSTR(MP_QSTR_websocket), MP_ROM_PTR(&mp_module_websocket) }, #endif +#if MICROPY_PY_FRAMEBUF + { MP_ROM_QSTR(MP_QSTR_framebuf), MP_ROM_PTR(&mp_module_framebuf) }, +#endif // extra builtin modules as defined by a port MICROPY_PORT_BUILTIN_MODULES diff --git a/py/py.mk b/py/py.mk index 899f333c62..3cfc6712a5 100644 --- a/py/py.mk +++ b/py/py.mk @@ -170,6 +170,7 @@ PY_O_BASENAME = \ ../extmod/modussl.o \ ../extmod/modurandom.o \ ../extmod/modwebsocket.o \ + ../extmod/modframebuf.o \ ../extmod/fsusermount.o \ ../extmod/vfs_fat.o \ ../extmod/vfs_fat_ffconf.o \ diff --git a/py/qstrdefs.h b/py/qstrdefs.h index f5e06f1a52..d8c7ccc428 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -761,3 +761,12 @@ Q(flush) #if MICROPY_PY_WEBSOCKET Q(websocket) #endif + +#if MICROPY_PY_FRAMEBUF +Q(framebuf) +Q(FrameBuffer1) +Q(fill) +Q(pixel) +Q(scroll) +Q(text) +#endif diff --git a/stmhal/font_petme128_8x8.h b/stmhal/font_petme128_8x8.h index 8df78a250a..7f928edda4 100644 --- a/stmhal/font_petme128_8x8.h +++ b/stmhal/font_petme128_8x8.h @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -const uint8_t font_petme128_8x8[] = { +static const uint8_t font_petme128_8x8[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 32= 0x00,0x00,0x00,0x4f,0x4f,0x00,0x00,0x00, // 33=! 0x00,0x07,0x07,0x00,0x00,0x07,0x07,0x00, // 34="