diff --git a/docs/redirects.txt b/docs/redirects.txt index 6eb7ddc4d8..d8fc46b358 100644 --- a/docs/redirects.txt +++ b/docs/redirects.txt @@ -152,7 +152,6 @@ shared-bindings/ustack/__init__.rst shared-bindings/ustack/ shared-bindings/vectorio/Circle.rst shared-bindings/vectorio/#vectorio.Circle shared-bindings/vectorio/Polygon.rst shared-bindings/vectorio/#vectorio.Polygon shared-bindings/vectorio/Rectangle.rst shared-bindings/vectorio/#vectorio.Rectangle -shared-bindings/vectorio/VectorShape.rst shared-bindings/vectorio/#vectorio.VectorShape shared-bindings/vectorio/__init__.rst shared-bindings/vectorio/ shared-bindings/watchdog/WatchDogMode.rst shared-bindings/watchdog/#watchdog.WatchDogMode shared-bindings/watchdog/WatchDogTimer.rst shared-bindings/watchdog/#watchdog.WatchDogTimer diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index f10f7fd019..3009ba40bf 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -318,6 +318,10 @@ msgstr "" msgid "'yield' outside function" msgstr "" +#: shared-module/vectorio/VectorShape.c +msgid "(x,y) integers required" +msgstr "" + #: py/compile.c msgid "*x must be assignment target" msgstr "" @@ -4310,7 +4314,7 @@ msgid "unreadable attribute" msgstr "" #: shared-bindings/displayio/TileGrid.c shared-bindings/vectorio/VectorShape.c -#: shared-module/vectorio/Polygon.c +#: shared-module/vectorio/Polygon.c shared-module/vectorio/VectorShape.c msgid "unsupported %q type" msgstr "" diff --git a/shared-bindings/displayio/Group.c b/shared-bindings/displayio/Group.c index d636ee335b..4783c97453 100644 --- a/shared-bindings/displayio/Group.c +++ b/shared-bindings/displayio/Group.c @@ -183,7 +183,7 @@ const mp_obj_property_t displayio_group_y_obj = { MP_ROM_NONE}, }; -//| def append(self, layer: Union[vectorio.VectorShape, Group, TileGrid]) -> None: +//| def append(self, layer: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]) -> None: //| """Append a layer to the group. It will be drawn above other layers.""" //| ... //| @@ -194,7 +194,7 @@ STATIC mp_obj_t displayio_group_obj_append(mp_obj_t self_in, mp_obj_t layer) { } MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_append_obj, displayio_group_obj_append); -//| def insert(self, index: int, layer: Union[vectorio.VectorShape, Group, TileGrid]) -> None: +//| def insert(self, index: int, layer: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]) -> None: //| """Insert a layer into the group.""" //| ... //| @@ -210,7 +210,7 @@ STATIC mp_obj_t displayio_group_obj_insert(mp_obj_t self_in, mp_obj_t index_obj, MP_DEFINE_CONST_FUN_OBJ_3(displayio_group_insert_obj, displayio_group_obj_insert); -//| def index(self, layer: Union[vectorio.VectorShape, Group, TileGrid]) -> int: +//| def index(self, layer: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]) -> int: //| """Returns the index of the first copy of layer. Raises ValueError if not found.""" //| ... //| @@ -224,7 +224,7 @@ STATIC mp_obj_t displayio_group_obj_index(mp_obj_t self_in, mp_obj_t layer) { } MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_index_obj, displayio_group_obj_index); -//| def pop(self, i: int = -1) -> Union[vectorio.VectorShape, Group, TileGrid]: +//| def pop(self, i: int = -1) -> Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]: //| """Remove the ith item and return it.""" //| ... //| @@ -247,7 +247,7 @@ STATIC mp_obj_t displayio_group_obj_pop(size_t n_args, const mp_obj_t *pos_args, MP_DEFINE_CONST_FUN_OBJ_KW(displayio_group_pop_obj, 1, displayio_group_obj_pop); -//| def remove(self, layer: Union[vectorio.VectorShape, Group, TileGrid]) -> None: +//| def remove(self, layer: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]) -> None: //| """Remove the first copy of layer. Raises ValueError if it is not present.""" //| ... //| @@ -280,7 +280,7 @@ STATIC mp_obj_t group_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } } -//| def __getitem__(self, index: int) -> Union[vectorio.VectorShape, Group, TileGrid]: +//| def __getitem__(self, index: int) -> Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]: //| """Returns the value at the given index. //| //| This allows you to:: @@ -288,7 +288,7 @@ STATIC mp_obj_t group_unary_op(mp_unary_op_t op, mp_obj_t self_in) { //| print(group[0])""" //| ... //| -//| def __setitem__(self, index: int, value: Union[vectorio.VectorShape, Group, TileGrid]) -> None: +//| def __setitem__(self, index: int, value: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]) -> None: //| """Sets the value at the given index. //| //| This allows you to:: diff --git a/shared-bindings/vectorio/Circle.c b/shared-bindings/vectorio/Circle.c index 673a976687..c0cc0bbcc2 100644 --- a/shared-bindings/vectorio/Circle.c +++ b/shared-bindings/vectorio/Circle.c @@ -1,5 +1,6 @@ - +#include "shared-bindings/vectorio/__init__.h" #include "shared-bindings/vectorio/Circle.h" +#include "shared-bindings/vectorio/VectorShape.h" #include @@ -11,15 +12,21 @@ //| class Circle: //| -//| def __init__(self, radius: int) -> None: +//| def __init__(self, pixel_shader: Union[displayio.ColorConverter, displayio.Palette], radius: int, x: int, y: int) -> None: //| """Circle is positioned on screen by its center point. //| -//| :param radius: The radius of the circle in pixels""" +//| :param pixel_shader: The pixel shader that produces colors from values +//| :param radius: The radius of the circle in pixels +//| :param x: Initial x position of the axis. +//| :param y: Initial y position of the axis.""" //| static mp_obj_t vectorio_circle_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_radius }; + enum { ARG_pixel_shader, ARG_radius, ARG_x, ARG_y }; static const mp_arg_t allowed_args[] = { + { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_radius, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, }; 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); @@ -33,9 +40,22 @@ static mp_obj_t vectorio_circle_make_new(const mp_obj_type_t *type, size_t n_arg self->base.type = &vectorio_circle_type; common_hal_vectorio_circle_construct(self, radius); + // VectorShape parts + mp_obj_t pixel_shader = args[ARG_pixel_shader].u_obj; + int16_t x = args[ARG_x].u_int; + int16_t y = args[ARG_y].u_int; + mp_obj_t vector_shape = vectorio_vector_shape_make_new(self, pixel_shader, x, y); + self->draw_protocol_instance = vector_shape; + return MP_OBJ_FROM_PTR(self); } +STATIC const vectorio_draw_protocol_t circle_draw_protocol = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_draw) + .draw_get_protocol_self = (draw_get_protocol_self_fun)common_hal_vectorio_circle_get_draw_protocol, + .draw_protocol_impl = &vectorio_vector_shape_draw_protocol_impl +}; + //| radius : int //| """The radius of the circle in pixels.""" @@ -62,13 +82,22 @@ const mp_obj_property_t vectorio_circle_radius_obj = { STATIC const mp_rom_map_elem_t vectorio_circle_locals_dict_table[] = { + // Properties { MP_ROM_QSTR(MP_QSTR_radius), MP_ROM_PTR(&vectorio_circle_radius_obj) }, + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&vectorio_vector_shape_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_location), MP_ROM_PTR(&vectorio_vector_shape_location_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&vectorio_vector_shape_pixel_shader_obj) }, }; STATIC MP_DEFINE_CONST_DICT(vectorio_circle_locals_dict, vectorio_circle_locals_dict_table); const mp_obj_type_t vectorio_circle_type = { { &mp_type_type }, .name = MP_QSTR_Circle, + .flags = MP_TYPE_FLAG_EXTENDED, .make_new = vectorio_circle_make_new, .locals_dict = (mp_obj_dict_t *)&vectorio_circle_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .protocol = &circle_draw_protocol, + ), }; diff --git a/shared-bindings/vectorio/Circle.h b/shared-bindings/vectorio/Circle.h index e8fc048eb8..37bbe9e65b 100644 --- a/shared-bindings/vectorio/Circle.h +++ b/shared-bindings/vectorio/Circle.h @@ -19,4 +19,6 @@ void common_hal_vectorio_circle_get_area(void *circle, displayio_area_t *out_are int16_t common_hal_vectorio_circle_get_radius(void *circle); void common_hal_vectorio_circle_set_radius(void *circle, int16_t radius); +mp_obj_t common_hal_vectorio_circle_get_draw_protocol(void *circle); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_CIRCLE_H diff --git a/shared-bindings/vectorio/Polygon.c b/shared-bindings/vectorio/Polygon.c index 23c7961ec7..5d493a30b6 100644 --- a/shared-bindings/vectorio/Polygon.c +++ b/shared-bindings/vectorio/Polygon.c @@ -1,6 +1,7 @@ - +#include "shared-bindings/vectorio/__init__.h" #include "shared-module/vectorio/__init__.h" #include "shared-bindings/vectorio/Polygon.h" +#include "shared-bindings/vectorio/VectorShape.h" #include @@ -16,15 +17,21 @@ //| class Polygon: -//| def __init__(self, points: List[Tuple[int, int]]) -> None: +//| def __init__(self, pixel_shader: Union[displayio.ColorConverter, displayio.Palette], points: List[Tuple[int, int]], x: int, y: int) -> None: //| """Represents a closed shape by ordered vertices //| -//| :param points: Vertices for the polygon""" +//| :param pixel_shader: The pixel shader that produces colors from values +//| :param points: Vertices for the polygon +//| :param x: Initial screen x position of the 0,0 origin in the points list. +//| :param y: Initial screen y position of the 0,0 origin in the points list.""" //| static mp_obj_t vectorio_polygon_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_points_list }; + enum { ARG_pixel_shader, ARG_points_list, ARG_x, ARG_y }; static const mp_arg_t allowed_args[] = { + { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_points, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, }; 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); @@ -38,9 +45,22 @@ static mp_obj_t vectorio_polygon_make_new(const mp_obj_type_t *type, size_t n_ar common_hal_vectorio_polygon_construct(self, args[ARG_points_list].u_obj); + // VectorShape parts + mp_obj_t pixel_shader = args[ARG_pixel_shader].u_obj; + int16_t x = args[ARG_x].u_int; + int16_t y = args[ARG_y].u_int; + mp_obj_t vector_shape = vectorio_vector_shape_make_new(self, pixel_shader, x, y); + self->draw_protocol_instance = vector_shape; + return MP_OBJ_FROM_PTR(self); } +STATIC const vectorio_draw_protocol_t polygon_draw_protocol = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_draw) + .draw_get_protocol_self = (draw_get_protocol_self_fun)common_hal_vectorio_polygon_get_draw_protocol, + .draw_protocol_impl = &vectorio_vector_shape_draw_protocol_impl +}; + //| points: List[Tuple[int, int]] //| """Set a new look and shape for this polygon""" @@ -67,13 +87,22 @@ const mp_obj_property_t vectorio_polygon_points_obj = { }; STATIC const mp_rom_map_elem_t vectorio_polygon_locals_dict_table[] = { + // Properties { MP_ROM_QSTR(MP_QSTR_points), MP_ROM_PTR(&vectorio_polygon_points_obj) }, + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&vectorio_vector_shape_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_location), MP_ROM_PTR(&vectorio_vector_shape_location_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&vectorio_vector_shape_pixel_shader_obj) }, }; STATIC MP_DEFINE_CONST_DICT(vectorio_polygon_locals_dict, vectorio_polygon_locals_dict_table); const mp_obj_type_t vectorio_polygon_type = { { &mp_type_type }, .name = MP_QSTR_Polygon, + .flags = MP_TYPE_FLAG_EXTENDED, .make_new = vectorio_polygon_make_new, .locals_dict = (mp_obj_dict_t *)&vectorio_polygon_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .protocol = &polygon_draw_protocol, + ), }; diff --git a/shared-bindings/vectorio/Polygon.h b/shared-bindings/vectorio/Polygon.h index 5594fbec4a..68136be6bd 100644 --- a/shared-bindings/vectorio/Polygon.h +++ b/shared-bindings/vectorio/Polygon.h @@ -20,5 +20,7 @@ void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *out_a mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self); void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list); +mp_obj_t common_hal_vectorio_polygon_get_draw_protocol(void *polygon); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_POLYGON_H diff --git a/shared-bindings/vectorio/Rectangle.c b/shared-bindings/vectorio/Rectangle.c index a50a8a14b3..aa2c166a6d 100644 --- a/shared-bindings/vectorio/Rectangle.c +++ b/shared-bindings/vectorio/Rectangle.c @@ -1,5 +1,7 @@ - +#include "shared-bindings/vectorio/__init__.h" #include "shared-bindings/vectorio/Rectangle.h" +#include "shared-module/vectorio/VectorShape.h" +#include "shared-bindings/vectorio/VectorShape.h" #include @@ -8,17 +10,23 @@ #include "supervisor/shared/translate.h" //| class Rectangle: -//| def __init__(self, width: int, height: int) -> None: +//| def __init__(self, pixel_shader: Union[displayio.ColorConverter, displayio.Palette], width: int, height: int, x: int, y: int) -> None: //| """Represents a rectangle by defining its bounds //| +//| :param pixel_shader: The pixel shader that produces colors from values //| :param width: The number of pixels wide -//| :param height: The number of pixels high""" +//| :param height: The number of pixels high +//| :param x: Initial x position of the top left corner. +//| :param y: Initial y position of the top left corner.""" //| static mp_obj_t vectorio_rectangle_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_width, ARG_height }; + enum { ARG_pixel_shader, ARG_width, ARG_height, ARG_x, ARG_y }; static const mp_arg_t allowed_args[] = { + { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, }; 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); @@ -36,17 +44,38 @@ static mp_obj_t vectorio_rectangle_make_new(const mp_obj_type_t *type, size_t n_ self->base.type = &vectorio_rectangle_type; common_hal_vectorio_rectangle_construct(self, width, height); + // VectorShape parts + mp_obj_t pixel_shader = args[ARG_pixel_shader].u_obj; + int16_t x = args[ARG_x].u_int; + int16_t y = args[ARG_y].u_int; + mp_obj_t vector_shape = vectorio_vector_shape_make_new(self, pixel_shader, x, y); + self->draw_protocol_instance = vector_shape; + return MP_OBJ_FROM_PTR(self); } +STATIC const vectorio_draw_protocol_t rectangle_draw_protocol = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_draw) + .draw_get_protocol_self = (draw_get_protocol_self_fun)common_hal_vectorio_rectangle_get_draw_protocol, + .draw_protocol_impl = &vectorio_vector_shape_draw_protocol_impl +}; STATIC const mp_rom_map_elem_t vectorio_rectangle_locals_dict_table[] = { + // Properties + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&vectorio_vector_shape_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_location), MP_ROM_PTR(&vectorio_vector_shape_location_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&vectorio_vector_shape_pixel_shader_obj) }, }; STATIC MP_DEFINE_CONST_DICT(vectorio_rectangle_locals_dict, vectorio_rectangle_locals_dict_table); const mp_obj_type_t vectorio_rectangle_type = { { &mp_type_type }, .name = MP_QSTR_Rectangle, + .flags = MP_TYPE_FLAG_EXTENDED, .make_new = vectorio_rectangle_make_new, .locals_dict = (mp_obj_dict_t *)&vectorio_rectangle_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .protocol = &rectangle_draw_protocol, + ), }; diff --git a/shared-bindings/vectorio/Rectangle.h b/shared-bindings/vectorio/Rectangle.h index bb461ed9d5..24b103c26e 100644 --- a/shared-bindings/vectorio/Rectangle.h +++ b/shared-bindings/vectorio/Rectangle.h @@ -12,4 +12,6 @@ uint32_t common_hal_vectorio_rectangle_get_pixel(void *rectangle, int16_t x, int void common_hal_vectorio_rectangle_get_area(void *rectangle, displayio_area_t *out_area); +mp_obj_t common_hal_vectorio_rectangle_get_draw_protocol(void *rectangle); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_RECTANGLE_H diff --git a/shared-bindings/vectorio/VectorShape.c b/shared-bindings/vectorio/VectorShape.c index ae8532eb95..6e4299a6d3 100644 --- a/shared-bindings/vectorio/VectorShape.c +++ b/shared-bindings/vectorio/VectorShape.c @@ -19,37 +19,16 @@ #include "supervisor/shared/translate.h" -//| class VectorShape: -//| def __init__(self, shape: Union[Polygon, Rectangle, Circle], pixel_shader: Union[displayio.ColorConverter, displayio.Palette], x: int=0, y: int=0) -> None: -//| """Binds a vector shape to a location and pixel color -//| -//| :param shape: The shape to draw. -//| :param pixel_shader: The pixel shader that produces colors from values -//| :param x: Initial x position of the center axis of the shape within the parent. -//| :param y: Initial y position of the center axis of the shape within the parent.""" -//| ... -//| -STATIC mp_obj_t vectorio_vector_shape_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_shape, ARG_pixel_shader, ARG_x, ARG_y }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_shape, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, - { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, - { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, - { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, - }; - 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); - - mp_obj_t pixel_shader = args[ARG_pixel_shader].u_obj; +// shape: The shape implementation to draw. +// pixel_shader: The pixel shader that produces colors from values. The shader can be a displayio.Palette(1); it will be asked to color pixel value 0. +// x: Initial x position of the center axis of the shape within the parent. +// y: Initial y position of the center axis of the shape within the parent.""" +mp_obj_t vectorio_vector_shape_make_new(const mp_obj_t shape, const mp_obj_t pixel_shader, int16_t x, int16_t y) { if (!mp_obj_is_type(pixel_shader, &displayio_colorconverter_type) && !mp_obj_is_type(pixel_shader, &displayio_palette_type)) { mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_pixel_shader); } - int16_t x = args[ARG_x].u_int; - int16_t y = args[ARG_y].u_int; - - mp_obj_t shape = args[ARG_shape].u_obj; vectorio_ishape_t ishape; // Wire up shape functions if (mp_obj_is_type(shape, &vectorio_polygon_type)) { @@ -92,18 +71,32 @@ STATIC mp_obj_t vectorio_vector_shape_make_new(const mp_obj_type_t *type, size_t return MP_OBJ_FROM_PTR(self); } +vectorio_draw_protocol_impl_t vectorio_vector_shape_draw_protocol_impl = { + .draw_fill_area = (draw_fill_area_fun)vectorio_vector_shape_fill_area, + .draw_get_dirty_area = (draw_get_dirty_area_fun)vectorio_vector_shape_get_dirty_area, + .draw_update_transform = (draw_update_transform_fun)vectorio_vector_shape_update_transform, + .draw_finish_refresh = (draw_finish_refresh_fun)vectorio_vector_shape_finish_refresh, + .draw_get_refresh_areas = (draw_get_refresh_areas_fun)vectorio_vector_shape_get_refresh_areas, +}; + + +// Stub checker does not approve of these shared properties. +// x: int +// """X position of the center point of the shape in the parent.""" +// +STATIC mp_obj_t vectorio_vector_shape_obj_get_x(mp_obj_t wrapper_shape) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); -//| x: int -//| """X position of the center point of the shape in the parent.""" -//| -STATIC mp_obj_t vectorio_vector_shape_obj_get_x(mp_obj_t self_in) { - vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); return MP_OBJ_NEW_SMALL_INT(common_hal_vectorio_vector_shape_get_x(self)); } MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_x_obj, vectorio_vector_shape_obj_get_x); -STATIC mp_obj_t vectorio_vector_shape_obj_set_x(mp_obj_t self_in, mp_obj_t x_obj) { - vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); +STATIC mp_obj_t vectorio_vector_shape_obj_set_x(mp_obj_t wrapper_shape, mp_obj_t x_obj) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); mp_int_t x = mp_obj_get_int(x_obj); common_hal_vectorio_vector_shape_set_x(self, x); @@ -119,17 +112,22 @@ const mp_obj_property_t vectorio_vector_shape_x_obj = { }; -//| y: int -//| """Y position of the center point of the shape in the parent.""" -//| -STATIC mp_obj_t vectorio_vector_shape_obj_get_y(mp_obj_t self_in) { - vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); +// y: int +// """Y position of the center point of the shape in the parent.""" +// +STATIC mp_obj_t vectorio_vector_shape_obj_get_y(mp_obj_t wrapper_shape) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + return MP_OBJ_NEW_SMALL_INT(common_hal_vectorio_vector_shape_get_y(self)); } MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_y_obj, vectorio_vector_shape_obj_get_y); -STATIC mp_obj_t vectorio_vector_shape_obj_set_y(mp_obj_t self_in, mp_obj_t y_obj) { - vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); +STATIC mp_obj_t vectorio_vector_shape_obj_set_y(mp_obj_t wrapper_shape, mp_obj_t y_obj) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); mp_int_t y = mp_obj_get_int(y_obj); common_hal_vectorio_vector_shape_set_y(self, y); @@ -145,17 +143,53 @@ const mp_obj_property_t vectorio_vector_shape_y_obj = { }; -//| pixel_shader: Union[displayio.ColorConverter, displayio.Palette] -//| """The pixel shader of the shape.""" -//| -STATIC mp_obj_t vectorio_vector_shape_obj_get_pixel_shader(mp_obj_t self_in) { - vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); +// location: Tuple[int, int] +// """location of the center point of the shape in the parent.""" +// +STATIC mp_obj_t vectorio_vector_shape_obj_get_location(mp_obj_t wrapper_shape) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + + return MP_OBJ_TO_PTR(common_hal_vectorio_vector_shape_get_location(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_location_obj, vectorio_vector_shape_obj_get_location); + +STATIC mp_obj_t vectorio_vector_shape_obj_set_location(mp_obj_t wrapper_shape, mp_obj_t location_obj) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + + common_hal_vectorio_vector_shape_set_location(self, location_obj); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_vector_shape_set_location_obj, vectorio_vector_shape_obj_set_location); + +const mp_obj_property_t vectorio_vector_shape_location_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&vectorio_vector_shape_get_location_obj, + (mp_obj_t)&vectorio_vector_shape_set_location_obj, + MP_ROM_NONE}, +}; + + +// pixel_shader: Union[ColorConverter, Palette] +// """The pixel shader of the shape.""" +// +STATIC mp_obj_t vectorio_vector_shape_obj_get_pixel_shader(mp_obj_t wrapper_shape) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + return common_hal_vectorio_vector_shape_get_pixel_shader(self); } MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_pixel_shader_obj, vectorio_vector_shape_obj_get_pixel_shader); -STATIC mp_obj_t vectorio_vector_shape_obj_set_pixel_shader(mp_obj_t self_in, mp_obj_t pixel_shader) { - vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); +STATIC mp_obj_t vectorio_vector_shape_obj_set_pixel_shader(mp_obj_t wrapper_shape, mp_obj_t pixel_shader) { + // Relies on the fact that only vector_shape impl gets matched with a VectorShape. + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape); + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape)); + if (!mp_obj_is_type(pixel_shader, &displayio_palette_type) && !mp_obj_is_type(pixel_shader, &displayio_colorconverter_type)) { mp_raise_TypeError(translate("pixel_shader must be displayio.Palette or displayio.ColorConverter")); } @@ -175,16 +209,11 @@ const mp_obj_property_t vectorio_vector_shape_pixel_shader_obj = { STATIC const mp_rom_map_elem_t vectorio_vector_shape_locals_dict_table[] = { - // Properties - { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) }, - { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&vectorio_vector_shape_y_obj) }, - { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&vectorio_vector_shape_pixel_shader_obj) }, }; STATIC MP_DEFINE_CONST_DICT(vectorio_vector_shape_locals_dict, vectorio_vector_shape_locals_dict_table); const mp_obj_type_t vectorio_vector_shape_type = { { &mp_type_type }, .name = MP_QSTR_VectorShape, - .make_new = vectorio_vector_shape_make_new, .locals_dict = (mp_obj_dict_t *)&vectorio_vector_shape_locals_dict, }; diff --git a/shared-bindings/vectorio/VectorShape.h b/shared-bindings/vectorio/VectorShape.h index fa11f73ffd..9ac6906000 100644 --- a/shared-bindings/vectorio/VectorShape.h +++ b/shared-bindings/vectorio/VectorShape.h @@ -1,11 +1,19 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H #define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H +#include "py/objproperty.h" +#include "py/objtuple.h" + +#include "shared-bindings/vectorio/__init__.h" #include "shared-module/vectorio/VectorShape.h" #include "shared-module/displayio/area.h" extern const mp_obj_type_t vectorio_vector_shape_type; +// Python shared bindings constructor +mp_obj_t vectorio_vector_shape_make_new(const mp_obj_t shape, const mp_obj_t pixel_shader, int16_t x, int16_t y); + +// C data constructor void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self, vectorio_ishape_t ishape, mp_obj_t pixel_shader, uint16_t x, uint16_t y); @@ -15,13 +23,22 @@ void common_hal_vectorio_vector_shape_set_dirty(void *self); mp_int_t common_hal_vectorio_vector_shape_get_x(vectorio_vector_shape_t *self); void common_hal_vectorio_vector_shape_set_x(vectorio_vector_shape_t *self, mp_int_t x); +mp_obj_tuple_t *common_hal_vectorio_vector_shape_get_location(vectorio_vector_shape_t *self); +void common_hal_vectorio_vector_shape_set_location(vectorio_vector_shape_t *self, mp_obj_t xy); + mp_int_t common_hal_vectorio_vector_shape_get_y(vectorio_vector_shape_t *self); void common_hal_vectorio_vector_shape_set_y(vectorio_vector_shape_t *self, mp_int_t y); mp_obj_t common_hal_vectorio_vector_shape_get_pixel_shader(vectorio_vector_shape_t *self); void common_hal_vectorio_vector_shape_set_pixel_shader(vectorio_vector_shape_t *self, mp_obj_t pixel_shader); - void vectorio_vector_shape_update_transform(vectorio_vector_shape_t *self, displayio_buffer_transform_t *group_transform); +// Composable property definition for shapes that use VectorShape +extern vectorio_draw_protocol_impl_t vectorio_vector_shape_draw_protocol_impl; +extern const mp_obj_property_t vectorio_vector_shape_x_obj; +extern const mp_obj_property_t vectorio_vector_shape_y_obj; +extern const mp_obj_property_t vectorio_vector_shape_location_obj; +extern const mp_obj_property_t vectorio_vector_shape_pixel_shader_obj; + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H diff --git a/shared-bindings/vectorio/__init__.c b/shared-bindings/vectorio/__init__.c index 12fb4d72b1..5a3729573e 100644 --- a/shared-bindings/vectorio/__init__.c +++ b/shared-bindings/vectorio/__init__.c @@ -6,7 +6,6 @@ #include "shared-bindings/vectorio/Circle.h" #include "shared-bindings/vectorio/Polygon.h" #include "shared-bindings/vectorio/Rectangle.h" -#include "shared-bindings/vectorio/VectorShape.h" //| """Lightweight 2d shapes for displays""" //| @@ -16,7 +15,6 @@ STATIC const mp_rom_map_elem_t vectorio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Circle), MP_ROM_PTR(&vectorio_circle_type) }, { MP_ROM_QSTR(MP_QSTR_Polygon), MP_ROM_PTR(&vectorio_polygon_type) }, { MP_ROM_QSTR(MP_QSTR_Rectangle), MP_ROM_PTR(&vectorio_rectangle_type) }, - { MP_ROM_QSTR(MP_QSTR_VectorShape), MP_ROM_PTR(&vectorio_vector_shape_type) }, }; STATIC MP_DEFINE_CONST_DICT(vectorio_module_globals, vectorio_module_globals_table); diff --git a/shared-bindings/vectorio/__init__.h b/shared-bindings/vectorio/__init__.h new file mode 100644 index 0000000000..a34195de26 --- /dev/null +++ b/shared-bindings/vectorio/__init__.h @@ -0,0 +1,41 @@ +#ifndef SHARED_MODULE_VECTORIO__INIT__H +#define SHARED_MODULE_VECTORIO__INIT__H + +#include +#include + +#include "py/obj.h" +#include "py/proto.h" + +#include "shared-module/displayio/area.h" +#include "shared-module/displayio/Palette.h" + +// Returns the object on which the rest of the draw protocol methods are invoked. +typedef mp_obj_t (*draw_get_protocol_self_fun)(mp_obj_t protocol_container); + +typedef bool (*draw_fill_area_fun)(mp_obj_t draw_protocol_self, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer); +typedef bool (*draw_get_dirty_area_fun)(mp_obj_t draw_protocol_self, displayio_area_t *current_dirty_area); +typedef void (*draw_update_transform_fun)(mp_obj_t draw_protocol_self, displayio_buffer_transform_t *group_transform); +typedef void (*draw_finish_refresh_fun)(mp_obj_t draw_protocol_self); +typedef displayio_area_t *(*draw_get_refresh_areas_fun)(mp_obj_t draw_protocol_self, displayio_area_t *tail); + +typedef struct _vectorio_draw_protocol_impl_t { + draw_fill_area_fun draw_fill_area; + draw_get_dirty_area_fun draw_get_dirty_area; + draw_update_transform_fun draw_update_transform; + draw_finish_refresh_fun draw_finish_refresh; + draw_get_refresh_areas_fun draw_get_refresh_areas; +} vectorio_draw_protocol_impl_t; + +// Draw protocol +typedef struct _vectorio_draw_protocol_t { + MP_PROTOCOL_HEAD // MP_QSTR_protocol_draw + + // Instance of the draw protocol + draw_get_protocol_self_fun draw_get_protocol_self; + + // Implementation functions for the draw protocol + vectorio_draw_protocol_impl_t *draw_protocol_impl; +} vectorio_draw_protocol_t; + +#endif diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index ec8ad7ba5a..b9179f0a00 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -144,10 +144,10 @@ static void _update_child_transforms(displayio_group_t *self) { for (size_t i = 0; i < self->members->len; i++) { mp_obj_t layer; #if CIRCUITPY_VECTORIO - layer = mp_obj_cast_to_native_base( - self->members->items[i], &vectorio_vector_shape_type); - if (layer != MP_OBJ_NULL) { - vectorio_vector_shape_update_transform(layer, &self->absolute_transform); + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, self->members->items[i]); + if (draw_protocol != NULL) { + layer = draw_protocol->draw_get_protocol_self(self->members->items[i]); + draw_protocol->draw_protocol_impl->draw_update_transform(layer, &self->absolute_transform); continue; } #endif @@ -241,15 +241,14 @@ void common_hal_displayio_group_set_y(displayio_group_t *self, mp_int_t y) { } static void _add_layer(displayio_group_t *self, mp_obj_t layer) { - mp_obj_t native_layer; #if CIRCUITPY_VECTORIO - native_layer = mp_obj_cast_to_native_base(layer, &vectorio_vector_shape_type); - if (native_layer != MP_OBJ_NULL) { - vectorio_vector_shape_update_transform(native_layer, &self->absolute_transform); + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, layer); + if (draw_protocol != NULL) { + draw_protocol->draw_protocol_impl->draw_update_transform(draw_protocol->draw_get_protocol_self(layer), &self->absolute_transform); return; } #endif - native_layer = mp_obj_cast_to_native_base(layer, &displayio_tilegrid_type); + mp_obj_t native_layer = mp_obj_cast_to_native_base(layer, &displayio_tilegrid_type); if (native_layer != MP_OBJ_NULL) { displayio_tilegrid_t *tilegrid = native_layer; if (tilegrid->in_group) { @@ -283,12 +282,12 @@ static void _remove_layer(displayio_group_t *self, size_t index) { displayio_area_t layer_area; bool rendered_last_frame = false; #if CIRCUITPY_VECTORIO - layer = mp_obj_cast_to_native_base( - self->members->items[index], &vectorio_vector_shape_type); - if (layer != MP_OBJ_NULL) { - bool has_dirty_area = vectorio_vector_shape_get_dirty_area(layer, &layer_area); + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, self->members->items[index]); + if (draw_protocol != NULL) { + layer = draw_protocol->draw_get_protocol_self(self->members->items[index]); + bool has_dirty_area = draw_protocol->draw_protocol_impl->draw_get_dirty_area(layer, &layer_area); rendered_last_frame = has_dirty_area; - vectorio_vector_shape_update_transform(layer, NULL); + draw_protocol->draw_protocol_impl->draw_update_transform(layer, NULL); } #endif layer = mp_obj_cast_to_native_base( @@ -362,10 +361,10 @@ bool displayio_group_fill_area(displayio_group_t *self, const _displayio_colorsp for (int32_t i = self->members->len - 1; i >= 0; i--) { mp_obj_t layer; #if CIRCUITPY_VECTORIO - layer = mp_obj_cast_to_native_base( - self->members->items[i], &vectorio_vector_shape_type); - if (layer != MP_OBJ_NULL) { - if (vectorio_vector_shape_fill_area(layer, colorspace, area, mask, buffer)) { + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, self->members->items[i]); + if (draw_protocol != NULL) { + layer = draw_protocol->draw_get_protocol_self(self->members->items[i]); + if (draw_protocol->draw_protocol_impl->draw_fill_area(layer, colorspace, area, mask, buffer)) { return true; } continue; @@ -396,10 +395,10 @@ void displayio_group_finish_refresh(displayio_group_t *self) { for (int32_t i = self->members->len - 1; i >= 0; i--) { mp_obj_t layer; #if CIRCUITPY_VECTORIO - layer = mp_obj_cast_to_native_base( - self->members->items[i], &vectorio_vector_shape_type); - if (layer != MP_OBJ_NULL) { - vectorio_vector_shape_finish_refresh(layer); + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, self->members->items[i]); + if (draw_protocol != NULL) { + layer = draw_protocol->draw_get_protocol_self(self->members->items[i]); + draw_protocol->draw_protocol_impl->draw_finish_refresh(layer); continue; } #endif @@ -427,10 +426,10 @@ displayio_area_t *displayio_group_get_refresh_areas(displayio_group_t *self, dis for (int32_t i = self->members->len - 1; i >= 0; i--) { mp_obj_t layer; #if CIRCUITPY_VECTORIO - layer = mp_obj_cast_to_native_base( - self->members->items[i], &vectorio_vector_shape_type); - if (layer != MP_OBJ_NULL) { - tail = vectorio_vector_shape_get_refresh_areas(layer, tail); + const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, self->members->items[i]); + if (draw_protocol != NULL) { + layer = draw_protocol->draw_get_protocol_self(self->members->items[i]); + tail = draw_protocol->draw_protocol_impl->draw_get_refresh_areas(layer, tail); continue; } #endif diff --git a/shared-module/vectorio/Circle.c b/shared-module/vectorio/Circle.c index 9c74580650..6b4c441620 100644 --- a/shared-module/vectorio/Circle.c +++ b/shared-module/vectorio/Circle.c @@ -59,3 +59,8 @@ void common_hal_vectorio_circle_set_radius(void *obj, int16_t radius) { self->on_dirty.event(self->on_dirty.obj); } } + +mp_obj_t common_hal_vectorio_circle_get_draw_protocol(void *circle) { + vectorio_circle_t *self = circle; + return self->draw_protocol_instance; +} diff --git a/shared-module/vectorio/Circle.h b/shared-module/vectorio/Circle.h index d6a77b1667..106bca6a71 100644 --- a/shared-module/vectorio/Circle.h +++ b/shared-module/vectorio/Circle.h @@ -11,6 +11,7 @@ typedef struct { mp_obj_base_t base; uint16_t radius; vectorio_event_t on_dirty; + mp_obj_t draw_protocol_instance; } vectorio_circle_t; #endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_CIRCLE_H diff --git a/shared-module/vectorio/Polygon.c b/shared-module/vectorio/Polygon.c index 00af1e0d7e..f7199c7f83 100644 --- a/shared-module/vectorio/Polygon.c +++ b/shared-module/vectorio/Polygon.c @@ -1,4 +1,3 @@ - #include "shared-module/vectorio/__init__.h" #include "shared-bindings/vectorio/Polygon.h" #include "shared-module/displayio/area.h" @@ -11,7 +10,7 @@ #define VECTORIO_POLYGON_DEBUG(...) (void)0 -// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) +// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__) // Converts a list of points tuples to a flat list of ints for speedier internal use. @@ -31,12 +30,12 @@ static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple VECTORIO_POLYGON_DEBUG("free(%d), ", sizeof(self->points_list)); gc_free(self->points_list); } - self->points_list = gc_alloc(2 * len * sizeof(int), false, false); - VECTORIO_POLYGON_DEBUG("alloc(%p, %d)", self->points_list, 2 * len * sizeof(int)); + self->points_list = gc_alloc(2 * len * sizeof(uint16_t), false, false); + VECTORIO_POLYGON_DEBUG("alloc(%p, %d)", self->points_list, 2 * len * sizeof(uint16_t)); } self->len = 2 * len; - for (size_t i = 0; i < len; ++i) { + for (uint16_t i = 0; i < len; ++i) { size_t tuple_len = 0; mp_obj_t *tuple_items; mp_obj_tuple_get(items[i], &tuple_len, &tuple_items); @@ -44,14 +43,19 @@ static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple if (tuple_len != 2) { mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point); } - if (!mp_obj_get_int_maybe(tuple_items[ 0 ], &self->points_list[2 * i ]) - || !mp_obj_get_int_maybe(tuple_items[ 1 ], &self->points_list[2 * i + 1]) + mp_int_t x; + mp_int_t y; + if (!mp_obj_get_int_maybe(tuple_items[ 0 ], &x) + || !mp_obj_get_int_maybe(tuple_items[ 1 ], &y) + || x < SHRT_MIN || x > SHRT_MAX || y < SHRT_MIN || y > SHRT_MAX ) { - self->len = 0; gc_free(self->points_list); self->points_list = NULL; mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); + self->len = 0; } + self->points_list[2 * i ] = (int16_t)x; + self->points_list[2 * i + 1] = (int16_t)y; } } @@ -69,16 +73,23 @@ void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t po mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self) { VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_get_points {len: %d, points_list: %p}\n", self, self->len, self->points_list); - mp_obj_t list = mp_obj_new_list(self->len / 2, NULL); + mp_obj_list_t *list = MP_OBJ_TO_PTR(mp_obj_new_list(0, NULL)); + + VECTORIO_POLYGON_DEBUG(" >points\n"); + for (uint16_t i = 0; i < self->len; i += 2) { + VECTORIO_POLYGON_DEBUG(" (%4d, %4d)\n", self->points_list[i], self->points_list[i + 1]); + + mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + pair->items[0] = mp_obj_new_int((mp_int_t)self->points_list[i ]); + pair->items[1] = mp_obj_new_int((mp_int_t)self->points_list[i + 1]); - for (size_t i = 0; i < self->len; i += 2) { - mp_obj_t tuple[] = { mp_obj_new_int(self->points_list[i]), mp_obj_new_int(self->points_list[i + 1]) }; mp_obj_list_append( list, - mp_obj_new_tuple(2, tuple) + pair ); } - return list; + VECTORIO_POLYGON_DEBUG(" x1 = SHRT_MAX; area->y1 = SHRT_MAX; area->x2 = SHRT_MIN; area->y2 = SHRT_MIN; - for (size_t i = 0; i < self->len; ++i) { - int x = self->points_list[i]; + for (uint16_t i = 0; i < self->len; ++i) { + int16_t x = self->points_list[i]; ++i; - int y = self->points_list[i]; - if (x <= area->x1) { - area->x1 = x - 1; + int16_t y = self->points_list[i]; + if (x < area->x1) { + VECTORIO_POLYGON_DEBUG(" x1: %d\n", x); + area->x1 = x; } - if (y <= area->y1) { - area->y1 = y - 1; + if (y < area->y1) { + VECTORIO_POLYGON_DEBUG(" y1: %d\n", y); + area->y1 = y; } - if (x >= area->x2) { - area->x2 = x + 1; + if (x > area->x2) { + VECTORIO_POLYGON_DEBUG(" x2: %d\n", x); + area->x2 = x; } - if (y >= area->y2) { - area->y2 = y + 1; + if (y > area->y2) { + VECTORIO_POLYGON_DEBUG(" y2: %d\n", y); + area->y2 = y; } } } @@ -127,7 +143,7 @@ void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *area) // <0 if the point is to the left of the line vector // 0 if the point is on the line // >0 if the point is to the right of the line vector -__attribute__((always_inline)) static inline int line_side(mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, int16_t px, int16_t py) { +__attribute__((always_inline)) static inline int line_side(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t px, int16_t py) { return (px - x1) * (y2 - y1) - (py - y1) * (x2 - x1); } @@ -141,14 +157,14 @@ uint32_t common_hal_vectorio_polygon_get_pixel(void *obj, int16_t x, int16_t y) return 0; } - int winding_number = 0; - int x1 = self->points_list[0]; - int y1 = self->points_list[1]; - for (size_t i = 2; i <= self->len + 1; ++i) { + int16_t winding_number = 0; + int16_t x1 = self->points_list[0]; + int16_t y1 = self->points_list[1]; + for (uint16_t i = 2; i <= self->len + 1; ++i) { VECTORIO_POLYGON_DEBUG(" {(%3d, %3d),", x1, y1); - int x2 = self->points_list[i % self->len]; + int16_t x2 = self->points_list[i % self->len]; ++i; - int y2 = self->points_list[i % self->len]; + int16_t y2 = self->points_list[i % self->len]; VECTORIO_POLYGON_DEBUG(" (%3d, %3d)}\n", x2, y2); if (y1 <= y) { if (y2 > y && line_side(x1, y1, x2, y2, x, y) < 0) { @@ -167,3 +183,8 @@ uint32_t common_hal_vectorio_polygon_get_pixel(void *obj, int16_t x, int16_t y) } return winding_number == 0 ? 0 : 1; } + +mp_obj_t common_hal_vectorio_polygon_get_draw_protocol(void *polygon) { + vectorio_polygon_t *self = polygon; + return self->draw_protocol_instance; +} diff --git a/shared-module/vectorio/Polygon.h b/shared-module/vectorio/Polygon.h index 70de9036d7..e1d94f9f97 100644 --- a/shared-module/vectorio/Polygon.h +++ b/shared-module/vectorio/Polygon.h @@ -9,9 +9,10 @@ typedef struct { mp_obj_base_t base; // An int array[ x, y, ... ] - int *points_list; - size_t len; + int16_t *points_list; + uint16_t len; vectorio_event_t on_dirty; + mp_obj_t draw_protocol_instance; } vectorio_polygon_t; #endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_POLYGON_H diff --git a/shared-module/vectorio/Rectangle.c b/shared-module/vectorio/Rectangle.c index 56be5ebb5e..2471daa608 100644 --- a/shared-module/vectorio/Rectangle.c +++ b/shared-module/vectorio/Rectangle.c @@ -12,10 +12,10 @@ void common_hal_vectorio_rectangle_construct(vectorio_rectangle_t *self, uint32_ uint32_t common_hal_vectorio_rectangle_get_pixel(void *obj, int16_t x, int16_t y) { vectorio_rectangle_t *self = obj; - if (x < 0 || x >= self->width || y >= self->height || y < 0) { - return 0; + if (x >= 0 && y >= 0 && x < self->width && y < self->height) { + return 1; } - return 1; + return 0; } @@ -32,3 +32,8 @@ uint32_t common_hal_vectorio_rectangle_get_height(void *rectangle) { vectorio_rectangle_t *self = rectangle; return self->height; } + +mp_obj_t common_hal_vectorio_rectangle_get_draw_protocol(void *rectangle) { + vectorio_rectangle_t *self = rectangle; + return self->draw_protocol_instance; +} diff --git a/shared-module/vectorio/Rectangle.h b/shared-module/vectorio/Rectangle.h index 56342a6d76..e3cfbb2676 100644 --- a/shared-module/vectorio/Rectangle.h +++ b/shared-module/vectorio/Rectangle.h @@ -9,6 +9,7 @@ typedef struct { mp_obj_base_t base; uint16_t width; uint16_t height; + mp_obj_t draw_protocol_instance; } vectorio_rectangle_t; #endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_RECTANGLE_H diff --git a/shared-module/vectorio/VectorShape.c b/shared-module/vectorio/VectorShape.c index f0b6a725c1..9d0279cf14 100644 --- a/shared-module/vectorio/VectorShape.c +++ b/shared-module/vectorio/VectorShape.c @@ -15,16 +15,51 @@ // Lifecycle actions. #define VECTORIO_SHAPE_DEBUG(...) (void)0 -// #define VECTORIO_SHAPE_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) +// #define VECTORIO_SHAPE_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__) // Used in both logging and ifdefs, for extra variables -// #define VECTORIO_PERF(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) +// #define VECTORIO_PERF(...) mp_printf(&mp_plat_print, __VA_ARGS__) // Really verbose. #define VECTORIO_SHAPE_PIXEL_DEBUG(...) (void)0 -// #define VECTORIO_SHAPE_PIXEL_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) +// #define VECTORIO_SHAPE_PIXEL_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__) + +#define U32_TO_BINARY_FMT "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c" +#define U32_TO_BINARY(u32) \ + (u32 & 0x80000000 ? '1' : '0'), \ + (u32 & 0x40000000 ? '1' : '0'), \ + (u32 & 0x20000000 ? '1' : '0'), \ + (u32 & 0x10000000 ? '1' : '0'), \ + (u32 & 0x8000000 ? '1' : '0'), \ + (u32 & 0x4000000 ? '1' : '0'), \ + (u32 & 0x2000000 ? '1' : '0'), \ + (u32 & 0x1000000 ? '1' : '0'), \ + (u32 & 0x800000 ? '1' : '0'), \ + (u32 & 0x400000 ? '1' : '0'), \ + (u32 & 0x200000 ? '1' : '0'), \ + (u32 & 0x100000 ? '1' : '0'), \ + (u32 & 0x80000 ? '1' : '0'), \ + (u32 & 0x40000 ? '1' : '0'), \ + (u32 & 0x20000 ? '1' : '0'), \ + (u32 & 0x10000 ? '1' : '0'), \ + (u32 & 0x8000 ? '1' : '0'), \ + (u32 & 0x4000 ? '1' : '0'), \ + (u32 & 0x2000 ? '1' : '0'), \ + (u32 & 0x1000 ? '1' : '0'), \ + (u32 & 0x800 ? '1' : '0'), \ + (u32 & 0x400 ? '1' : '0'), \ + (u32 & 0x200 ? '1' : '0'), \ + (u32 & 0x100 ? '1' : '0'), \ + (u32 & 0x80 ? '1' : '0'), \ + (u32 & 0x40 ? '1' : '0'), \ + (u32 & 0x20 ? '1' : '0'), \ + (u32 & 0x10 ? '1' : '0'), \ + (u32 & 0x8 ? '1' : '0'), \ + (u32 & 0x4 ? '1' : '0'), \ + (u32 & 0x2 ? '1' : '0'), \ + (u32 & 0x1 ? '1' : '0') inline __attribute__((always_inline)) @@ -32,55 +67,98 @@ static int32_t max(int32_t a, int32_t b) { return a > b ? a : b; } +inline __attribute__((always_inline)) +static uint32_t min(uint32_t a, uint32_t b) { + return a < b ? a : b; +} + +inline __attribute__((always_inline)) +static void area_transpose(displayio_area_t *to_transpose) { + int16_t swap = to_transpose->y1; + to_transpose->y1 = to_transpose->x1; + to_transpose->x1 = swap; + swap = to_transpose->y2; + to_transpose->y2 = to_transpose->x2; + to_transpose->x2 = swap; +} inline __attribute__((always_inline)) static void _get_screen_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) { - VECTORIO_SHAPE_DEBUG("%p get_screen_area tform:{x:%d y:%d dx:%d dy:%d scl:%d w:%d h:%d mx:%d my:%d tr:%d}", self, + VECTORIO_SHAPE_DEBUG("%p get_screen_area (%3d,%3d) tform:{x:%d y:%d dx:%d dy:%d scl:%d w:%d h:%d mx:%d my:%d tr:%d}", self, self->x, self->y, self->absolute_transform->x, self->absolute_transform->y, self->absolute_transform->dx, self->absolute_transform->dy, self->absolute_transform->scale, self->absolute_transform->width, self->absolute_transform->height, self->absolute_transform->mirror_x, self->absolute_transform->mirror_y, self->absolute_transform->transpose_xy ); self->ishape.get_area(self->ishape.shape, out_area); VECTORIO_SHAPE_DEBUG(" in:{(%5d,%5d), (%5d,%5d)}", out_area->x1, out_area->y1, out_area->x2, out_area->y2); + + int16_t x; + int16_t y; if (self->absolute_transform->transpose_xy) { - int16_t swap = out_area->x1; - out_area->x1 = (out_area->y1 + self->y) * self->absolute_transform->dx + self->absolute_transform->x; - out_area->y1 = (swap + self->x) * self->absolute_transform->dy + self->absolute_transform->y; - swap = out_area->x2; - out_area->x2 = (out_area->y2 + self->y) * self->absolute_transform->dx + self->absolute_transform->x; - out_area->y2 = (swap + self->x) * self->absolute_transform->dy + self->absolute_transform->y; + x = self->absolute_transform->x + self->absolute_transform->dx * self->y; + y = self->absolute_transform->y + self->absolute_transform->dy * self->x; + if (self->absolute_transform->dx < 1) { + out_area->y1 = out_area->y1 * -1 + 1; + out_area->y2 = out_area->y2 * -1 + 1; + } + if (self->absolute_transform->dy < 1) { + out_area->x1 = out_area->x1 * -1 + 1; + out_area->x2 = out_area->x2 * -1 + 1; + } + area_transpose(out_area); } else { - out_area->x1 = (out_area->x1 + self->x) * self->absolute_transform->dx + self->absolute_transform->x; - out_area->y1 = (out_area->y1 + self->y) * self->absolute_transform->dy + self->absolute_transform->y; - out_area->x2 = (out_area->x2 + self->x) * self->absolute_transform->dx + self->absolute_transform->x; - out_area->y2 = (out_area->y2 + self->y) * self->absolute_transform->dy + self->absolute_transform->y; + x = self->absolute_transform->x + self->absolute_transform->dx * self->x; + y = self->absolute_transform->y + self->absolute_transform->dy * self->y; + + if (self->absolute_transform->dx < 1) { + out_area->x1 = out_area->x1 * -1 + 1; + out_area->x2 = out_area->x2 * -1 + 1; + } + if (self->absolute_transform->dy < 1) { + out_area->y1 = out_area->y1 * -1 + 1; + out_area->y2 = out_area->y2 * -1 + 1; + } } - // We might have mirrored due to dx displayio_area_canon(out_area); + displayio_area_shift(out_area, x, y); + VECTORIO_SHAPE_DEBUG(" out:{(%5d,%5d), (%5d,%5d)}\n", out_area->x1, out_area->y1, out_area->x2, out_area->y2); } // For use by Group to know where it needs to redraw on layer removal. bool vectorio_vector_shape_get_dirty_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) { - displayio_area_copy(&self->ephemeral_dirty_area, out_area); + out_area->x1 = out_area->x2; + displayio_area_union( + &self->ephemeral_dirty_area, + &self->current_area, + out_area + ); return true; // For now just always redraw. } -// This must be invoked each time a shape changes its position or its shape in any way. +// This must be invoked after each time a shape changes its position, shape or appearance in any way. void common_hal_vectorio_vector_shape_set_dirty(void *vector_shape) { vectorio_vector_shape_t *self = vector_shape; // In screen space. Need to offset the shape space. displayio_area_t current_area; _get_screen_area(self, ¤t_area); - VECTORIO_SHAPE_DEBUG("%p shape_dirty current:{(%3d,%3d), (%3d,%3d)} dirty:{(%3d,%3d), (%3d,%3d)}", + VECTORIO_SHAPE_DEBUG("%p shape_dirty new:{(%3d,%3d), (%3d,%3d)} dirty:{(%3d,%3d), (%3d,%3d)}", self, current_area.x1, current_area.y1, current_area.x2, current_area.y2, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); - self->dirty = true; - // Dirty area tracks the shape's footprint between draws. It's reset on refresh finish, - displayio_area_union(&self->ephemeral_dirty_area, ¤t_area, &self->ephemeral_dirty_area); - VECTORIO_SHAPE_DEBUG(" -> expanded:{(%3d,%3d), (%3d,%3d)}\n", self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); + + bool moved = !displayio_area_equal(¤t_area, &self->current_area); + if (moved) { + displayio_area_union(&self->current_area, &self->ephemeral_dirty_area, &self->ephemeral_dirty_area); + VECTORIO_SHAPE_DEBUG(" stale:{(%3d,%3d), (%3d,%3d)} -> expanded:{(%3d,%3d), (%3d,%3d)}\n", + self->current_area.x1, self->current_area.y1, self->current_area.x2, self->current_area.y2, + self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); + + // Dirty area tracks the shape's footprint between draws. It's reset on refresh finish. + displayio_area_copy(¤t_area, &self->current_area); + } + self->current_area_dirty = true; } @@ -92,10 +170,11 @@ void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self, self->y = y; self->pixel_shader = pixel_shader; self->ishape = ishape; - self->dirty = true; self->absolute_transform = &null_transform; // Critical to have a valid transform before getting screen area. - _get_screen_area(self, &self->ephemeral_dirty_area); + self->ephemeral_dirty_area.x1 = self->ephemeral_dirty_area.x2; // Cheat to set area to 0 self->ephemeral_dirty_area.next = NULL; + self->current_area_dirty = true; + _get_screen_area(self, &self->current_area); } @@ -130,6 +209,37 @@ void common_hal_vectorio_vector_shape_set_y(vectorio_vector_shape_t *self, mp_in common_hal_vectorio_vector_shape_set_dirty(self); } +mp_obj_tuple_t *common_hal_vectorio_vector_shape_get_location(vectorio_vector_shape_t *self) { + VECTORIO_SHAPE_DEBUG("%p get_location\n", self); + mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + pair->items[0] = mp_obj_new_int((mp_int_t)self->x); + pair->items[1] = mp_obj_new_int((mp_int_t)self->y); + return pair; +} + + +void common_hal_vectorio_vector_shape_set_location(vectorio_vector_shape_t *self, mp_obj_t xy) { + VECTORIO_SHAPE_DEBUG("%p set_location\n", self); + size_t tuple_len = 0; + mp_obj_t *tuple_items; + mp_obj_tuple_get(xy, &tuple_len, &tuple_items); + if (tuple_len != 2) { + mp_raise_TypeError_varg(translate("(x,y) integers required")); + } + + mp_int_t x; + mp_int_t y; + if (!mp_obj_get_int_maybe(tuple_items[ 0 ], &x) + || !mp_obj_get_int_maybe(tuple_items[ 1 ], &y) + || x < SHRT_MIN || x > SHRT_MAX || y < SHRT_MIN || y > SHRT_MAX + ) { + mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); + } + self->x = (int16_t)x; + self->y = (int16_t)y; + common_hal_vectorio_vector_shape_set_dirty(self); +} + mp_obj_t common_hal_vectorio_vector_shape_get_pixel_shader(vectorio_vector_shape_t *self) { VECTORIO_SHAPE_DEBUG("%p get_pixel_shader\n", self); @@ -153,13 +263,12 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ uint64_t start = common_hal_time_monotonic_ns(); uint64_t pixel_time = 0; #endif - displayio_area_t overlap; - VECTORIO_SHAPE_DEBUG("%p fill_area dirty:%d fill: {(%5d,%5d), (%5d,%5d)} dirty: {(%5d,%5d), (%5d,%5d)}", - self, self->dirty, - area->x1, area->y1, area->x2, area->y2, - self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2 + VECTORIO_SHAPE_DEBUG("%p fill_area: fill: {(%5d,%5d), (%5d,%5d)}", + self, + area->x1, area->y1, area->x2, area->y2 ); - if (!displayio_area_compute_overlap(area, &self->ephemeral_dirty_area, &overlap)) { + displayio_area_t overlap; + if (!displayio_area_compute_overlap(area, &self->current_area, &overlap)) { VECTORIO_SHAPE_DEBUG(" no overlap\n"); return false; } @@ -168,27 +277,35 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ bool full_coverage = displayio_area_equal(area, &overlap); uint8_t pixels_per_byte = 8 / colorspace->depth; + VECTORIO_SHAPE_DEBUG(" xy:(%3d %3d) tform:{x:%d y:%d dx:%d dy:%d scl:%d w:%d h:%d mx:%d my:%d tr:%d}", + self->x, self->y, + self->absolute_transform->x, self->absolute_transform->y, self->absolute_transform->dx, self->absolute_transform->dy, self->absolute_transform->scale, + self->absolute_transform->width, self->absolute_transform->height, self->absolute_transform->mirror_x, self->absolute_transform->mirror_y, self->absolute_transform->transpose_xy + ); - uint32_t linestride_px = displayio_area_width(area); - uint32_t line_dirty_offset_px = (overlap.y1 - area->y1) * linestride_px; - uint32_t column_dirty_offset_px = overlap.x1 - area->x1; + uint16_t linestride_px = displayio_area_width(area); + uint16_t line_dirty_offset_px = (overlap.y1 - area->y1) * linestride_px; + uint16_t column_dirty_offset_px = overlap.x1 - area->x1; VECTORIO_SHAPE_DEBUG(", linestride:%3d line_offset:%3d col_offset:%3d depth:%2d ppb:%2d shape:%s", linestride_px, line_dirty_offset_px, column_dirty_offset_px, colorspace->depth, pixels_per_byte, mp_obj_get_type_str(self->ishape.shape)); displayio_input_pixel_t input_pixel; displayio_output_pixel_t output_pixel; - uint32_t mask_start_px = line_dirty_offset_px; + displayio_area_t shape_area; + self->ishape.get_area(self->ishape.shape, &shape_area); + + uint16_t mask_start_px = line_dirty_offset_px; for (input_pixel.y = overlap.y1; input_pixel.y < overlap.y2; ++input_pixel.y) { mask_start_px += column_dirty_offset_px; for (input_pixel.x = overlap.x1; input_pixel.x < overlap.x2; ++input_pixel.x) { // Check the mask first to see if the pixel has already been set. - uint32_t pixel_index = mask_start_px + (input_pixel.x - overlap.x1); + uint16_t pixel_index = mask_start_px + (input_pixel.x - overlap.x1); uint32_t *mask_doubleword = &(mask[pixel_index / 32]); uint8_t mask_bit = pixel_index % 32; - VECTORIO_SHAPE_PIXEL_DEBUG("%p pixel_index: %5u mask_bit: %2u", self, pixel_index, mask_bit); + VECTORIO_SHAPE_PIXEL_DEBUG("\n%p pixel_index: %5u mask_bit: %2u mask: "U32_TO_BINARY_FMT, self, pixel_index, mask_bit, U32_TO_BINARY(*mask_doubleword)); if ((*mask_doubleword & (1u << mask_bit)) != 0) { - VECTORIO_SHAPE_PIXEL_DEBUG(" masked\n"); + VECTORIO_SHAPE_PIXEL_DEBUG(" masked"); continue; } output_pixel.pixel = 0; @@ -197,12 +314,39 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ int16_t pixel_to_get_x; int16_t pixel_to_get_y; if (self->absolute_transform->transpose_xy) { - pixel_to_get_x = (input_pixel.y - self->absolute_transform->dy * self->x - self->absolute_transform->y) / self->absolute_transform->dy; - pixel_to_get_y = (input_pixel.x - self->absolute_transform->dx * self->y - self->absolute_transform->x) / self->absolute_transform->dx; + pixel_to_get_x = input_pixel.y - self->absolute_transform->y - self->absolute_transform->dy * self->x; + pixel_to_get_y = input_pixel.x - self->absolute_transform->x - self->absolute_transform->dx * self->y; + + VECTORIO_SHAPE_PIXEL_DEBUG(" a(%3d, %3d)", pixel_to_get_x, pixel_to_get_y); + if (self->absolute_transform->dx < 1) { + pixel_to_get_y *= -1; + } + if (self->absolute_transform->dy < 1) { + pixel_to_get_x *= -1; + } + VECTORIO_SHAPE_PIXEL_DEBUG(" b(%3d, %3d)", pixel_to_get_x, pixel_to_get_y); } else { - pixel_to_get_x = (input_pixel.x - self->absolute_transform->dx * self->x - self->absolute_transform->x) / self->absolute_transform->dx; - pixel_to_get_y = (input_pixel.y - self->absolute_transform->dy * self->y - self->absolute_transform->y) / self->absolute_transform->dy; + pixel_to_get_x = input_pixel.x - self->absolute_transform->x - self->absolute_transform->dx * self->x; + pixel_to_get_y = input_pixel.y - self->absolute_transform->y - self->absolute_transform->dy * self->y; + + VECTORIO_SHAPE_PIXEL_DEBUG(" a(%3d, %3d)", pixel_to_get_x, pixel_to_get_y); + if (self->absolute_transform->dx < 1) { + pixel_to_get_x *= -1; + } + if (self->absolute_transform->dy < 1) { + pixel_to_get_y *= -1; + } + VECTORIO_SHAPE_PIXEL_DEBUG(" b(%3d, %3d)", pixel_to_get_x, pixel_to_get_y); + + // It's mirrored via dx. Maybe we need to add support for also separately mirroring? + // if (self->absolute_transform->mirror_x) { + // pixel_to_get_x = (shape_area.x2 - shape_area.x1) - (pixel_to_get_x - shape_area.x1) + shape_area.x1 - 1; + // } + // if (self->absolute_transform->mirror_y) { + // pixel_to_get_y = (shape_area.y2 - shape_area.y1) - (pixel_to_get_y - shape_area.y1) + +shape_area.y1 - 1; + // } } + VECTORIO_SHAPE_PIXEL_DEBUG(" get_pixel %p (%3d, %3d) -> ( %3d, %3d )", self->ishape.shape, input_pixel.x, input_pixel.y, pixel_to_get_x, pixel_to_get_y); #ifdef VECTORIO_PERF uint64_t pre_pixel = common_hal_time_monotonic_ns(); @@ -214,39 +358,50 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ #endif VECTORIO_SHAPE_PIXEL_DEBUG(" -> %d", input_pixel.pixel); - output_pixel.opaque = true; - if (self->pixel_shader == mp_const_none) { - output_pixel.pixel = input_pixel.pixel; - } else if (mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) { - output_pixel.opaque = displayio_palette_get_color(self->pixel_shader, colorspace, input_pixel.pixel, &output_pixel.pixel); - } else if (mp_obj_is_type(self->pixel_shader, &displayio_colorconverter_type)) { - displayio_colorconverter_convert(self->pixel_shader, colorspace, &input_pixel, &output_pixel); - } - if (!output_pixel.opaque) { - VECTORIO_SHAPE_PIXEL_DEBUG(" (encountered transparent pixel; input area is not fully covered)\n"); + // vectorio shapes use 0 to mean "area is not covered." + // We can skip all the rest of the work for this pixel if it's not currently covered by the shape. + if (input_pixel.pixel == 0) { + VECTORIO_SHAPE_PIXEL_DEBUG(" (encountered transparent pixel; input area is not fully covered)"); full_coverage = false; } else { + // Pixel is not transparent. Let's pull the pixel value index down to 0-base for more error-resistant palettes. + input_pixel.pixel -= 1; + output_pixel.opaque = true; + + if (self->pixel_shader == mp_const_none) { + output_pixel.pixel = input_pixel.pixel; + } else if (mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) { + output_pixel.opaque = displayio_palette_get_color(self->pixel_shader, colorspace, input_pixel.pixel, &output_pixel.pixel); + } else if (mp_obj_is_type(self->pixel_shader, &displayio_colorconverter_type)) { + displayio_colorconverter_convert(self->pixel_shader, colorspace, &input_pixel, &output_pixel); + } + + // We double-check this to fast-path the case when a pixel is not covered by the shape & not call the color converter unnecessarily. + if (!output_pixel.opaque) { + VECTORIO_SHAPE_PIXEL_DEBUG(" (encountered transparent pixel from colorconverter; input area is not fully covered)"); + full_coverage = false; + } + *mask_doubleword |= 1u << mask_bit; if (colorspace->depth == 16) { - VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %04x 16\n", output_pixel.pixel); + VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %04x 16", output_pixel.pixel); *(((uint16_t *)buffer) + pixel_index) = output_pixel.pixel; } else if (colorspace->depth == 8) { - VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %02x 8\n", output_pixel.pixel); + VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %02x 8", output_pixel.pixel); *(((uint8_t *)buffer) + pixel_index) = output_pixel.pixel; } else if (colorspace->depth < 8) { // Reorder the offsets to pack multiple rows into a byte (meaning they share a column). if (!colorspace->pixels_in_byte_share_row) { - uint16_t width = linestride_px; - uint16_t row = pixel_index / width; - uint16_t col = pixel_index % width; - pixel_index = col * pixels_per_byte + (row / pixels_per_byte) * pixels_per_byte * width + row % pixels_per_byte; + uint16_t row = pixel_index / linestride_px; + uint16_t col = pixel_index % linestride_px; + pixel_index = col * pixels_per_byte + (row / pixels_per_byte) * pixels_per_byte * linestride_px + row % pixels_per_byte; } uint8_t shift = (pixel_index % pixels_per_byte) * colorspace->depth; if (colorspace->reverse_pixels_in_byte) { // Reverse the shift by subtracting it from the leftmost shift. shift = (pixels_per_byte - 1) * colorspace->depth - shift; } - VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %2d %d\n", output_pixel.pixel, colorspace->depth); + VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %2d %d", output_pixel.pixel, colorspace->depth); ((uint8_t *)buffer)[pixel_index / pixels_per_byte] |= output_pixel.pixel << shift; } } @@ -265,20 +420,23 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ (double)(pixel_time / 1000.0 / pixels) ); #endif - VECTORIO_SHAPE_DEBUG(" -> pixels:%4d\n"); + VECTORIO_SHAPE_DEBUG(" -> pixels:%4d\n", (overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1)); return full_coverage; } void vectorio_vector_shape_finish_refresh(vectorio_vector_shape_t *self) { - if (!self->dirty) { + if (displayio_area_empty(&self->ephemeral_dirty_area) && !self->current_area_dirty) { return; } VECTORIO_SHAPE_DEBUG("%p finish_refresh was:{(%3d,%3d), (%3d,%3d)}\n", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); - self->dirty = false; - // Reset dirty area tracking to current footprint - _get_screen_area(self, &self->ephemeral_dirty_area); + // Reset dirty area to nothing + self->ephemeral_dirty_area.x1 = self->ephemeral_dirty_area.x2; // Cheat to set area to empty self->ephemeral_dirty_area.next = NULL; + + self->current_area_dirty = false; // We don't clear current area so we can remember what to clean up if we move + self->current_area.next = NULL; + VECTORIO_SHAPE_DEBUG("%p finish_refresh now:{(%3d,%3d), (%3d,%3d)}\n", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); if (mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) { @@ -291,16 +449,55 @@ void vectorio_vector_shape_finish_refresh(vectorio_vector_shape_t *self) { // Assembles a singly linked list of dirty areas from all components on the display. displayio_area_t *vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_t *self, displayio_area_t *tail) { - if (self->dirty + if (self->current_area_dirty || (mp_obj_is_type(self->pixel_shader, &displayio_palette_type) && displayio_palette_needs_refresh(self->pixel_shader)) || (mp_obj_is_type(self->pixel_shader, &displayio_colorconverter_type) && displayio_colorconverter_needs_refresh(self->pixel_shader)) ) { - VECTORIO_SHAPE_DEBUG("%p get_refresh_area dirty:%d {(%3d,%3d), (%3d,%3d)}", self, self->dirty, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); - common_hal_vectorio_vector_shape_set_dirty(self); - // vector.add_to_head + if (!displayio_area_empty(&self->ephemeral_dirty_area)) { + // Both are dirty, check if we should combine the areas or draw separately + // Draws as few pixels as possible both when animations move short distances and large distances. + // The display core implementation currently doesn't combine areas to reduce redrawing of masked areas. If it does, + // this could be simplified to just return the 2 possibly overlapping areas. + displayio_area_t area_swap; + displayio_area_compute_overlap(&self->ephemeral_dirty_area, &self->current_area, &area_swap); + uint32_t overlap_size = displayio_area_size(&area_swap); + displayio_area_union(&self->ephemeral_dirty_area, &self->current_area, &area_swap); // Leave area_swap as the union area for later. + uint32_t union_size = displayio_area_size(&area_swap); + uint32_t current_size = displayio_area_size(&self->current_area); + uint32_t dirty_size = displayio_area_size(&self->ephemeral_dirty_area); + + VECTORIO_SHAPE_DEBUG("%p get_refresh_area: dirty{(%3d,%3d), (%3d,%3d)} + current{(%3d,%3d), (%3d,%3d)} = union{(%3d,%3d), (%3d,%3d)}: union%d - dirty%d - curr%d + overlap%d = excluded%d : ", self, + self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2, + self->current_area.x1, self->current_area.y1, self->current_area.x2, self->current_area.y2, + area_swap.x1, area_swap.y1, area_swap.x2, area_swap.y2, + union_size, dirty_size, current_size, overlap_size, (int32_t)union_size - dirty_size - current_size + overlap_size + ); + + if ((int32_t)union_size - dirty_size - current_size + overlap_size <= min(dirty_size, current_size)) { + // The excluded / non-overlapping area from the disjoint dirty and current areas is smaller + // than the smallest area we need to draw. Redrawing the overlapping area would cost more + // than just drawing the union disjoint area once. + VECTORIO_SHAPE_DEBUG("combining to take disjoint area\n"); + displayio_area_copy(&area_swap, &self->ephemeral_dirty_area); + } else { + // The excluded area between the 2 dirty areas is larger than the smallest dirty area. It would be + // more costly to combine these areas than possibly redraw some overlap. + VECTORIO_SHAPE_DEBUG("excluded area too large, drawing separate area\n"); + self->current_area.next = tail; + tail = &self->current_area; + } + + self->ephemeral_dirty_area.next = tail; + tail = &self->ephemeral_dirty_area; + } else { + self->current_area.next = tail; + tail = &self->current_area; + VECTORIO_SHAPE_DEBUG("%p get_refresh_area: redrawing current: {(%3d,%3d), (%3d,%3d)}\n", self, self->current_area.x1, self->current_area.y1, self->current_area.x2, self->current_area.y2); + } + } else if (!displayio_area_empty(&self->ephemeral_dirty_area)) { self->ephemeral_dirty_area.next = tail; - VECTORIO_SHAPE_DEBUG(" this_area: %p next: %p after: %p\n", &self->ephemeral_dirty_area, tail, tail == NULL ? NULL : tail->next); - return &self->ephemeral_dirty_area; + tail = &self->ephemeral_dirty_area; + VECTORIO_SHAPE_DEBUG("%p get_refresh_area redrawing dirty: {(%3d,%3d), (%3d,%3d)}\n", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); } return tail; } diff --git a/shared-module/vectorio/VectorShape.h b/shared-module/vectorio/VectorShape.h index 1896c72d6e..fdbae964a8 100644 --- a/shared-module/vectorio/VectorShape.h +++ b/shared-module/vectorio/VectorShape.h @@ -31,11 +31,12 @@ typedef struct { int16_t x; int16_t y; displayio_buffer_transform_t *absolute_transform; - bool dirty; // True if we need to draw // Tracks current shape footprint and expands outward as the shape dirties and changes. // This is suboptimal if you move your shape far. Could add more state to only redraw // exactly what we left behind. displayio_area_t ephemeral_dirty_area; + displayio_area_t current_area; + bool current_area_dirty; } vectorio_vector_shape_t; displayio_area_t *vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_t *self, displayio_area_t *tail);