diff options
Diffstat (limited to 'circuitpython/shared-bindings/displayio')
26 files changed, 3909 insertions, 0 deletions
diff --git a/circuitpython/shared-bindings/displayio/Bitmap.c b/circuitpython/shared-bindings/displayio/Bitmap.c new file mode 100644 index 0000000..3af152f --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Bitmap.c @@ -0,0 +1,370 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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 "shared-bindings/displayio/Bitmap.h" +#include "shared-module/displayio/Bitmap.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class Bitmap: +//| """Stores values of a certain size in a 2D array +//| +//| Bitmaps can be treated as read-only buffers. If the number of bits in a pixel is 8, 16, or 32; and the number of bytes +//| per row is a multiple of 4, then the resulting memoryview will correspond directly with the bitmap's contents. Otherwise, +//| the bitmap data is packed into the memoryview with unspecified padding. +//| +//| A Bitmap can be treated as a buffer, allowing its content to be +//| viewed and modified using e.g., with ``ulab.numpy.frombuffer``, +//| but the `displayio.Bitmap.dirty` method must be used to inform +//| displayio when a bitmap was modified through the buffer interface. +//| +//| `bitmaptools.arrayblit` can also be useful to move data efficiently +//| into a Bitmap. +//| """ +//| +//| def __init__(self, width: int, height: int, value_count: int) -> None: +//| """Create a Bitmap object with the given fixed size. Each pixel stores a value that is used to +//| index into a corresponding palette. This enables differently colored sprites to share the +//| underlying Bitmap. value_count is used to minimize the memory used to store the Bitmap. +//| +//| :param int width: The number of values wide +//| :param int height: The number of values high +//| :param int value_count: The number of possible pixel values.""" +//| ... +//| +STATIC mp_obj_t displayio_bitmap_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 3, 3, false); + uint32_t width = mp_obj_get_int(all_args[0]); + uint32_t height = mp_obj_get_int(all_args[1]); + uint32_t value_count = mp_obj_get_int(all_args[2]); + uint32_t bits = 1; + + if (value_count == 0) { + mp_raise_ValueError(translate("value_count must be > 0")); + } + while ((value_count - 1) >> bits) { + if (bits < 8) { + bits <<= 1; + } else { + bits += 8; + } + } + + displayio_bitmap_t *self = m_new_obj(displayio_bitmap_t); + self->base.type = &displayio_bitmap_type; + common_hal_displayio_bitmap_construct(self, width, height, bits); + + return MP_OBJ_FROM_PTR(self); +} +//| width: int +//| """Width of the bitmap. (read only)""" +//| +STATIC mp_obj_t displayio_bitmap_obj_get_width(mp_obj_t self_in) { + displayio_bitmap_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_bitmap_get_width(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(displayio_bitmap_get_width_obj, displayio_bitmap_obj_get_width); + +MP_PROPERTY_GETTER(displayio_bitmap_width_obj, + (mp_obj_t)&displayio_bitmap_get_width_obj); + +//| height: int +//| """Height of the bitmap. (read only)""" +//| +STATIC mp_obj_t displayio_bitmap_obj_get_height(mp_obj_t self_in) { + displayio_bitmap_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_bitmap_get_height(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(displayio_bitmap_get_height_obj, displayio_bitmap_obj_get_height); + +MP_PROPERTY_GETTER(displayio_bitmap_height_obj, + (mp_obj_t)&displayio_bitmap_get_height_obj); + +//| def __getitem__(self, index: Union[Tuple[int, int], int]) -> int: +//| """Returns the value at the given index. The index can either be an x,y tuple or an int equal +//| to ``y * width + x``. +//| +//| This allows you to:: +//| +//| print(bitmap[0,1])""" +//| ... +//| +//| def __setitem__(self, index: Union[Tuple[int, int], int], value: int) -> None: +//| """Sets the value at the given index. The index can either be an x,y tuple or an int equal +//| to ``y * width + x``. +//| +//| This allows you to:: +//| +//| bitmap[0,1] = 3""" +//| ... +//| +STATIC mp_obj_t bitmap_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t value_obj) { + if (value_obj == mp_const_none) { + // delete item + mp_raise_AttributeError(translate("Cannot delete values")); + return mp_const_none; + } + + displayio_bitmap_t *self = MP_OBJ_TO_PTR(self_in); + + if (mp_obj_is_type(index_obj, &mp_type_slice)) { + // TODO(tannewt): Implement subscr after slices support start, stop and step tuples. + mp_raise_NotImplementedError(translate("Slices not supported")); + return mp_const_none; + } + + uint16_t x = 0; + uint16_t y = 0; + if (mp_obj_is_small_int(index_obj)) { + mp_int_t i = MP_OBJ_SMALL_INT_VALUE(index_obj); + uint16_t width = common_hal_displayio_bitmap_get_width(self); + x = i % width; + y = i / width; + } else { + mp_obj_t *items; + mp_obj_get_array_fixed_n(index_obj, 2, &items); + x = mp_obj_get_int(items[0]); + y = mp_obj_get_int(items[1]); + if (x >= common_hal_displayio_bitmap_get_width(self) || y >= common_hal_displayio_bitmap_get_height(self)) { + mp_raise_IndexError(translate("pixel coordinates out of bounds")); + } + } + + if (value_obj == MP_OBJ_SENTINEL) { + // load + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_bitmap_get_pixel(self, x, y)); + } else { + mp_uint_t value = (mp_uint_t)mp_obj_get_int(value_obj); + if ((value >> common_hal_displayio_bitmap_get_bits_per_value(self)) != 0) { + mp_raise_ValueError(translate("pixel value requires too many bits")); + } + common_hal_displayio_bitmap_set_pixel(self, x, y, value); + } + return mp_const_none; +} + +//| def blit(self, x: int, y: int, source_bitmap: Bitmap, *, x1: int, y1: int, x2: int, y2: int, skip_index: int) -> None: +//| """Inserts the source_bitmap region defined by rectangular boundaries +//| (x1,y1) and (x2,y2) into the bitmap at the specified (x,y) location. +//| +//| :param int x: Horizontal pixel location in bitmap where source_bitmap upper-left +//| corner will be placed +//| :param int y: Vertical pixel location in bitmap where source_bitmap upper-left +//| corner will be placed +//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied +//| :param int x1: Minimum x-value for rectangular bounding box to be copied from the source bitmap +//| :param int y1: Minimum y-value for rectangular bounding box to be copied from the source bitmap +//| :param int x2: Maximum x-value (exclusive) for rectangular bounding box to be copied from the source bitmap +//| :param int y2: Maximum y-value (exclusive) for rectangular bounding box to be copied from the source bitmap +//| :param int skip_index: bitmap palette index in the source that will not be copied, +//| set to None to copy all pixels""" +//| ... +//| +STATIC mp_obj_t displayio_bitmap_obj_blit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_x, ARG_y, ARG_source, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_skip_index}; + static const mp_arg_t allowed_args[] = { + {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, + {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, + {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + {MP_QSTR_x1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + {MP_QSTR_y1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + {MP_QSTR_x2, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->width + {MP_QSTR_y2, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->height + {MP_QSTR_skip_index, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_bitmap_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + int16_t x = args[ARG_x].u_int; + int16_t y = args[ARG_y].u_int; + + displayio_bitmap_t *source = mp_arg_validate_type(args[ARG_source].u_obj, &displayio_bitmap_type, MP_QSTR_source_bitmap); + + + // ensure that the target bitmap (self) has at least as many `bits_per_value` as the source + if (self->bits_per_value < source->bits_per_value) { + mp_raise_ValueError(translate("source palette too large")); + } + + int16_t x1 = args[ARG_x1].u_int; + int16_t y1 = args[ARG_y1].u_int; + int16_t x2, y2; + // if x2 or y2 is None, then set as the maximum size of the source bitmap + if (args[ARG_x2].u_obj == mp_const_none) { + x2 = source->width; + } else { + x2 = mp_obj_get_int(args[ARG_x2].u_obj); + } + // int16_t y2; + if (args[ARG_y2].u_obj == mp_const_none) { + y2 = source->height; + } else { + y2 = mp_obj_get_int(args[ARG_y2].u_obj); + } + + // Check x,y are within self (target) bitmap boundary + if ((x < 0) || (y < 0) || (x > self->width) || (y > self->height)) { + mp_raise_ValueError(translate("out of range of target")); + } + // Check x1,y1,x2,y2 are within source bitmap boundary + if ((x1 < 0) || (x1 > source->width) || + (y1 < 0) || (y1 > source->height) || + (x2 < 0) || (x2 > source->width) || + (y2 < 0) || (y2 > source->height)) { + mp_raise_ValueError(translate("out of range of source")); + } + + // Ensure x1 < x2 and y1 < y2 + if (x1 > x2) { + int16_t temp = x2; + x2 = x1; + x1 = temp; + } + if (y1 > y2) { + int16_t temp = y2; + y2 = y1; + y1 = temp; + } + + uint32_t skip_index; + bool skip_index_none; // flag whether skip_value was None + + if (args[ARG_skip_index].u_obj == mp_const_none) { + skip_index = 0; + skip_index_none = true; + } else { + skip_index = mp_obj_get_int(args[ARG_skip_index].u_obj); + skip_index_none = false; + } + + common_hal_displayio_bitmap_blit(self, x, y, source, x1, y1, x2, y2, skip_index, skip_index_none); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_bitmap_blit_obj, 1, displayio_bitmap_obj_blit); + +//| def fill(self, value: int) -> None: +//| """Fills the bitmap with the supplied palette index value.""" +//| ... +//| +STATIC mp_obj_t displayio_bitmap_obj_fill(mp_obj_t self_in, mp_obj_t value_obj) { + displayio_bitmap_t *self = MP_OBJ_TO_PTR(self_in); + + mp_uint_t value = (mp_uint_t)mp_obj_get_int(value_obj); + if ((value >> common_hal_displayio_bitmap_get_bits_per_value(self)) != 0) { + mp_raise_ValueError(translate("pixel value requires too many bits")); + } + common_hal_displayio_bitmap_fill(self, value); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_bitmap_fill_obj, displayio_bitmap_obj_fill); + +//| def dirty(self, x1: int=0, y1: int=0, x2: int=-1, y2:int = -1) -> None: +//| """Inform displayio of bitmap updates done via the buffer +//| protocol. +//| +//| :param int x1: Minimum x-value for rectangular bounding box to be considered as modified +//| :param int y1: Minimum y-value for rectangular bounding box to be considered as modified +//| :param int x2: Maximum x-value (exclusive) for rectangular bounding box to be considered as modified +//| :param int y2: Maximum y-value (exclusive) for rectangular bounding box to be considered as modified +//| +//| If x1 or y1 are not specified, they are taken as 0. If x2 or y2 +//| are not specified, or are given as -1, they are taken as the width +//| and height of the image. Thus, calling dirty() with the +//| default arguments treats the whole bitmap as modified. +//| +//| When a bitmap is modified through the buffer protocol, the +//| display will not be properly updated unless the bitmap is +//| notified of the "dirty rectangle" that encloses all modified +//| pixels.""" +//| ... +//| +STATIC mp_obj_t displayio_bitmap_obj_dirty(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + displayio_bitmap_t *self = MP_OBJ_TO_PTR(pos_args[0]); + enum { ARG_x1, ARG_y1, ARG_x2, ARG_y2 }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_x1, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_y1, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_x2, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_y2, MP_ARG_INT, {.u_int = -1} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_area_t dirty_area = { + .x1 = args[ARG_x1].u_int, + .y1 = args[ARG_y1].u_int, + .x2 = args[ARG_x2].u_int == -1 ? self->width : args[ARG_x2].u_int, + .y2 = args[ARG_y2].u_int == -1 ? self->height : args[ARG_y2].u_int, + }; + + displayio_bitmap_set_dirty_area(self, &dirty_area); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_bitmap_dirty_obj, 0, displayio_bitmap_obj_dirty); + +STATIC const mp_rom_map_elem_t displayio_bitmap_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_bitmap_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_bitmap_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&displayio_bitmap_blit_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&displayio_bitmap_fill_obj) }, + { MP_ROM_QSTR(MP_QSTR_dirty), MP_ROM_PTR(&displayio_bitmap_dirty_obj) }, + +}; +STATIC MP_DEFINE_CONST_DICT(displayio_bitmap_locals_dict, displayio_bitmap_locals_dict_table); + +// (the get_buffer protocol returns 0 for success, 1 for failure) +STATIC mp_int_t bitmap_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + displayio_bitmap_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_displayio_bitmap_get_buffer(self, bufinfo, flags); +} + +const mp_obj_type_t displayio_bitmap_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Bitmap, + .make_new = displayio_bitmap_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_bitmap_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .subscr = bitmap_subscr, + .buffer_p = { .get_buffer = bitmap_get_buffer }, + ), +}; diff --git a/circuitpython/shared-bindings/displayio/Bitmap.h b/circuitpython/shared-bindings/displayio/Bitmap.h new file mode 100644 index 0000000..4580475 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Bitmap.h @@ -0,0 +1,50 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_BITMAP_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_BITMAP_H + +#include "shared-module/displayio/Bitmap.h" + +extern const mp_obj_type_t displayio_bitmap_type; + +void common_hal_displayio_bitmap_construct(displayio_bitmap_t *self, uint32_t width, + uint32_t height, uint32_t bits_per_value); + +void common_hal_displayio_bitmap_load_row(displayio_bitmap_t *self, uint16_t y, uint8_t *data, + uint16_t len); +uint16_t common_hal_displayio_bitmap_get_height(displayio_bitmap_t *self); +uint16_t common_hal_displayio_bitmap_get_width(displayio_bitmap_t *self); +uint32_t common_hal_displayio_bitmap_get_bits_per_value(displayio_bitmap_t *self); +void common_hal_displayio_bitmap_set_pixel(displayio_bitmap_t *bitmap, int16_t x, int16_t y, uint32_t value); +void common_hal_displayio_bitmap_blit(displayio_bitmap_t *self, int16_t x, int16_t y, displayio_bitmap_t *source, + int16_t x1, int16_t y1, int16_t x2, int16_t y2, + uint32_t skip_index, bool skip_index_none); +uint32_t common_hal_displayio_bitmap_get_pixel(displayio_bitmap_t *bitmap, int16_t x, int16_t y); +void common_hal_displayio_bitmap_fill(displayio_bitmap_t *bitmap, uint32_t value); +int common_hal_displayio_bitmap_get_buffer(displayio_bitmap_t *self, mp_buffer_info_t *bufinfo, mp_uint_t flags); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_BITMAP_H diff --git a/circuitpython/shared-bindings/displayio/ColorConverter.c b/circuitpython/shared-bindings/displayio/ColorConverter.c new file mode 100644 index 0000000..18b8866 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/ColorConverter.c @@ -0,0 +1,152 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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 "shared-bindings/displayio/ColorConverter.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/enum.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class ColorConverter: +//| """Converts one color format to another.""" +//| +//| def __init__(self, *, input_colorspace: Colorspace=Colorspace.RGB888, dither: bool = False) -> None: +//| """Create a ColorConverter object to convert color formats. +//| +//| :param Colorspace colorspace: The source colorspace, one of the Colorspace constants +//| :param bool dither: Adds random noise to dither the output image""" +//| ... +//| + +STATIC mp_obj_t displayio_colorconverter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_dither, ARG_input_colorspace }; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_dither, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_input_colorspace, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = (void *)&displayio_colorspace_RGB888_obj} }, + }; + 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); + + displayio_colorconverter_t *self = m_new_obj(displayio_colorconverter_t); + self->base.type = &displayio_colorconverter_type; + + common_hal_displayio_colorconverter_construct(self, args[ARG_dither].u_bool, (displayio_colorspace_t)cp_enum_value(&displayio_colorspace_type, args[ARG_input_colorspace].u_obj)); + + return MP_OBJ_FROM_PTR(self); +} + +//| def convert(self, color: int) -> int: +//| """Converts the given color to RGB565 according to the Colorspace""" +//| ... +//| +STATIC mp_obj_t displayio_colorconverter_obj_convert(mp_obj_t self_in, mp_obj_t color_obj) { + displayio_colorconverter_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t color; + if (!mp_obj_get_int_maybe(color_obj, &color)) { + mp_raise_ValueError(translate("color should be an int")); + } + _displayio_colorspace_t colorspace; + colorspace.depth = 16; + uint32_t output_color; + common_hal_displayio_colorconverter_convert(self, &colorspace, color, &output_color); + return MP_OBJ_NEW_SMALL_INT(output_color); +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_colorconverter_convert_obj, displayio_colorconverter_obj_convert); + +//| dither: bool +//| """When `True` the ColorConverter dithers the output by adding random noise when +//| truncating to display bitdepth""" +//| +STATIC mp_obj_t displayio_colorconverter_obj_get_dither(mp_obj_t self_in) { + displayio_colorconverter_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_displayio_colorconverter_get_dither(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_colorconverter_get_dither_obj, displayio_colorconverter_obj_get_dither); + +STATIC mp_obj_t displayio_colorconverter_obj_set_dither(mp_obj_t self_in, mp_obj_t dither) { + displayio_colorconverter_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_displayio_colorconverter_set_dither(self, mp_obj_is_true(dither)); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_colorconverter_set_dither_obj, displayio_colorconverter_obj_set_dither); + +MP_PROPERTY_GETSET(displayio_colorconverter_dither_obj, + (mp_obj_t)&displayio_colorconverter_get_dither_obj, + (mp_obj_t)&displayio_colorconverter_set_dither_obj); + +//| def make_transparent(self, color: int) -> None: +//| """Set the transparent color or index for the ColorConverter. This will +//| raise an Exception if there is already a selected transparent index. +//| +//| :param int color: The color to be transparent""" +//| +STATIC mp_obj_t displayio_colorconverter_make_transparent(mp_obj_t self_in, mp_obj_t transparent_color_obj) { + displayio_colorconverter_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t transparent_color = mp_obj_get_int(transparent_color_obj); + common_hal_displayio_colorconverter_make_transparent(self, transparent_color); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_colorconverter_make_transparent_obj, displayio_colorconverter_make_transparent); + +//| def make_opaque(self, color: int) -> None: +//| """Make the ColorConverter be opaque and have no transparent pixels. +//| +//| :param int color: [IGNORED] Use any value""" +//| +STATIC mp_obj_t displayio_colorconverter_make_opaque(mp_obj_t self_in, mp_obj_t transparent_color_obj) { + displayio_colorconverter_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t transparent_color = mp_obj_get_int(transparent_color_obj); + common_hal_displayio_colorconverter_make_opaque(self, transparent_color); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_colorconverter_make_opaque_obj, displayio_colorconverter_make_opaque); + +STATIC const mp_rom_map_elem_t displayio_colorconverter_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_convert), MP_ROM_PTR(&displayio_colorconverter_convert_obj) }, + { MP_ROM_QSTR(MP_QSTR_dither), MP_ROM_PTR(&displayio_colorconverter_dither_obj) }, + { MP_ROM_QSTR(MP_QSTR_make_transparent), MP_ROM_PTR(&displayio_colorconverter_make_transparent_obj) }, + { MP_ROM_QSTR(MP_QSTR_make_opaque), MP_ROM_PTR(&displayio_colorconverter_make_opaque_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_colorconverter_locals_dict, displayio_colorconverter_locals_dict_table); + +const mp_obj_type_t displayio_colorconverter_type = { + { &mp_type_type }, + .name = MP_QSTR_ColorConverter, + .make_new = displayio_colorconverter_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_colorconverter_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/ColorConverter.h b/circuitpython/shared-bindings/displayio/ColorConverter.h new file mode 100644 index 0000000..12fa8da --- /dev/null +++ b/circuitpython/shared-bindings/displayio/ColorConverter.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_COLORCONVERTER_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_COLORCONVERTER_H + +#include "shared-bindings/displayio/__init__.h" +#include "shared-module/displayio/ColorConverter.h" + +#include "shared-module/displayio/Palette.h" + +extern const mp_obj_type_t displayio_colorconverter_type; + +void common_hal_displayio_colorconverter_construct(displayio_colorconverter_t *self, bool dither, displayio_colorspace_t input_colorspace); +void common_hal_displayio_colorconverter_convert(displayio_colorconverter_t *colorconverter, const _displayio_colorspace_t *colorspace, uint32_t input_color, uint32_t *output_color); +uint32_t displayio_colorconverter_convert_pixel(displayio_colorspace_t colorspace, uint32_t pixel); + +void common_hal_displayio_colorconverter_set_dither(displayio_colorconverter_t *self, bool dither); +bool common_hal_displayio_colorconverter_get_dither(displayio_colorconverter_t *self); + +void common_hal_displayio_colorconverter_make_transparent(displayio_colorconverter_t *self, uint32_t transparent_color); +void common_hal_displayio_colorconverter_make_opaque(displayio_colorconverter_t *self, uint32_t transparent_color); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_COLORCONVERTER_H diff --git a/circuitpython/shared-bindings/displayio/Colorspace.c b/circuitpython/shared-bindings/displayio/Colorspace.c new file mode 100644 index 0000000..3692dc2 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Colorspace.c @@ -0,0 +1,77 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 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/enum.h" +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/displayio/__init__.h" + +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB888, DISPLAYIO_COLORSPACE_RGB888); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565, DISPLAYIO_COLORSPACE_RGB565); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB565_SWAPPED, DISPLAYIO_COLORSPACE_RGB565_SWAPPED); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555, DISPLAYIO_COLORSPACE_RGB555); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, RGB555_SWAPPED, DISPLAYIO_COLORSPACE_RGB555_SWAPPED); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR565, DISPLAYIO_COLORSPACE_BGR565); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR565_SWAPPED, DISPLAYIO_COLORSPACE_BGR565_SWAPPED); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR555, DISPLAYIO_COLORSPACE_BGR555); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, BGR555_SWAPPED, DISPLAYIO_COLORSPACE_BGR555_SWAPPED); +MAKE_ENUM_VALUE(displayio_colorspace_type, displayio_colorspace, L8, DISPLAYIO_COLORSPACE_L8); + +//| class Colorspace: +//| """The colorspace for a `ColorConverter` to operate in""" +//| +//| RGB888: Colorspace +//| """The standard 24-bit colorspace. Bits 0-7 are blue, 8-15 are green, and 16-24 are red. (0xRRGGBB)""" +//| +//| RGB565: Colorspace +//| """The standard 16-bit colorspace. Bits 0-4 are blue, bits 5-10 are green, and 11-15 are red (0bRRRRRGGGGGGBBBBB)""" +//| +//| RGB565_SWAPPED: Colorspace +//| """The swapped 16-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB565""" +//| +//| RGB555: Colorspace +//| """The standard 15-bit colorspace. Bits 0-4 are blue, bits 5-9 are green, and 11-14 are red. The top bit is ignored. (0bxRRRRRGGGGGBBBBB)""" +//| +//| RGB555_SWAPPED: Colorspace +//| """The swapped 15-bit colorspace. First, the high and low 8 bits of the number are swapped, then they are interpreted as for RGB555""" +//| +MAKE_ENUM_MAP(displayio_colorspace) { + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB888), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB565_SWAPPED), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, RGB555_SWAPPED), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR565), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR565_SWAPPED), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR555), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, BGR555_SWAPPED), + MAKE_ENUM_MAP_ENTRY(displayio_colorspace, L8), +}; +STATIC MP_DEFINE_CONST_DICT(displayio_colorspace_locals_dict, displayio_colorspace_locals_table); + +MAKE_PRINTER(displayio, displayio_colorspace); +MAKE_ENUM_TYPE(displayio, ColorSpace, displayio_colorspace); diff --git a/circuitpython/shared-bindings/displayio/Display.c b/circuitpython/shared-bindings/displayio/Display.c new file mode 100644 index 0000000..b19c0ba --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Display.c @@ -0,0 +1,524 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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 "shared-bindings/displayio/Display.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/translate.h" + +//| _DisplayBus = Union['FourWire', 'paralleldisplay.ParallelBus', 'I2CDisplay'] +//| """:py:class:`FourWire`, :py:class:`paralleldisplay.ParallelBus` or :py:class:`I2CDisplay`""" +//| + +//| +//| class Display: +//| """Manage updating a display over a display bus +//| +//| This initializes a display and connects it into CircuitPython. Unlike other +//| objects in CircuitPython, Display objects live until `displayio.release_displays()` +//| is called. This is done so that CircuitPython can use the display itself. +//| +//| Most people should not use this class directly. Use a specific display driver instead that will +//| contain the initialization sequence at minimum.""" +//| +//| def __init__(self, display_bus: _DisplayBus, init_sequence: ReadableBuffer, *, width: int, height: int, colstart: int = 0, rowstart: int = 0, rotation: int = 0, color_depth: int = 16, grayscale: bool = False, pixels_in_byte_share_row: bool = True, bytes_per_cell: int = 1, reverse_pixels_in_byte: bool = False, set_column_command: int = 0x2a, set_row_command: int = 0x2b, write_ram_command: int = 0x2c, backlight_pin: Optional[microcontroller.Pin] = None, brightness_command: Optional[int] = None, brightness: float = 1.0, auto_brightness: bool = False, single_byte_bounds: bool = False, data_as_commands: bool = False, auto_refresh: bool = True, native_frames_per_second: int = 60, backlight_on_high: bool = True, SH1107_addressing: bool = False) -> None: +//| r"""Create a Display object on the given display bus (`FourWire`, `ParallelBus` or `I2CDisplay`). +//| +//| The ``init_sequence`` is bitpacked to minimize the ram impact. Every command begins with a +//| command byte followed by a byte to determine the parameter count and delay. When the top bit +//| of the second byte is 1 (0x80), a delay will occur after the command parameters are sent. +//| The remaining 7 bits are the parameter count excluding any delay byte. The bytes following +//| are the parameters. When the delay bit is set, a single byte after the parameters specifies +//| the delay duration in milliseconds. The value 0xff will lead to an extra long 500 ms delay +//| instead of 255 ms. The next byte will begin a new command definition. +//| Here is an example: +//| +//| .. code-block:: python +//| +//| init_sequence = (b"\xe1\x0f\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F" # Set Gamma +//| b"\x11\x80\x78"# Exit Sleep then delay 0x78 (120ms) +//| b"\x29\x81\xaa\x78"# Display on then delay 0x78 (120ms) +//| ) +//| display = displayio.Display(display_bus, init_sequence, width=320, height=240) +//| +//| The first command is 0xe1 with 15 (0xf) parameters following. The second is 0x11 with 0 +//| parameters and a 120ms (0x78) delay. The third command is 0x29 with one parameter 0xaa and a +//| 120ms delay (0x78). Multiple byte literals (b"") are merged together on load. The parens +//| are needed to allow byte literals on subsequent lines. +//| +//| The initialization sequence should always leave the display memory access inline with the scan +//| of the display to minimize tearing artifacts. +//| +//| :param display_bus: The bus that the display is connected to +//| :type _DisplayBus: FourWire, ParallelBus or I2CDisplay +//| :param ~circuitpython_typing.ReadableBuffer init_sequence: Byte-packed initialization sequence. +//| :param int width: Width in pixels +//| :param int height: Height in pixels +//| :param int colstart: The index if the first visible column +//| :param int rowstart: The index if the first visible row +//| :param int rotation: The rotation of the display in degrees clockwise. Must be in 90 degree increments (0, 90, 180, 270) +//| :param int color_depth: The number of bits of color per pixel transmitted. (Some displays +//| support 18 bit but 16 is easier to transmit. The last bit is extrapolated.) +//| :param bool grayscale: True if the display only shows a single color. +//| :param bool pixels_in_byte_share_row: True when pixels are less than a byte and a byte includes pixels from the same row of the display. When False, pixels share a column. +//| :param int bytes_per_cell: Number of bytes per addressable memory location when color_depth < 8. When greater than one, bytes share a row or column according to pixels_in_byte_share_row. +//| :param bool reverse_pixels_in_byte: Reverses the pixel order within each byte when color_depth < 8. Does not apply across multiple bytes even if there is more than one byte per cell (bytes_per_cell.) +//| :param bool reverse_bytes_in_word: Reverses the order of bytes within a word when color_depth == 16 +//| :param int set_column_command: Command used to set the start and end columns to update +//| :param int set_row_command: Command used so set the start and end rows to update +//| :param int write_ram_command: Command used to write pixels values into the update region. Ignored if data_as_commands is set. +//| :param microcontroller.Pin backlight_pin: Pin connected to the display's backlight +//| :param int brightness_command: Command to set display brightness. Usually available in OLED controllers. +//| :param float brightness: Initial display brightness. This value is ignored if auto_brightness is True. +//| :param bool auto_brightness: If True, brightness is controlled via an ambient light sensor or other mechanism. +//| :param bool single_byte_bounds: Display column and row commands use single bytes +//| :param bool data_as_commands: Treat all init and boundary data as SPI commands. Certain displays require this. +//| :param bool auto_refresh: Automatically refresh the screen +//| :param int native_frames_per_second: Number of display refreshes per second that occur with the given init_sequence. +//| :param bool backlight_on_high: If True, pulling the backlight pin high turns the backlight on. +//| :param bool SH1107_addressing: Special quirk for SH1107, use upper/lower column set and page set +//| :param int set_vertical_scroll: This parameter is accepted but ignored for backwards compatibility. It will be removed in a future release. +//| """ +//| ... +//| +STATIC mp_obj_t displayio_display_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_display_bus, ARG_init_sequence, ARG_width, ARG_height, ARG_colstart, ARG_rowstart, + ARG_rotation, ARG_color_depth, ARG_grayscale, ARG_pixels_in_byte_share_row, + ARG_bytes_per_cell, ARG_reverse_pixels_in_byte, ARG_reverse_bytes_in_word, + ARG_set_column_command, ARG_set_row_command, ARG_write_ram_command, + ARG_set_vertical_scroll, ARG_backlight_pin, ARG_brightness_command, + ARG_brightness, ARG_auto_brightness, ARG_single_byte_bounds, ARG_data_as_commands, + ARG_auto_refresh, ARG_native_frames_per_second, ARG_backlight_on_high, + ARG_SH1107_addressing }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_display_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_init_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_width, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_colstart, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_rowstart, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_rotation, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_color_depth, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 16} }, + { MP_QSTR_grayscale, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_pixels_in_byte_share_row, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_bytes_per_cell, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + { MP_QSTR_reverse_pixels_in_byte, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_reverse_bytes_in_word, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_set_column_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2a} }, + { MP_QSTR_set_row_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2b} }, + { MP_QSTR_write_ram_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2c} }, + { MP_QSTR_set_vertical_scroll, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x0} }, + { MP_QSTR_backlight_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_brightness_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_BRIGHTNESS_COMMAND} }, + { MP_QSTR_brightness, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} }, + { MP_QSTR_auto_brightness, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_single_byte_bounds, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_data_as_commands, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_auto_refresh, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_native_frames_per_second, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 60} }, + { MP_QSTR_backlight_on_high, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_SH1107_addressing, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} } + }; + 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 display_bus = args[ARG_display_bus].u_obj; + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_init_sequence].u_obj, &bufinfo, MP_BUFFER_READ); + + const mcu_pin_obj_t *backlight_pin = validate_obj_is_free_pin_or_none(args[ARG_backlight_pin].u_obj); + + mp_float_t brightness = mp_obj_get_float(args[ARG_brightness].u_obj); + + mp_int_t rotation = args[ARG_rotation].u_int; + if (rotation % 90 != 0) { + mp_raise_ValueError(translate("Display rotation must be in 90 degree increments")); + } + + const bool sh1107_addressing = args[ARG_SH1107_addressing].u_bool; + const mp_int_t color_depth = args[ARG_color_depth].u_int; + if (sh1107_addressing && color_depth != 1) { + mp_raise_ValueError_varg(translate("%q must be 1 when %q is True"), MP_QSTR_color_depth, MP_QSTR_SH1107_addressing); + } + + primary_display_t *disp = allocate_display_or_raise(); + displayio_display_obj_t *self = &disp->display; + + self->base.type = &displayio_display_type; + common_hal_displayio_display_construct( + self, + display_bus, args[ARG_width].u_int, args[ARG_height].u_int, args[ARG_colstart].u_int, args[ARG_rowstart].u_int, rotation, + color_depth, args[ARG_grayscale].u_bool, + args[ARG_pixels_in_byte_share_row].u_bool, + args[ARG_bytes_per_cell].u_bool, + args[ARG_reverse_pixels_in_byte].u_bool, + args[ARG_reverse_bytes_in_word].u_bool, + args[ARG_set_column_command].u_int, args[ARG_set_row_command].u_int, + args[ARG_write_ram_command].u_int, + bufinfo.buf, bufinfo.len, + MP_OBJ_TO_PTR(backlight_pin), + args[ARG_brightness_command].u_int, + brightness, + args[ARG_auto_brightness].u_bool, + args[ARG_single_byte_bounds].u_bool, + args[ARG_data_as_commands].u_bool, + args[ARG_auto_refresh].u_bool, + args[ARG_native_frames_per_second].u_int, + args[ARG_backlight_on_high].u_bool, + sh1107_addressing + ); + + return self; +} + +// Helper to ensure we have the native super class instead of a subclass. +static displayio_display_obj_t *native_display(mp_obj_t display_obj) { + mp_obj_t native_display = mp_obj_cast_to_native_base(display_obj, &displayio_display_type); + mp_obj_assert_native_inited(native_display); + return MP_OBJ_TO_PTR(native_display); +} + +//| def show(self, group: Group) -> None: +//| """Switches to displaying the given group of layers. When group is None, the default +//| CircuitPython terminal will be shown. +//| +//| :param Group group: The group to show.""" +//| ... +//| +STATIC mp_obj_t displayio_display_obj_show(mp_obj_t self_in, mp_obj_t group_in) { + displayio_display_obj_t *self = native_display(self_in); + displayio_group_t *group = NULL; + if (group_in != mp_const_none) { + group = MP_OBJ_TO_PTR(native_group(group_in)); + } + + bool ok = common_hal_displayio_display_show(self, group); + if (!ok) { + mp_raise_ValueError(translate("Group already used")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_show_obj, displayio_display_obj_show); + +//| def refresh(self, *, target_frames_per_second: Optional[int] = None, minimum_frames_per_second: int = 0) -> bool: +//| """When auto_refresh is off, and :py:attr:`target_frames_per_second` is not `None` this waits +//| for the target frame rate and then refreshes the display, +//| returning `True`. If the call has taken too long since the last refresh call for the given +//| target frame rate, then the refresh returns `False` immediately without updating the screen to +//| hopefully help getting caught up. +//| +//| If the time since the last successful refresh is below the minimum frame rate, then an +//| exception will be raised. The default :py:attr:`minimum_frames_per_second` of 0 disables this behavior. +//| +//| When auto_refresh is off, and :py:attr:`target_frames_per_second` is `None` this +//| will update the display immediately. +//| +//| When auto_refresh is on, updates the display immediately. (The display will also update +//| without calls to this.) +//| +//| :param Optional[int] target_frames_per_second: The target frame rate that :py:func:`refresh` should try to +//| achieve. Set to `None` for immediate refresh. +//| :param int minimum_frames_per_second: The minimum number of times the screen should be updated per second.""" +//| ... +//| +STATIC mp_obj_t displayio_display_obj_refresh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_target_frames_per_second, ARG_minimum_frames_per_second }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_target_frames_per_second, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_minimum_frames_per_second, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_display_obj_t *self = native_display(pos_args[0]); + uint32_t maximum_ms_per_real_frame = 0xffffffff; + mp_int_t minimum_frames_per_second = args[ARG_minimum_frames_per_second].u_int; + if (minimum_frames_per_second > 0) { + maximum_ms_per_real_frame = 1000 / minimum_frames_per_second; + } + + uint32_t target_ms_per_frame; + if (args[ARG_target_frames_per_second].u_obj == mp_const_none) { + target_ms_per_frame = 0xffffffff; + } else { + target_ms_per_frame = 1000 / mp_obj_get_int(args[ARG_target_frames_per_second].u_obj); + } + + return mp_obj_new_bool(common_hal_displayio_display_refresh(self, target_ms_per_frame, maximum_ms_per_real_frame)); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_display_refresh_obj, 1, displayio_display_obj_refresh); + +//| auto_refresh: bool +//| """True when the display is refreshed automatically.""" +//| +STATIC mp_obj_t displayio_display_obj_get_auto_refresh(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return mp_obj_new_bool(common_hal_displayio_display_get_auto_refresh(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_auto_refresh_obj, displayio_display_obj_get_auto_refresh); + +STATIC mp_obj_t displayio_display_obj_set_auto_refresh(mp_obj_t self_in, mp_obj_t auto_refresh) { + displayio_display_obj_t *self = native_display(self_in); + + common_hal_displayio_display_set_auto_refresh(self, mp_obj_is_true(auto_refresh)); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_set_auto_refresh_obj, displayio_display_obj_set_auto_refresh); + +MP_PROPERTY_GETSET(displayio_display_auto_refresh_obj, + (mp_obj_t)&displayio_display_get_auto_refresh_obj, + (mp_obj_t)&displayio_display_set_auto_refresh_obj); + +//| brightness: float +//| """The brightness of the display as a float. 0.0 is off and 1.0 is full brightness. When +//| `auto_brightness` is True, the value of `brightness` will change automatically. +//| If `brightness` is set, `auto_brightness` will be disabled and will be set to False.""" +//| +STATIC mp_obj_t displayio_display_obj_get_brightness(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + mp_float_t brightness = common_hal_displayio_display_get_brightness(self); + if (brightness < 0) { + mp_raise_RuntimeError(translate("Brightness not adjustable")); + } + return mp_obj_new_float(brightness); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_brightness_obj, displayio_display_obj_get_brightness); + +STATIC mp_obj_t displayio_display_obj_set_brightness(mp_obj_t self_in, mp_obj_t brightness_obj) { + displayio_display_obj_t *self = native_display(self_in); + common_hal_displayio_display_set_auto_brightness(self, false); + mp_float_t brightness = mp_obj_get_float(brightness_obj); + if (brightness < 0 || brightness > 1.0) { + mp_raise_ValueError(translate("Brightness must be 0-1.0")); + } + bool ok = common_hal_displayio_display_set_brightness(self, brightness); + if (!ok) { + mp_raise_RuntimeError(translate("Brightness not adjustable")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_set_brightness_obj, displayio_display_obj_set_brightness); + +MP_PROPERTY_GETSET(displayio_display_brightness_obj, + (mp_obj_t)&displayio_display_get_brightness_obj, + (mp_obj_t)&displayio_display_set_brightness_obj); + +//| auto_brightness: bool +//| """True when the display brightness is adjusted automatically, based on an ambient +//| light sensor or other method. Note that some displays may have this set to True by default, +//| but not actually implement automatic brightness adjustment. `auto_brightness` is set to False +//| if `brightness` is set manually.""" +//| +STATIC mp_obj_t displayio_display_obj_get_auto_brightness(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return mp_obj_new_bool(common_hal_displayio_display_get_auto_brightness(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_auto_brightness_obj, displayio_display_obj_get_auto_brightness); + +STATIC mp_obj_t displayio_display_obj_set_auto_brightness(mp_obj_t self_in, mp_obj_t auto_brightness) { + displayio_display_obj_t *self = native_display(self_in); + + common_hal_displayio_display_set_auto_brightness(self, mp_obj_is_true(auto_brightness)); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_set_auto_brightness_obj, displayio_display_obj_set_auto_brightness); + +MP_PROPERTY_GETSET(displayio_display_auto_brightness_obj, + (mp_obj_t)&displayio_display_get_auto_brightness_obj, + (mp_obj_t)&displayio_display_set_auto_brightness_obj); + + + + +//| width: int +//| """Gets the width of the board""" +//| +STATIC mp_obj_t displayio_display_obj_get_width(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_display_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_width_obj, displayio_display_obj_get_width); + +MP_PROPERTY_GETTER(displayio_display_width_obj, + (mp_obj_t)&displayio_display_get_width_obj); + +//| height: int +//| """Gets the height of the board""" +//| +STATIC mp_obj_t displayio_display_obj_get_height(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_display_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_height_obj, displayio_display_obj_get_height); + +MP_PROPERTY_GETTER(displayio_display_height_obj, + (mp_obj_t)&displayio_display_get_height_obj); + +//| rotation: int +//| """The rotation of the display as an int in degrees.""" +//| +STATIC mp_obj_t displayio_display_obj_get_rotation(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_display_get_rotation(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_rotation_obj, displayio_display_obj_get_rotation); +STATIC mp_obj_t displayio_display_obj_set_rotation(mp_obj_t self_in, mp_obj_t value) { + displayio_display_obj_t *self = native_display(self_in); + common_hal_displayio_display_set_rotation(self, mp_obj_get_int(value)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_set_rotation_obj, displayio_display_obj_set_rotation); + + +MP_PROPERTY_GETSET(displayio_display_rotation_obj, + (mp_obj_t)&displayio_display_get_rotation_obj, + (mp_obj_t)&displayio_display_set_rotation_obj); + +//| bus: _DisplayBus +//| """The bus being used by the display""" +//| +//| +STATIC mp_obj_t displayio_display_obj_get_bus(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return common_hal_displayio_display_get_bus(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_bus_obj, displayio_display_obj_get_bus); + +MP_PROPERTY_GETTER(displayio_display_bus_obj, + (mp_obj_t)&displayio_display_get_bus_obj); + +//| root_group: Group +//| """The root group on the display.""" +//| +//| +STATIC mp_obj_t displayio_display_obj_get_root_group(mp_obj_t self_in) { + displayio_display_obj_t *self = native_display(self_in); + return common_hal_displayio_display_get_root_group(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_root_group_obj, displayio_display_obj_get_root_group); + +MP_PROPERTY_GETTER(displayio_display_root_group_obj, + (mp_obj_t)&displayio_display_get_root_group_obj); + + +//| def fill_row(self, y: int, buffer: WriteableBuffer) -> WriteableBuffer: +//| """Extract the pixels from a single row +//| +//| :param int y: The top edge of the area +//| :param ~circuitpython_typing.WriteableBuffer buffer: The buffer in which to place the pixel data""" +//| ... +//| +STATIC mp_obj_t displayio_display_obj_fill_row(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_y, ARG_buffer }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_y, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = -1} }, + { MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED, {} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + displayio_display_obj_t *self = native_display(pos_args[0]); + mp_int_t y = args[ARG_y].u_int; + mp_obj_t *result = args[ARG_buffer].u_obj; + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(result, &bufinfo, MP_BUFFER_WRITE); + + if (self->core.colorspace.depth != 16) { + mp_raise_ValueError(translate("Display must have a 16 bit colorspace.")); + } + + displayio_area_t area = { + .x1 = 0, + .y1 = y, + .x2 = self->core.width, + .y2 = y + 1 + }; + uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; + uint16_t buffer_size = self->core.width / pixels_per_word; + uint16_t pixels_per_buffer = displayio_area_size(&area); + if (pixels_per_buffer % pixels_per_word) { + buffer_size += 1; + } + + uint32_t *result_buffer = bufinfo.buf; + size_t result_buffer_size = bufinfo.len; + + if (result_buffer_size >= (buffer_size * 4)) { + volatile uint32_t mask_length = (pixels_per_buffer / 32) + 1; + uint32_t mask[mask_length]; + + for (uint16_t k = 0; k < mask_length; k++) { + mask[k] = 0x00000000; + } + + displayio_display_core_fill_area(&self->core, &area, mask, result_buffer); + return result; + } else { + mp_raise_ValueError(translate("Buffer is too small")); + } +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_display_fill_row_obj, 1, displayio_display_obj_fill_row); + +STATIC const mp_rom_map_elem_t displayio_display_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&displayio_display_show_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&displayio_display_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill_row), MP_ROM_PTR(&displayio_display_fill_row_obj) }, + + { MP_ROM_QSTR(MP_QSTR_auto_refresh), MP_ROM_PTR(&displayio_display_auto_refresh_obj) }, + + { MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&displayio_display_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_auto_brightness), MP_ROM_PTR(&displayio_display_auto_brightness_obj) }, + + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_display_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_display_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_rotation), MP_ROM_PTR(&displayio_display_rotation_obj) }, + { MP_ROM_QSTR(MP_QSTR_bus), MP_ROM_PTR(&displayio_display_bus_obj) }, + { MP_ROM_QSTR(MP_QSTR_root_group), MP_ROM_PTR(&displayio_display_root_group_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_display_locals_dict, displayio_display_locals_dict_table); + +const mp_obj_type_t displayio_display_type = { + { &mp_type_type }, + .name = MP_QSTR_Display, + .make_new = displayio_display_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_display_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/Display.h b/circuitpython/shared-bindings/displayio/Display.h new file mode 100644 index 0000000..f193e61 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Display.h @@ -0,0 +1,74 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_DISPLAY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_DISPLAY_H + +#include "common-hal/microcontroller/Pin.h" + +#include "shared-module/displayio/Display.h" +#include "shared-module/displayio/Group.h" + +extern const mp_obj_type_t displayio_display_type; + +#define NO_BRIGHTNESS_COMMAND 0x100 + +void common_hal_displayio_display_construct(displayio_display_obj_t *self, + mp_obj_t bus, uint16_t width, uint16_t height, + int16_t colstart, int16_t rowstart, uint16_t rotation, uint16_t color_depth, bool grayscale, + bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte, bool reverse_bytes_in_word, + uint8_t set_column_command, uint8_t set_row_command, uint8_t write_ram_command, + uint8_t *init_sequence, uint16_t init_sequence_len, const mcu_pin_obj_t *backlight_pin, uint16_t brightness_command, + mp_float_t brightness, bool auto_brightness, + bool single_byte_bounds, bool data_as_commands, bool auto_refresh, uint16_t native_frames_per_second, + bool backlight_on_high, bool SH1107_addressing); + +bool common_hal_displayio_display_show(displayio_display_obj_t *self, + displayio_group_t *root_group); + +bool common_hal_displayio_display_refresh(displayio_display_obj_t *self, uint32_t target_ms_per_frame, uint32_t maximum_ms_per_real_frame); + +bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t *self); +void common_hal_displayio_display_set_auto_refresh(displayio_display_obj_t *self, bool auto_refresh); + +uint16_t common_hal_displayio_display_get_width(displayio_display_obj_t *self); +uint16_t common_hal_displayio_display_get_height(displayio_display_obj_t *self); +uint16_t common_hal_displayio_display_get_rotation(displayio_display_obj_t *self); +void common_hal_displayio_display_set_rotation(displayio_display_obj_t *self, int rotation); + +bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t *self); +void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t *self, bool auto_brightness); + +bool common_hal_displayio_display_get_dither(displayio_display_obj_t *self); +void common_hal_displayio_display_set_dither(displayio_display_obj_t *self, bool dither); + +mp_float_t common_hal_displayio_display_get_brightness(displayio_display_obj_t *self); +bool common_hal_displayio_display_set_brightness(displayio_display_obj_t *self, mp_float_t brightness); + +mp_obj_t common_hal_displayio_display_get_bus(displayio_display_obj_t *self); +mp_obj_t common_hal_displayio_display_get_root_group(displayio_display_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_DISPLAY_H diff --git a/circuitpython/shared-bindings/displayio/EPaperDisplay.c b/circuitpython/shared-bindings/displayio/EPaperDisplay.c new file mode 100644 index 0000000..94d2978 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/EPaperDisplay.c @@ -0,0 +1,363 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft 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 "shared-bindings/displayio/EPaperDisplay.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/translate.h" + +//| class EPaperDisplay: +//| """Manage updating an epaper display over a display bus +//| +//| This initializes an epaper display and connects it into CircuitPython. Unlike other +//| objects in CircuitPython, EPaperDisplay objects live until `displayio.release_displays()` +//| is called. This is done so that CircuitPython can use the display itself. +//| +//| Most people should not use this class directly. Use a specific display driver instead that will +//| contain the startup and shutdown sequences at minimum.""" +//| +//| def __init__(self, display_bus: _DisplayBus, +//| start_sequence: ReadableBuffer, stop_sequence: ReadableBuffer, *, +//| width: int, height: int, ram_width: int, ram_height: int, +//| colstart: int = 0, rowstart: int = 0, rotation: int = 0, +//| set_column_window_command: Optional[int] = None, +//| set_row_window_command: Optional[int] = None, +//| set_current_column_command: Optional[int] = None, +//| set_current_row_command: Optional[int] = None, +//| write_black_ram_command: int, black_bits_inverted: bool = False, +//| write_color_ram_command: Optional[int] = None, +//| color_bits_inverted: bool = False, highlight_color: int = 0x000000, +//| refresh_display_command: int, refresh_time: float = 40, +//| busy_pin: Optional[microcontroller.Pin] = None, busy_state: bool = True, +//| seconds_per_frame: float = 180, always_toggle_chip_select: bool = False, +//| grayscale: bool = False, two_byte_sequence_length: bool = False) -> None: +//| """Create a EPaperDisplay object on the given display bus (`displayio.FourWire` or `paralleldisplay.ParallelBus`). +//| +//| The ``start_sequence`` and ``stop_sequence`` are bitpacked to minimize the ram impact. Every +//| command begins with a command byte followed by a byte to determine the parameter count and +//| delay. When the top bit of the second byte is 1 (0x80), a delay will occur after the command +//| parameters are sent. The remaining 7 bits are the parameter count excluding any delay +//| byte. The bytes following are the parameters. When the delay bit is set, a single byte after +//| the parameters specifies the delay duration in milliseconds. The value 0xff will lead to an +//| extra long 500 ms delay instead of 255 ms. The next byte will begin a new command definition. +//| +//| :param display_bus: The bus that the display is connected to +//| :type _DisplayBus: displayio.FourWire or paralleldisplay.ParallelBus +//| :param ~circuitpython_typing.ReadableBuffer start_sequence: Byte-packed initialization sequence. +//| :param ~circuitpython_typing.ReadableBuffer stop_sequence: Byte-packed initialization sequence. +//| :param int width: Width in pixels +//| :param int height: Height in pixels +//| :param int ram_width: RAM width in pixels +//| :param int ram_height: RAM height in pixels +//| :param int colstart: The index if the first visible column +//| :param int rowstart: The index if the first visible row +//| :param int rotation: The rotation of the display in degrees clockwise. Must be in 90 degree increments (0, 90, 180, 270) +//| :param int set_column_window_command: Command used to set the start and end columns to update +//| :param int set_row_window_command: Command used so set the start and end rows to update +//| :param int set_current_column_command: Command used to set the current column location +//| :param int set_current_row_command: Command used to set the current row location +//| :param int write_black_ram_command: Command used to write pixels values into the update region +//| :param bool black_bits_inverted: True if 0 bits are used to show black pixels. Otherwise, 1 means to show black. +//| :param int write_color_ram_command: Command used to write pixels values into the update region +//| :param bool color_bits_inverted: True if 0 bits are used to show the color. Otherwise, 1 means to show color. +//| :param int highlight_color: RGB888 of source color to highlight with third ePaper color. +//| :param int refresh_display_command: Command used to start a display refresh +//| :param float refresh_time: Time it takes to refresh the display before the stop_sequence should be sent. Ignored when busy_pin is provided. +//| :param microcontroller.Pin busy_pin: Pin used to signify the display is busy +//| :param bool busy_state: State of the busy pin when the display is busy +//| :param float seconds_per_frame: Minimum number of seconds between screen refreshes +//| :param bool always_toggle_chip_select: When True, chip select is toggled every byte +//| :param bool grayscale: When true, the color ram is the low bit of 2-bit grayscale +//| :param bool two_byte_sequence_length: When true, use two bytes to define sequence length""" +//| ... +//| +STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_display_bus, ARG_start_sequence, ARG_stop_sequence, ARG_width, ARG_height, + ARG_ram_width, ARG_ram_height, ARG_colstart, ARG_rowstart, ARG_rotation, + ARG_set_column_window_command, ARG_set_row_window_command, ARG_set_current_column_command, + ARG_set_current_row_command, ARG_write_black_ram_command, ARG_black_bits_inverted, + ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_highlight_color, + ARG_refresh_display_command, ARG_refresh_time, ARG_busy_pin, ARG_busy_state, + ARG_seconds_per_frame, ARG_always_toggle_chip_select, ARG_grayscale, ARG_two_byte_sequence_length }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_display_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_start_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_stop_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_width, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_ram_width, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_ram_height, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, }, + { MP_QSTR_colstart, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_rowstart, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_rotation, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_set_column_window_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_COMMAND} }, + { MP_QSTR_set_row_window_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_COMMAND} }, + { MP_QSTR_set_current_column_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_COMMAND} }, + { MP_QSTR_set_current_row_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_COMMAND} }, + { MP_QSTR_write_black_ram_command, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_black_bits_inverted, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_write_color_ram_command, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_color_bits_inverted, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_highlight_color, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} }, + { MP_QSTR_refresh_display_command, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_refresh_time, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(40)} }, + { MP_QSTR_busy_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_busy_state, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_seconds_per_frame, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(180)} }, + { MP_QSTR_always_toggle_chip_select, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_grayscale, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_two_byte_sequence_length, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + 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 display_bus = args[ARG_display_bus].u_obj; + + mp_buffer_info_t start_bufinfo; + mp_get_buffer_raise(args[ARG_start_sequence].u_obj, &start_bufinfo, MP_BUFFER_READ); + mp_buffer_info_t stop_bufinfo; + mp_get_buffer_raise(args[ARG_stop_sequence].u_obj, &stop_bufinfo, MP_BUFFER_READ); + + + const mcu_pin_obj_t *busy_pin = validate_obj_is_free_pin_or_none(args[ARG_busy_pin].u_obj); + + mp_int_t rotation = args[ARG_rotation].u_int; + if (rotation % 90 != 0) { + mp_raise_ValueError(translate("Display rotation must be in 90 degree increments")); + } + + primary_display_t *disp = allocate_display_or_raise(); + displayio_epaperdisplay_obj_t *self = &disp->epaper_display; + ; + + mp_float_t refresh_time = mp_obj_get_float(args[ARG_refresh_time].u_obj); + mp_float_t seconds_per_frame = mp_obj_get_float(args[ARG_seconds_per_frame].u_obj); + + mp_int_t write_color_ram_command = NO_COMMAND; + mp_int_t highlight_color = args[ARG_highlight_color].u_int; + if (args[ARG_write_color_ram_command].u_obj != mp_const_none) { + write_color_ram_command = mp_obj_get_int(args[ARG_write_color_ram_command].u_obj); + } + + self->base.type = &displayio_epaperdisplay_type; + common_hal_displayio_epaperdisplay_construct( + self, + display_bus, + start_bufinfo.buf, start_bufinfo.len, stop_bufinfo.buf, stop_bufinfo.len, + args[ARG_width].u_int, args[ARG_height].u_int, args[ARG_ram_width].u_int, args[ARG_ram_height].u_int, + args[ARG_colstart].u_int, args[ARG_rowstart].u_int, rotation, + args[ARG_set_column_window_command].u_int, args[ARG_set_row_window_command].u_int, + args[ARG_set_current_column_command].u_int, args[ARG_set_current_row_command].u_int, + args[ARG_write_black_ram_command].u_int, args[ARG_black_bits_inverted].u_bool, write_color_ram_command, + args[ARG_color_bits_inverted].u_bool, highlight_color, args[ARG_refresh_display_command].u_int, refresh_time, + busy_pin, args[ARG_busy_state].u_bool, seconds_per_frame, + args[ARG_always_toggle_chip_select].u_bool, args[ARG_grayscale].u_bool, args[ARG_two_byte_sequence_length].u_bool + ); + + return self; +} + +// Helper to ensure we have the native super class instead of a subclass. +static displayio_epaperdisplay_obj_t *native_display(mp_obj_t display_obj) { + mp_obj_t native_display = mp_obj_cast_to_native_base(display_obj, &displayio_epaperdisplay_type); + mp_obj_assert_native_inited(native_display); + return MP_OBJ_TO_PTR(native_display); +} + +//| def show(self, group: Group) -> None: +//| """Switches to displaying the given group of layers. When group is None, the default +//| CircuitPython terminal will be shown. +//| +//| :param Group group: The group to show.""" +//| ... +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_show(mp_obj_t self_in, mp_obj_t group_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + displayio_group_t *group = NULL; + if (group_in != mp_const_none) { + group = MP_OBJ_TO_PTR(native_group(group_in)); + } + + bool ok = common_hal_displayio_epaperdisplay_show(self, group); + if (!ok) { + mp_raise_ValueError(translate("Group already used")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_epaperdisplay_show_obj, displayio_epaperdisplay_obj_show); + +//| def update_refresh_mode(self, start_sequence: ReadableBuffer, seconds_per_frame: float = 180) -> None: +//| """Updates the ``start_sequence`` and ``seconds_per_frame`` parameters to enable +//| varying the refresh mode of the display.""" +//| +STATIC mp_obj_t displayio_epaperdisplay_update_refresh_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_start_sequence, ARG_seconds_per_frame }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_start_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_seconds_per_frame, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(180)} }, + }; + displayio_epaperdisplay_obj_t *self = native_display(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get parameters + mp_buffer_info_t start_sequence; + mp_get_buffer_raise(args[ARG_start_sequence].u_obj, &start_sequence, MP_BUFFER_READ); + float seconds_per_frame = mp_obj_get_float(args[ARG_seconds_per_frame].u_obj); + + // Update parameters + displayio_epaperdisplay_change_refresh_mode_parameters(self, &start_sequence, seconds_per_frame); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_epaperdisplay_update_refresh_mode_obj, 1, displayio_epaperdisplay_update_refresh_mode); + +//| def refresh(self) -> None: +//| """Refreshes the display immediately or raises an exception if too soon. Use +//| ``time.sleep(display.time_to_refresh)`` to sleep until a refresh can occur.""" +//| ... +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_refresh(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + bool ok = common_hal_displayio_epaperdisplay_refresh(self); + if (!ok) { + mp_raise_RuntimeError(translate("Refresh too soon")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_refresh_obj, displayio_epaperdisplay_obj_refresh); + +//| time_to_refresh: float +//| """Time, in fractional seconds, until the ePaper display can be refreshed.""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_time_to_refresh(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return mp_obj_new_float(common_hal_displayio_epaperdisplay_get_time_to_refresh(self) / 1000.0); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_time_to_refresh_obj, displayio_epaperdisplay_obj_get_time_to_refresh); + +MP_PROPERTY_GETTER(displayio_epaperdisplay_time_to_refresh_obj, + (mp_obj_t)&displayio_epaperdisplay_get_time_to_refresh_obj); + +//| busy: bool +//| """True when the display is refreshing. This uses the ``busy_pin`` when available or the +//| ``refresh_time`` otherwise.""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_busy(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return mp_obj_new_bool(common_hal_displayio_epaperdisplay_get_busy(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_busy_obj, displayio_epaperdisplay_obj_get_busy); + +MP_PROPERTY_GETTER(displayio_epaperdisplay_busy_obj, + (mp_obj_t)&displayio_epaperdisplay_get_busy_obj); + +//| width: int +//| """Gets the width of the display in pixels""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_width(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_epaperdisplay_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_width_obj, displayio_epaperdisplay_obj_get_width); + +MP_PROPERTY_GETTER(displayio_epaperdisplay_width_obj, + (mp_obj_t)&displayio_epaperdisplay_get_width_obj); + +//| height: int +//| """Gets the height of the display in pixels""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_height(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_epaperdisplay_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_height_obj, displayio_epaperdisplay_obj_get_height); + +MP_PROPERTY_GETTER(displayio_epaperdisplay_height_obj, + (mp_obj_t)&displayio_epaperdisplay_get_height_obj); + +//| rotation: int +//| """The rotation of the display as an int in degrees.""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_rotation(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_epaperdisplay_get_rotation(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_rotation_obj, displayio_epaperdisplay_obj_get_rotation); +STATIC mp_obj_t displayio_epaperdisplay_obj_set_rotation(mp_obj_t self_in, mp_obj_t value) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + common_hal_displayio_epaperdisplay_set_rotation(self, mp_obj_get_int(value)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_epaperdisplay_set_rotation_obj, displayio_epaperdisplay_obj_set_rotation); + + +MP_PROPERTY_GETSET(displayio_epaperdisplay_rotation_obj, + (mp_obj_t)&displayio_epaperdisplay_get_rotation_obj, + (mp_obj_t)&displayio_epaperdisplay_set_rotation_obj); + +//| bus: _DisplayBus +//| """The bus being used by the display""" +//| +STATIC mp_obj_t displayio_epaperdisplay_obj_get_bus(mp_obj_t self_in) { + displayio_epaperdisplay_obj_t *self = native_display(self_in); + return common_hal_displayio_epaperdisplay_get_bus(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_bus_obj, displayio_epaperdisplay_obj_get_bus); + +MP_PROPERTY_GETTER(displayio_epaperdisplay_bus_obj, + (mp_obj_t)&displayio_epaperdisplay_get_bus_obj); + + +STATIC const mp_rom_map_elem_t displayio_epaperdisplay_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&displayio_epaperdisplay_show_obj) }, + { MP_ROM_QSTR(MP_QSTR_update_refresh_mode), MP_ROM_PTR(&displayio_epaperdisplay_update_refresh_mode_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&displayio_epaperdisplay_refresh_obj) }, + + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_epaperdisplay_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_epaperdisplay_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_rotation), MP_ROM_PTR(&displayio_epaperdisplay_rotation_obj) }, + { MP_ROM_QSTR(MP_QSTR_bus), MP_ROM_PTR(&displayio_epaperdisplay_bus_obj) }, + { MP_ROM_QSTR(MP_QSTR_busy), MP_ROM_PTR(&displayio_epaperdisplay_busy_obj) }, + { MP_ROM_QSTR(MP_QSTR_time_to_refresh), MP_ROM_PTR(&displayio_epaperdisplay_time_to_refresh_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_epaperdisplay_locals_dict, displayio_epaperdisplay_locals_dict_table); + +const mp_obj_type_t displayio_epaperdisplay_type = { + { &mp_type_type }, + .name = MP_QSTR_EPaperDisplay, + .make_new = displayio_epaperdisplay_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_epaperdisplay_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/EPaperDisplay.h b/circuitpython/shared-bindings/displayio/EPaperDisplay.h new file mode 100644 index 0000000..ba6dffc --- /dev/null +++ b/circuitpython/shared-bindings/displayio/EPaperDisplay.h @@ -0,0 +1,62 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_EPAPERDISPLAY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_EPAPERDISPLAY_H + +#include "common-hal/microcontroller/Pin.h" + +#include "shared-module/displayio/EPaperDisplay.h" +#include "shared-module/displayio/Group.h" + +extern const mp_obj_type_t displayio_epaperdisplay_type; + +#define NO_COMMAND 0x100 + +void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t *self, + mp_obj_t bus, const uint8_t *start_sequence, uint16_t start_sequence_len, const uint8_t *stop_sequence, uint16_t stop_sequence_len, + uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, int16_t colstart, int16_t rowstart, uint16_t rotation, + uint16_t set_column_window_command, uint16_t set_row_window_command, + uint16_t set_current_column_command, uint16_t set_current_row_command, + uint16_t write_black_ram_command, bool black_bits_inverted, uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint16_t refresh_display_command, mp_float_t refresh_time, + const mcu_pin_obj_t *busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select, bool grayscale, bool two_byte_sequence_length); + +bool common_hal_displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t *self); + +bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t *self, displayio_group_t *root_group); + +// Returns time in milliseconds. +uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t *self); +bool common_hal_displayio_epaperdisplay_get_busy(displayio_epaperdisplay_obj_t *self); + +uint16_t common_hal_displayio_epaperdisplay_get_width(displayio_epaperdisplay_obj_t *self); +uint16_t common_hal_displayio_epaperdisplay_get_height(displayio_epaperdisplay_obj_t *self); +uint16_t common_hal_displayio_epaperdisplay_get_rotation(displayio_epaperdisplay_obj_t *self); +void common_hal_displayio_epaperdisplay_set_rotation(displayio_epaperdisplay_obj_t *self, int rotation); + +mp_obj_t common_hal_displayio_epaperdisplay_get_bus(displayio_epaperdisplay_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_EPAPERDISPLAY_H diff --git a/circuitpython/shared-bindings/displayio/FourWire.c b/circuitpython/shared-bindings/displayio/FourWire.c new file mode 100644 index 0000000..90aa5c6 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/FourWire.c @@ -0,0 +1,172 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Scott Shawcroft 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 "shared-bindings/displayio/FourWire.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/translate.h" + +//| class FourWire: +//| """Manage updating a display over SPI four wire protocol in the background while Python code runs. +//| It doesn't handle display initialization.""" +//| +//| def __init__(self, spi_bus: busio.SPI, *, command: Optional[microcontroller.Pin], chip_select: microcontroller.Pin, reset: Optional[microcontroller.Pin] = None, baudrate: int = 24000000, polarity: int = 0, phase: int = 0) -> None: +//| """Create a FourWire object associated with the given pins. +//| +//| The SPI bus and pins are then in use by the display until `displayio.release_displays()` is +//| called even after a reload. (It does this so CircuitPython can use the display after your code +//| is done.) So, the first time you initialize a display bus in code.py you should call +//| :py:func:`displayio.release_displays` first, otherwise it will error after the first code.py run. +//| +//| If the ``command`` pin is not specified, a 9-bit SPI mode will be simulated by adding a +//| data/command bit to every bit being transmitted, and splitting the resulting data back +//| into 8-bit bytes for transmission. The extra bits that this creates at the end are ignored +//| by the receiving device. +//| +//| :param busio.SPI spi_bus: The SPI bus that make up the clock and data lines +//| :param microcontroller.Pin command: Data or command pin. When None, 9-bit SPI is simulated. +//| :param microcontroller.Pin chip_select: Chip select pin +//| :param microcontroller.Pin reset: Reset pin. When None only software reset can be used +//| :param int baudrate: Maximum baudrate in Hz for the display on the bus +//| :param int polarity: the base state of the clock line (0 or 1) +//| :param int phase: the edge of the clock that data is captured. First (0) +//| or second (1). Rising or falling depends on clock polarity.""" +//| ... +//| +STATIC mp_obj_t displayio_fourwire_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_spi_bus, ARG_command, ARG_chip_select, ARG_reset, ARG_baudrate, ARG_polarity, ARG_phase }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_spi_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_command, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_chip_select, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_reset, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_baudrate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 24000000} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + 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); + + const mcu_pin_obj_t *command = validate_obj_is_free_pin_or_none(args[ARG_command].u_obj); + const mcu_pin_obj_t *chip_select = validate_obj_is_free_pin(args[ARG_chip_select].u_obj); + const mcu_pin_obj_t *reset = validate_obj_is_free_pin_or_none(args[ARG_reset].u_obj); + + mp_obj_t spi = mp_arg_validate_type(args[ARG_spi_bus].u_obj, &busio_spi_type, MP_QSTR_spi_bus); + + displayio_fourwire_obj_t *self = &allocate_display_bus_or_raise()->fourwire_bus; + self->base.type = &displayio_fourwire_type; + + uint8_t polarity = args[ARG_polarity].u_int; + if (polarity != 0 && polarity != 1) { + mp_raise_ValueError(translate("Invalid polarity")); + } + uint8_t phase = args[ARG_phase].u_int; + if (phase != 0 && phase != 1) { + mp_raise_ValueError(translate("Invalid phase")); + } + + common_hal_displayio_fourwire_construct(self, + MP_OBJ_TO_PTR(spi), command, chip_select, reset, args[ARG_baudrate].u_int, polarity, phase); + return self; +} + +//| def reset(self) -> None: +//| """Performs a hardware reset via the reset pin. Raises an exception if called when no reset pin +//| is available.""" +//| ... +//| +STATIC mp_obj_t displayio_fourwire_obj_reset(mp_obj_t self_in) { + displayio_fourwire_obj_t *self = self_in; + + if (!common_hal_displayio_fourwire_reset(self)) { + mp_raise_RuntimeError(translate("no reset pin available")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_fourwire_reset_obj, displayio_fourwire_obj_reset); + +//| def send(self, command: int, data: ReadableBuffer, *, toggle_every_byte: bool = False) -> None: +//| """Sends the given command value followed by the full set of data. Display state, such as +//| vertical scroll, set via ``send`` may or may not be reset once the code is done.""" +//| ... +//| +STATIC mp_obj_t displayio_fourwire_obj_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_command, ARG_data, ARG_toggle_every_byte }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_command, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_toggle_every_byte, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_int_t command_int = args[ARG_command].u_int; + if (command_int > 255 || command_int < 0) { + mp_raise_ValueError(translate("Command must be an int between 0 and 255")); + } + displayio_fourwire_obj_t *self = pos_args[0]; + uint8_t command = command_int; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ); + + // Wait for display bus to be available. + while (!common_hal_displayio_fourwire_begin_transaction(self)) { + RUN_BACKGROUND_TASKS; + } + display_chip_select_behavior_t chip_select = CHIP_SELECT_UNTOUCHED; + if (args[ARG_toggle_every_byte].u_bool) { + chip_select = CHIP_SELECT_TOGGLE_EVERY_BYTE; + } + common_hal_displayio_fourwire_send(self, DISPLAY_COMMAND, chip_select, &command, 1); + common_hal_displayio_fourwire_send(self, DISPLAY_DATA, chip_select, ((uint8_t *)bufinfo.buf), bufinfo.len); + common_hal_displayio_fourwire_end_transaction(self); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_fourwire_send_obj, 1, displayio_fourwire_obj_send); + +STATIC const mp_rom_map_elem_t displayio_fourwire_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&displayio_fourwire_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&displayio_fourwire_send_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_fourwire_locals_dict, displayio_fourwire_locals_dict_table); + +const mp_obj_type_t displayio_fourwire_type = { + { &mp_type_type }, + .name = MP_QSTR_FourWire, + .make_new = displayio_fourwire_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_fourwire_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/FourWire.h b/circuitpython/shared-bindings/displayio/FourWire.h new file mode 100644 index 0000000..300327f --- /dev/null +++ b/circuitpython/shared-bindings/displayio/FourWire.h @@ -0,0 +1,56 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_FOURWIRE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_FOURWIRE_H + +#include "shared-module/displayio/FourWire.h" + +#include "shared-bindings/displayio/__init__.h" +#include "common-hal/microcontroller/Pin.h" + +#include "shared-module/displayio/Group.h" + +extern const mp_obj_type_t displayio_fourwire_type; + +void common_hal_displayio_fourwire_construct(displayio_fourwire_obj_t *self, + busio_spi_obj_t *spi, const mcu_pin_obj_t *command, + const mcu_pin_obj_t *chip_select, const mcu_pin_obj_t *reset, uint32_t baudrate, + uint8_t polarity, uint8_t phase); + +void common_hal_displayio_fourwire_deinit(displayio_fourwire_obj_t *self); + +bool common_hal_displayio_fourwire_reset(mp_obj_t self); +bool common_hal_displayio_fourwire_bus_free(mp_obj_t self); + +bool common_hal_displayio_fourwire_begin_transaction(mp_obj_t self); + +void common_hal_displayio_fourwire_send(mp_obj_t self, display_byte_type_t byte_type, + display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length); + +void common_hal_displayio_fourwire_end_transaction(mp_obj_t self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_FOURWIRE_H diff --git a/circuitpython/shared-bindings/displayio/Group.c b/circuitpython/shared-bindings/displayio/Group.c new file mode 100644 index 0000000..188331f --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Group.c @@ -0,0 +1,355 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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 "shared-bindings/displayio/Group.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + +//| class Group: +//| """Manage a group of sprites and groups and how they are inter-related.""" +//| +//| def __init__(self, *, scale: int = 1, x: int = 0, y: int = 0) -> None: +//| """Create a Group of a given size and scale. Scale is in one dimension. For example, scale=2 +//| leads to a layer's pixel being 2x2 pixels when in the group. +//| +//| :param int scale: Scale of layer pixels in one dimension. +//| :param int x: Initial x position within the parent. +//| :param int y: Initial y position within the parent.""" +//| ... +//| +STATIC mp_obj_t displayio_group_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_scale, ARG_x, ARG_y }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_scale, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + { 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_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_int_t scale = args[ARG_scale].u_int; + if (scale < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_scale); + } + + displayio_group_t *self = m_new_obj(displayio_group_t); + self->base.type = &displayio_group_type; + common_hal_displayio_group_construct(self, scale, args[ARG_x].u_int, args[ARG_y].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +// Helper to ensure we have the native super class instead of a subclass. +displayio_group_t *native_group(mp_obj_t group_obj) { + mp_obj_t native_group = mp_obj_cast_to_native_base(group_obj, &displayio_group_type); + if (native_group == MP_OBJ_NULL) { + mp_raise_ValueError_varg(translate("Must be a %q subclass."), MP_QSTR_Group); + } + mp_obj_assert_native_inited(native_group); + return MP_OBJ_TO_PTR(native_group); +} + +//| hidden: bool +//| """True when the Group and all of it's layers are not visible. When False, the Group's layers +//| are visible if they haven't been hidden.""" +//| +STATIC mp_obj_t displayio_group_obj_get_hidden(mp_obj_t self_in) { + displayio_group_t *self = native_group(self_in); + return mp_obj_new_bool(common_hal_displayio_group_get_hidden(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_group_get_hidden_obj, displayio_group_obj_get_hidden); + +STATIC mp_obj_t displayio_group_obj_set_hidden(mp_obj_t self_in, mp_obj_t hidden_obj) { + displayio_group_t *self = native_group(self_in); + + common_hal_displayio_group_set_hidden(self, mp_obj_is_true(hidden_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_set_hidden_obj, displayio_group_obj_set_hidden); + +MP_PROPERTY_GETSET(displayio_group_hidden_obj, + (mp_obj_t)&displayio_group_get_hidden_obj, + (mp_obj_t)&displayio_group_set_hidden_obj); + +//| scale: int +//| """Scales each pixel within the Group in both directions. For example, when scale=2 each pixel +//| will be represented by 2x2 pixels.""" +//| +STATIC mp_obj_t displayio_group_obj_get_scale(mp_obj_t self_in) { + displayio_group_t *self = native_group(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_group_get_scale(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_group_get_scale_obj, displayio_group_obj_get_scale); + +STATIC mp_obj_t displayio_group_obj_set_scale(mp_obj_t self_in, mp_obj_t scale_obj) { + displayio_group_t *self = native_group(self_in); + + mp_int_t scale = mp_obj_get_int(scale_obj); + if (scale < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_scale); + } + common_hal_displayio_group_set_scale(self, scale); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_set_scale_obj, displayio_group_obj_set_scale); + +MP_PROPERTY_GETSET(displayio_group_scale_obj, + (mp_obj_t)&displayio_group_get_scale_obj, + (mp_obj_t)&displayio_group_set_scale_obj); + +//| x: int +//| """X position of the Group in the parent.""" +//| +STATIC mp_obj_t displayio_group_obj_get_x(mp_obj_t self_in) { + displayio_group_t *self = native_group(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_group_get_x(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_group_get_x_obj, displayio_group_obj_get_x); + +STATIC mp_obj_t displayio_group_obj_set_x(mp_obj_t self_in, mp_obj_t x_obj) { + displayio_group_t *self = native_group(self_in); + + mp_int_t x = mp_obj_get_int(x_obj); + common_hal_displayio_group_set_x(self, x); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_set_x_obj, displayio_group_obj_set_x); + +MP_PROPERTY_GETSET(displayio_group_x_obj, + (mp_obj_t)&displayio_group_get_x_obj, + (mp_obj_t)&displayio_group_set_x_obj); + +//| y: int +//| """Y position of the Group in the parent.""" +//| +STATIC mp_obj_t displayio_group_obj_get_y(mp_obj_t self_in) { + displayio_group_t *self = native_group(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_group_get_y(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_group_get_y_obj, displayio_group_obj_get_y); + +STATIC mp_obj_t displayio_group_obj_set_y(mp_obj_t self_in, mp_obj_t y_obj) { + displayio_group_t *self = native_group(self_in); + + mp_int_t y = mp_obj_get_int(y_obj); + common_hal_displayio_group_set_y(self, y); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_set_y_obj, displayio_group_obj_set_y); + +MP_PROPERTY_GETSET(displayio_group_y_obj, + (mp_obj_t)&displayio_group_get_y_obj, + (mp_obj_t)&displayio_group_set_y_obj); + +//| 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.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_append(mp_obj_t self_in, mp_obj_t layer) { + displayio_group_t *self = native_group(self_in); + common_hal_displayio_group_insert(self, common_hal_displayio_group_get_len(self), layer); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_append_obj, displayio_group_obj_append); + +//| def insert(self, index: int, layer: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]) -> None: +//| """Insert a layer into the group.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_insert(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t layer) { + displayio_group_t *self = native_group(self_in); + if ((size_t)MP_OBJ_SMALL_INT_VALUE(index_obj) == common_hal_displayio_group_get_len(self)) { + return displayio_group_obj_append(self_in, layer); + } + size_t index = mp_get_index(&displayio_group_type, common_hal_displayio_group_get_len(self), index_obj, false); + common_hal_displayio_group_insert(self, index, layer); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(displayio_group_insert_obj, displayio_group_obj_insert); + + +//| 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.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_index(mp_obj_t self_in, mp_obj_t layer) { + displayio_group_t *self = native_group(self_in); + mp_int_t index = common_hal_displayio_group_index(self, layer); + if (index < 0) { + mp_raise_ValueError(translate("object not in sequence")); + } + return MP_OBJ_NEW_SMALL_INT(index); +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_index_obj, displayio_group_obj_index); + +//| def pop(self, i: int = -1) -> Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]: +//| """Remove the ith item and return it.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_pop(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_i }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i, MP_ARG_INT, {.u_int = -1} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_group_t *self = native_group(pos_args[0]); + + size_t index = mp_get_index(&displayio_group_type, + common_hal_displayio_group_get_len(self), + MP_OBJ_NEW_SMALL_INT(args[ARG_i].u_int), + false); + return common_hal_displayio_group_pop(self, index); +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_group_pop_obj, 1, displayio_group_obj_pop); + + +//| 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.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_remove(mp_obj_t self_in, mp_obj_t layer) { + mp_obj_t index = displayio_group_obj_index(self_in, layer); + displayio_group_t *self = native_group(self_in); + + common_hal_displayio_group_pop(self, MP_OBJ_SMALL_INT_VALUE(index)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_remove_obj, displayio_group_obj_remove); + +//| def __bool__(self) -> bool: +//| ... +//| +//| def __len__(self) -> int: +//| """Returns the number of layers in a Group""" +//| ... +//| +STATIC mp_obj_t group_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + displayio_group_t *self = native_group(self_in); + uint16_t len = common_hal_displayio_group_get_len(self); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(len); + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| 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:: +//| +//| print(group[0])""" +//| ... +//| +//| 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:: +//| +//| group[0] = sprite""" +//| ... +//| +//| def __delitem__(self, index: int) -> None: +//| """Deletes the value at the given index. +//| +//| This allows you to:: +//| +//| del group[0]""" +//| ... +//| +STATIC mp_obj_t group_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t value) { + displayio_group_t *self = native_group(self_in); + + if (mp_obj_is_type(index_obj, &mp_type_slice)) { + mp_raise_NotImplementedError(translate("Slices not supported")); + } else { + size_t index = mp_get_index(&displayio_group_type, common_hal_displayio_group_get_len(self), index_obj, false); + + if (value == MP_OBJ_SENTINEL) { + // load + return common_hal_displayio_group_get(self, index); + } else if (value == MP_OBJ_NULL) { + common_hal_displayio_group_pop(self, index); + } else { + common_hal_displayio_group_set(self, index, value); + } + } + return mp_const_none; +} + +//| def sort(self, key: function, reverse: bool) -> None: +//| """Sort the members of the group.""" +//| ... +//| +STATIC mp_obj_t displayio_group_obj_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + displayio_group_t *self = native_group(pos_args[0]); + mp_obj_t *args = m_new(mp_obj_t, n_args); + for (size_t i = 1; i < n_args; ++i) { + args[i] = pos_args[i]; + } + args[0] = MP_OBJ_FROM_PTR(self->members); + return mp_obj_list_sort(n_args, args, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(displayio_group_sort_obj, 1, displayio_group_obj_sort); + +STATIC const mp_rom_map_elem_t displayio_group_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_hidden), MP_ROM_PTR(&displayio_group_hidden_obj) }, + { MP_ROM_QSTR(MP_QSTR_scale), MP_ROM_PTR(&displayio_group_scale_obj) }, + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&displayio_group_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&displayio_group_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&displayio_group_append_obj) }, + { MP_ROM_QSTR(MP_QSTR_insert), MP_ROM_PTR(&displayio_group_insert_obj) }, + { MP_ROM_QSTR(MP_QSTR_index), MP_ROM_PTR(&displayio_group_index_obj) }, + { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&displayio_group_pop_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&displayio_group_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&displayio_group_sort_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_group_locals_dict, displayio_group_locals_dict_table); + +const mp_obj_type_t displayio_group_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Group, + .make_new = displayio_group_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_group_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .subscr = group_subscr, + .unary_op = group_unary_op, + .getiter = mp_obj_new_generic_iterator, + ), +}; diff --git a/circuitpython/shared-bindings/displayio/Group.h b/circuitpython/shared-bindings/displayio/Group.h new file mode 100644 index 0000000..266fce9 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Group.h @@ -0,0 +1,53 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_GROUP_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_GROUP_H + +#include "shared-module/displayio/Group.h" + +extern const mp_obj_type_t displayio_group_type; + +displayio_group_t *native_group(mp_obj_t group_obj); + +void common_hal_displayio_group_construct(displayio_group_t *self, uint32_t scale, mp_int_t x, mp_int_t y); +uint32_t common_hal_displayio_group_get_scale(displayio_group_t *self); +void common_hal_displayio_group_set_scale(displayio_group_t *self, uint32_t scale); +bool common_hal_displayio_group_get_hidden(displayio_group_t *self); +void common_hal_displayio_group_set_hidden(displayio_group_t *self, bool hidden); +mp_int_t common_hal_displayio_group_get_x(displayio_group_t *self); +void common_hal_displayio_group_set_x(displayio_group_t *self, mp_int_t x); +mp_int_t common_hal_displayio_group_get_y(displayio_group_t *self); +void common_hal_displayio_group_set_y(displayio_group_t *self, mp_int_t y); +void common_hal_displayio_group_append(displayio_group_t *self, mp_obj_t layer); +void common_hal_displayio_group_insert(displayio_group_t *self, size_t index, mp_obj_t layer); +size_t common_hal_displayio_group_get_len(displayio_group_t *self); +mp_obj_t common_hal_displayio_group_pop(displayio_group_t *self, size_t index); +mp_int_t common_hal_displayio_group_index(displayio_group_t *self, mp_obj_t layer); +mp_obj_t common_hal_displayio_group_get(displayio_group_t *self, size_t index); +void common_hal_displayio_group_set(displayio_group_t *self, size_t index, mp_obj_t layer); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_GROUP_H diff --git a/circuitpython/shared-bindings/displayio/I2CDisplay.c b/circuitpython/shared-bindings/displayio/I2CDisplay.c new file mode 100644 index 0000000..fbfbd04 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/I2CDisplay.c @@ -0,0 +1,134 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft 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 "shared-bindings/displayio/I2CDisplay.h" + +#include <stdint.h> +#include <string.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/translate.h" + +//| class I2CDisplay: +//| """Manage updating a display over I2C in the background while Python code runs. +//| It doesn't handle display initialization.""" +//| +//| def __init__(self, i2c_bus: busio.I2C, *, device_address: int, reset: Optional[microcontroller.Pin] = None) -> None: +//| """Create a I2CDisplay object associated with the given I2C bus and reset pin. +//| +//| The I2C bus and pins are then in use by the display until `displayio.release_displays()` is +//| called even after a reload. (It does this so CircuitPython can use the display after your code +//| is done.) So, the first time you initialize a display bus in code.py you should call +//| :py:func:`displayio.release_displays` first, otherwise it will error after the first code.py run. +//| +//| :param busio.I2C i2c_bus: The I2C bus that make up the clock and data lines +//| :param int device_address: The I2C address of the device +//| :param microcontroller.Pin reset: Reset pin. When None only software reset can be used""" +//| ... +//| +STATIC mp_obj_t displayio_i2cdisplay_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_i2c_bus, ARG_device_address, ARG_reset }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_device_address, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_reset, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + }; + 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); + + const mcu_pin_obj_t *reset = validate_obj_is_free_pin_or_none(args[ARG_reset].u_obj); + + mp_obj_t i2c = mp_arg_validate_type(args[ARG_i2c_bus].u_obj, &busio_i2c_type, MP_QSTR_i2c_bus); + displayio_i2cdisplay_obj_t *self = &allocate_display_bus_or_raise()->i2cdisplay_bus; + self->base.type = &displayio_i2cdisplay_type; + + common_hal_displayio_i2cdisplay_construct(self, + MP_OBJ_TO_PTR(i2c), args[ARG_device_address].u_int, reset); + return self; +} + +//| def reset(self) -> None: +//| """Performs a hardware reset via the reset pin. Raises an exception if called when no reset pin +//| is available.""" +//| ... +//| +STATIC mp_obj_t displayio_i2cdisplay_obj_reset(mp_obj_t self_in) { + displayio_i2cdisplay_obj_t *self = self_in; + + if (!common_hal_displayio_i2cdisplay_reset(self)) { + mp_raise_RuntimeError(translate("no reset pin available")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_i2cdisplay_reset_obj, displayio_i2cdisplay_obj_reset); + +//| def send(self, command: int, data: ReadableBuffer) -> None: +//| """Sends the given command value followed by the full set of data. Display state, such as +//| vertical scroll, set via ``send`` may or may not be reset once the code is done.""" +//| ... +//| +STATIC mp_obj_t displayio_i2cdisplay_obj_send(mp_obj_t self, mp_obj_t command_obj, mp_obj_t data_obj) { + mp_int_t command_int = MP_OBJ_SMALL_INT_VALUE(command_obj); + if (!mp_obj_is_small_int(command_obj) || command_int > 255 || command_int < 0) { + mp_raise_ValueError(translate("Command must be an int between 0 and 255")); + } + uint8_t command = command_int; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ); + + // Wait for display bus to be available. + while (!common_hal_displayio_i2cdisplay_begin_transaction(self)) { + RUN_BACKGROUND_TASKS; + } + uint8_t full_command[bufinfo.len + 1]; + full_command[0] = command; + memcpy(full_command + 1, ((uint8_t *)bufinfo.buf), bufinfo.len); + common_hal_displayio_i2cdisplay_send(self, DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, full_command, bufinfo.len + 1); + common_hal_displayio_i2cdisplay_end_transaction(self); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(displayio_i2cdisplay_send_obj, displayio_i2cdisplay_obj_send); + +STATIC const mp_rom_map_elem_t displayio_i2cdisplay_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&displayio_i2cdisplay_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&displayio_i2cdisplay_send_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_i2cdisplay_locals_dict, displayio_i2cdisplay_locals_dict_table); + +const mp_obj_type_t displayio_i2cdisplay_type = { + { &mp_type_type }, + .name = MP_QSTR_I2CDisplay, + .make_new = displayio_i2cdisplay_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_i2cdisplay_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/I2CDisplay.h b/circuitpython/shared-bindings/displayio/I2CDisplay.h new file mode 100644 index 0000000..d40cd19 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/I2CDisplay.h @@ -0,0 +1,52 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017, 2018 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_I2CDISPLAY_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_I2CDISPLAY_H + +#include "shared-module/displayio/I2CDisplay.h" + +#include "shared-bindings/displayio/__init__.h" +#include "common-hal/microcontroller/Pin.h" + +extern const mp_obj_type_t displayio_i2cdisplay_type; + +void common_hal_displayio_i2cdisplay_construct(displayio_i2cdisplay_obj_t *self, + busio_i2c_obj_t *i2c, uint16_t device_address, const mcu_pin_obj_t *reset); + +void common_hal_displayio_i2cdisplay_deinit(displayio_i2cdisplay_obj_t *self); + +bool common_hal_displayio_i2cdisplay_reset(mp_obj_t self); +bool common_hal_displayio_i2cdisplay_bus_free(mp_obj_t self); + +bool common_hal_displayio_i2cdisplay_begin_transaction(mp_obj_t self); + +void common_hal_displayio_i2cdisplay_send(mp_obj_t self, display_byte_type_t byte_type, + display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length); + +void common_hal_displayio_i2cdisplay_end_transaction(mp_obj_t self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYBUSIO_I2CDISPLAY_H diff --git a/circuitpython/shared-bindings/displayio/OnDiskBitmap.c b/circuitpython/shared-bindings/displayio/OnDiskBitmap.c new file mode 100644 index 0000000..aa749bf --- /dev/null +++ b/circuitpython/shared-bindings/displayio/OnDiskBitmap.c @@ -0,0 +1,161 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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 "shared-bindings/displayio/OnDiskBitmap.h" + +#include <stdint.h> + +#include "py/runtime.h" +#include "py/objproperty.h" +#include "supervisor/shared/translate.h" +#include "shared-bindings/displayio/OnDiskBitmap.h" + +//| class OnDiskBitmap: +//| """Loads values straight from disk. This minimizes memory use but can lead to +//| much slower pixel load times. These load times may result in frame tearing where only part of +//| the image is visible. +//| +//| It's easiest to use on a board with a built in display such as the `Hallowing M0 Express +//| <https://www.adafruit.com/product/3900>`_. +//| +//| .. code-block:: Python +//| +//| import board +//| import displayio +//| import time +//| import pulseio +//| +//| board.DISPLAY.auto_brightness = False +//| board.DISPLAY.brightness = 0 +//| splash = displayio.Group() +//| board.DISPLAY.show(splash) +//| +//| odb = displayio.OnDiskBitmap('/sample.bmp') +//| face = displayio.TileGrid(odb, pixel_shader=odb.pixel_shader) +//| splash.append(face) +//| # Wait for the image to load. +//| board.DISPLAY.refresh(target_frames_per_second=60) +//| +//| # Fade up the backlight +//| for i in range(100): +//| board.DISPLAY.brightness = 0.01 * i +//| time.sleep(0.05) +//| +//| # Wait forever +//| while True: +//| pass""" +//| +//| def __init__(self, file: Union[str,typing.BinaryIO]) -> None: +//| """Create an OnDiskBitmap object with the given file. +//| +//| :param file file: The name of the bitmap file. For backwards compatibility, a file opened in binary mode may also be passed. +//| +//| Older versions of CircuitPython required a file opened in binary +//| mode. CircuitPython 7.0 modified OnDiskBitmap so that it takes a +//| filename instead, and opens the file internally. A future version +//| of CircuitPython will remove the ability to pass in an opened file. +//| """ +//| ... +//| +STATIC mp_obj_t displayio_ondiskbitmap_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_obj_t arg = all_args[0]; + + if (mp_obj_is_str(arg)) { + arg = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), arg, MP_ROM_QSTR(MP_QSTR_rb)); + } + if (!mp_obj_is_type(arg, &mp_type_fileio)) { + mp_raise_TypeError(translate("file must be a file opened in byte mode")); + } + + displayio_ondiskbitmap_t *self = m_new_obj(displayio_ondiskbitmap_t); + self->base.type = &displayio_ondiskbitmap_type; + common_hal_displayio_ondiskbitmap_construct(self, MP_OBJ_TO_PTR(arg)); + + return MP_OBJ_FROM_PTR(self); +} + +//| width: int +//| """Width of the bitmap. (read only)""" +//| +STATIC mp_obj_t displayio_ondiskbitmap_obj_get_width(mp_obj_t self_in) { + displayio_ondiskbitmap_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_ondiskbitmap_get_width(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(displayio_ondiskbitmap_get_width_obj, displayio_ondiskbitmap_obj_get_width); + +MP_PROPERTY_GETTER(displayio_ondiskbitmap_width_obj, + (mp_obj_t)&displayio_ondiskbitmap_get_width_obj); + +//| height: int +//| """Height of the bitmap. (read only)""" +//| +STATIC mp_obj_t displayio_ondiskbitmap_obj_get_height(mp_obj_t self_in) { + displayio_ondiskbitmap_t *self = MP_OBJ_TO_PTR(self_in); + + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_ondiskbitmap_get_height(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(displayio_ondiskbitmap_get_height_obj, displayio_ondiskbitmap_obj_get_height); + +MP_PROPERTY_GETTER(displayio_ondiskbitmap_height_obj, + (mp_obj_t)&displayio_ondiskbitmap_get_height_obj); + +//| pixel_shader: Union[ColorConverter, Palette] +//| """The image's pixel_shader. The type depends on the underlying +//| bitmap's structure. The pixel shader can be modified (e.g., to set the +//| transparent pixel or, for palette shaded images, to update the palette.)""" +//| +STATIC mp_obj_t displayio_ondiskbitmap_obj_get_pixel_shader(mp_obj_t self_in) { + displayio_ondiskbitmap_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_displayio_ondiskbitmap_get_pixel_shader(self); +} + +MP_DEFINE_CONST_FUN_OBJ_1(displayio_ondiskbitmap_get_pixel_shader_obj, displayio_ondiskbitmap_obj_get_pixel_shader); + +const mp_obj_property_t displayio_ondiskbitmap_pixel_shader_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&displayio_ondiskbitmap_get_pixel_shader_obj, + (mp_obj_t)MP_ROM_NONE, + (mp_obj_t)MP_ROM_NONE}, +}; + + +STATIC const mp_rom_map_elem_t displayio_ondiskbitmap_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_ondiskbitmap_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&displayio_ondiskbitmap_pixel_shader_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_ondiskbitmap_width_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_ondiskbitmap_locals_dict, displayio_ondiskbitmap_locals_dict_table); + +const mp_obj_type_t displayio_ondiskbitmap_type = { + { &mp_type_type }, + .name = MP_QSTR_OnDiskBitmap, + .make_new = displayio_ondiskbitmap_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_ondiskbitmap_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/OnDiskBitmap.h b/circuitpython/shared-bindings/displayio/OnDiskBitmap.h new file mode 100644 index 0000000..6d534ec --- /dev/null +++ b/circuitpython/shared-bindings/displayio/OnDiskBitmap.h @@ -0,0 +1,43 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_ONDISKBITMAP_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_ONDISKBITMAP_H + +#include "shared-module/displayio/OnDiskBitmap.h" +#include "extmod/vfs_fat.h" + +extern const mp_obj_type_t displayio_ondiskbitmap_type; + +void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self, pyb_file_obj_t *file); + +uint32_t common_hal_displayio_ondiskbitmap_get_pixel(displayio_ondiskbitmap_t *bitmap, + int16_t x, int16_t y); + +uint16_t common_hal_displayio_ondiskbitmap_get_height(displayio_ondiskbitmap_t *self); +mp_obj_t common_hal_displayio_ondiskbitmap_get_pixel_shader(displayio_ondiskbitmap_t *self); +uint16_t common_hal_displayio_ondiskbitmap_get_width(displayio_ondiskbitmap_t *self); +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_ONDISKBITMAP_H diff --git a/circuitpython/shared-bindings/displayio/Palette.c b/circuitpython/shared-bindings/displayio/Palette.c new file mode 100644 index 0000000..ad6d7c3 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Palette.c @@ -0,0 +1,216 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft 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 "shared-bindings/displayio/Palette.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class Palette: +//| """Map a pixel palette_index to a full color. Colors are transformed to the display's format internally to +//| save memory.""" +//| +//| def __init__(self, color_count: int) -> None: +//| """Create a Palette object to store a set number of colors. +//| +//| :param int color_count: The number of colors in the Palette""" +//| ... +//| +// TODO(tannewt): Add support for other color formats. +// TODO(tannewt): Add support for 8-bit alpha blending. +//| +STATIC mp_obj_t displayio_palette_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_color_count }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_color_count, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + 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); + + displayio_palette_t *self = m_new_obj(displayio_palette_t); + self->base.type = &displayio_palette_type; + common_hal_displayio_palette_construct(self, args[ARG_color_count].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +//| def __bool__(self) -> bool: +//| ... +//| +//| def __len__(self) -> int: +//| """Returns the number of colors in a Palette""" +//| ... +//| +STATIC mp_obj_t group_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + displayio_palette_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(common_hal_displayio_palette_get_len(self)); + default: + return MP_OBJ_NULL; // op not supported + } +} + +//| def __getitem__(self, index: int) -> Optional[int]: +//| r"""Return the pixel color at the given index as an integer.""" +//| ... +//| +//| def __setitem__(self, index: int, value: Union[int, ReadableBuffer, Tuple[int, int, int]]) -> None: +//| r"""Sets the pixel color at the given index. The index should be an integer in the range 0 to color_count-1. +//| +//| The value argument represents a color, and can be from 0x000000 to 0xFFFFFF (to represent an RGB value). +//| Value can be an int, bytes (3 bytes (RGB) or 4 bytes (RGB + pad byte)), bytearray, +//| or a tuple or list of 3 integers. +//| +//| This allows you to:: +//| +//| palette[0] = 0xFFFFFF # set using an integer +//| palette[1] = b'\xff\xff\x00' # set using 3 bytes +//| palette[2] = b'\xff\xff\x00\x00' # set using 4 bytes +//| palette[3] = bytearray(b'\x00\x00\xFF') # set using a bytearay of 3 or 4 bytes +//| palette[4] = (10, 20, 30) # set using a tuple of 3 integers""" +//| ... +//| +STATIC mp_obj_t palette_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { + if (value == MP_OBJ_NULL) { + // delete item + return MP_OBJ_NULL; // op not supported + } + // Slicing not supported. Use a duplicate Palette to swap multiple colors atomically. + if (mp_obj_is_type(index_in, &mp_type_slice)) { + return MP_OBJ_NULL; + } + displayio_palette_t *self = MP_OBJ_TO_PTR(self_in); + size_t index = mp_get_index(&displayio_palette_type, self->color_count, index_in, false); + // index read + if (value == MP_OBJ_SENTINEL) { + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_palette_get_color(self, index)); + } + + // Convert a tuple or list to a bytearray. + if (mp_obj_is_type(value, &mp_type_tuple) || + mp_obj_is_type(value, &mp_type_list)) { + value = mp_type_bytes.make_new(&mp_type_bytes, 1, 0, &value); + } + + uint32_t color; + mp_int_t int_value; + mp_buffer_info_t bufinfo; + if (mp_get_buffer(value, &bufinfo, MP_BUFFER_READ)) { + if (bufinfo.typecode != 'b' && bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE) { + mp_raise_ValueError(translate("color buffer must be a bytearray or array of type 'b' or 'B'")); + } + uint8_t *buf = bufinfo.buf; + if (bufinfo.len == 3 || bufinfo.len == 4) { + color = buf[0] << 16 | buf[1] << 8 | buf[2]; + } else { + mp_raise_ValueError(translate("color buffer must be 3 bytes (RGB) or 4 bytes (RGB + pad byte)")); + } + } else if (mp_obj_get_int_maybe(value, &int_value)) { + if (int_value < 0 || int_value > 0xffffff) { + mp_raise_TypeError(translate("color must be between 0x000000 and 0xffffff")); + } + color = int_value; + } else { + mp_raise_TypeError(translate("color buffer must be a buffer, tuple, list, or int")); + } + common_hal_displayio_palette_set_color(self, index, color); + return mp_const_none; +} + +//| def make_transparent(self, palette_index: int) -> None: +//| ... +//| +STATIC mp_obj_t displayio_palette_obj_make_transparent(mp_obj_t self_in, mp_obj_t palette_index_obj) { + displayio_palette_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t palette_index; + if (!mp_obj_get_int_maybe(palette_index_obj, &palette_index)) { + mp_raise_ValueError(translate("palette_index should be an int")); + } + common_hal_displayio_palette_make_transparent(self, palette_index); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_palette_make_transparent_obj, displayio_palette_obj_make_transparent); + +//| def make_opaque(self, palette_index: int) -> None: +//| ... +//| +STATIC mp_obj_t displayio_palette_obj_make_opaque(mp_obj_t self_in, mp_obj_t palette_index_obj) { + displayio_palette_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t palette_index; + if (!mp_obj_get_int_maybe(palette_index_obj, &palette_index)) { + mp_raise_ValueError(translate("palette_index should be an int")); + } + common_hal_displayio_palette_make_opaque(self, palette_index); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_palette_make_opaque_obj, displayio_palette_obj_make_opaque); + +//| def is_transparent(self, palette_index: int) -> bool: +//| """Returns `True` if the palette index is transparent. Returns `False` if opaque.""" +//| ... +//| +STATIC mp_obj_t displayio_palette_obj_is_transparent(mp_obj_t self_in, mp_obj_t palette_index_obj) { + displayio_palette_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t palette_index; + if (!mp_obj_get_int_maybe(palette_index_obj, &palette_index)) { + mp_raise_ValueError(translate("palette_index should be an int")); + } + return mp_obj_new_bool(common_hal_displayio_palette_is_transparent(self, palette_index)); +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_palette_is_transparent_obj, displayio_palette_obj_is_transparent); + +STATIC const mp_rom_map_elem_t displayio_palette_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_make_transparent), MP_ROM_PTR(&displayio_palette_make_transparent_obj) }, + { MP_ROM_QSTR(MP_QSTR_make_opaque), MP_ROM_PTR(&displayio_palette_make_opaque_obj) }, + { MP_ROM_QSTR(MP_QSTR_is_transparent), MP_ROM_PTR(&displayio_palette_is_transparent_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_palette_locals_dict, displayio_palette_locals_dict_table); + +const mp_obj_type_t displayio_palette_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_Palette, + .make_new = displayio_palette_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_palette_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .subscr = palette_subscr, + .unary_op = group_unary_op, + .getiter = mp_obj_new_generic_iterator, + ), +}; diff --git a/circuitpython/shared-bindings/displayio/Palette.h b/circuitpython/shared-bindings/displayio/Palette.h new file mode 100644 index 0000000..d9a7980 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Palette.h @@ -0,0 +1,43 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_PALETTE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_PALETTE_H + +#include "shared-module/displayio/Palette.h" + +extern const mp_obj_type_t displayio_palette_type; + +void common_hal_displayio_palette_construct(displayio_palette_t *self, uint16_t color_count); +void common_hal_displayio_palette_set_color(displayio_palette_t *self, uint32_t palette_index, uint32_t color); +uint32_t common_hal_displayio_palette_get_color(displayio_palette_t *self, uint32_t palette_index); +uint32_t common_hal_displayio_palette_get_len(displayio_palette_t *self); + +void common_hal_displayio_palette_make_opaque(displayio_palette_t *self, uint32_t palette_index); +void common_hal_displayio_palette_make_transparent(displayio_palette_t *self, uint32_t palette_index); +bool common_hal_displayio_palette_is_transparent(displayio_palette_t *self, uint32_t palette_index); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_PALETTE_H diff --git a/circuitpython/shared-bindings/displayio/Shape.c b/circuitpython/shared-bindings/displayio/Shape.c new file mode 100644 index 0000000..f39e782 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Shape.c @@ -0,0 +1,117 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft 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 "shared-bindings/displayio/Shape.h" + +#include <stdint.h> + +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| class Shape: +//| """Represents a shape made by defining boundaries that may be mirrored.""" +//| +//| def __init__(self, width: int, height: int, *, mirror_x: bool = False, mirror_y: bool = False) -> None: +//| """Create a Shape object with the given fixed size. Each pixel is one bit and is stored by the +//| column boundaries of the shape on each row. Each row's boundary defaults to the full row. +//| +//| :param int width: The number of pixels wide +//| :param int height: The number of pixels high +//| :param bool mirror_x: When true the left boundary is mirrored to the right. +//| :param bool mirror_y: When true the top boundary is mirrored to the bottom.""" +//| ... +//| +STATIC mp_obj_t displayio_shape_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_width, ARG_height, ARG_mirror_x, ARG_mirror_y }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_mirror_x, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + { MP_QSTR_mirror_y, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + 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_int_t width = args[ARG_width].u_int; + if (width < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_width); + } + mp_int_t height = args[ARG_height].u_int; + if (height < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_height); + } + + displayio_shape_t *self = m_new_obj(displayio_shape_t); + self->base.type = &displayio_shape_type; + common_hal_displayio_shape_construct(self, + width, + height, + args[ARG_mirror_x].u_bool, + args[ARG_mirror_y].u_bool); + + return MP_OBJ_FROM_PTR(self); +} + + +//| def set_boundary(self, y: int, start_x: int, end_x: int) -> None: +//| """Loads pre-packed data into the given row.""" +//| ... +//| +STATIC mp_obj_t displayio_shape_obj_set_boundary(size_t n_args, const mp_obj_t *args) { + (void)n_args; + displayio_shape_t *self = MP_OBJ_TO_PTR(args[0]); + mp_int_t y; + if (!mp_obj_get_int_maybe(args[1], &y)) { + mp_raise_ValueError(translate("y should be an int")); + } + mp_int_t start_x; + if (!mp_obj_get_int_maybe(args[2], &start_x)) { + mp_raise_ValueError(translate("start_x should be an int")); + } + mp_int_t end_x; + if (!mp_obj_get_int_maybe(args[3], &end_x)) { + mp_raise_ValueError(translate("end_x should be an int")); + } + common_hal_displayio_shape_set_boundary(self, y, start_x, end_x); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(displayio_shape_set_boundary_obj, 4, 4, displayio_shape_obj_set_boundary); + +STATIC const mp_rom_map_elem_t displayio_shape_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_set_boundary), MP_ROM_PTR(&displayio_shape_set_boundary_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_shape_locals_dict, displayio_shape_locals_dict_table); + +const mp_obj_type_t displayio_shape_type = { + { &mp_type_type }, + .name = MP_QSTR_Shape, + .make_new = displayio_shape_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_shape_locals_dict, +}; diff --git a/circuitpython/shared-bindings/displayio/Shape.h b/circuitpython/shared-bindings/displayio/Shape.h new file mode 100644 index 0000000..961b57c --- /dev/null +++ b/circuitpython/shared-bindings/displayio/Shape.h @@ -0,0 +1,41 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_SHAPE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_SHAPE_H + +#include "shared-module/displayio/Shape.h" + +extern const mp_obj_type_t displayio_shape_type; + +void common_hal_displayio_shape_construct(displayio_shape_t *self, uint32_t width, + uint32_t height, bool mirror_x, bool mirror_y); + +void common_hal_displayio_shape_set_boundary(displayio_shape_t *self, uint16_t y, uint16_t start_x, + uint16_t end_x); +uint32_t common_hal_displayio_shape_get_pixel(void *shape, int16_t x, int16_t y); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_SHAPE_H diff --git a/circuitpython/shared-bindings/displayio/TileGrid.c b/circuitpython/shared-bindings/displayio/TileGrid.c new file mode 100644 index 0000000..113721b --- /dev/null +++ b/circuitpython/shared-bindings/displayio/TileGrid.c @@ -0,0 +1,502 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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 "shared-bindings/displayio/TileGrid.h" + +#include <stdint.h> + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "shared-bindings/displayio/Bitmap.h" +#include "shared-bindings/displayio/ColorConverter.h" +#include "shared-bindings/displayio/OnDiskBitmap.h" +#include "shared-bindings/displayio/Palette.h" +#include "shared-bindings/displayio/Shape.h" +#include "supervisor/shared/translate.h" + +//| class TileGrid: +//| """A grid of tiles sourced out of one bitmap +//| +//| Position a grid of tiles sourced from a bitmap and pixel_shader combination. Multiple grids +//| can share bitmaps and pixel shaders. +//| +//| A single tile grid is also known as a Sprite.""" +//| +//| def __init__(self, bitmap: Union[Bitmap, OnDiskBitmap, Shape], *, pixel_shader: Union[ColorConverter, Palette], width: int = 1, height: int = 1, tile_width: Optional[int] = None, tile_height: Optional[int] = None, default_tile: int = 0, x: int = 0, y: int = 0) -> None: +//| """Create a TileGrid object. The bitmap is source for 2d pixels. The pixel_shader is used to +//| convert the value and its location to a display native pixel color. This may be a simple color +//| palette lookup, a gradient, a pattern or a color transformer. +//| +//| To save RAM usage, tile values are only allowed in the range from 0 to 255 inclusive (single byte values). +//| +//| tile_width and tile_height match the height of the bitmap by default. +//| +//| :param Bitmap,OnDiskBitmap,Shape bitmap: The bitmap storing one or more tiles. +//| :param ColorConverter,Palette pixel_shader: The pixel shader that produces colors from values +//| :param int width: Width of the grid in tiles. +//| :param int height: Height of the grid in tiles. +//| :param int tile_width: Width of a single tile in pixels. Defaults to the full Bitmap and must evenly divide into the Bitmap's dimensions. +//| :param int tile_height: Height of a single tile in pixels. Defaults to the full Bitmap and must evenly divide into the Bitmap's dimensions. +//| :param int default_tile: Default tile index to show. +//| :param int x: Initial x position of the left edge within the parent. +//| :param int y: Initial y position of the top edge within the parent.""" +//| +STATIC mp_obj_t displayio_tilegrid_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_bitmap, ARG_pixel_shader, ARG_width, ARG_height, ARG_tile_width, ARG_tile_height, ARG_default_tile, ARG_x, ARG_y }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_width, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + { MP_QSTR_tile_width, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_tile_height, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_default_tile, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { 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_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t bitmap = args[ARG_bitmap].u_obj; + + uint16_t bitmap_width; + uint16_t bitmap_height; + mp_obj_t native = mp_obj_cast_to_native_base(bitmap, &displayio_shape_type); + if (native != MP_OBJ_NULL) { + displayio_shape_t *bmp = MP_OBJ_TO_PTR(native); + bitmap_width = bmp->width; + bitmap_height = bmp->height; + } else if (mp_obj_is_type(bitmap, &displayio_bitmap_type)) { + displayio_bitmap_t *bmp = MP_OBJ_TO_PTR(bitmap); + native = bitmap; + bitmap_width = bmp->width; + bitmap_height = bmp->height; + } else if (mp_obj_is_type(bitmap, &displayio_ondiskbitmap_type)) { + displayio_ondiskbitmap_t *bmp = MP_OBJ_TO_PTR(bitmap); + native = bitmap; + bitmap_width = bmp->width; + bitmap_height = bmp->height; + } else { + mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_bitmap); + } + mp_obj_t pixel_shader = args[ARG_pixel_shader].u_obj; + 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); + } + uint16_t tile_width = args[ARG_tile_width].u_int; + if (tile_width == 0) { + tile_width = bitmap_width; + } + uint16_t tile_height = args[ARG_tile_height].u_int; + if (tile_height == 0) { + tile_height = bitmap_height; + } + if (bitmap_width % tile_width != 0) { + mp_raise_ValueError(translate("Tile width must exactly divide bitmap width")); + } + if (bitmap_height % tile_height != 0) { + mp_raise_ValueError(translate("Tile height must exactly divide bitmap height")); + } + + int16_t x = args[ARG_x].u_int; + int16_t y = args[ARG_y].u_int; + + displayio_tilegrid_t *self = m_new_obj(displayio_tilegrid_t); + self->base.type = &displayio_tilegrid_type; + common_hal_displayio_tilegrid_construct(self, native, + bitmap_width / tile_width, bitmap_height / tile_height, + pixel_shader, args[ARG_width].u_int, args[ARG_height].u_int, + tile_width, tile_height, x, y, args[ARG_default_tile].u_int); + return MP_OBJ_FROM_PTR(self); +} + +// Helper to ensure we have the native super class instead of a subclass. +static displayio_tilegrid_t *native_tilegrid(mp_obj_t tilegrid_obj) { + mp_obj_t native_tilegrid = mp_obj_cast_to_native_base(tilegrid_obj, &displayio_tilegrid_type); + mp_obj_assert_native_inited(native_tilegrid); + return MP_OBJ_TO_PTR(native_tilegrid); +} + +//| hidden: bool +//| """True when the TileGrid is hidden. This may be False even when a part of a hidden Group.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_hidden(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return mp_obj_new_bool(common_hal_displayio_tilegrid_get_hidden(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_hidden_obj, displayio_tilegrid_obj_get_hidden); + +STATIC mp_obj_t displayio_tilegrid_obj_set_hidden(mp_obj_t self_in, mp_obj_t hidden_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + common_hal_displayio_tilegrid_set_hidden(self, mp_obj_is_true(hidden_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_hidden_obj, displayio_tilegrid_obj_set_hidden); + +MP_PROPERTY_GETSET(displayio_tilegrid_hidden_obj, + (mp_obj_t)&displayio_tilegrid_get_hidden_obj, + (mp_obj_t)&displayio_tilegrid_set_hidden_obj); + +//| x: int +//| """X position of the left edge in the parent.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_x(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_x(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_x_obj, displayio_tilegrid_obj_get_x); + +STATIC mp_obj_t displayio_tilegrid_obj_set_x(mp_obj_t self_in, mp_obj_t x_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + mp_int_t x = mp_obj_get_int(x_obj); + common_hal_displayio_tilegrid_set_x(self, x); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_x_obj, displayio_tilegrid_obj_set_x); + +MP_PROPERTY_GETSET(displayio_tilegrid_x_obj, + (mp_obj_t)&displayio_tilegrid_get_x_obj, + (mp_obj_t)&displayio_tilegrid_set_x_obj); + +//| y: int +//| """Y position of the top edge in the parent.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_y(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_y(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_y_obj, displayio_tilegrid_obj_get_y); + +STATIC mp_obj_t displayio_tilegrid_obj_set_y(mp_obj_t self_in, mp_obj_t y_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + mp_int_t y = mp_obj_get_int(y_obj); + common_hal_displayio_tilegrid_set_y(self, y); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_y_obj, displayio_tilegrid_obj_set_y); + +MP_PROPERTY_GETSET(displayio_tilegrid_y_obj, + (mp_obj_t)&displayio_tilegrid_get_y_obj, + (mp_obj_t)&displayio_tilegrid_set_y_obj); + +//| width: int +//| """Width of the tilegrid in tiles.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_width(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_width_obj, displayio_tilegrid_obj_get_width); + +MP_PROPERTY_GETTER(displayio_tilegrid_width_obj, + (mp_obj_t)&displayio_tilegrid_get_width_obj); + +//| height: int +//| """Height of the tilegrid in tiles.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_height(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_height_obj, displayio_tilegrid_obj_get_height); + +MP_PROPERTY_GETTER(displayio_tilegrid_height_obj, + (mp_obj_t)&displayio_tilegrid_get_height_obj); + +//| tile_width: int +//| """Width of a single tile in pixels.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_tile_width(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_tile_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_tile_width_obj, displayio_tilegrid_obj_get_tile_width); + +MP_PROPERTY_GETTER(displayio_tilegrid_tile_width_obj, + (mp_obj_t)&displayio_tilegrid_get_tile_width_obj); + +//| tile_height: int +//| """Height of a single tile in pixels.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_tile_height(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_tile_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_tile_height_obj, displayio_tilegrid_obj_get_tile_height); + +MP_PROPERTY_GETTER(displayio_tilegrid_tile_height_obj, + (mp_obj_t)&displayio_tilegrid_get_tile_height_obj); + +//| flip_x: bool +//| """If true, the left edge rendered will be the right edge of the right-most tile.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_flip_x(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return mp_obj_new_bool(common_hal_displayio_tilegrid_get_flip_x(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_flip_x_obj, displayio_tilegrid_obj_get_flip_x); + +STATIC mp_obj_t displayio_tilegrid_obj_set_flip_x(mp_obj_t self_in, mp_obj_t flip_x_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + common_hal_displayio_tilegrid_set_flip_x(self, mp_obj_is_true(flip_x_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_flip_x_obj, displayio_tilegrid_obj_set_flip_x); + +MP_PROPERTY_GETSET(displayio_tilegrid_flip_x_obj, + (mp_obj_t)&displayio_tilegrid_get_flip_x_obj, + (mp_obj_t)&displayio_tilegrid_set_flip_x_obj); + +//| flip_y: bool +//| """If true, the top edge rendered will be the bottom edge of the bottom-most tile.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_flip_y(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return mp_obj_new_bool(common_hal_displayio_tilegrid_get_flip_y(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_flip_y_obj, displayio_tilegrid_obj_get_flip_y); + +STATIC mp_obj_t displayio_tilegrid_obj_set_flip_y(mp_obj_t self_in, mp_obj_t flip_y_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + common_hal_displayio_tilegrid_set_flip_y(self, mp_obj_is_true(flip_y_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_flip_y_obj, displayio_tilegrid_obj_set_flip_y); + +MP_PROPERTY_GETSET(displayio_tilegrid_flip_y_obj, + (mp_obj_t)&displayio_tilegrid_get_flip_y_obj, + (mp_obj_t)&displayio_tilegrid_set_flip_y_obj); + + +//| transpose_xy: bool +//| """If true, the TileGrid's axis will be swapped. When combined with mirroring, any 90 degree +//| rotation can be achieved along with the corresponding mirrored version.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_transpose_xy(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return mp_obj_new_bool(common_hal_displayio_tilegrid_get_transpose_xy(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_transpose_xy_obj, displayio_tilegrid_obj_get_transpose_xy); + +STATIC mp_obj_t displayio_tilegrid_obj_set_transpose_xy(mp_obj_t self_in, mp_obj_t transpose_xy_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + common_hal_displayio_tilegrid_set_transpose_xy(self, mp_obj_is_true(transpose_xy_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_transpose_xy_obj, displayio_tilegrid_obj_set_transpose_xy); + +MP_PROPERTY_GETSET(displayio_tilegrid_transpose_xy_obj, + (mp_obj_t)&displayio_tilegrid_get_transpose_xy_obj, + (mp_obj_t)&displayio_tilegrid_set_transpose_xy_obj); + +//| pixel_shader: Union[ColorConverter, Palette] +//| """The pixel shader of the tilegrid.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_pixel_shader(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return common_hal_displayio_tilegrid_get_pixel_shader(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_pixel_shader_obj, displayio_tilegrid_obj_get_pixel_shader); + +STATIC mp_obj_t displayio_tilegrid_obj_set_pixel_shader(mp_obj_t self_in, mp_obj_t pixel_shader) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + 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")); + } + + common_hal_displayio_tilegrid_set_pixel_shader(self, pixel_shader); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_pixel_shader_obj, displayio_tilegrid_obj_set_pixel_shader); + +MP_PROPERTY_GETSET(displayio_tilegrid_pixel_shader_obj, + (mp_obj_t)&displayio_tilegrid_get_pixel_shader_obj, + (mp_obj_t)&displayio_tilegrid_set_pixel_shader_obj); + +//| bitmap: Union[Bitmap,OnDiskBitmap,Shape] +//| """The bitmap of the tilegrid.""" +//| +STATIC mp_obj_t displayio_tilegrid_obj_get_bitmap(mp_obj_t self_in) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + return common_hal_displayio_tilegrid_get_bitmap(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(displayio_tilegrid_get_bitmap_obj, displayio_tilegrid_obj_get_bitmap); + +STATIC mp_obj_t displayio_tilegrid_obj_set_bitmap(mp_obj_t self_in, mp_obj_t bitmap) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + uint16_t new_bitmap_width; + uint16_t new_bitmap_height; + mp_obj_t native = mp_obj_cast_to_native_base(bitmap, &displayio_shape_type); + if (native != MP_OBJ_NULL) { + displayio_shape_t *bmp = MP_OBJ_TO_PTR(native); + new_bitmap_width = bmp->width; + new_bitmap_height = bmp->height; + } else if (mp_obj_is_type(bitmap, &displayio_bitmap_type)) { + displayio_bitmap_t *bmp = MP_OBJ_TO_PTR(bitmap); + native = bitmap; + new_bitmap_width = bmp->width; + new_bitmap_height = bmp->height; + } else if (mp_obj_is_type(bitmap, &displayio_ondiskbitmap_type)) { + displayio_ondiskbitmap_t *bmp = MP_OBJ_TO_PTR(bitmap); + native = bitmap; + new_bitmap_width = bmp->width; + new_bitmap_height = bmp->height; + } else { + mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_bitmap); + } + + mp_obj_t old_native = mp_obj_cast_to_native_base(self->bitmap, &displayio_shape_type); + if (old_native != MP_OBJ_NULL) { + displayio_shape_t *old_bmp = MP_OBJ_TO_PTR(old_native); + if (old_bmp->width != new_bitmap_width || old_bmp->height != new_bitmap_height) { + mp_raise_ValueError(translate("New bitmap must be same size as old bitmap")); + } + } else if (mp_obj_is_type(self->bitmap, &displayio_bitmap_type)) { + displayio_bitmap_t *old_bmp = MP_OBJ_TO_PTR(self->bitmap); + old_native = self->bitmap; + if (old_bmp->width != new_bitmap_width || old_bmp->height != new_bitmap_height) { + mp_raise_ValueError(translate("New bitmap must be same size as old bitmap")); + } + } else if (mp_obj_is_type(self->bitmap, &displayio_ondiskbitmap_type)) { + displayio_ondiskbitmap_t *old_bmp = MP_OBJ_TO_PTR(self->bitmap); + old_native = self->bitmap; + if (old_bmp->width != new_bitmap_width || old_bmp->height != new_bitmap_height) { + mp_raise_ValueError(translate("New bitmap must be same size as old bitmap")); + } + } + + common_hal_displayio_tilegrid_set_bitmap(self, bitmap); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_set_bitmap_obj, displayio_tilegrid_obj_set_bitmap); + +MP_PROPERTY_GETSET(displayio_tilegrid_bitmap_obj, + (mp_obj_t)&displayio_tilegrid_get_bitmap_obj, + (mp_obj_t)&displayio_tilegrid_set_bitmap_obj); + +//| def __getitem__(self, index: Union[Tuple[int, int], int]) -> int: +//| """Returns the tile index at the given index. The index can either be an x,y tuple or an int equal +//| to ``y * width + x``. +//| +//| This allows you to:: +//| +//| print(grid[0])""" +//| ... +//| +//| def __setitem__(self, index: Union[Tuple[int, int], int], value: int) -> None: +//| """Sets the tile index at the given index. The index can either be an x,y tuple or an int equal +//| to ``y * width + x``. +//| +//| This allows you to:: +//| +//| grid[0] = 10 +//| +//| or:: +//| +//| grid[0,0] = 10""" +//| ... +//| +STATIC mp_obj_t tilegrid_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t value_obj) { + displayio_tilegrid_t *self = native_tilegrid(self_in); + + + if (mp_obj_is_type(index_obj, &mp_type_slice)) { + mp_raise_NotImplementedError(translate("Slices not supported")); + } else { + uint16_t x = 0; + uint16_t y = 0; + if (mp_obj_is_small_int(index_obj)) { + mp_int_t i = MP_OBJ_SMALL_INT_VALUE(index_obj); + uint16_t width = common_hal_displayio_tilegrid_get_width(self); + x = i % width; + y = i / width; + } else { + mp_obj_t *items; + mp_obj_get_array_fixed_n(index_obj, 2, &items); + x = mp_obj_get_int(items[0]); + y = mp_obj_get_int(items[1]); + } + if (x >= common_hal_displayio_tilegrid_get_width(self) || + y >= common_hal_displayio_tilegrid_get_height(self)) { + mp_raise_IndexError(translate("Tile index out of bounds")); + } + + if (value_obj == MP_OBJ_SENTINEL) { + // load + return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_tilegrid_get_tile(self, x, y)); + } else if (value_obj == mp_const_none) { + return MP_OBJ_NULL; // op not supported + } else { + mp_int_t value = mp_obj_get_int(value_obj); + if (value < 0 || value > 255) { + mp_raise_ValueError(translate("Tile value out of bounds")); + } + common_hal_displayio_tilegrid_set_tile(self, x, y, value); + } + } + return mp_const_none; +} + +STATIC const mp_rom_map_elem_t displayio_tilegrid_locals_dict_table[] = { + // Properties + { MP_ROM_QSTR(MP_QSTR_hidden), MP_ROM_PTR(&displayio_tilegrid_hidden_obj) }, + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&displayio_tilegrid_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&displayio_tilegrid_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_tilegrid_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_tilegrid_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_tile_width), MP_ROM_PTR(&displayio_tilegrid_tile_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_tile_height), MP_ROM_PTR(&displayio_tilegrid_tile_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_flip_x), MP_ROM_PTR(&displayio_tilegrid_flip_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_flip_y), MP_ROM_PTR(&displayio_tilegrid_flip_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_transpose_xy), MP_ROM_PTR(&displayio_tilegrid_transpose_xy_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&displayio_tilegrid_pixel_shader_obj) }, + { MP_ROM_QSTR(MP_QSTR_bitmap), MP_ROM_PTR(&displayio_tilegrid_bitmap_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_tilegrid_locals_dict, displayio_tilegrid_locals_dict_table); + +const mp_obj_type_t displayio_tilegrid_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_TileGrid, + .make_new = displayio_tilegrid_make_new, + .locals_dict = (mp_obj_dict_t *)&displayio_tilegrid_locals_dict, + MP_TYPE_EXTENDED_FIELDS( + .subscr = tilegrid_subscr, + ), +}; diff --git a/circuitpython/shared-bindings/displayio/TileGrid.h b/circuitpython/shared-bindings/displayio/TileGrid.h new file mode 100644 index 0000000..2c79412 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/TileGrid.h @@ -0,0 +1,71 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_TILEGRID_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_TILEGRID_H + +#include "shared-module/displayio/TileGrid.h" + +extern const mp_obj_type_t displayio_tilegrid_type; + +void common_hal_displayio_tilegrid_construct(displayio_tilegrid_t *self, mp_obj_t bitmap, + uint16_t bitmap_width_in_tiles, uint16_t bitmap_height_in_tiles, + mp_obj_t pixel_shader, uint16_t width, uint16_t height, + uint16_t tile_width, uint16_t tile_height, uint16_t x, uint16_t y, uint8_t default_tile); + +bool common_hal_displayio_tilegrid_get_hidden(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_hidden(displayio_tilegrid_t *self, bool hidden); +mp_int_t common_hal_displayio_tilegrid_get_x(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_x(displayio_tilegrid_t *self, mp_int_t x); +mp_int_t common_hal_displayio_tilegrid_get_y(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_y(displayio_tilegrid_t *self, mp_int_t y); +mp_obj_t common_hal_displayio_tilegrid_get_pixel_shader(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_pixel_shader(displayio_tilegrid_t *self, mp_obj_t pixel_shader); + +mp_obj_t common_hal_displayio_tilegrid_get_bitmap(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_bitmap(displayio_tilegrid_t *self, mp_obj_t bitmap); + + +bool common_hal_displayio_tilegrid_get_flip_x(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_flip_x(displayio_tilegrid_t *self, bool flip_x); +bool common_hal_displayio_tilegrid_get_flip_y(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_flip_y(displayio_tilegrid_t *self, bool flip_y); +bool common_hal_displayio_tilegrid_get_transpose_xy(displayio_tilegrid_t *self); +void common_hal_displayio_tilegrid_set_transpose_xy(displayio_tilegrid_t *self, bool transpose_xy); + +uint16_t common_hal_displayio_tilegrid_get_width(displayio_tilegrid_t *self); +uint16_t common_hal_displayio_tilegrid_get_height(displayio_tilegrid_t *self); + +uint16_t common_hal_displayio_tilegrid_get_tile_width(displayio_tilegrid_t *self); +uint16_t common_hal_displayio_tilegrid_get_tile_height(displayio_tilegrid_t *self); + +uint8_t common_hal_displayio_tilegrid_get_tile(displayio_tilegrid_t *self, uint16_t x, uint16_t y); +void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t x, uint16_t y, uint8_t tile_index); + +// Private API for scrolling the TileGrid. +void common_hal_displayio_tilegrid_set_top_left(displayio_tilegrid_t *self, uint16_t x, uint16_t y); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_TILEGRID_H diff --git a/circuitpython/shared-bindings/displayio/__init__.c b/circuitpython/shared-bindings/displayio/__init__.c new file mode 100644 index 0000000..2e52f12 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/__init__.c @@ -0,0 +1,106 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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 <stdint.h> + +#include "py/enum.h" +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/displayio/__init__.h" +#include "shared-bindings/displayio/Bitmap.h" +#include "shared-bindings/displayio/ColorConverter.h" +#include "shared-bindings/displayio/Display.h" +#include "shared-bindings/displayio/EPaperDisplay.h" +#include "shared-bindings/displayio/FourWire.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-bindings/displayio/I2CDisplay.h" +#include "shared-bindings/displayio/OnDiskBitmap.h" +#include "shared-bindings/displayio/Palette.h" +#if CIRCUITPY_PARALLELDISPLAY +#include "shared-bindings/paralleldisplay/ParallelBus.h" +#endif +#include "shared-bindings/displayio/Shape.h" +#include "shared-bindings/displayio/TileGrid.h" + +//| """Native helpers for driving displays +//| +//| The `displayio` module contains classes to manage display output +//| including synchronizing with refresh rates and partial updating. +//| +//| For more a more thorough explanation and guide for using `displayio`, please +//| refer to `this Learn guide +//| <https://learn.adafruit.com/circuitpython-display-support-using-displayio>`_. +//| """ +//| + +//| import paralleldisplay + +//| def release_displays() -> None: +//| """Releases any actively used displays so their busses and pins can be used again. This will also +//| release the builtin display on boards that have one. You will need to reinitialize it yourself +//| afterwards. This may take seconds to complete if an active EPaperDisplay is refreshing. +//| +//| Use this once in your code.py if you initialize a display. Place it right before the +//| initialization so the display is active as long as possible.""" +//| ... +//| +STATIC mp_obj_t displayio_release_displays(void) { + common_hal_displayio_release_displays(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(displayio_release_displays_obj, displayio_release_displays); + + +STATIC const mp_rom_map_elem_t displayio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_displayio) }, + { MP_ROM_QSTR(MP_QSTR_Bitmap), MP_ROM_PTR(&displayio_bitmap_type) }, + { MP_ROM_QSTR(MP_QSTR_ColorConverter), MP_ROM_PTR(&displayio_colorconverter_type) }, + { MP_ROM_QSTR(MP_QSTR_Colorspace), MP_ROM_PTR(&displayio_colorspace_type) }, + { MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&displayio_display_type) }, + { MP_ROM_QSTR(MP_QSTR_EPaperDisplay), MP_ROM_PTR(&displayio_epaperdisplay_type) }, + { MP_ROM_QSTR(MP_QSTR_Group), MP_ROM_PTR(&displayio_group_type) }, + { MP_ROM_QSTR(MP_QSTR_OnDiskBitmap), MP_ROM_PTR(&displayio_ondiskbitmap_type) }, + { MP_ROM_QSTR(MP_QSTR_Palette), MP_ROM_PTR(&displayio_palette_type) }, + { MP_ROM_QSTR(MP_QSTR_Shape), MP_ROM_PTR(&displayio_shape_type) }, + { MP_ROM_QSTR(MP_QSTR_TileGrid), MP_ROM_PTR(&displayio_tilegrid_type) }, + + { MP_ROM_QSTR(MP_QSTR_FourWire), MP_ROM_PTR(&displayio_fourwire_type) }, + { MP_ROM_QSTR(MP_QSTR_I2CDisplay), MP_ROM_PTR(&displayio_i2cdisplay_type) }, + #if CIRCUITPY_PARALLELDISPLAY + { MP_ROM_QSTR(MP_QSTR_ParallelBus), MP_ROM_PTR(¶lleldisplay_parallelbus_type) }, + #endif + + { MP_ROM_QSTR(MP_QSTR_release_displays), MP_ROM_PTR(&displayio_release_displays_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(displayio_module_globals, displayio_module_globals_table); + +const mp_obj_module_t displayio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&displayio_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_displayio, displayio_module, CIRCUITPY_DISPLAYIO); diff --git a/circuitpython/shared-bindings/displayio/__init__.h b/circuitpython/shared-bindings/displayio/__init__.h new file mode 100644 index 0000000..b297e4d --- /dev/null +++ b/circuitpython/shared-bindings/displayio/__init__.h @@ -0,0 +1,68 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO___INIT___H + +#include "py/enum.h" +#include "py/obj.h" + +typedef enum { + DISPLAY_COMMAND, + DISPLAY_DATA +} display_byte_type_t; + +typedef enum { + CHIP_SELECT_UNTOUCHED, + CHIP_SELECT_TOGGLE_EVERY_BYTE +} display_chip_select_behavior_t; + +typedef enum displayio_colorspace { + DISPLAYIO_COLORSPACE_RGB888, + DISPLAYIO_COLORSPACE_RGB565, + DISPLAYIO_COLORSPACE_RGB555, + DISPLAYIO_COLORSPACE_RGB565_SWAPPED, + DISPLAYIO_COLORSPACE_RGB555_SWAPPED, + DISPLAYIO_COLORSPACE_BGR565, + DISPLAYIO_COLORSPACE_BGR555, + DISPLAYIO_COLORSPACE_BGR565_SWAPPED, + DISPLAYIO_COLORSPACE_BGR555_SWAPPED, + DISPLAYIO_COLORSPACE_L8, +} displayio_colorspace_t; + +typedef bool (*display_bus_bus_reset)(mp_obj_t bus); +typedef bool (*display_bus_bus_free)(mp_obj_t bus); +typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); +typedef void (*display_bus_send)(mp_obj_t bus, display_byte_type_t byte_type, + display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length); +typedef void (*display_bus_end_transaction)(mp_obj_t bus); + +void common_hal_displayio_release_displays(void); + +extern const mp_obj_type_t displayio_colorspace_type; +extern const cp_enum_obj_t displayio_colorspace_RGB888_obj; + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO___INIT___H diff --git a/circuitpython/shared-bindings/displayio/area.c b/circuitpython/shared-bindings/displayio/area.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/circuitpython/shared-bindings/displayio/area.c |