Skip to content

Commit 225e22b

Browse files
deshipudpgeorge
authored andcommitted
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.
1 parent 8b82429 commit 225e22b

4 files changed

Lines changed: 312 additions & 88 deletions

File tree

extmod/modframebuf.c

Lines changed: 195 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,64 @@
3535

3636
#include "stmhal/font_petme128_8x8.h"
3737

38-
// 1-bit frame buffer, each byte is a column of 8 pixels
39-
typedef struct _mp_obj_framebuf1_t {
38+
typedef struct _mp_obj_framebuf_t {
4039
mp_obj_base_t base;
41-
uint8_t *buf;
40+
void *buf;
4241
uint16_t width, height, stride;
43-
} mp_obj_framebuf1_t;
42+
uint8_t format;
43+
} mp_obj_framebuf_t;
4444

45-
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) {
46-
mp_arg_check_num(n_args, n_kw, 3, 4, false);
45+
typedef void (*setpixel_t)(const mp_obj_framebuf_t*, int, int, uint32_t);
46+
typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t*, int, int);
4747

48-
mp_obj_framebuf1_t *o = m_new_obj(mp_obj_framebuf1_t);
48+
typedef struct _mp_framebuf_p_t {
49+
setpixel_t setpixel;
50+
getpixel_t getpixel;
51+
} mp_framebuf_p_t;
52+
53+
// Functions for MVLSB format
54+
55+
STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) {
56+
size_t index = (y >> 3) * fb->stride + x;
57+
uint8_t offset = y & 0x07;
58+
((uint8_t*)fb->buf)[index] = (((uint8_t*)fb->buf)[index] & ~(0x01 << offset)) | ((color != 0) << offset);
59+
}
60+
61+
STATIC uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
62+
return (((uint8_t*)fb->buf)[(y >> 3) * fb->stride + x] >> (y & 0x07)) & 0x01;
63+
}
64+
65+
// Functions for RGB565 format
66+
67+
STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) {
68+
((uint16_t*)fb->buf)[x + y * fb->stride] = color;
69+
}
70+
71+
STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
72+
return ((uint16_t*)fb->buf)[x + y * fb->stride];
73+
}
74+
75+
// constants for formats
76+
#define FRAMEBUF_MVLSB (0)
77+
#define FRAMEBUF_RGB565 (1)
78+
79+
STATIC mp_framebuf_p_t formats[] = {
80+
[FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel},
81+
[FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel},
82+
};
83+
84+
static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t color) {
85+
formats[fb->format].setpixel(fb, x, y, color);
86+
}
87+
88+
static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, int x, int y) {
89+
return formats[fb->format].getpixel(fb, x, y);
90+
}
91+
92+
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) {
93+
mp_arg_check_num(n_args, n_kw, 4, 5, false);
94+
95+
mp_obj_framebuf_t *o = m_new_obj(mp_obj_framebuf_t);
4996
o->base.type = type;
5097

5198
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
54101

55102
o->width = mp_obj_get_int(args[1]);
56103
o->height = mp_obj_get_int(args[2]);
57-
o->stride = o->width;
58-
if (n_args >= 4) {
59-
o->stride = mp_obj_get_int(args[3]);
104+
o->format = mp_obj_get_int(args[3]);
105+
if (n_args >= 5) {
106+
o->stride = mp_obj_get_int(args[4]);
107+
} else {
108+
o->stride = o->width;
109+
}
110+
111+
switch (o->format) {
112+
case FRAMEBUF_MVLSB:
113+
case FRAMEBUF_RGB565:
114+
break;
115+
default:
116+
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
117+
"invalid format"));
60118
}
61119

62120
return MP_OBJ_FROM_PTR(o);
63121
}
64122

65-
STATIC mp_obj_t framebuf1_fill(mp_obj_t self_in, mp_obj_t col_in) {
66-
mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(self_in);
123+
STATIC mp_obj_t framebuf_fill(mp_obj_t self_in, mp_obj_t col_in) {
124+
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
67125
mp_int_t col = mp_obj_get_int(col_in);
68-
if (col) {
69-
col = 0xff;
126+
for (int y = 0; y < self->height; ++y) {
127+
for (int x = 0; x < self->width; ++x) {
128+
setpixel(self, x, y, col);
129+
}
70130
}
71-
int end = (self->height + 7) >> 3;
72-
for (int y = 0; y < end; ++y) {
73-
memset(self->buf + y * self->stride, col, self->width);
131+
return mp_const_none;
132+
}
133+
STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf_fill_obj, framebuf_fill);
134+
135+
STATIC mp_obj_t framebuf_fill_rect(size_t n_args, const mp_obj_t *args) {
136+
(void)n_args;
137+
138+
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
139+
mp_int_t x = mp_obj_get_int(args[1]);
140+
mp_int_t y = mp_obj_get_int(args[2]);
141+
mp_int_t width = mp_obj_get_int(args[3]);
142+
mp_int_t height = mp_obj_get_int(args[4]);
143+
mp_int_t color = mp_obj_get_int(args[5]);
144+
145+
if (x + width <= 0 || y + height <= 0 || y >= self->height || x >= self->width) {
146+
// No operation needed.
147+
return mp_const_none;
148+
}
149+
150+
// clip to the framebuffer
151+
int xend = MIN(self->width, x + width);
152+
int yend = MIN(self->height, y + height);
153+
x = MAX(MIN(x, self->width), 0);
154+
y = MAX(MIN(y, self->height), 0);
155+
156+
for (; y < yend; ++y) {
157+
for (int xc = x; xc < xend; ++xc) {
158+
setpixel(self, xc, y, color);
159+
}
74160
}
75161
return mp_const_none;
76162
}
77-
STATIC MP_DEFINE_CONST_FUN_OBJ_2(framebuf1_fill_obj, framebuf1_fill);
163+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_fill_rect_obj, 6, 6, framebuf_fill_rect);
78164

79-
STATIC mp_obj_t framebuf1_pixel(size_t n_args, const mp_obj_t *args) {
80-
mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(args[0]);
165+
STATIC mp_obj_t framebuf_pixel(size_t n_args, const mp_obj_t *args) {
166+
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
81167
mp_int_t x = mp_obj_get_int(args[1]);
82168
mp_int_t y = mp_obj_get_int(args[2]);
83169
if (0 <= x && x < self->width && 0 <= y && y < self->height) {
84-
int index = (y / 8) * self->stride + x;
85170
if (n_args == 3) {
86171
// get
87-
return MP_OBJ_NEW_SMALL_INT((self->buf[index] >> (y & 7)) & 1);
172+
return MP_OBJ_NEW_SMALL_INT(getpixel(self, x, y));
88173
} else {
89174
// set
90-
if (mp_obj_get_int(args[3])) {
91-
self->buf[index] |= (1 << (y & 7));
92-
} else {
93-
self->buf[index] &= ~(1 << (y & 7));
94-
}
175+
setpixel(self, x, y, mp_obj_get_int(args[3]));
95176
}
96177
}
97178
return mp_const_none;
98179
}
99-
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf1_pixel_obj, 3, 4, framebuf1_pixel);
180+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_pixel_obj, 3, 4, framebuf_pixel);
100181

101-
STATIC mp_obj_t framebuf1_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) {
102-
mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(self_in);
103-
mp_int_t xstep = mp_obj_get_int(xstep_in);
104-
mp_int_t ystep = mp_obj_get_int(ystep_in);
105-
int end = (self->height + 7) >> 3;
106-
if (ystep > 0) {
107-
for (int y = end; y > 0;) {
108-
--y;
109-
for (int x = 0; x < self->width; ++x) {
110-
int prev = 0;
111-
if (y > 0) {
112-
prev = (self->buf[(y - 1) * self->stride + x] >> (8 - ystep)) & ((1 << ystep) - 1);
113-
}
114-
self->buf[y * self->stride + x] = (self->buf[y * self->stride + x] << ystep) | prev;
115-
}
116-
}
117-
} else if (ystep < 0) {
118-
for (int y = 0; y < end; ++y) {
119-
for (int x = 0; x < self->width; ++x) {
120-
int prev = 0;
121-
if (y + 1 < end) {
122-
prev = self->buf[(y + 1) * self->stride + x] << (8 + ystep);
123-
}
124-
self->buf[y * self->stride + x] = (self->buf[y * self->stride + x] >> -ystep) | prev;
182+
STATIC mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args) {
183+
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
184+
mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(args[1]);
185+
mp_int_t x = mp_obj_get_int(args[2]);
186+
mp_int_t y = mp_obj_get_int(args[3]);
187+
mp_int_t key = -1;
188+
if (n_args > 4) {
189+
key = mp_obj_get_int(args[4]);
190+
}
191+
192+
if (
193+
(x >= self->width) ||
194+
(y >= self->height) ||
195+
(-x >= source->width) ||
196+
(-y >= source->height)
197+
) {
198+
// Out of bounds, no-op.
199+
return mp_const_none;
200+
}
201+
202+
// Clip.
203+
int x0 = MAX(0, x);
204+
int y0 = MAX(0, y);
205+
int x1 = MAX(0, -x);
206+
int y1 = MAX(0, -y);
207+
int x0end = MIN(self->width, x + source->width);
208+
int y0end = MIN(self->height, y + source->height);
209+
uint32_t color;
210+
211+
for (; y0 < y0end; ++y0) {
212+
int cx1 = x1;
213+
for (int cx0 = x0; cx0 < x0end; ++cx0) {
214+
color = getpixel(source, cx1, y1);
215+
if (color != key) {
216+
setpixel(self, cx0, y0, color);
125217
}
218+
++cx1;
126219
}
220+
++y1;
127221
}
222+
return mp_const_none;
223+
}
224+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_blit_obj, 4, 5, framebuf_blit);
225+
226+
STATIC mp_obj_t framebuf_scroll(mp_obj_t self_in, mp_obj_t xstep_in, mp_obj_t ystep_in) {
227+
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(self_in);
228+
mp_int_t xstep = mp_obj_get_int(xstep_in);
229+
mp_int_t ystep = mp_obj_get_int(ystep_in);
230+
int sx, y, xend, yend, dx, dy;
128231
if (xstep < 0) {
129-
for (int y = 0; y < end; ++y) {
130-
for (int x = 0; x < self->width + xstep; ++x) {
131-
self->buf[y * self->stride + x] = self->buf[y * self->stride + x - xstep];
132-
}
133-
}
134-
} else if (xstep > 0) {
135-
for (int y = 0; y < end; ++y) {
136-
for (int x = self->width - 1; x >= xstep; --x) {
137-
self->buf[y * self->stride + x] = self->buf[y * self->stride + x - xstep];
138-
}
232+
sx = 0;
233+
xend = self->width + xstep;
234+
dx = 1;
235+
} else {
236+
sx = self->width - 1;
237+
xend = xstep - 1;
238+
dx = -1;
239+
}
240+
if (ystep < 0) {
241+
y = 0;
242+
yend = self->height + ystep;
243+
dy = 1;
244+
} else {
245+
y = self->height - 1;
246+
yend = ystep - 1;
247+
dy = -1;
248+
}
249+
for (; y != yend; y += dy) {
250+
for (int x = sx; x != xend; x += dx) {
251+
setpixel(self, x, y, getpixel(self, x - xstep, y - ystep));
139252
}
140253
}
141-
// TODO: Should we clear the margin created by scrolling?
142254
return mp_const_none;
143255
}
144-
STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf1_scroll_obj, framebuf1_scroll);
256+
STATIC MP_DEFINE_CONST_FUN_OBJ_3(framebuf_scroll_obj, framebuf_scroll);
145257

146-
STATIC mp_obj_t framebuf1_text(size_t n_args, const mp_obj_t *args) {
258+
STATIC mp_obj_t framebuf_text(size_t n_args, const mp_obj_t *args) {
147259
// extract arguments
148-
mp_obj_framebuf1_t *self = MP_OBJ_TO_PTR(args[0]);
260+
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
149261
const char *str = mp_obj_str_get_str(args[1]);
150262
mp_int_t x0 = mp_obj_get_int(args[2]);
151263
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) {
170282
for (int y = y0; vline_data; vline_data >>= 1, y++) { // scan over vertical column
171283
if (vline_data & 1) { // only draw if pixel set
172284
if (0 <= y && y < self->height) { // clip y
173-
uint byte_pos = x0 + self->stride * ((uint)y >> 3);
174-
if (col == 0) {
175-
// clear pixel
176-
self->buf[byte_pos] &= ~(1 << (y & 7));
177-
} else {
178-
// set pixel
179-
self->buf[byte_pos] |= 1 << (y & 7);
180-
}
285+
setpixel(self, x0, y, col);
181286
}
182287
}
183288
}
184289
}
185290
}
186291
}
187-
188292
return mp_const_none;
189293
}
190-
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf1_text_obj, 4, 5, framebuf1_text);
294+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_text_obj, 4, 5, framebuf_text);
191295

192-
STATIC const mp_rom_map_elem_t framebuf1_locals_dict_table[] = {
193-
{ MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf1_fill_obj) },
194-
{ MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf1_pixel_obj) },
195-
{ MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf1_scroll_obj) },
196-
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf1_text_obj) },
296+
STATIC const mp_rom_map_elem_t framebuf_locals_dict_table[] = {
297+
{ MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&framebuf_fill_obj) },
298+
{ MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&framebuf_fill_rect_obj) },
299+
{ MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&framebuf_pixel_obj) },
300+
{ MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&framebuf_blit_obj) },
301+
{ MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&framebuf_scroll_obj) },
302+
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&framebuf_text_obj) },
197303
};
198-
STATIC MP_DEFINE_CONST_DICT(framebuf1_locals_dict, framebuf1_locals_dict_table);
304+
STATIC MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table);
199305

200-
STATIC const mp_obj_type_t mp_type_framebuf1 = {
306+
STATIC const mp_obj_type_t mp_type_framebuf = {
201307
{ &mp_type_type },
202-
.name = MP_QSTR_FrameBuffer1,
203-
.make_new = framebuf1_make_new,
204-
.locals_dict = (mp_obj_t)&framebuf1_locals_dict,
308+
.name = MP_QSTR_FrameBuffer,
309+
.make_new = framebuf_make_new,
310+
.locals_dict = (mp_obj_t)&framebuf_locals_dict,
205311
};
206312

207313
STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = {
208314
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_framebuf) },
209-
{ MP_ROM_QSTR(MP_QSTR_FrameBuffer1), MP_ROM_PTR(&mp_type_framebuf1) },
315+
{ MP_ROM_QSTR(MP_QSTR_FrameBuffer), MP_ROM_PTR(&mp_type_framebuf) },
316+
{ MP_ROM_QSTR(MP_QSTR_MVLSB), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB) },
317+
{ MP_ROM_QSTR(MP_QSTR_RGB565), MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565) },
210318
};
211319

212320
STATIC MP_DEFINE_CONST_DICT(framebuf_module_globals, framebuf_module_globals_table);

tests/extmod/framebuf1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
w = 5
99
h = 16
1010
buf = bytearray(w * h // 8)
11-
fbuf = framebuf.FrameBuffer1(buf, w, h, w)
11+
fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.MVLSB)
1212

1313
# fill
1414
fbuf.fill(1)

0 commit comments

Comments
 (0)