How about you just take the existing framebuf module, rename it and build on top of it?
Native C module: accessing objects. [SOLVED]
Re: Native C module: accessing objects
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Native C module: accessing objects
I was hoping to avoid having users build the firmware: this is a significant barrier for many people.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: Native C module: accessing objects
If https://github.com/micropython/micropython/pull/5711 ever gets looked at this will be possible I think.
Re: Native C module: accessing objects
The underlying issue here is that without a loader/symbol table, there's no way for your .mpy to be able to know the location of the existing framebuf code. We work around this for the core functionality by providing mp_fun_table. It's not really clear what the best way to support this -- there's a long list of "core adjacent" functionality (like framebuf), as well as things like port-specific HAL functionality (see below).pythoncoder wrote: ↑Tue Sep 08, 2020 8:15 amI was hoping to avoid having users build the firmware: this is a significant barrier for many people.
However, to elaborate on deshipu's suggestion, a possible way to work around this is to duplicate any code from framebuf that you need. When your blit method gets called, you're passed two mp_obj_t that happen to point to two mp_obj_framebuf_t instances.
So nothing stops you providing your own copy of the mp_obj_framebuf_t struct definition inside yournativemodule.c, using MP_OBJ_TO_PTR to convert to that, and then you can write your own functions that operate on this struct. You might find that you end up having to make duplicate implementations of some of the "real" functions from modframebuf.c, so it's not ideal from an efficiency perspective.
As it stands, this doesn't quite solve Peter's problem as it provides port-specific functionality, but the same mechanism could maybe be used.chibill wrote: ↑Tue Sep 08, 2020 8:12 pmIf https://github.com/micropython/micropython/pull/5711 ever gets looked at this will be possible I think.
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Native C module: accessing objects
Thanks everyone - I will experiment...
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: Native C module: accessing objects
But framebuf is a loadable module now?pythoncoder wrote: ↑Tue Sep 08, 2020 8:15 amI was hoping to avoid having users build the firmware: this is a significant barrier for many people.
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Native C module: accessing objects
It is indeed and I think I'm well on the way to doing this. I've managed to add an additional FrameBuffer.blit_x method which is currently just a copy of blit but I can run it on the Unix build and on STM. Now to rename it and make it do what I want...
It hadn't occurred to me that it was possible - indeed rather simple - to add a method yet retain all the existing functionality of the class. Very cunning.
Thanks all for the pointers
It hadn't occurred to me that it was possible - indeed rather simple - to add a method yet retain all the existing functionality of the class. Very cunning.
Thanks all for the pointers
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Awesome
Several hours on I remain gobsmacked at how easy that was. The docs had convinced me that adding a method to a native class was probably impossible. It actually took a few minutes: compiled and ran on the second attempt. It still strikes me as the closest thing to magic that I've seen in years...
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: Native C module: accessing objects
Would this "magic" could be used to bring unsupported esp32 periferics like "pcnt" without building a specific firmware ?
nodemcu V2 (amica)
micropython firmware Daily build 05/31/2016
micropython firmware Daily build 05/31/2016
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Not quite so simple: please help
Adding functionality to framebuf was easy, but using it is proving crash-prone. I'd appreciate some feedback as to whether my C code is legitimate before I raise an issue (code posted at end of this message).
I added a render(framebuf, row, col, fgcolor, bgcolor) method which renders a monochrome glyph to a color framebuf. This works, both in testing at the REPL and in my application but the board crashes after a number of iterations. I have reduced the failing Python code to this test case
This code runs on a Pyboard D SF2W for 93 iterations before crashing. There is evidence that the crash is caused by an allocation issue.
framebuf_r is my native C module. The rather convoluted code is because MicroPython evidently sees my FrameBuffer and the built-in FrameBuffer as different types. My attempt to use my type throughout failed because it seems that I can only subclass the display device from the built-in type. And I can't blit between the two types of FrameBuffer without getting a TypeError.
I don't know if this type conflict is to be expected - the code could be much simplified if it did not occur or if I could subclass the device from my FrameBuffer, using that class throughout.
Here is the C code. The render method is a trivial adaptation of blit. Comments welcome:
I added a render(framebuf, row, col, fgcolor, bgcolor) method which renders a monochrome glyph to a color framebuf. This works, both in testing at the REPL and in my application but the board crashes after a number of iterations. I have reduced the failing Python code to this test case
Code: Select all
import framebuf_r
import framebuf
# Emulate a display driver subclassed from framebuf.FrameBuffer
db = bytearray(40 * 80 * 2)
device = framebuf.FrameBuffer(db, 40, 80, framebuf.RGB565)
def foo():
width = 20 # Glyph dimensions
height = 40
i = 0
while True:
# Source monochrome glyph
buf = bytearray(width * height // 8)
fbc = framebuf_r.FrameBuffer(buf, width, height, framebuf_r.MONO_HMSB)
# Destination: temporary color frame buffer for the glyph
bufd = bytearray(width * height * 2)
fbd = framebuf_r.FrameBuffer(bufd, width, height, framebuf_r.RGB565)
# Render it in specified colors to the temporary buffer
fbd.render(fbc, 0, 0, 0x5555, 0xaaaa) # fbc must be a framebuf_r otherwise we get a TypeError
# Instantiate a FrameBuffer pointed at color glyph
# Cannot be a framebuf_r
fbx = framebuf.FrameBuffer(bufd, width, height, framebuf.RGB565)
device.blit(fbx, 0, 0)
i += 1
print(i)
framebuf_r is my native C module. The rather convoluted code is because MicroPython evidently sees my FrameBuffer and the built-in FrameBuffer as different types. My attempt to use my type throughout failed because it seems that I can only subclass the display device from the built-in type. And I can't blit between the two types of FrameBuffer without getting a TypeError.
I don't know if this type conflict is to be expected - the code could be much simplified if it did not occur or if I could subclass the device from my FrameBuffer, using that class throughout.
Here is the C code. The render method is a trivial adaptation of blit. Comments welcome:
Code: Select all
#define MICROPY_PY_FRAMEBUF (1)
#include "py/dynruntime.h"
#if !defined(__linux__)
void *memset(void *s, int c, size_t n) {
return mp_fun_table.memset_(s, c, n);
}
#endif
mp_obj_type_t mp_type_framebuf;
#include "extmod/modframebuf.c"
// render(mono_framebuf, x, y, fgcolor, bgcolor=0)
STATIC mp_obj_t framebuf_render(size_t n_args, const mp_obj_t *args) {
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args[0]);
mp_obj_t source_in = mp_obj_cast_to_native_base(args[1], MP_OBJ_FROM_PTR(&mp_type_framebuf));
if (source_in == MP_OBJ_NULL) {
mp_raise_TypeError(NULL);
}
mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(source_in);
mp_int_t x = mp_obj_get_int(args[2]);
mp_int_t y = mp_obj_get_int(args[3]);
mp_int_t fgcol = mp_obj_get_int(args[4]);
mp_int_t bgcol = 0;
if (n_args > 5) {
bgcol = mp_obj_get_int(args[5]);
}
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);
for (; y0 < y0end; ++y0) {
int cx1 = x1;
for (int cx0 = x0; cx0 < x0end; ++cx0) {
uint32_t col = getpixel(source, cx1, y1);
if (col == 0) {
setpixel(self, cx0, y0, bgcol);
}
else {
setpixel(self, cx0, y0, fgcol);
}
++cx1;
}
++y1;
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_render_obj, 5, 6, framebuf_render);
mp_map_elem_t framebuf_locals_dict_table[11];
STATIC MP_DEFINE_CONST_DICT(framebuf_locals_dict, framebuf_locals_dict_table);
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
MP_DYNRUNTIME_INIT_ENTRY
mp_type_framebuf.base.type = (void*)&mp_type_type;
mp_type_framebuf.name = MP_QSTR_FrameBuffer;
mp_type_framebuf.make_new = framebuf_make_new;
mp_type_framebuf.buffer_p.get_buffer = framebuf_get_buffer;
framebuf_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_fill), MP_OBJ_FROM_PTR(&framebuf_fill_obj) };
framebuf_locals_dict_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_fill_rect), MP_OBJ_FROM_PTR(&framebuf_fill_rect_obj) };
framebuf_locals_dict_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_pixel), MP_OBJ_FROM_PTR(&framebuf_pixel_obj) };
framebuf_locals_dict_table[3] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_hline), MP_OBJ_FROM_PTR(&framebuf_hline_obj) };
framebuf_locals_dict_table[4] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_vline), MP_OBJ_FROM_PTR(&framebuf_vline_obj) };
framebuf_locals_dict_table[5] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_rect), MP_OBJ_FROM_PTR(&framebuf_rect_obj) };
framebuf_locals_dict_table[6] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_line), MP_OBJ_FROM_PTR(&framebuf_line_obj) };
framebuf_locals_dict_table[7] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_blit), MP_OBJ_FROM_PTR(&framebuf_blit_obj) };
framebuf_locals_dict_table[8] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_scroll), MP_OBJ_FROM_PTR(&framebuf_scroll_obj) };
framebuf_locals_dict_table[9] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_text), MP_OBJ_FROM_PTR(&framebuf_text_obj) };
framebuf_locals_dict_table[10] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_render), MP_OBJ_FROM_PTR(&framebuf_render_obj) };
mp_type_framebuf.locals_dict = (void*)&framebuf_locals_dict;
mp_store_global(MP_QSTR_FrameBuffer, MP_OBJ_FROM_PTR(&mp_type_framebuf));
mp_store_global(MP_QSTR_FrameBuffer1, MP_OBJ_FROM_PTR(&legacy_framebuffer1_obj));
mp_store_global(MP_QSTR_MVLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB));
mp_store_global(MP_QSTR_MONO_VLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MVLSB));
mp_store_global(MP_QSTR_RGB565, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_RGB565));
mp_store_global(MP_QSTR_GS2_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS2_HMSB));
mp_store_global(MP_QSTR_GS4_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS4_HMSB));
mp_store_global(MP_QSTR_GS8, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_GS8));
mp_store_global(MP_QSTR_MONO_HLSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MHLSB));
mp_store_global(MP_QSTR_MONO_HMSB, MP_OBJ_NEW_SMALL_INT(FRAMEBUF_MHMSB));
MP_DYNRUNTIME_INIT_EXIT
}
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.