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)
```
This commit is contained in:
Jeff Epler 2022-11-10 11:02:31 -06:00
parent 1853f49139
commit f5c637dc10
No known key found for this signature in database
GPG Key ID: D5BF15AB975AB4DE
17 changed files with 658 additions and 30 deletions

View File

@ -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 ""

View File

@ -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

View File

@ -9,5 +9,6 @@ QSPI_FLASH_FILESYSTEM = 1
EXTERNAL_FLASH_DEVICES = "GD25Q16C"
CIRCUITPY_DISPLAYIO = 1
CIRCUITPY_PIXELMAP = 0
CIRCUITPY_REQUIRE_I2C_PULLUPS = 0

View File

@ -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 \

View File

@ -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)

View File

@ -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).

View File

@ -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

View File

@ -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[] = {

View File

@ -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,
),
};

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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]));
}
}

View File

@ -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;