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 0659588ae0..67683abfdc 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -35,11 +35,11 @@ msgid "" "https://github.com/adafruit/circuitpython/issues\n" msgstr "" -#: py/obj.c shared-module/traceback/__init__.c +#: py/obj.c msgid " File \"%q\"" msgstr "" -#: py/obj.c shared-module/traceback/__init__.c +#: py/obj.c msgid " File \"%q\", line %d" msgstr "" @@ -318,11 +318,15 @@ 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 "" -#: py/obj.c shared-module/traceback/__init__.c +#: py/obj.c msgid ", in %q\n" msgstr "" @@ -2211,7 +2215,7 @@ msgstr "" msgid "Touch alarms not available" msgstr "" -#: py/obj.c shared-module/traceback/__init__.c +#: py/obj.c msgid "Traceback (most recent call last):\n" msgstr "" @@ -3453,10 +3457,6 @@ msgstr "" msgid "lhs and rhs should be compatible" msgstr "" -#: shared-bindings/traceback/__init__.c -msgid "limit should be an int" -msgstr "" - #: py/emitnative.c msgid "local '%q' has type '%q' but source is '%q'" msgstr "" @@ -4314,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/locale/pt_BR.po b/locale/pt_BR.po index cad52558af..c120f92ec0 100644 --- a/locale/pt_BR.po +++ b/locale/pt_BR.po @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2021-07-24 15:35+0000\n" +"PO-Revision-Date: 2021-08-08 05:33+0000\n" "Last-Translator: Wellington Terumi Uemura \n" "Language-Team: \n" "Language: pt_BR\n" @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: main.c msgid "" @@ -3164,7 +3164,7 @@ msgstr "o arquivo deve ser um arquivo aberto no modo byte" #: shared-bindings/traceback/__init__.c msgid "file write is not available" -msgstr "" +msgstr "a gravação de arquivos não está disponível" #: shared-bindings/storage/__init__.c msgid "filesystem must provide mount method" @@ -3456,7 +3456,7 @@ msgstr "element_size %d é inválido, deve ser, 1, 2, ou 4" #: shared-bindings/traceback/__init__.c msgid "invalid exception" -msgstr "" +msgstr "exceção inválida" #: extmod/modframebuf.c msgid "invalid format" @@ -3497,7 +3497,7 @@ msgstr "sintaxe inválida para o número" #: py/objexcept.c shared-bindings/traceback/__init__.c msgid "invalid traceback" -msgstr "" +msgstr "rastreamento inválido" #: py/objtype.c msgid "issubclass() arg 1 must be a class" @@ -3548,7 +3548,7 @@ msgstr "o lhs e rhs devem ser compatíveis" #: shared-bindings/traceback/__init__.c msgid "limit should be an int" -msgstr "" +msgstr "o limite deve ser um inteiro" #: py/emitnative.c msgid "local '%q' has type '%q' but source is '%q'" diff --git a/ports/atmel-samd/boards/grandcentral_m4_express/pins.c b/ports/atmel-samd/boards/grandcentral_m4_express/pins.c index b125aca086..7db9491dd3 100644 --- a/ports/atmel-samd/boards/grandcentral_m4_express/pins.c +++ b/ports/atmel-samd/boards/grandcentral_m4_express/pins.c @@ -52,6 +52,8 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_PB22) }, { MP_OBJ_NEW_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_PB23) }, { MP_OBJ_NEW_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_PB00) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_PB01) }, { MP_OBJ_NEW_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB01) }, { MP_OBJ_NEW_QSTR(MP_QSTR_TX3), MP_ROM_PTR(&pin_PB16) }, diff --git a/ports/esp32s2/boards/atmegazero_esp32s2/pins.c b/ports/esp32s2/boards/atmegazero_esp32s2/pins.c index cfc4ef6223..96dd940985 100644 --- a/ports/esp32s2/boards/atmegazero_esp32s2/pins.c +++ b/ports/esp32s2/boards/atmegazero_esp32s2/pins.c @@ -64,9 +64,9 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_DAC1), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_DAC2), MP_ROM_PTR(&pin_GPIO18) }, - { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO37) }, { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO36) }, - { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO35) }, { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO40) }, diff --git a/py/builtinhelp.c b/py/builtinhelp.c index 344ded80b0..8590c4beea 100644 --- a/py/builtinhelp.c +++ b/py/builtinhelp.c @@ -131,10 +131,7 @@ STATIC void mp_help_print_modules(void) { #if MICROPY_ENABLE_EXTERNAL_IMPORT // let the user know there may be other modules available from the filesystem - const compressed_string_t *compressed = translate("Plus any modules on the filesystem\n"); - char decompressed[decompress_length(compressed)]; - decompress(compressed, decompressed); - mp_print_str(MP_PYTHON_PRINTER, decompressed); + mp_printf(MP_PYTHON_PRINTER, "%S", translate("Plus any modules on the filesystem\n")); #endif } #endif @@ -150,18 +147,10 @@ STATIC void mp_help_print_obj(const mp_obj_t obj) { const mp_obj_type_t *type = mp_obj_get_type(obj); // try to print something sensible about the given object - const compressed_string_t *compressed = translate("object "); - char decompressed_object[decompress_length(compressed)]; - decompress(compressed, decompressed_object); - - mp_print_str(MP_PYTHON_PRINTER, decompressed_object); + mp_cprintf(MP_PYTHON_PRINTER, translate("object ")); mp_obj_print(obj, PRINT_STR); - compressed = translate(" is of type %q\n"); - char decompressed_typestring[decompress_length(compressed)]; - decompress(compressed, decompressed_typestring); - - mp_printf(MP_PYTHON_PRINTER, decompressed_typestring, type->name); + mp_cprintf(MP_PYTHON_PRINTER, translate(" is of type %q\n"), type->name); mp_map_t *map = NULL; if (type == &mp_type_module) { @@ -186,11 +175,9 @@ STATIC void mp_help_print_obj(const mp_obj_t obj) { STATIC mp_obj_t mp_builtin_help(size_t n_args, const mp_obj_t *args) { if (n_args == 0) { // print a general help message. Translate only works on single strings on one line. - const compressed_string_t *compressed = - translate("Welcome to Adafruit CircuitPython %s!\n\nPlease visit learn.adafruit.com/category/circuitpython for project guides.\n\nTo list built-in modules please do `help(\"modules\")`.\n"); - char decompressed[decompress_length(compressed)]; - decompress(compressed, decompressed); - mp_printf(MP_PYTHON_PRINTER, decompressed, MICROPY_GIT_TAG); + mp_cprintf(MP_PYTHON_PRINTER, + translate("Welcome to Adafruit CircuitPython %s!\n\nPlease visit learn.adafruit.com/category/circuitpython for project guides.\n\nTo list built-in modules please do `help(\"modules\")`.\n"), + MICROPY_GIT_TAG); } else { // try to print something sensible about the given object mp_help_print_obj(args[0]); diff --git a/py/mpprint.c b/py/mpprint.c index b99b5d658d..af485a50f2 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -376,6 +376,13 @@ int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, c } #endif +static int print_str_common(const mp_print_t *print, const char *str, int prec, size_t len, int flags, int fill, int width) { + if (prec >= 0 && (size_t)prec < len) { + len = prec; + } + return mp_print_strn(print, str, len, flags, fill, width); +} + int mp_printf(const mp_print_t *print, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -484,19 +491,24 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { qstr qst = va_arg(args, qstr); size_t len; const char *str = (const char *)qstr_data(qst, &len); - if (prec >= 0 && (size_t)prec < len) { - len = prec; - } - chrs += mp_print_strn(print, str, len, flags, fill, width); + chrs += print_str_common(print, str, prec, len, flags, fill, width); + break; + } + case 'S': { + compressed_string_t *arg = va_arg(args, compressed_string_t *); + size_t len_with_nul = decompress_length(arg); + size_t len = len_with_nul - 1; + char str[len_with_nul]; + decompress(arg, str); + chrs += print_str_common(print, str, prec, len, flags, fill, width); break; } case 's': { const char *str = va_arg(args, const char *); #ifndef NDEBUG // With debugging enabled, catch printing of null string pointers - if (prec != 0 && str == NULL) { - chrs += mp_print_strn(print, "(null)", 6, flags, fill, width); - break; + if (str == NULL) { + str = "(null)"; } #endif size_t len = strlen(str); @@ -574,3 +586,19 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { } return chrs; } + +int mp_cprintf(const mp_print_t *print, const compressed_string_t *compressed_fmt, ...) { + va_list ap; + va_start(ap, compressed_fmt); + int ret = mp_vcprintf(print, compressed_fmt, ap); + va_end(ap); + return ret; +} + +int mp_vcprintf(const mp_print_t *print, const compressed_string_t *compressed_fmt, va_list args) { + char fmt[decompress_length(compressed_fmt)]; + // TODO: Optimise this to format-while-decompressing (and not require the temp stack space). + decompress(compressed_fmt, fmt); + + return mp_vprintf(print, fmt, args); +} diff --git a/py/mpprint.h b/py/mpprint.h index 4458ea883b..9284935f96 100644 --- a/py/mpprint.h +++ b/py/mpprint.h @@ -71,4 +71,10 @@ int mp_printf(const mp_print_t *print, const char *fmt, ...); int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args); #endif +struct compressed_string; +int mp_cprintf(const mp_print_t *print, const struct compressed_string *compressed_fmt, ...); +#ifdef va_start +int mp_vcprintf(const mp_print_t *print, const struct compressed_string *compressed_fmt, va_list args); +#endif + #endif // MICROPY_INCLUDED_PY_MPPRINT_H diff --git a/py/obj.c b/py/obj.c index c8a1106a3d..fa3cd0c51c 100644 --- a/py/obj.c +++ b/py/obj.c @@ -143,41 +143,55 @@ void mp_obj_print(mp_obj_t o_in, mp_print_kind_t kind) { } // helper function to print an exception with traceback -void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) { +void mp_obj_print_exception_with_limit(const mp_print_t *print, mp_obj_t exc, mp_int_t limit) { if (mp_obj_is_exception_instance(exc) && stack_ok()) { size_t n, *values; mp_obj_exception_get_traceback(exc, &n, &values); if (n > 0) { assert(n % 3 == 0); - // Decompress the format strings - const compressed_string_t *traceback = MP_ERROR_TEXT("Traceback (most recent call last):\n"); - char decompressed[decompress_length(traceback)]; - decompress(traceback, decompressed); #if MICROPY_ENABLE_SOURCE_LINE const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\", line %d"); #else const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\""); #endif - char decompressed_frame[decompress_length(frame)]; - decompress(frame, decompressed_frame); const compressed_string_t *block_fmt = MP_ERROR_TEXT(", in %q\n"); - char decompressed_block[decompress_length(block_fmt)]; - decompress(block_fmt, decompressed_block); + + // Set traceback formatting + // Default: Print full traceback + limit = limit * 3; + mp_int_t i = n - 3, j; + if (limit > 0) { + // Print upto limit traceback + // entries from caller's frame + if ((unsigned)limit > n) { + limit = n; + } + limit = n - limit; + } else if (limit < 0) { + // Print upto limit traceback + // entries from last + if ((unsigned)-limit > n) { + limit = -n; + } + i = 0, limit = limit + 3; + } // Print the traceback - mp_print_str(print, decompressed); - for (int i = n - 3; i >= 0; i -= 3) { + mp_cprintf(print, MP_ERROR_TEXT("Traceback (most recent call last):\n")); + + for (; i >= limit; i -= 3) { + j = (i < 0) ? -i : i; #if MICROPY_ENABLE_SOURCE_LINE - mp_printf(print, decompressed_frame, values[i], (int)values[i + 1]); + mp_cprintf(print, frame, values[j], (int)values[j + 1]); #else - mp_printf(print, decompressed_frame, values[i]); + mp_cprintf(print, frame, values[j]); #endif - // the block name can be NULL if it's unknown - qstr block = values[i + 2]; + // The block name can be NULL if it's unknown + qstr block = values[j + 2]; if (block == MP_QSTRnull) { mp_print_str(print, "\n"); } else { - mp_printf(print, decompressed_block, block); + mp_cprintf(print, block_fmt, block); } } } @@ -186,6 +200,10 @@ void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) { mp_print_str(print, "\n"); } +void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) { + mp_obj_print_exception_with_limit(print, exc, 0); +} + bool PLACE_IN_ITCM(mp_obj_is_true)(mp_obj_t arg) { if (arg == mp_const_false) { return 0; diff --git a/py/obj.h b/py/obj.h index b91932b11e..a043154ada 100644 --- a/py/obj.h +++ b/py/obj.h @@ -888,6 +888,7 @@ mp_obj_t mp_obj_cast_to_native_base(mp_obj_t self_in, mp_const_obj_t native_type void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); void mp_obj_print(mp_obj_t o, mp_print_kind_t kind); void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc); +void mp_obj_print_exception_with_limit(const mp_print_t *print, mp_obj_t exc, mp_int_t limit); bool mp_obj_is_true(mp_obj_t arg); bool mp_obj_is_callable(mp_obj_t o_in); diff --git a/py/objexcept.c b/py/objexcept.c index 6488413115..e572225bbc 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -465,12 +465,9 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const com o_str->data = NULL; } else { // We have some memory to format the string. - // TODO: Optimise this to format-while-decompressing (and not require the temp stack space). struct _exc_printer_t exc_pr = {!used_emg_buf, o_str_alloc, 0, o_str_buf}; mp_print_t print = {&exc_pr, exc_add_strn}; - char fmt_decompressed[decompress_length(fmt)]; - decompress(fmt, fmt_decompressed); - mp_vprintf(&print, fmt_decompressed, ap); + mp_vcprintf(&print, fmt, ap); exc_pr.buf[exc_pr.len] = '\0'; o_str->len = exc_pr.len; o_str->data = exc_pr.buf; 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/traceback/__init__.c b/shared-bindings/traceback/__init__.c index d5290559ca..dc5be20a88 100644 --- a/shared-bindings/traceback/__init__.c +++ b/shared-bindings/traceback/__init__.c @@ -38,6 +38,75 @@ //| ... //| +STATIC void traceback_exception_common(mp_print_t *print, mp_obj_t value, mp_obj_t tb_obj, mp_obj_t limit_obj) { + if (!mp_obj_is_exception_instance(value)) { + mp_raise_TypeError(translate("invalid exception")); + } + mp_obj_exception_t exc = *(mp_obj_exception_t *)MP_OBJ_TO_PTR(value); + + mp_int_t limit = 0; + bool print_tb = true; + if (limit_obj != mp_const_none) { + limit = mp_obj_get_int(limit_obj); + print_tb = (limit != 0); + } + + if (tb_obj != mp_const_none && print_tb) { + if (!mp_obj_is_type(tb_obj, &mp_type_traceback)) { + mp_raise_TypeError(translate("invalid traceback")); + } + exc.traceback = MP_OBJ_TO_PTR(tb_obj); + } else { + exc.traceback = (mp_obj_traceback_t *)&mp_const_empty_traceback_obj; + } + + shared_module_traceback_print_exception(&exc, print, limit); +} + +//| def format_exception(etype: Type[BaseException], value: BaseException, tb: TracebackType, +//| limit: Optional[int] = None, chain: Optional[bool] = True) -> None: +//| """Format a stack trace and the exception information. +//| +//| The arguments have the same meaning as the corresponding arguments +//| to print_exception(). The return value is a list of strings, each +//| ending in a newline and some containing internal newlines. When +//| these lines are concatenated and printed, exactly the same text is +//| printed as does print_exception(). +//| +//| .. note: Setting `chain` will have no effect as chained exceptions are not yet implemented. +//| +//| :param Type[BaseException] etype: This is ignored and inferred from the type of ``value``. +//| :param BaseException value: The exception. Must be an instance of `BaseException`. +//| :param TracebackType tb: The traceback object. If `None`, the traceback will not be printed. +//| :param int limit: Print up to limit stack trace entries (starting from the caller’s frame) if limit is positive. +//| Otherwise, print the last ``abs(limit)`` entries. If limit is omitted or None, all entries are printed. +//| :param bool chain: If `True` then chained exceptions will be printed (note: not yet implemented). +//| +//| """ +//| ... +//| +STATIC mp_obj_t traceback_format_exception(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_etype, ARG_value, ARG_tb, ARG_limit, ARG_chain }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_etype, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_value, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_tb, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_limit, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_chain, MP_ARG_BOOL, {.u_bool = true} }, + }; + + 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_print_t print; + vstr_t vstr; + vstr_init_print(&vstr, 0, &print); + traceback_exception_common(&print, args[ARG_value].u_obj, args[ARG_tb].u_obj, args[ARG_limit].u_obj); + return mp_obj_new_str_from_vstr(&mp_type_str, &vstr); +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(traceback_format_exception_obj, 3, traceback_format_exception); + //| def print_exception(etype: Type[BaseException], value: BaseException, tb: TracebackType, //| limit: Optional[int] = None, file: Optional[io.FileIO] = None, chain: Optional[bool] = True) -> None: //| @@ -57,6 +126,7 @@ //| """ //| ... //| + STATIC mp_obj_t traceback_print_exception(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_etype, ARG_value, ARG_tb, ARG_limit, ARG_file, ARG_chain }; static const mp_arg_t allowed_args[] = { @@ -71,11 +141,6 @@ STATIC mp_obj_t traceback_print_exception(size_t n_args, const mp_obj_t *pos_arg 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); - if (!mp_obj_is_exception_instance(args[ARG_value].u_obj)) { - mp_raise_TypeError(translate("invalid exception")); - } - mp_obj_exception_t exc = *(mp_obj_exception_t *)MP_OBJ_TO_PTR(args[ARG_value].u_obj); - mp_print_t print = mp_plat_print; if (args[ARG_file].u_obj != mp_const_none) { #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES @@ -87,25 +152,7 @@ STATIC mp_obj_t traceback_print_exception(size_t n_args, const mp_obj_t *pos_arg #endif } - mp_int_t limit = 0; - bool print_tb = true; - if (args[ARG_limit].u_obj != mp_const_none) { - if (!mp_obj_get_int_maybe(args[ARG_limit].u_obj, &limit)) { - mp_raise_TypeError(translate("limit should be an int")); - } - print_tb = (limit != 0); - } - - if (args[ARG_tb].u_obj != mp_const_none && print_tb) { - if (!mp_obj_is_type(args[ARG_tb].u_obj, &mp_type_traceback)) { - mp_raise_TypeError(translate("invalid traceback")); - } - exc.traceback = MP_OBJ_TO_PTR(args[ARG_tb].u_obj); - } else { - exc.traceback = NULL; - } - - shared_module_traceback_print_exception(&exc, &print, limit); + traceback_exception_common(&print, args[ARG_value].u_obj, args[ARG_tb].u_obj, args[ARG_limit].u_obj); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(traceback_print_exception_obj, 3, traceback_print_exception); @@ -114,6 +161,7 @@ STATIC const mp_rom_map_elem_t traceback_module_globals_table[] = { // module name { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_traceback) }, // module functions + { MP_ROM_QSTR(MP_QSTR_format_exception), MP_ROM_PTR(&traceback_format_exception_obj) }, { MP_ROM_QSTR(MP_QSTR_print_exception), MP_ROM_PTR(&traceback_print_exception_obj) }, }; STATIC MP_DEFINE_CONST_DICT(traceback_module_globals, traceback_module_globals_table); 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/traceback/__init__.c b/shared-module/traceback/__init__.c index 388b1c18de..a29bd5743f 100644 --- a/shared-module/traceback/__init__.c +++ b/shared-module/traceback/__init__.c @@ -27,67 +27,5 @@ #include "shared-module/traceback/__init__.h" void shared_module_traceback_print_exception(mp_obj_exception_t *exc, mp_print_t *print, mp_int_t limit) { - // Print traceback - if (exc->traceback != NULL) { - size_t n = exc->traceback->len; - size_t *values = exc->traceback->data; - if (n > 0) { - assert(n % 3 == 0); - // Decompress the format strings - const compressed_string_t *traceback = MP_ERROR_TEXT("Traceback (most recent call last):\n"); - char decompressed[decompress_length(traceback)]; - decompress(traceback, decompressed); - #if MICROPY_ENABLE_SOURCE_LINE - const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\", line %d"); - #else - const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\""); - #endif - char decompressed_frame[decompress_length(frame)]; - decompress(frame, decompressed_frame); - const compressed_string_t *block_fmt = MP_ERROR_TEXT(", in %q\n"); - char decompressed_block[decompress_length(block_fmt)]; - decompress(block_fmt, decompressed_block); - - // Set traceback formatting - // Default: Print full traceback - limit = limit * 3; - mp_int_t i = n - 3, j; - if (limit > 0) { - // Print upto limit traceback - // entries from caller's frame - if ((unsigned)limit > n) { - limit = n; - } - limit = n - limit; - } else if (limit < 0) { - // Print upto limit traceback - // entries from last - if ((unsigned)-limit > n) { - limit = -n; - } - i = 0, limit = limit + 3; - } - - // Print the traceback - mp_print_str(print, decompressed); - for (; i >= limit; i -= 3) { - j = (i < 0) ? -i : i; - #if MICROPY_ENABLE_SOURCE_LINE - mp_printf(print, decompressed_frame, values[j], (int)values[j + 1]); - #else - mp_printf(print, decompressed_frame, values[j]); - #endif - // The block name can be NULL if it's unknown - qstr block = values[j + 2]; - if (block == MP_QSTRnull) { - mp_print_str(print, "\n"); - } else { - mp_printf(print, decompressed_block, block); - } - } - } - } - // Print exception - mp_obj_print_helper(print, exc, PRINT_EXC); - mp_print_str(print, "\n"); + mp_obj_print_exception_with_limit(print, exc, limit); } 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); diff --git a/supervisor/shared/translate.c b/supervisor/shared/translate.c index 4d899ad6d0..a14fa5c728 100644 --- a/supervisor/shared/translate.c +++ b/supervisor/shared/translate.c @@ -35,12 +35,11 @@ #endif #include "py/misc.h" +#include "py/mpprint.h" #include "supervisor/serial.h" void serial_write_compressed(const compressed_string_t *compressed) { - char decompressed[decompress_length(compressed)]; - decompress(compressed, decompressed); - serial_write(decompressed); + mp_printf(MP_PYTHON_PRINTER, "%S", compressed); } STATIC void get_word(int n, const mchar_t **pos, const mchar_t **end) { diff --git a/supervisor/shared/translate.h b/supervisor/shared/translate.h index 26a961a3ed..da58e1eb78 100644 --- a/supervisor/shared/translate.h +++ b/supervisor/shared/translate.h @@ -69,7 +69,7 @@ // flexible array}, but is also future-proofed against strings with // UTF-8 length above 256, with a savings of about 1.375 bytes per // string. -typedef struct { +typedef struct compressed_string { uint8_t data; const uint8_t tail[]; } compressed_string_t;