From 78089107bc23c0016aa8eea56a046ad2e5150599 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Sat, 3 Apr 2021 10:41:20 -0500 Subject: [PATCH] displayio.Bitmap: Allow modification though the buffer protocol It is required to call .dirty() with appropriate arguments after modifications through the buffer protocol, or the display might not be updated correctly. --- shared-bindings/bitmaptools/__init__.c | 3 +- shared-bindings/displayio/Bitmap.c | 56 +++++++++++++++++++++++++- shared-module/displayio/Bitmap.c | 2 +- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index bfe4d346cf..3d352e45c2 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -397,8 +397,7 @@ STATIC mp_obj_t bitmaptools_arrayblit(size_t n_args, const mp_obj_t *pos_args, m { MP_QSTR_x2, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_y2, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_skip_index, MP_ARG_OBJ, {.u_obj = mp_const_none } }, - } - ; + }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); diff --git a/shared-bindings/displayio/Bitmap.c b/shared-bindings/displayio/Bitmap.c index 3b2a836eb9..900428b6e4 100644 --- a/shared-bindings/displayio/Bitmap.c +++ b/shared-bindings/displayio/Bitmap.c @@ -25,6 +25,7 @@ */ #include "shared-bindings/displayio/Bitmap.h" +#include "shared-module/displayio/Bitmap.h" #include @@ -43,8 +44,13 @@ //| per row is a multiple of 4, then the resulting memoryview will correspond directly with the bitmap's contents. Otherwise, //| the bitmap data is packed into the memoryview with unspecified padding. //| -//| A read-only buffer can be used e.g., with ``ulab.numpy.frombuffer`` to efficiently create an array with the same content as a Bitmap; -//| to move data efficiently from ulab back into a Bitmap, use `bitmaptools.arrayblit`. +//| A Bitmap can be treated as a buffer, allowing its content to be +//| viewed and modified using e.g., with ``ulab.numpy.frombuffer``, +//| but the `displayio.Bitmap.dirty` method must be used to inform +//| displayio when a bitmap was modified through the buffer interface. +//| +//| `bitmaptools.arrayblit` can also be useful to omve data efficiently +//| into a Bitmap. //| """ //| //| def __init__(self, width: int, height: int, value_count: int) -> None: @@ -299,11 +305,57 @@ STATIC mp_obj_t displayio_bitmap_obj_fill(mp_obj_t self_in, mp_obj_t value_obj) } MP_DEFINE_CONST_FUN_OBJ_2(displayio_bitmap_fill_obj, displayio_bitmap_obj_fill); +//| def dirty(self, x1: int=0, y1: int=0, x2: int=-1, y2:int = -1) -> None: +//| """Inform displayio of bitmap updates done via the buffer +//| protocol. +//| +//| :param int x1: Minimum x-value for rectangular bounding box to be considered as modified +//| :param int y1: Minimum y-value for rectangular bounding box to be considered as modified +//| :param int x2: Maximum x-value (exclusive) for rectangular bounding box to be considered as modified +//| :param int y2: Maximum y-value (exclusive) for rectangular bounding box to be considered as modified +//| +//| If x1 or y1 are not specified, they are taken as 0. If x2 or y2 +//| are not specified, or are given as -1, they are taken as the width +//| and height of the image. Thus, calling dirty() with the +//| default arguments treats the whole bitmap as modified. +//| +//| When a bitmap is modified through the buffer protocol, the +//| display will not be properly updated unless the bitmap is +//| notified of the "dirty rectangle" that encloses all modified +//| pixels.""" +//| ... +//| +STATIC mp_obj_t displayio_bitmap_obj_dirty(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + displayio_bitmap_t *self = MP_OBJ_TO_PTR(pos_args[0]); + enum { ARG_x1, ARG_y1, ARG_x2, ARG_y2 }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_x1, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_y1, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_x2, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_y2, MP_ARG_INT, {.u_int = -1} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_area_t dirty_area = { + .x1 = args[ARG_x1].u_int, + .y1 = args[ARG_y1].u_int, + .x2 = args[ARG_x2].u_int == -1 ? self->width : args[ARG_x2].u_int, + .y2 = args[ARG_y2].u_int == -1 ? self->height : args[ARG_y2].u_int, + }; + + displayio_bitmap_set_dirty_area(self, &dirty_area); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_bitmap_dirty_obj, 0, displayio_bitmap_obj_dirty); + STATIC const mp_rom_map_elem_t displayio_bitmap_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_bitmap_height_obj) }, { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_bitmap_width_obj) }, { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&displayio_bitmap_blit_obj) }, { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&displayio_bitmap_fill_obj) }, + { MP_ROM_QSTR(MP_QSTR_dirty), MP_ROM_PTR(&displayio_bitmap_dirty_obj) }, }; STATIC MP_DEFINE_CONST_DICT(displayio_bitmap_locals_dict, displayio_bitmap_locals_dict_table); diff --git a/shared-module/displayio/Bitmap.c b/shared-module/displayio/Bitmap.c index 872a20b9e9..e218049b6c 100644 --- a/shared-module/displayio/Bitmap.c +++ b/shared-module/displayio/Bitmap.c @@ -236,7 +236,7 @@ void common_hal_displayio_bitmap_fill(displayio_bitmap_t *self, uint32_t value) } int common_hal_displayio_bitmap_get_buffer(displayio_bitmap_t *self, mp_buffer_info_t *bufinfo, mp_uint_t flags) { - if (flags & MP_BUFFER_WRITE) { + if ((flags & MP_BUFFER_WRITE) && self->read_only) { return 1; } bufinfo->len = self->stride * self->height * sizeof(size_t);