From f5c637dc10e2374ca1282cbc430c94fc0973b2f6 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 10 Nov 2022 11:02:31 -0600 Subject: [PATCH] Add adafruit_pixelmap.PixelMap .. a fast helper for animations. It is similar to and inspired by the PixelMap helper in Adafruit LED Animation library, but with an extremely fast 'paste' method for setting a series of pixels. This is a common operation for many animations, and can give a substantial speed improvement. It's named `adafruit_pixelmap` so that we can package a compatible version in pure Python for systems that can't fit it in C in flash, or for Blinka. This is a proof of concept and can make a very fast comet animation: ```python import time import adafruit_pixelbuf import adafruti_pixelmap import board import neopixel from supervisor import ticks_ms from adafruit_led_animation.animation.solid import Solid from adafruit_led_animation import color pixel_pin = board.GP0 pixel_num = 96 pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=1, auto_write=False, pixel_order="RGB") evens = adafruit_pixelmap.PixelMap(pixels, tuple(range(0, pixel_num, 2))) odd_indices = tuple((i, i+2) for i in range(1, pixel_num, 4)) print(odd_indices) odds = adafruit_pixelbuf.PixelMap(pixels, odd_indices) assert len(odds) == len(odd_indices) comet_length = 16 comet1 = [color.calculate_intensity(color.GREEN, ((1+i) / comet_length) ** 2.4) for i in range(comet_length)] comet2 = [color.calculate_intensity(color.PURPLE, ((1+i) / comet_length) ** 2.4) for i in range(comet_length)] pos1 = 0 pos2 = 96//4 while True: evens.paste(comet1, pos1, wrap=True, reverse=False, others=0) pos1 = (pos1 + 1) % len(evens) odds.paste(comet2, pos2, wrap=True, reverse=True, others=0) pos2 = (pos2 - 1) % len(odds) pixels.show() m = ticks_ms() if m % 2000 > 1000: time.sleep(.02) ``` --- locale/circuitpython.pot | 9 + ports/atmel-samd/mpconfigport.mk | 1 + .../aramcon_badge_2019/mpconfigboard.mk | 1 + py/circuitpy_defns.mk | 5 + py/circuitpy_mpconfig.mk | 3 + shared-bindings/adafruit_pixelbuf/PixelBuf.c | 28 +- shared-bindings/adafruit_pixelbuf/PixelBuf.h | 3 + shared-bindings/adafruit_pixelbuf/__init__.c | 9 + shared-bindings/adafruit_pixelmap/PixelMap.c | 245 ++++++++++++++++++ shared-bindings/adafruit_pixelmap/PixelMap.h | 42 +++ shared-bindings/adafruit_pixelmap/__init__.c | 62 +++++ shared-bindings/adafruit_pixelmap/__init__.h | 30 +++ shared-module/adafruit_pixelbuf/PixelBuf.c | 21 +- shared-module/adafruit_pixelbuf/PixelBuf.h | 6 +- shared-module/adafruit_pixelmap/PixelMap.c | 184 +++++++++++++ shared-module/adafruit_pixelmap/PixelMap.h | 39 +++ shared-module/adafruit_pixelmap/__init__.c | 0 17 files changed, 658 insertions(+), 30 deletions(-) create mode 100644 shared-bindings/adafruit_pixelmap/PixelMap.c create mode 100644 shared-bindings/adafruit_pixelmap/PixelMap.h create mode 100644 shared-bindings/adafruit_pixelmap/__init__.c create mode 100644 shared-bindings/adafruit_pixelmap/__init__.h create mode 100644 shared-module/adafruit_pixelmap/PixelMap.c create mode 100644 shared-module/adafruit_pixelmap/PixelMap.h create mode 100644 shared-module/adafruit_pixelmap/__init__.c diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 779a75deb6..382aa710c5 100755 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -2267,6 +2267,7 @@ msgid "Unkown error code %d" msgstr "" #: shared-bindings/adafruit_pixelbuf/PixelBuf.c +#: shared-module/adafruit_pixelbuf/PixelMap.c #, c-format msgid "Unmatched number of items on RHS (expected %d, got %d)." msgstr "" @@ -3152,6 +3153,10 @@ msgstr "" msgid "index is out of bounds" msgstr "" +#: shared-bindings/adafruit_pixelbuf/PixelMap.c +msgid "index must be tuple or int" +msgstr "" + #: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c #: ports/espressif/common-hal/pulseio/PulseIn.c py/obj.c #: shared-bindings/bitmaptools/__init__.c @@ -3537,6 +3542,10 @@ msgstr "" msgid "negative shift count" msgstr "" +#: shared-bindings/adafruit_pixelbuf/PixelMap.c +msgid "nested index must be int" +msgstr "" + #: shared-module/sdcardio/SDCard.c msgid "no SD card" msgstr "" diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index e6f5759f1c..05aed89eb2 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -42,6 +42,7 @@ CIRCUITPY_I2CTARGET ?= 0 CIRCUITPY_JSON ?= 0 CIRCUITPY_KEYPAD ?= 0 CIRCUITPY_MSGPACK ?= 0 +CIRCUITPY_PIXELMAP ?= 0 CIRCUITPY_RE ?= 0 CIRCUITPY_SDCARDIO ?= 0 CIRCUITPY_SYNTHIO ?= 0 diff --git a/ports/nrf/boards/aramcon_badge_2019/mpconfigboard.mk b/ports/nrf/boards/aramcon_badge_2019/mpconfigboard.mk index 06cd9633d1..5bf2eb721b 100644 --- a/ports/nrf/boards/aramcon_badge_2019/mpconfigboard.mk +++ b/ports/nrf/boards/aramcon_badge_2019/mpconfigboard.mk @@ -9,5 +9,6 @@ QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "GD25Q16C" CIRCUITPY_DISPLAYIO = 1 +CIRCUITPY_PIXELMAP = 0 CIRCUITPY_REQUIRE_I2C_PULLUPS = 0 diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index ee51a58c58..0d70068e43 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -266,6 +266,9 @@ endif ifeq ($(CIRCUITPY_PIXELBUF),1) SRC_PATTERNS += adafruit_pixelbuf/% endif +ifeq ($(CIRCUITPY_PIXELMAP),1) +SRC_PATTERNS += adafruit_pixelmap/% +endif ifeq ($(CIRCUITPY_QRIO),1) SRC_PATTERNS += qrio/% endif @@ -543,6 +546,8 @@ SRC_SHARED_MODULE_ALL = \ _eve/__init__.c \ adafruit_pixelbuf/PixelBuf.c \ adafruit_pixelbuf/__init__.c \ + adafruit_pixelmap/PixelMap.c \ + adafruit_pixelmap/__init__.c \ _stage/Layer.c \ _stage/Text.c \ _stage/__init__.c \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index fbf0b587b7..3ca23263d4 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -319,6 +319,9 @@ CFLAGS += -DCIRCUITPY_PEW=$(CIRCUITPY_PEW) CIRCUITPY_PIXELBUF ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_PIXELBUF=$(CIRCUITPY_PIXELBUF) +CIRCUITPY_PIXELMAP ?= $(CIRCUITPY_PIXELBUF) +CFLAGS += -DCIRCUITPY_PIXELMAP=$(CIRCUITPY_PIXELMAP) + # Only for SAMD boards for the moment CIRCUITPY_PS2IO ?= 0 CFLAGS += -DCIRCUITPY_PS2IO=$(CIRCUITPY_PS2IO) diff --git a/shared-bindings/adafruit_pixelbuf/PixelBuf.c b/shared-bindings/adafruit_pixelbuf/PixelBuf.c index 6203f672ad..2efde4cafc 100644 --- a/shared-bindings/adafruit_pixelbuf/PixelBuf.c +++ b/shared-bindings/adafruit_pixelbuf/PixelBuf.c @@ -253,9 +253,7 @@ STATIC mp_obj_t pixelbuf_pixelbuf_show(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pixelbuf_pixelbuf_show_obj, pixelbuf_pixelbuf_show); -//| def fill( -//| self, color: Union[int, Tuple[int, int, int], Tuple[int, int, int, float]] -//| ) -> None: +//| def fill(self, color: PixelType) -> None: //| """Fills the given pixelbuf with the given color.""" //| ... @@ -267,29 +265,21 @@ STATIC mp_obj_t pixelbuf_pixelbuf_fill(mp_obj_t self_in, mp_obj_t value) { STATIC MP_DEFINE_CONST_FUN_OBJ_2(pixelbuf_pixelbuf_fill_obj, pixelbuf_pixelbuf_fill); //| @overload -//| def __getitem__( -//| self, index: slice -//| ) -> Union[Tuple[Tuple[int, int, int], ...], Tuple[Tuple[int, int, int, float], ...]]: ... -//| @overload -//| def __getitem__( -//| self, index: int -//| ) -> Union[Tuple[int, int, int], Tuple[int, int, int, float]]: +//| def __getitem__(self, index: slice) -> PixelReturnSequence: //| """Returns the pixel value at the given index as a tuple of (Red, Green, Blue[, White]) values //| between 0 and 255. When in PWM (DotStar) mode, the 4th tuple value is a float of the pixel //| intensity from 0-1.0.""" //| ... //| @overload -//| def __setitem__( -//| self, index: slice, value: Tuple[Union[int, Tuple[float, ...], List[float]], ...] -//| ) -> None: ... +//| def __getitem__(self, index: int) -> PixelReturnType: +//| """Returns the pixel value at the given index as a tuple of (Red, Green, Blue[, White]) values +//| between 0 and 255. When in PWM (DotStar) mode, the 4th tuple value is a float of the pixel +//| intensity from 0-1.0.""" +//| ... //| @overload -//| def __setitem__( -//| self, index: slice, value: List[Union[int, Tuple[float, ...], List[float]]] -//| ) -> None: ... +//| def __setitem__(self, index: slice, value: PixelSequence) -> None: ... //| @overload -//| def __setitem__( -//| self, index: int, value: Union[int, Tuple[float, ...], List[float]] -//| ) -> None: +//| def __setitem__(self, index: int, value: PixelType) -> None: //| """Sets the pixel value at the given index. Value can either be a tuple or integer. Tuples are //| The individual (Red, Green, Blue[, White]) values between 0 and 255. If given an integer, the //| red, green and blue values are packed into the lower three bytes (0xRRGGBB). diff --git a/shared-bindings/adafruit_pixelbuf/PixelBuf.h b/shared-bindings/adafruit_pixelbuf/PixelBuf.h index 7ae3d6acf8..88eb06f0a0 100644 --- a/shared-bindings/adafruit_pixelbuf/PixelBuf.h +++ b/shared-bindings/adafruit_pixelbuf/PixelBuf.h @@ -27,6 +27,7 @@ #ifndef CP_SHARED_BINDINGS_PIXELBUF_PIXELBUF_H #define CP_SHARED_BINDINGS_PIXELBUF_PIXELBUF_H +#include "py/objtuple.h" #include "shared-module/adafruit_pixelbuf/PixelBuf.h" extern const mp_obj_type_t pixelbuf_pixelbuf_type; @@ -48,5 +49,7 @@ void common_hal_adafruit_pixelbuf_pixelbuf_show(mp_obj_t self); mp_obj_t common_hal_adafruit_pixelbuf_pixelbuf_get_pixel(mp_obj_t self, size_t index); void common_hal_adafruit_pixelbuf_pixelbuf_set_pixel(mp_obj_t self, size_t index, mp_obj_t item); void common_hal_adafruit_pixelbuf_pixelbuf_set_pixels(mp_obj_t self_in, size_t start, mp_int_t step, size_t slice_len, mp_obj_t *values, mp_obj_tuple_t *flatten_to); +void common_hal_adafruit_pixelbuf_pixelbuf_parse_color(mp_obj_t self, mp_obj_t color, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w); +void common_hal_adafruit_pixelbuf_pixelbuf_set_pixel_color(mp_obj_t self, size_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w); #endif // CP_SHARED_BINDINGS_PIXELBUF_PIXELBUF_H diff --git a/shared-bindings/adafruit_pixelbuf/__init__.c b/shared-bindings/adafruit_pixelbuf/__init__.c index 40bf80094c..fd2915ee0a 100644 --- a/shared-bindings/adafruit_pixelbuf/__init__.c +++ b/shared-bindings/adafruit_pixelbuf/__init__.c @@ -39,6 +39,15 @@ //| RGB(W) strip/matrix manipulation, such as DotStar and Neopixel. //| //| Byteorders are configured with strings, such as "RGB" or "RGBD".""" +//| +//| # The types accepted when getting a pixel value +//| PixelReturnType = Union[ +//| Tuple[int, int, int], Tuple[int, int, int, int], Tuple[int, int, int, float] +//| ] +//| PixelReturnSequence = Tuple[PixelReturnType] +//| # The types returned when getting a pixel value +//| PixelType = Union[int, PixelReturnType] +//| PixelSequence = Union[Tuple[PixelType], List[PixelType]] // TODO: Pull in docs from adafruit_pixelbuf. STATIC const mp_rom_map_elem_t pixelbuf_module_globals_table[] = { diff --git a/shared-bindings/adafruit_pixelmap/PixelMap.c b/shared-bindings/adafruit_pixelmap/PixelMap.c new file mode 100644 index 0000000000..3361534ca1 --- /dev/null +++ b/shared-bindings/adafruit_pixelmap/PixelMap.c @@ -0,0 +1,245 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Rose Hooper + * Copyright (c) 2022 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" + +#include "shared-bindings/adafruit_pixelmap/PixelMap.h" +#include "shared-bindings/adafruit_pixelbuf/PixelBuf.h" +#include "shared-module/adafruit_pixelmap/PixelMap.h" + +//| from adafruit_pixelbuf import PixelBuf, PixelReturnType, PixelSequence, PixelType +//| +//| class PixelMap: +//| def __init__(self, pixelbuf: PixelBuf, indices: Tuple[Union[int, Tuple[int]]]) -> None: +//| """Construct a PixelMap object that uses the given indices of the underlying pixelbuf""" + +STATIC mp_obj_t pixelmap_pixelmap_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_pixelbuf, ARG_indices }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pixelbuf, MP_ARG_REQUIRED }, + { MP_QSTR_indices, MP_ARG_REQUIRED }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t pixelbuf = args[ARG_pixelbuf].u_obj; + + mp_obj_t native_pixelbuf = mp_obj_cast_to_native_base(pixelbuf, &pixelbuf_pixelbuf_type); + if (!native_pixelbuf) { + (void)mp_arg_validate_type(args[ARG_pixelbuf].u_obj, &pixelbuf_pixelbuf_type, MP_QSTR_pixelbuf); + } + mp_obj_assert_native_inited(native_pixelbuf); + + size_t buflen = common_hal_adafruit_pixelbuf_pixelbuf_get_len(pixelbuf); + + mp_obj_t indices = mp_arg_validate_type(args[ARG_indices].u_obj, &mp_type_tuple, MP_QSTR_indices); + + // validate indices + size_t len; + mp_obj_t *items; + mp_obj_tuple_get(indices, &len, &items); + mp_arg_validate_length_min(len, 1, MP_QSTR_items); + + for (size_t i = 0; i < len; i++) { + mp_obj_t item = items[i]; + if (mp_obj_is_small_int(item)) { + mp_arg_validate_index_range(MP_OBJ_SMALL_INT_VALUE(item), 0, buflen - 1, MP_QSTR_index); + } else if (mp_obj_is_tuple_compatible(item)) { + size_t len1; + mp_obj_t *items1; + mp_obj_tuple_get(item, &len1, &items1); + for (size_t j = 0; j < len1; j++) { + mp_obj_t item1 = items1[j]; + if (!mp_obj_is_small_int(item1)) { + mp_raise_TypeError(translate("nested index must be int")); + } + mp_arg_validate_index_range(MP_OBJ_SMALL_INT_VALUE(item1), 0, buflen - 1, MP_QSTR_index); + } + } else { + mp_raise_TypeError(translate("index must be tuple or int")); + } + } + + pixelmap_pixelmap_obj_t *self = m_new_obj(pixelmap_pixelmap_obj_t); + self->base.type = &pixelmap_pixelmap_type; + shared_module_pixelmap_pixelmap_construct(self, pixelbuf, indices); + + return MP_OBJ_FROM_PTR(self); +} + +//| auto_write: bool +//| """True if updates should be automatically written""" +STATIC mp_obj_t pixelmap_pixelmap_auto_write_get(const mp_obj_t self_in) { + pixelmap_pixelmap_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(shared_module_pixelmap_pixelmap_auto_write_get(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(pixelmap_pixelmap_auto_write_get_obj, pixelmap_pixelmap_auto_write_get); + +STATIC mp_obj_t pixelmap_pixelmap_auto_write_set(const mp_obj_t self_in, const mp_obj_t arg) { + pixelmap_pixelmap_obj_t *self = MP_OBJ_TO_PTR(self_in); + shared_module_pixelmap_pixelmap_auto_write_set(self, mp_obj_is_true(arg)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(pixelmap_pixelmap_auto_write_set_obj, pixelmap_pixelmap_auto_write_set); + +MP_PROPERTY_GETSET(pixelmap_pixelmap_auto_write_obj, + (mp_obj_t)&pixelmap_pixelmap_auto_write_get_obj, + (mp_obj_t)&pixelmap_pixelmap_auto_write_set_obj); + +//| bpp: int +//| """The number of bytes per pixel in the buffer (read-only)""" +STATIC mp_obj_t pixelmap_pixelmap_obj_get_bpp(mp_obj_t self_in) { + pixelmap_pixelmap_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_adafruit_pixelbuf_pixelbuf_get_bpp(self->pixelbuf)); +} +MP_DEFINE_CONST_FUN_OBJ_1(pixelmap_pixelmap_get_bpp_obj, pixelmap_pixelmap_obj_get_bpp); + +MP_PROPERTY_GETTER(pixelmap_pixelmap_bpp_obj, + (mp_obj_t)&pixelmap_pixelmap_get_bpp_obj); + +//| byteorder: str +//| """byteorder string for the buffer (read-only)""" +STATIC mp_obj_t pixelmap_pixelmap_obj_get_byteorder(mp_obj_t self_in) { + pixelmap_pixelmap_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_adafruit_pixelbuf_pixelbuf_get_byteorder_string(self->pixelbuf); +} +MP_DEFINE_CONST_FUN_OBJ_1(pixelmap_pixelmap_get_byteorder, pixelmap_pixelmap_obj_get_byteorder); +MP_PROPERTY_GETTER(pixelmap_pixelmap_byteorder_obj, + (mp_obj_t)&pixelmap_pixelmap_get_byteorder); + +//| +//| def fill(self, color: PixelType, /) -> None: +//| """Fill all the pixels in the map with the given color""" +STATIC mp_obj_t pixelmap_pixelmap_fill(const mp_obj_t self_in, const mp_obj_t color) { + pixelmap_pixelmap_obj_t *self = MP_OBJ_TO_PTR(self_in); + + shared_module_pixelmap_pixelmap_fill(self, color); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(pixelmap_pixelmap_fill_obj, pixelmap_pixelmap_fill); + +//| +//| def indices(self, index: int, /) -> Tuple[int]: +//| """Return the PixelBuf indices for a PixelMap index""" +STATIC mp_obj_t pixelmap_pixelmap_indices(const mp_obj_t self_in, const mp_obj_t index) { + pixelmap_pixelmap_obj_t *self = MP_OBJ_TO_PTR(self_in); + + return shared_module_pixelmap_pixelmap_indices(self, mp_obj_get_int(index)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(pixelmap_pixelmap_indices_obj, pixelmap_pixelmap_indices); + + +//| def __getitem__(self, index: int) -> PixelReturnType: +//| """Retrieve the value of one of the underlying pixels at 'index'. +//| +//| Note that slices are not supported by PixelMap.__getitem__""" +//| @overload +//| def __setitem__(self, index: slice, value: PixelSequence) -> None: ... +//| @overload +//| def __setitem__(self, index: int, value: PixelType) -> None: +//| """Sets the pixel value at the given index. Value can either be a tuple or integer. Tuples are +//| The individual (Red, Green, Blue[, White]) values between 0 and 255. If given an integer, the +//| red, green and blue values are packed into the lower three bytes (0xRRGGBB). +//| For RGBW byteorders, if given only RGB values either as an int or as a tuple, the white value +//| is used instead when the red, green, and blue values are the same.""" +//| ... +STATIC mp_obj_t pixelmap_pixelmap_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { + pixelmap_pixelmap_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (value == MP_OBJ_NULL) { + // delete + return MP_OBJ_NULL; // op not supported + } else if (value == MP_OBJ_SENTINEL) { + int index = mp_obj_get_int(index_in); + return shared_module_pixelmap_pixelmap_getitem(self, index); + } + + // get + if (0) { + #if MICROPY_PY_BUILTINS_SLICE + } else if (mp_obj_is_type(index_in, &mp_type_slice)) { + shared_module_pixelmap_pixelmap_setslice(self, index_in, value); + #endif + } else { + shared_module_pixelmap_pixelmap_setitem(self, mp_obj_get_int(index_in), value); + } + return mp_const_none; +} + +//| def __len__(self) -> int: +//| """Length of the map""" +STATIC mp_obj_t pixelmap_pixelmap_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + pixelmap_pixelmap_obj_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_const_true; + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(self->len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def show(self) -> None: +//| """Transmits the color data to the pixels so that they are shown. This is done automatically +//| when `auto_write` is True.""" +//| ... +//| + +STATIC mp_obj_t pixelmap_pixelmap_show(mp_obj_t self_in) { + pixelmap_pixelmap_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_adafruit_pixelbuf_pixelbuf_show(self->pixelbuf); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pixelmap_pixelmap_show_obj, pixelmap_pixelmap_show); + +STATIC const mp_rom_map_elem_t pixelmap_pixelmap_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_auto_write), MP_ROM_PTR(&pixelmap_pixelmap_auto_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_bpp), MP_ROM_PTR(&pixelmap_pixelmap_bpp_obj) }, + { MP_ROM_QSTR(MP_QSTR_byteorder), MP_ROM_PTR(&pixelmap_pixelmap_byteorder_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&pixelmap_pixelmap_fill_obj) }, + { MP_ROM_QSTR(MP_QSTR_indices), MP_ROM_PTR(&pixelmap_pixelmap_indices_obj) }, + { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&pixelmap_pixelmap_show_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(pixelmap_pixelmap_locals_dict, pixelmap_pixelmap_locals_dict_table); + + +const mp_obj_type_t pixelmap_pixelmap_type = { + { &mp_type_type }, + .name = MP_QSTR_PixelMap, + .flags = MP_TYPE_FLAG_EXTENDED, + .locals_dict = (mp_obj_t)&pixelmap_pixelmap_locals_dict, + .make_new = pixelmap_pixelmap_make_new, + MP_TYPE_EXTENDED_FIELDS( + .subscr = pixelmap_pixelmap_subscr, + .unary_op = pixelmap_pixelmap_unary_op, + ), +}; diff --git a/shared-bindings/adafruit_pixelmap/PixelMap.h b/shared-bindings/adafruit_pixelmap/PixelMap.h new file mode 100644 index 0000000000..420df1b9e9 --- /dev/null +++ b/shared-bindings/adafruit_pixelmap/PixelMap.h @@ -0,0 +1,42 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Rose Hooper + * Copyright (c) 2022 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once +#include "py/obj.h" + +extern const mp_obj_type_t pixelmap_pixelmap_type; + +typedef struct _pixelmap_pixelmap_obj pixelmap_pixelmap_obj_t; + +void shared_module_pixelmap_pixelmap_construct(pixelmap_pixelmap_obj_t *self, mp_obj_t pixelbuf, mp_obj_t indices); +bool shared_module_pixelmap_pixelmap_auto_write_get(pixelmap_pixelmap_obj_t *self); +void shared_module_pixelmap_pixelmap_auto_write_set(pixelmap_pixelmap_obj_t *self, bool auto_write); +void shared_module_pixelmap_pixelmap_fill(pixelmap_pixelmap_obj_t *self, const mp_obj_t color); +mp_obj_t shared_module_pixelmap_pixelmap_indices(pixelmap_pixelmap_obj_t *self, int index); +void shared_module_pixelmap_pixelmap_setslice(pixelmap_pixelmap_obj_t *self, const mp_obj_t slice_in, const mp_obj_t value); +mp_obj_t shared_module_pixelmap_pixelmap_getitem(pixelmap_pixelmap_obj_t *self, mp_int_t index); +void shared_module_pixelmap_pixelmap_setitem(pixelmap_pixelmap_obj_t *self, mp_int_t index, const mp_obj_t value); diff --git a/shared-bindings/adafruit_pixelmap/__init__.c b/shared-bindings/adafruit_pixelmap/__init__.c new file mode 100644 index 0000000000..7b10471c83 --- /dev/null +++ b/shared-bindings/adafruit_pixelmap/__init__.c @@ -0,0 +1,62 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Rose Hooper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "py/objproperty.h" + +#include "shared-bindings/adafruit_pixelmap/__init__.h" +#include "shared-bindings/adafruit_pixelmap/PixelMap.h" + + +//| """A fast pixel mapping library +//| +//| The `adafruit_pixelmap` module provides the :py:class:`PixelMap` class to accelerate +//| RGB(W) strip/matrix manipulation, such as DotStar and Neopixel.""" +//| +//| # The types accepted when getting a pixel value +//| PixelReturnType = Union[ +//| Tuple[int, int, int], Tuple[int, int, int, int], Tuple[int, int, int, float] +//| ] +//| PixelReturnSequence = Tuple[PixelReturnType] +//| # The types returned when getting a pixel value +//| PixelType = Union[int, PixelReturnType] +//| PixelSequence = Union[Tuple[PixelType], List[PixelType]] + +STATIC const mp_rom_map_elem_t pixelmap_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_adafruit_pixelmap) }, + { MP_ROM_QSTR(MP_QSTR_PixelMap), MP_ROM_PTR(&pixelmap_pixelmap_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(pixelmap_module_globals, pixelmap_module_globals_table); + +const mp_obj_module_t pixelmap_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&pixelmap_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_adafruit_pixelmap, pixelmap_module, CIRCUITPY_PIXELMAP); diff --git a/shared-bindings/adafruit_pixelmap/__init__.h b/shared-bindings/adafruit_pixelmap/__init__.h new file mode 100644 index 0000000000..9a84bc68a7 --- /dev/null +++ b/shared-bindings/adafruit_pixelmap/__init__.h @@ -0,0 +1,30 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Rose Hooper + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef CP_SHARED_BINDINGS_PIXELBUF_INIT_H +#define CP_SHARED_BINDINGS_PIXELBUF_INIT_H + +#endif // CP_SHARED_BINDINGS_PIXELBUF_INIT_H diff --git a/shared-module/adafruit_pixelbuf/PixelBuf.c b/shared-module/adafruit_pixelbuf/PixelBuf.c index cab97feace..72f03c6b26 100644 --- a/shared-module/adafruit_pixelbuf/PixelBuf.c +++ b/shared-module/adafruit_pixelbuf/PixelBuf.c @@ -152,7 +152,7 @@ STATIC uint8_t _pixelbuf_get_as_uint8(mp_obj_t obj) { translate("can't convert %q to %q"), mp_obj_get_type_qstr(obj), MP_QSTR_int); } -STATIC void _pixelbuf_parse_color(pixelbuf_pixelbuf_obj_t *self, mp_obj_t color, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w) { +static void pixelbuf_parse_color(pixelbuf_pixelbuf_obj_t *self, mp_obj_t color, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w) { pixelbuf_byteorder_details_t *byteorder = &self->byteorder; // w is shared between white in NeoPixels and brightness in dotstars (so that DotStars can have // per-pixel brightness). Set the defaults here in case it isn't set below. @@ -195,7 +195,12 @@ STATIC void _pixelbuf_parse_color(pixelbuf_pixelbuf_obj_t *self, mp_obj_t color, } } -STATIC void _pixelbuf_set_pixel_color(pixelbuf_pixelbuf_obj_t *self, size_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { +void common_hal_adafruit_pixelbuf_pixelbuf_parse_color(mp_obj_t self_in, mp_obj_t color, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w) { + pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in); + pixelbuf_parse_color(self, color, r, g, b, w); +} + +static void pixelbuf_set_pixel_color(pixelbuf_pixelbuf_obj_t *self, size_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { // DotStars don't have white, instead they have 5 bit brightness so pack it into w. Shift right // by three to leave the top five bits. if (self->bytes_per_pixel == 4 && self->byteorder.is_dotstar) { @@ -232,14 +237,18 @@ STATIC void _pixelbuf_set_pixel_color(pixelbuf_pixelbuf_obj_t *self, size_t inde scaled_buffer[rgbw_order->b] = (b * self->scaled_brightness) / 256; } } +void common_hal_adafruit_pixelbuf_pixelbuf_set_pixel_color(mp_obj_t self_in, size_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in); + pixelbuf_set_pixel_color(self, index, r, g, b, w); +} STATIC void _pixelbuf_set_pixel(pixelbuf_pixelbuf_obj_t *self, size_t index, mp_obj_t value) { uint8_t r; uint8_t g; uint8_t b; uint8_t w; - _pixelbuf_parse_color(self, value, &r, &g, &b, &w); - _pixelbuf_set_pixel_color(self, index, r, g, b, w); + common_hal_adafruit_pixelbuf_pixelbuf_parse_color(self, value, &r, &g, &b, &w); + common_hal_adafruit_pixelbuf_pixelbuf_set_pixel_color(self, index, r, g, b, w); } void common_hal_adafruit_pixelbuf_pixelbuf_set_pixels(mp_obj_t self_in, size_t start, mp_int_t step, size_t slice_len, mp_obj_t *values, @@ -322,10 +331,10 @@ void common_hal_adafruit_pixelbuf_pixelbuf_fill(mp_obj_t self_in, mp_obj_t fill_ uint8_t g; uint8_t b; uint8_t w; - _pixelbuf_parse_color(self, fill_color, &r, &g, &b, &w); + common_hal_adafruit_pixelbuf_pixelbuf_parse_color(self, fill_color, &r, &g, &b, &w); for (size_t i = 0; i < self->pixel_count; i++) { - _pixelbuf_set_pixel_color(self, i, r, g, b, w); + common_hal_adafruit_pixelbuf_pixelbuf_set_pixel_color(self, i, r, g, b, w); } if (self->auto_write) { common_hal_adafruit_pixelbuf_pixelbuf_show(self_in); diff --git a/shared-module/adafruit_pixelbuf/PixelBuf.h b/shared-module/adafruit_pixelbuf/PixelBuf.h index b526254f29..a4a753baa0 100644 --- a/shared-module/adafruit_pixelbuf/PixelBuf.h +++ b/shared-module/adafruit_pixelbuf/PixelBuf.h @@ -24,13 +24,11 @@ * THE SOFTWARE. */ +#pragma once #include "py/obj.h" #include "py/objarray.h" -#ifndef PIXELBUF_SHARED_MODULE_H -#define PIXELBUF_SHARED_MODULE_H - typedef struct { uint8_t r; uint8_t g; @@ -68,5 +66,3 @@ typedef struct { #define DOTSTAR_LED_START 0b11100000 #define DOTSTAR_LED_START_FULL_BRIGHT 0xFF - -#endif diff --git a/shared-module/adafruit_pixelmap/PixelMap.c b/shared-module/adafruit_pixelmap/PixelMap.c new file mode 100644 index 0000000000..6897b172af --- /dev/null +++ b/shared-module/adafruit_pixelmap/PixelMap.c @@ -0,0 +1,184 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Rose Hooper + * Copyright (c) 2022 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/smallint.h" +#include "py/runtime.h" + +#include "shared-bindings/adafruit_pixelmap/PixelMap.h" +#include "shared-bindings/adafruit_pixelbuf/PixelBuf.h" +#include "shared-module/adafruit_pixelmap/PixelMap.h" + +typedef union { + uint32_t rgbw; + struct { + uint8_t r, g, b, w; + }; +} color_u; + +static void pixelmap_set_pixel_rgbw(pixelmap_pixelmap_obj_t *self, size_t i, color_u rgbw) { + mp_arg_validate_index_range(i, 0, self->len, MP_QSTR_index); + + mp_obj_t item = self->items[i]; + if (mp_obj_is_small_int(item)) { + common_hal_adafruit_pixelbuf_pixelbuf_set_pixel_color(self->pixelbuf, MP_OBJ_SMALL_INT_VALUE(item), rgbw.r, rgbw.g, rgbw.b, rgbw.w); + } else { + size_t len; + mp_obj_t *items; + mp_obj_tuple_get(item, &len, &items); + + for (size_t j = 0; j < len; j++) { + common_hal_adafruit_pixelbuf_pixelbuf_set_pixel_color(self->pixelbuf, MP_OBJ_SMALL_INT_VALUE(items[j]), rgbw.r, rgbw.g, rgbw.b, rgbw.w); + } + } +} + +static void pixelmap_set_pixel(pixelmap_pixelmap_obj_t *self, size_t i, mp_obj_t color) { + color_u rgbw; + common_hal_adafruit_pixelbuf_pixelbuf_parse_color(self->pixelbuf, color, &rgbw.r, &rgbw.g, &rgbw.b, &rgbw.w); + pixelmap_set_pixel_rgbw(self, i, rgbw); +} + +void shared_module_pixelmap_pixelmap_construct(pixelmap_pixelmap_obj_t *self, mp_obj_t pixelbuf, mp_obj_t indices) { + self->pixelbuf = pixelbuf; + self->indices = indices; + mp_obj_tuple_get(indices, &self->len, &self->items); +} + +static bool auto_write_get_and_clear(pixelmap_pixelmap_obj_t *self) { + bool auto_write = self->auto_write && common_hal_adafruit_pixelbuf_pixelbuf_get_auto_write(self->pixelbuf); + if (auto_write) { + common_hal_adafruit_pixelbuf_pixelbuf_set_auto_write(self->pixelbuf, false); + } + return auto_write; +} + +static void auto_write_reapply(pixelmap_pixelmap_obj_t *self, bool auto_write) { + if (auto_write) { + common_hal_adafruit_pixelbuf_pixelbuf_set_auto_write(self->pixelbuf, true); + common_hal_adafruit_pixelbuf_pixelbuf_show(self->pixelbuf); + } +} + +bool shared_module_pixelmap_pixelmap_auto_write_get(pixelmap_pixelmap_obj_t *self) { + return self->auto_write; +} + +void shared_module_pixelmap_pixelmap_auto_write_set(pixelmap_pixelmap_obj_t *self, bool auto_write) { + self->auto_write = auto_write; +} + +void shared_module_pixelmap_pixelmap_fill(pixelmap_pixelmap_obj_t *self, const mp_obj_t color) { + color_u rgbw; + common_hal_adafruit_pixelbuf_pixelbuf_parse_color(self->pixelbuf, color, &rgbw.r, &rgbw.g, &rgbw.b, &rgbw.w); + bool auto_write = auto_write_get_and_clear(self); + + for (size_t i = 0; i < self->len; i++) { + pixelmap_set_pixel_rgbw(self, i, rgbw); + } + + auto_write_reapply(self, auto_write); +} + +mp_obj_t shared_module_pixelmap_pixelmap_indices(pixelmap_pixelmap_obj_t *self, int index) { + mp_arg_validate_index_range(index, 0, self->len - 1, MP_QSTR_index); + + mp_obj_t item = self->items[index]; + if (mp_obj_is_small_int(item)) { + return mp_obj_new_tuple(1, &item); + } else { + return item; + } +} + +#if MICROPY_PY_BUILTINS_SLICE +void shared_module_pixelmap_pixelmap_setslice(pixelmap_pixelmap_obj_t *self, const mp_obj_t slice_in, const mp_obj_t values) { + mp_bound_slice_t slice; + mp_seq_get_fast_slice_indexes(self->len, slice_in, &slice); + size_t slice_len; + if (slice.step > 0) { + slice_len = slice.stop - slice.start; + } else { + slice_len = 1 + slice.start - slice.stop; + } + if (slice.step > 1 || slice.step < -1) { + size_t step = slice.step > 0 ? slice.step : slice.step * -1; + slice_len = (slice_len / step) + (slice_len % step ? 1 : 0); + } + + size_t num_items = mp_obj_get_int(mp_obj_len(values)); + if (num_items != slice_len) { + mp_raise_ValueError_varg(translate("Unmatched number of items on RHS (expected %d, got %d)."), slice_len, num_items); + } + + bool auto_write = auto_write_get_and_clear(self); + + // because we didn't preflight the pixel values, an exception could occur. + // In that case we need to do the auto-write of any pixels that were set + // before re-raising the exception + nlr_buf_t nlr; + + size_t start = slice.start; + mp_int_t step = slice.step; + + if (nlr_push(&nlr) == 0) { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(values, &iter_buf); + mp_obj_t item; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + pixelmap_set_pixel(self, start, item); + start += step; + } + nlr_pop(); + auto_write_reapply(self, auto_write); + } else { + auto_write_reapply(self, auto_write); + // exception converting color value, re-raise + nlr_raise(MP_OBJ_FROM_PTR(nlr.ret_val)); + } +} +#endif + +void shared_module_pixelmap_pixelmap_setitem(pixelmap_pixelmap_obj_t *self, mp_int_t i, mp_obj_t color) { + color_u rgbw; + common_hal_adafruit_pixelbuf_pixelbuf_parse_color(self->pixelbuf, color, &rgbw.r, &rgbw.g, &rgbw.b, &rgbw.w); + bool auto_write = auto_write_get_and_clear(self); + pixelmap_set_pixel_rgbw(self, i, rgbw); + auto_write_reapply(self, auto_write); +} + +mp_obj_t shared_module_pixelmap_pixelmap_getitem(pixelmap_pixelmap_obj_t *self, mp_int_t i) { + mp_arg_validate_index_range(i, 0, self->len, MP_QSTR_index); + mp_obj_t item = self->items[i]; + if (mp_obj_is_small_int(item)) { + return common_hal_adafruit_pixelbuf_pixelbuf_get_pixel(self->pixelbuf, MP_OBJ_SMALL_INT_VALUE(item)); + } else { + size_t len; + mp_obj_t *items; + mp_obj_tuple_get(item, &len, &items); + return common_hal_adafruit_pixelbuf_pixelbuf_get_pixel(self->pixelbuf, MP_OBJ_SMALL_INT_VALUE(items[0])); + } +} diff --git a/shared-module/adafruit_pixelmap/PixelMap.h b/shared-module/adafruit_pixelmap/PixelMap.h new file mode 100644 index 0000000000..192d6a4f9c --- /dev/null +++ b/shared-module/adafruit_pixelmap/PixelMap.h @@ -0,0 +1,39 @@ +/* + * This file is part of the CircuitPython project, https://github.com/adafruit/circuitpython + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Rose Hooper + * Copyright (c) 2022 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "shared-module/adafruit_pixelbuf/PixelBuf.h" + +typedef struct _pixelmap_pixelmap_obj { + mp_obj_base_t base; + mp_obj_t pixelbuf; + mp_obj_t indices; + size_t len; + mp_obj_t *items; + bool auto_write; +} pixelmap_pixelmap_obj_t; diff --git a/shared-module/adafruit_pixelmap/__init__.c b/shared-module/adafruit_pixelmap/__init__.c new file mode 100644 index 0000000000..e69de29bb2