From 206d0e598adb66e2b23a39a7d11e2f5087513205 Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Sat, 2 May 2020 02:21:35 -0700 Subject: Add vectorio: for drawing shapes vectorio builds on m4 express feather Concrete shapes are composed into a VectorShape which is put into a displayio Group for display. VectorShape provides transpose and x/y positioning for shape implementations. Included Shapes: * Circle - A radius; Circle is positioned at its axis in the VectorShape. - You can freely modify the radius to grow and shrink the circle in-place. * Polygon - An ordered list of points. - Beteween each successive point an edge is inferred. A final edge closing the shape is inferred between the last point and the first point. - You can modify the points in a Polygon. The points' coordinate system is relative to (0, 0) so if you'd like a top-center justified 10x20 rectangle you can do points [(-5, 0), (5, 0), (5, 20), (0, 20)] and your VectorShape x and y properties will position the rectangle relative to its top center point * Rectangle A width and a height. --- shared-module/displayio/Group.c | 35 +++- shared-module/displayio/display_core.c | 4 + shared-module/vectorio/Circle.c | 55 +++++++ shared-module/vectorio/Circle.h | 17 ++ shared-module/vectorio/Polygon.c | 110 +++++++++++++ shared-module/vectorio/Polygon.h | 16 ++ shared-module/vectorio/Rectangle.c | 35 ++++ shared-module/vectorio/Rectangle.h | 15 ++ shared-module/vectorio/VectorShape.c | 282 +++++++++++++++++++++++++++++++++ shared-module/vectorio/VectorShape.h | 51 ++++++ shared-module/vectorio/__init__.c | 3 + shared-module/vectorio/__init__.h | 15 ++ 12 files changed, 637 insertions(+), 1 deletion(-) create mode 100644 shared-module/vectorio/Circle.c create mode 100644 shared-module/vectorio/Circle.h create mode 100644 shared-module/vectorio/Polygon.c create mode 100644 shared-module/vectorio/Polygon.h create mode 100644 shared-module/vectorio/Rectangle.c create mode 100644 shared-module/vectorio/Rectangle.h create mode 100644 shared-module/vectorio/VectorShape.c create mode 100644 shared-module/vectorio/VectorShape.h create mode 100644 shared-module/vectorio/__init__.c create mode 100644 shared-module/vectorio/__init__.h (limited to 'shared-module') diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index d69e9f585..9d50dabec 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -29,6 +29,11 @@ #include "py/runtime.h" #include "shared-bindings/displayio/TileGrid.h" +#if CIRCUITPY_VECTORIO +#include "shared-bindings/vectorio/VectorShape.h" +#endif + + void common_hal_displayio_group_construct(displayio_group_t* self, uint32_t max_size, uint32_t scale, mp_int_t x, mp_int_t y) { displayio_group_child_t* children = m_new(displayio_group_child_t, max_size); displayio_group_construct(self, children, max_size, scale, x, y); @@ -200,7 +205,14 @@ void common_hal_displayio_group_set_y(displayio_group_t* self, mp_int_t y) { } static mp_obj_t _add_layer(displayio_group_t* self, mp_obj_t layer) { - mp_obj_t native_layer = mp_instance_cast_to_native_base(layer, &displayio_group_type); + mp_obj_t native_layer; +#if CIRCUITPY_VECTORIO + native_layer = mp_instance_cast_to_native_base(layer, &vectorio_vector_shape_type); + if (native_layer != MP_OBJ_NULL) { + return native_layer; + } +#endif + native_layer = mp_instance_cast_to_native_base(layer, &displayio_group_type); if (native_layer == MP_OBJ_NULL) { native_layer = mp_instance_cast_to_native_base(layer, &displayio_tilegrid_type); if (native_layer == MP_OBJ_NULL) { @@ -317,6 +329,15 @@ bool displayio_group_fill_area(displayio_group_t *self, const _displayio_colorsp bool full_coverage = false; for (int32_t i = self->size - 1; i >= 0 ; i--) { mp_obj_t layer = self->children[i].native; +#if CIRCUITPY_VECTORIO + if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + if (vectorio_vector_shape_fill_area(layer, colorspace, area, mask, buffer)) { + full_coverage = true; + break; + } + } + else +#endif if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { if (displayio_tilegrid_fill_area(layer, colorspace, area, mask, buffer)) { full_coverage = true; @@ -336,6 +357,12 @@ void displayio_group_finish_refresh(displayio_group_t *self) { self->item_removed = false; for (int32_t i = self->size - 1; i >= 0 ; i--) { mp_obj_t layer = self->children[i].native; +#if CIRCUITPY_VECTORIO + if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + vectorio_vector_shape_finish_refresh(layer); + } + else +#endif if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { displayio_tilegrid_finish_refresh(layer); } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { @@ -352,6 +379,12 @@ displayio_area_t* displayio_group_get_refresh_areas(displayio_group_t *self, dis for (int32_t i = self->size - 1; i >= 0 ; i--) { mp_obj_t layer = self->children[i].native; +#if CIRCUITPY_VECTORIO + if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + tail = vectorio_vector_shape_get_refresh_areas(layer, tail); + } + else +#endif if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { tail = displayio_tilegrid_get_refresh_areas(layer, tail); } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { diff --git a/shared-module/displayio/display_core.c b/shared-module/displayio/display_core.c index d01100f7f..43f2d1937 100644 --- a/shared-module/displayio/display_core.c +++ b/shared-module/displayio/display_core.c @@ -40,6 +40,9 @@ #include #include +#define DISPLAYIO_CORE_DEBUG(...) (void)0 +// #define DISPLAYIO_CORE_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) + void displayio_display_core_construct(displayio_display_core_t* self, mp_obj_t bus, 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 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) { @@ -298,6 +301,7 @@ void displayio_display_core_start_refresh(displayio_display_core_t* self) { void displayio_display_core_finish_refresh(displayio_display_core_t* self) { if (self->current_group != NULL) { + DISPLAYIO_CORE_DEBUG("displayiocore group_finish_refresh\n"); displayio_group_finish_refresh(self->current_group); } self->full_refresh = false; diff --git a/shared-module/vectorio/Circle.c b/shared-module/vectorio/Circle.c new file mode 100644 index 000000000..6bcd6318f --- /dev/null +++ b/shared-module/vectorio/Circle.c @@ -0,0 +1,55 @@ + +#include "shared-bindings/vectorio/Circle.h" +#include "shared-module/vectorio/__init__.h" +#include "shared-module/displayio/area.h" + +#include "py/runtime.h" +#include "stdlib.h" + + +void common_hal_vectorio_circle_construct(vectorio_circle_t *self, uint16_t radius) { + self->radius = radius; + self->on_dirty.obj = NULL; +} + +void common_hal_vectorio_circle_set_on_dirty(vectorio_circle_t *self, vectorio_event_t on_dirty) { + if (self->on_dirty.obj != NULL) { + mp_raise_TypeError(translate("circle can only be registered in one parent")); + } + self->on_dirty = on_dirty; +} + + +uint32_t common_hal_vectorio_circle_get_pixel(void *obj, int16_t x, int16_t y) { + vectorio_circle_t *self = obj; + int16_t radius = abs(self->radius); + x = abs(x); + y = abs(y); + if (x+y <= radius) return 1; + if (x > radius) return 0; + if (y > radius) return 0; + return (int32_t)x*x + (int32_t)y*y <= (int32_t)radius*radius; +} + + +void common_hal_vectorio_circle_get_area(void *circle, displayio_area_t *out_area) { + vectorio_circle_t *self = circle; + out_area->x1 = -1 * self->radius; + out_area->y1 = -1 * self->radius; + out_area->x2 = self->radius + 1; + out_area->y2 = self->radius + 1; +} + +int16_t common_hal_vectorio_circle_get_radius(void *obj) { + vectorio_circle_t *self = obj; + return self->radius; +} + +void common_hal_vectorio_circle_set_radius(void *obj, int16_t radius) { + vectorio_circle_t *self = obj; + self->radius = abs(radius); + if (self->on_dirty.obj != NULL) { + self->on_dirty.event(self->on_dirty.obj); + } +} + diff --git a/shared-module/vectorio/Circle.h b/shared-module/vectorio/Circle.h new file mode 100644 index 000000000..4b43d767e --- /dev/null +++ b/shared-module/vectorio/Circle.h @@ -0,0 +1,17 @@ +#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_CIRCLE_H +#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_CIRCLE_H + +#include + +#include "py/obj.h" + +#include "shared-module/vectorio/__init__.h" + +typedef struct { + mp_obj_base_t base; + uint16_t radius; + vectorio_event_t on_dirty; +} vectorio_circle_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_CIRCLE_H + diff --git a/shared-module/vectorio/Polygon.c b/shared-module/vectorio/Polygon.c new file mode 100644 index 000000000..6722912c2 --- /dev/null +++ b/shared-module/vectorio/Polygon.c @@ -0,0 +1,110 @@ + +#include "shared-module/vectorio/__init__.h" +#include "shared-bindings/vectorio/Polygon.h" +#include "shared-module/displayio/area.h" + +#include "py/runtime.h" +#include "stdlib.h" +#include + + +#define VECTORIO_POLYGON_DEBUG(...) (void)0 +// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) + + +void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t points_list) { + VECTORIO_POLYGON_DEBUG("%p polygon_construct\n", self); + self->points_list = points_list; + self->on_dirty.obj = NULL; +} + + +mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self) { + return self->points_list; +} +void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list) { + self->points_list = points_list; + if (self->on_dirty.obj != NULL) { + self->on_dirty.event(self->on_dirty.obj); + } +} + +void common_hal_vectorio_polygon_set_on_dirty(vectorio_polygon_t *self, vectorio_event_t notification) { + if ( self->on_dirty.obj != NULL ) { + mp_raise_TypeError(translate("polygon can only be registered in one parent")); + } + self->on_dirty = notification; +} + + +void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *area) { + VECTORIO_POLYGON_DEBUG("%p polygon get_area", polygon); + vectorio_polygon_t *self = polygon; + size_t len; + mp_obj_t *points; + mp_obj_list_get(self->points_list, &len, &points); + VECTORIO_POLYGON_DEBUG(" len: %2d, points: %d\n", len, len/2); + + area->x1 = SHRT_MAX; + area->y1 = SHRT_MAX; + area->x2 = SHRT_MIN; + area->y2 = SHRT_MIN; + for (size_t i=0; i < len; ++i) { + mp_int_t x = mp_obj_get_int(points[i]); + ++i; + mp_int_t y = mp_obj_get_int(points[i]); + if (x <= area->x1) area->x1 = x-1; + if (y <= area->y1) area->y1 = y-1; + if (x >= area->x2) area->x2 = x+1; + if (y >= area->y2) area->y2 = y+1; + } +} + + +// <0 if the point is to the left of the line vector +// 0 if the point is on the line +// >0 if the point is to the right of the line vector +__attribute__((always_inline)) static inline int line_side( mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, int16_t px, int16_t py ) { + return (px - x1) * (y2 - y1) + - (py - y1) * (x2 - x1); +} + + +uint32_t common_hal_vectorio_polygon_get_pixel(void *obj, int16_t x, int16_t y) { + VECTORIO_POLYGON_DEBUG("%p polygon get_pixel %d, %d\n", obj, x, y); + vectorio_polygon_t *self = obj; + size_t len; + mp_obj_t *points; + mp_obj_list_get(self->points_list, &len, &points); + + if (len == 0) { + return 0; + } + + int winding_number = 0; + mp_int_t x1 = mp_obj_get_int(points[0]); + mp_int_t y1 = mp_obj_get_int(points[1]); + for (size_t i=2; i <= len + 1; ++i) { + VECTORIO_POLYGON_DEBUG(" {(%3d, %3d),", x1, y1); + mp_int_t x2 = mp_obj_get_int(points[i % len]); + ++i; + mp_int_t y2 = mp_obj_get_int(points[i % len]); + VECTORIO_POLYGON_DEBUG(" (%3d, %3d)}\n", x2, y2); + if ( y1 <= y ) { + if ( y2 > y && line_side(x1, y1, x2, y2, x, y) > 0 ) { + // Wind up, point is to the right of the edge vector + ++winding_number; + VECTORIO_POLYGON_DEBUG(" wind:%2d winding_number:%2d\n", 1, winding_number); + } + } else if ( y2 <= y && line_side(x1, y1, x2, y2, x, y) < 0 ) { + // Wind down, point is to the left of the edge vector + --winding_number; + VECTORIO_POLYGON_DEBUG(" wind:%2d winding_number:%2d\n", -1, winding_number); + } + + x1 = x2; + y1 = y2; + } + return winding_number == 0 ? 0 : 1; +} + diff --git a/shared-module/vectorio/Polygon.h b/shared-module/vectorio/Polygon.h new file mode 100644 index 000000000..1aef854a7 --- /dev/null +++ b/shared-module/vectorio/Polygon.h @@ -0,0 +1,16 @@ +#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_POLYGON_H +#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_POLYGON_H + +#include + +#include "py/obj.h" +#include "shared-module/vectorio/__init__.h" + +typedef struct { + mp_obj_base_t base; + // A micropython List[ x, y, ... ] + mp_obj_t points_list; + vectorio_event_t on_dirty; +} vectorio_polygon_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_POLYGON_H diff --git a/shared-module/vectorio/Rectangle.c b/shared-module/vectorio/Rectangle.c new file mode 100644 index 000000000..dfad58a4d --- /dev/null +++ b/shared-module/vectorio/Rectangle.c @@ -0,0 +1,35 @@ +#include "shared-bindings/vectorio/Rectangle.h" +#include "shared-module/displayio/area.h" + +#include "py/runtime.h" + + +void common_hal_vectorio_rectangle_construct(vectorio_rectangle_t *self, uint32_t width, uint32_t height) { + self->width = width; + self->height = height; +} + + +uint32_t common_hal_vectorio_rectangle_get_pixel(void *obj, int16_t x, int16_t y) { + vectorio_rectangle_t *self = obj; + if (x < 0 || x >= self->width || y >= self->height || y < 0) { + return 0; + } + return 1; +} + + +void common_hal_vectorio_rectangle_get_area(void *rectangle, displayio_area_t *out_area) { + vectorio_rectangle_t *self = rectangle; + out_area->x1 = 0; + out_area->y1 = 0; + out_area->x2 = self->width; + out_area->y2 = self->height; +} + + +uint32_t common_hal_vectorio_rectangle_get_height(void *rectangle) { + vectorio_rectangle_t *self = rectangle; + return self->height; +} + diff --git a/shared-module/vectorio/Rectangle.h b/shared-module/vectorio/Rectangle.h new file mode 100644 index 000000000..c1f2a7a64 --- /dev/null +++ b/shared-module/vectorio/Rectangle.h @@ -0,0 +1,15 @@ +#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_RECTANGLE_H +#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_RECTANGLE_H + +#include + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + uint16_t width; + uint16_t height; +} vectorio_rectangle_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_RECTANGLE_H + diff --git a/shared-module/vectorio/VectorShape.c b/shared-module/vectorio/VectorShape.c new file mode 100644 index 000000000..985b77858 --- /dev/null +++ b/shared-module/vectorio/VectorShape.c @@ -0,0 +1,282 @@ + +#include "stdlib.h" + +#include "shared-module/vectorio/__init__.h" +#include "shared-bindings/vectorio/VectorShape.h" + +#include "py/runtime.h" +#include "shared-bindings/displayio/ColorConverter.h" +#include "shared-bindings/displayio/Palette.h" + +#include "shared-bindings/vectorio/Circle.h" +#include "shared-bindings/vectorio/Polygon.h" +#include "shared-bindings/vectorio/Rectangle.h" + +// Lifecycle actions. +#define VECTORIO_SHAPE_DEBUG(...) (void)0 +// #define VECTORIO_SHAPE_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) + + +// Really verbose. +#define VECTORIO_SHAPE_PIXEL_DEBUG(...) (void)0 +// #define VECTORIO_SHAPE_PIXEL_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) + + +inline __attribute__((always_inline)) +static int32_t max(int32_t a, int32_t b) { + return a > b ? a : b; +} + + +inline __attribute__((always_inline)) +static void _transpose_area(displayio_area_t *out_area) { + int16_t swap = out_area->x1; + out_area->x1 = out_area->y1; + out_area->y1 = swap; + swap = out_area->x2; + out_area->x2 = out_area->y2; + out_area->y2 = swap; +} + + +inline __attribute__((always_inline)) +static void _get_shape_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) { + VECTORIO_SHAPE_DEBUG("%p get_area\n", self); + self->ishape.get_area(self->ishape.shape, out_area); +} + + +inline __attribute__((always_inline)) +static void _get_screen_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) { + VECTORIO_SHAPE_DEBUG("%p get_screen_area\n", self); + self->ishape.get_area(self->ishape.shape, out_area); + if (self->transpose_xy) { + _transpose_area(out_area); + displayio_area_shift(out_area, self->y, self->x); + } else { + displayio_area_shift(out_area, self->x, self->y); + } +} + + +// This must be invoked each time a shape changes its position or its shape in any way. +void common_hal_vectorio_vector_shape_set_dirty(void *vector_shape) { + vectorio_vector_shape_t *self = vector_shape; + // In screen space. Need to offset the shape space. + displayio_area_t current_area; + _get_screen_area(self, ¤t_area); + VECTORIO_SHAPE_DEBUG("%p shape_dirty current:{(%3d,%3d), (%3d,%3d)} dirty:{(%3d,%3d), (%3d,%3d)}", + self, + current_area.x1, current_area.y1, current_area.x2, current_area.y2, + self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); + self->dirty = true; + // Dirty area tracks the shape's footprint between draws. It's reset on refresh finish, + displayio_area_expand(&self->ephemeral_dirty_area, ¤t_area); + VECTORIO_SHAPE_DEBUG(" -> expanded:{(%3d,%3d), (%3d,%3d)}\n", self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); +} + + +void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self, + vectorio_ishape_t ishape, + mp_obj_t pixel_shader, uint16_t x, uint16_t y, bool transpose_xy) { + VECTORIO_SHAPE_DEBUG("%p vector_shape_construct x:%3d, y:%3d\n", self, x, y); + self->x = x; + self->y = y; + self->pixel_shader = pixel_shader; + self->ishape = ishape; + self->transpose_xy = transpose_xy; + self->dirty = true; + _get_screen_area(self, &self->ephemeral_dirty_area); + self->ephemeral_dirty_area.next = NULL; +} + + +mp_int_t common_hal_vectorio_vector_shape_get_x(vectorio_vector_shape_t *self) { + VECTORIO_SHAPE_DEBUG("%p get_x\n", self); + return self->x; +} + + +void common_hal_vectorio_vector_shape_set_x(vectorio_vector_shape_t *self, mp_int_t x) { + VECTORIO_SHAPE_DEBUG("%p set_x %d\n", self, x); + if (self->x == x) { + return; + } + self->x = x; + common_hal_vectorio_vector_shape_set_dirty(self); +} + + +mp_int_t common_hal_vectorio_vector_shape_get_y(vectorio_vector_shape_t *self) { + VECTORIO_SHAPE_DEBUG("%p get_y\n", self); + return self->y; +} + + +void common_hal_vectorio_vector_shape_set_y(vectorio_vector_shape_t *self, mp_int_t y) { + VECTORIO_SHAPE_DEBUG("%p set_y %d\n", self, y); + if (self->y == y) { + return; + } + self->y = y; + common_hal_vectorio_vector_shape_set_dirty(self); +} + + +bool common_hal_vectorio_vector_shape_get_transpose_xy(vectorio_vector_shape_t *self) { + VECTORIO_SHAPE_DEBUG("%p get_transpose_xy\n", self); + return self->transpose_xy; +} + + +mp_obj_t common_hal_vectorio_vector_shape_get_pixel_shader(vectorio_vector_shape_t *self) { + VECTORIO_SHAPE_DEBUG("%p get_pixel_shader\n", self); + return self->pixel_shader; +} + +void common_hal_vectorio_vector_shape_set_pixel_shader(vectorio_vector_shape_t *self, mp_obj_t pixel_shader) { + VECTORIO_SHAPE_DEBUG("%p set_pixel_shader\n", self); + self->pixel_shader = pixel_shader; + common_hal_vectorio_vector_shape_set_dirty(self); +} + + +bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displayio_colorspace_t* colorspace, const displayio_area_t* area, uint32_t* mask, uint32_t *buffer) { + // Shape areas are relative to 0,0. This will allow rotation about a known axis. + // The consequence is that the area reported by the shape itself is _relative_ to 0,0. + // To make it relative to the VectorShape position, we must shift it. + // Pixels are drawn on the screen_area (shifted) coordinate space, while pixels are _determined_ from + // the shape_area (unshifted) space. + displayio_area_t overlap; + displayio_area_t shape_area; + _get_shape_area(self, &shape_area); + VECTORIO_SHAPE_DEBUG("%p fill_area dirty:%d fill: {(%3d,%3d), (%3d,%3d)} dirty: {(%3d,%3d), (%3d,%3d)}", + self, self->dirty, + area->x1, area->y1, area->x2, area->y2, + self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2 + ); + if (!displayio_area_compute_overlap(area, &self->ephemeral_dirty_area, &overlap)) { + VECTORIO_SHAPE_DEBUG(" no overlap\n"); + return false; + } + VECTORIO_SHAPE_DEBUG(", overlap: {(%3d,%3d), (%3d,%3d)}", overlap.x1, overlap.y1, overlap.x2, overlap.y2); + + bool full_coverage = displayio_area_equal(area, &overlap); + + uint8_t pixels_per_byte = 8 / colorspace->depth; + + uint32_t linestride_px = displayio_area_width(area); + uint32_t line_dirty_offset_px = (overlap.y1 - area->y1) * linestride_px; + uint32_t column_dirty_offset_px = overlap.x1 - area->x1; + VECTORIO_SHAPE_DEBUG(", linestride:%3d line_offset:%3d col_offset:%3d depth:%2d ppb:%2d\n", linestride_px, line_dirty_offset_px, column_dirty_offset_px, colorspace->depth, pixels_per_byte); + + displayio_input_pixel_t input_pixel; + displayio_output_pixel_t output_pixel; + + uint32_t mask_start_px = line_dirty_offset_px; + for (input_pixel.y = overlap.y1; input_pixel.y < overlap.y2; ++input_pixel.y) { + mask_start_px += column_dirty_offset_px; + for (input_pixel.x = overlap.x1; input_pixel.x < overlap.x2; ++input_pixel.x) { + // Check the mask first to see if the pixel has already been set. + uint32_t pixel_index = mask_start_px + (input_pixel.x - overlap.x1); + uint32_t *mask_doubleword = &(mask[pixel_index / 32]); + uint8_t mask_bit = pixel_index % 32; + VECTORIO_SHAPE_PIXEL_DEBUG("%p pixel_index: %5u mask_bit: %2u", self, pixel_index, mask_bit); + if ((*mask_doubleword & (1u << mask_bit)) != 0) { + VECTORIO_SHAPE_PIXEL_DEBUG(" masked\n"); + continue; + } + output_pixel.pixel = 0; + + // Get the target pixel based on the shape's coordinate space + int16_t pixel_to_get_x; + int16_t pixel_to_get_y; + if (self->transpose_xy) { + pixel_to_get_x = input_pixel.y - self->x; + pixel_to_get_y = input_pixel.x - self->y; + } else { + pixel_to_get_x = input_pixel.x - self->x; + pixel_to_get_y = input_pixel.y - self->y; + } + VECTORIO_SHAPE_PIXEL_DEBUG(" get_pixel %p (%3d, %3d) -> ( %3d, %3d )", self->ishape.shape, input_pixel.x, input_pixel.y, pixel_to_get_x, pixel_to_get_y); + input_pixel.pixel = self->ishape.get_pixel(self->ishape.shape, pixel_to_get_x, pixel_to_get_y); + VECTORIO_SHAPE_PIXEL_DEBUG(" -> %d", input_pixel.pixel); + + output_pixel.opaque = true; + if (self->pixel_shader == mp_const_none) { + output_pixel.pixel = input_pixel.pixel; + } else if (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_palette_type)) { + output_pixel.opaque = displayio_palette_get_color(self->pixel_shader, colorspace, input_pixel.pixel, &output_pixel.pixel); + } else if (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_colorconverter_type)) { + displayio_colorconverter_convert(self->pixel_shader, colorspace, &input_pixel, &output_pixel); + } + if (!output_pixel.opaque) { + VECTORIO_SHAPE_PIXEL_DEBUG(" (encountered transparent pixel; input area is not fully covered)\n"); + full_coverage = false; + } else { + *mask_doubleword |= 1u << mask_bit; + if (colorspace->depth == 16) { + VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %04x 16\n", output_pixel.pixel); + *(((uint16_t*) buffer) + pixel_index) = output_pixel.pixel; + } else if (colorspace->depth == 8) { + VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %02x 8\n", output_pixel.pixel); + *(((uint8_t*) buffer) + pixel_index) = output_pixel.pixel; + } else if (colorspace->depth < 8) { + // Reorder the offsets to pack multiple rows into a byte (meaning they share a column). + if (!colorspace->pixels_in_byte_share_row) { + uint16_t width = linestride_px; + uint16_t row = pixel_index / width; + uint16_t col = pixel_index % width; + pixel_index = col * pixels_per_byte + (row / pixels_per_byte) * pixels_per_byte * width + row % pixels_per_byte; + } + uint8_t shift = (pixel_index % pixels_per_byte) * colorspace->depth; + if (colorspace->reverse_pixels_in_byte) { + // Reverse the shift by subtracting it from the leftmost shift. + shift = (pixels_per_byte - 1) * colorspace->depth - shift; + } + VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %2d %d\n", output_pixel.pixel, colorspace->depth); + ((uint8_t*)buffer)[pixel_index / pixels_per_byte] |= output_pixel.pixel << shift; + } + } + } + mask_start_px += linestride_px - column_dirty_offset_px; + } + return full_coverage; +} + + +void vectorio_vector_shape_finish_refresh(vectorio_vector_shape_t *self) { + if ( !self->dirty ) { + return; + } + VECTORIO_SHAPE_DEBUG("%p finish_refresh was:{(%3d,%3d), (%3d,%3d)}\n", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); + self->dirty = false; + // Reset dirty area tracking to current footprint + _get_screen_area(self, &self->ephemeral_dirty_area); + self->ephemeral_dirty_area.next = NULL; + VECTORIO_SHAPE_DEBUG("%p finish_refresh now:{(%3d,%3d), (%3d,%3d)}\n", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); + + if (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_palette_type)) { + displayio_palette_finish_refresh(self->pixel_shader); + } else if (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_colorconverter_type)) { + displayio_colorconverter_finish_refresh(self->pixel_shader); + } +} + + +// Assembles a singly linked list of dirty areas from all components on the display. +displayio_area_t* vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_t *self, displayio_area_t* tail) { + if (self->dirty + || (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_palette_type) && displayio_palette_needs_refresh(self->pixel_shader)) + || (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_colorconverter_type) && displayio_colorconverter_needs_refresh(self->pixel_shader)) + ) { + VECTORIO_SHAPE_DEBUG("%p get_refresh_area dirty:%d {(%3d,%3d), (%3d,%3d)}", self, self->dirty, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); + common_hal_vectorio_vector_shape_set_dirty(self); + // vector.add_to_head + self->ephemeral_dirty_area.next = tail; + VECTORIO_SHAPE_DEBUG(" this_area: %p next: %p after: %p\n", &self->ephemeral_dirty_area, tail, tail == NULL ? NULL : tail->next); + return &self->ephemeral_dirty_area; + } + return tail; +} + diff --git a/shared-module/vectorio/VectorShape.h b/shared-module/vectorio/VectorShape.h new file mode 100644 index 000000000..ac60e5b98 --- /dev/null +++ b/shared-module/vectorio/VectorShape.h @@ -0,0 +1,51 @@ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_SHAPE_H +#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_SHAPE_H + +#include +#include + +#include "py/obj.h" +#include "shared-module/displayio/area.h" +#include "shared-module/displayio/Palette.h" + +typedef void get_area_function(mp_obj_t shape, displayio_area_t *out_area); +typedef uint32_t get_pixel_function(mp_obj_t shape, int16_t x, int16_t y); + +// This struct binds a shape's common Shape support functions (its vector shape interface) +// to its instance pointer. We only check at construction time what the type of the +// associated shape is and link the correct functions up. +// Later when using the shape for drawing logic these functions may be invoked +// unconditionally. This simplifies the addition of new types and restricts the +// respective responsibilities of VectorShape and actual shape implementations. +typedef struct { + mp_obj_t shape; + get_area_function *get_area; + get_pixel_function *get_pixel; +} vectorio_ishape_t; + +typedef struct { + mp_obj_base_t base; + vectorio_ishape_t ishape; + mp_obj_t pixel_shader; + int16_t x; + int16_t y; + bool transpose_xy; + bool dirty; // True if we need to draw + // Tracks current shape footprint and expands outward as the shape dirties and changes. + // This is suboptimal if you move your shape far. Could add more state to only redraw + // exactly what we left behind. + displayio_area_t ephemeral_dirty_area; +} vectorio_vector_shape_t; + +displayio_area_t* vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_t *self, displayio_area_t* tail); + +// Area is always in absolute screen coordinates. +bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer); + +// Fills in out_area with the maximum bounds of all related pixels in the last rendered frame. Returns +// false if the vector shape wasn't rendered in the last frame. +bool vectorio_vector_shape_get_previous_area(vectorio_vector_shape_t *self, displayio_area_t *out_area); +void vectorio_vector_shape_finish_refresh(vectorio_vector_shape_t *self); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_SHAPE_H diff --git a/shared-module/vectorio/__init__.c b/shared-module/vectorio/__init__.c new file mode 100644 index 000000000..473c509c3 --- /dev/null +++ b/shared-module/vectorio/__init__.c @@ -0,0 +1,3 @@ + +// Don't need anything in here yet + diff --git a/shared-module/vectorio/__init__.h b/shared-module/vectorio/__init__.h new file mode 100644 index 000000000..6ae381f06 --- /dev/null +++ b/shared-module/vectorio/__init__.h @@ -0,0 +1,15 @@ +#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_INIT_H +#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_INIT_H + +#include "py/obj.h" + +typedef void event_function(mp_obj_t obj); + +typedef struct { + mp_obj_t obj; + event_function *event; +} vectorio_event_t; + + +#endif + -- cgit v1.2.3 From 4086600b61f14e043f02c1b8771e7780b82f313c Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Fri, 8 May 2020 23:03:15 -0700 Subject: vectorio: switch per-shape transform to Display Rather than maintain a transform per-shape, we'll just use whatever settings are on the Display. Currently only transpose is done. --- shared-bindings/vectorio/VectorShape.c | 22 ++-------------------- shared-bindings/vectorio/VectorShape.h | 8 +++++--- shared-module/displayio/Group.c | 6 ++++++ shared-module/vectorio/VectorShape.c | 34 ++++++++++++++++++++++++---------- shared-module/vectorio/VectorShape.h | 2 +- 5 files changed, 38 insertions(+), 34 deletions(-) (limited to 'shared-module') diff --git a/shared-bindings/vectorio/VectorShape.c b/shared-bindings/vectorio/VectorShape.c index 8f99577b4..02a42536d 100644 --- a/shared-bindings/vectorio/VectorShape.c +++ b/shared-bindings/vectorio/VectorShape.c @@ -32,13 +32,12 @@ //| :param int y: Initial y position of the center axis of the shape within the parent. //| STATIC mp_obj_t vectorio_vector_shape_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_shape, ARG_pixel_shader, ARG_x, ARG_y, ARG_transpose_xy }; + enum { ARG_shape, ARG_pixel_shader, ARG_x, ARG_y }; static const mp_arg_t allowed_args[] = { { MP_QSTR_shape, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, - { MP_QSTR_transpose_xy, 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, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -74,7 +73,7 @@ STATIC mp_obj_t vectorio_vector_shape_make_new(const mp_obj_type_t *type, size_t vectorio_vector_shape_t *self = m_new_obj(vectorio_vector_shape_t); self->base.type = &vectorio_vector_shape_type; common_hal_vectorio_vector_shape_construct(self, - ishape, pixel_shader, x, y, args[ARG_transpose_xy].u_bool + ishape, pixel_shader, x, y ); // Wire up event callbacks @@ -150,23 +149,6 @@ const mp_obj_property_t vectorio_vector_shape_y_obj = { }; -//| .. attribute:: transpose_xy -//| -//| true if the object is to be flipped. -//| -STATIC mp_obj_t vectorio_vector_shape_obj_get_transpose_xy(mp_obj_t self_in) { - vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_bool(common_hal_vectorio_vector_shape_get_transpose_xy(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_transpose_xy_obj, vectorio_vector_shape_obj_get_transpose_xy); - -const mp_obj_property_t vectorio_vector_shape_transpose_xy_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&vectorio_vector_shape_get_transpose_xy_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - //| .. attribute:: pixel_shader //| //| The pixel shader of the shape. diff --git a/shared-bindings/vectorio/VectorShape.h b/shared-bindings/vectorio/VectorShape.h index ad745b30f..d098504e9 100644 --- a/shared-bindings/vectorio/VectorShape.h +++ b/shared-bindings/vectorio/VectorShape.h @@ -2,12 +2,13 @@ #define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H #include "shared-module/vectorio/VectorShape.h" +#include "shared-module/displayio/area.h" extern const mp_obj_type_t vectorio_vector_shape_type; void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self, vectorio_ishape_t ishape, - mp_obj_t pixel_shader, uint16_t x, uint16_t y, bool transpose_xy); + mp_obj_t pixel_shader, uint16_t x, uint16_t y); void common_hal_vectorio_vector_shape_set_dirty(void *self); @@ -17,9 +18,10 @@ void common_hal_vectorio_vector_shape_set_x(vectorio_vector_shape_t *self, mp_in mp_int_t common_hal_vectorio_vector_shape_get_y(vectorio_vector_shape_t *self); void common_hal_vectorio_vector_shape_set_y(vectorio_vector_shape_t *self, mp_int_t y); -bool common_hal_vectorio_vector_shape_get_transpose_xy(vectorio_vector_shape_t *self); - mp_obj_t common_hal_vectorio_vector_shape_get_pixel_shader(vectorio_vector_shape_t *self); void common_hal_vectorio_vector_shape_set_pixel_shader(vectorio_vector_shape_t *self, mp_obj_t pixel_shader); + +void vectorio_vector_shape_update_transform(vectorio_vector_shape_t *self, displayio_buffer_transform_t *group_transform); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index 9d50dabec..0c31de532 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -122,6 +122,12 @@ static void _update_child_transforms(displayio_group_t* self) { } for (size_t i = 0; i < self->size; i++) { mp_obj_t layer = self->children[i].native; +#if CIRCUITPY_VECTORIO + if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + vectorio_vector_shape_update_transform(layer, &self->absolute_transform); + } + else +#endif if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { displayio_tilegrid_update_transform(layer, &self->absolute_transform); } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { diff --git a/shared-module/vectorio/VectorShape.c b/shared-module/vectorio/VectorShape.c index 985b77858..6030c1e3d 100644 --- a/shared-module/vectorio/VectorShape.c +++ b/shared-module/vectorio/VectorShape.c @@ -50,7 +50,7 @@ inline __attribute__((always_inline)) static void _get_screen_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) { VECTORIO_SHAPE_DEBUG("%p get_screen_area\n", self); self->ishape.get_area(self->ishape.shape, out_area); - if (self->transpose_xy) { + if (self->absolute_transform->transpose_xy) { _transpose_area(out_area); displayio_area_shift(out_area, self->y, self->x); } else { @@ -76,18 +76,32 @@ void common_hal_vectorio_vector_shape_set_dirty(void *vector_shape) { } +static displayio_buffer_transform_t null_transform = { + .x = 0, + .y = 0, + .dx = 0, + .dy = 0, + .scale = 1, + .width = 0, + .height = 0, + .mirror_x = false, + .mirror_y = false, + .transpose_xy = false +}; + + void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self, vectorio_ishape_t ishape, - mp_obj_t pixel_shader, uint16_t x, uint16_t y, bool transpose_xy) { + mp_obj_t pixel_shader, uint16_t x, uint16_t y) { VECTORIO_SHAPE_DEBUG("%p vector_shape_construct x:%3d, y:%3d\n", self, x, y); self->x = x; self->y = y; self->pixel_shader = pixel_shader; self->ishape = ishape; - self->transpose_xy = transpose_xy; self->dirty = true; _get_screen_area(self, &self->ephemeral_dirty_area); self->ephemeral_dirty_area.next = NULL; + self->absolute_transform = &null_transform; } @@ -123,12 +137,6 @@ void common_hal_vectorio_vector_shape_set_y(vectorio_vector_shape_t *self, mp_in } -bool common_hal_vectorio_vector_shape_get_transpose_xy(vectorio_vector_shape_t *self) { - VECTORIO_SHAPE_DEBUG("%p get_transpose_xy\n", self); - return self->transpose_xy; -} - - mp_obj_t common_hal_vectorio_vector_shape_get_pixel_shader(vectorio_vector_shape_t *self) { VECTORIO_SHAPE_DEBUG("%p get_pixel_shader\n", self); return self->pixel_shader; @@ -191,7 +199,7 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ // Get the target pixel based on the shape's coordinate space int16_t pixel_to_get_x; int16_t pixel_to_get_y; - if (self->transpose_xy) { + if (self->absolute_transform->transpose_xy) { pixel_to_get_x = input_pixel.y - self->x; pixel_to_get_y = input_pixel.x - self->y; } else { @@ -280,3 +288,9 @@ displayio_area_t* vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_ return tail; } +void vectorio_vector_shape_update_transform(vectorio_vector_shape_t *self, displayio_buffer_transform_t *group_transform) { + self->absolute_transform = group_transform; + common_hal_vectorio_vector_shape_set_dirty(self); +} + + diff --git a/shared-module/vectorio/VectorShape.h b/shared-module/vectorio/VectorShape.h index ac60e5b98..8fc698dbc 100644 --- a/shared-module/vectorio/VectorShape.h +++ b/shared-module/vectorio/VectorShape.h @@ -30,7 +30,7 @@ typedef struct { mp_obj_t pixel_shader; int16_t x; int16_t y; - bool transpose_xy; + displayio_buffer_transform_t *absolute_transform; bool dirty; // True if we need to draw // Tracks current shape footprint and expands outward as the shape dirties and changes. // This is suboptimal if you move your shape far. Could add more state to only redraw -- cgit v1.2.3 From 6660311a96f063e935e85a228f8b12e3a07ca95c Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Sun, 10 May 2020 14:17:57 -0700 Subject: vectorio: respect display transpose and mirror. VectorShape now just uses the Group's and Display's absolute transforms. --- shared-module/displayio/Group.c | 7 ++++ shared-module/vectorio/Circle.c | 4 +-- shared-module/vectorio/VectorShape.c | 69 +++++++++++++++++++----------------- 3 files changed, 45 insertions(+), 35 deletions(-) (limited to 'shared-module') diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index 0c31de532..d6df01f3d 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -215,6 +215,7 @@ static mp_obj_t _add_layer(displayio_group_t* self, mp_obj_t layer) { #if CIRCUITPY_VECTORIO native_layer = mp_instance_cast_to_native_base(layer, &vectorio_vector_shape_type); if (native_layer != MP_OBJ_NULL) { + vectorio_vector_shape_update_transform(native_layer, &self->absolute_transform); return native_layer; } #endif @@ -247,6 +248,12 @@ static void _remove_layer(displayio_group_t* self, size_t index) { mp_obj_t layer = self->children[index].native; displayio_area_t layer_area; bool rendered_last_frame = false; +#if CIRCUITPY_VECTORIO + if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + vectorio_vector_shape_update_transform(layer, NULL); + } + else +#endif if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { displayio_tilegrid_t* tilegrid = layer; rendered_last_frame = displayio_tilegrid_get_previous_area(tilegrid, &layer_area); diff --git a/shared-module/vectorio/Circle.c b/shared-module/vectorio/Circle.c index 6bcd6318f..e6e3268e7 100644 --- a/shared-module/vectorio/Circle.c +++ b/shared-module/vectorio/Circle.c @@ -34,8 +34,8 @@ uint32_t common_hal_vectorio_circle_get_pixel(void *obj, int16_t x, int16_t y) { void common_hal_vectorio_circle_get_area(void *circle, displayio_area_t *out_area) { vectorio_circle_t *self = circle; - out_area->x1 = -1 * self->radius; - out_area->y1 = -1 * self->radius; + out_area->x1 = -1 * self->radius - 1; + out_area->y1 = -1 * self->radius - 1; out_area->x2 = self->radius + 1; out_area->y2 = self->radius + 1; } diff --git a/shared-module/vectorio/VectorShape.c b/shared-module/vectorio/VectorShape.c index 6030c1e3d..b23baa4cf 100644 --- a/shared-module/vectorio/VectorShape.c +++ b/shared-module/vectorio/VectorShape.c @@ -28,34 +28,39 @@ static int32_t max(int32_t a, int32_t b) { } -inline __attribute__((always_inline)) -static void _transpose_area(displayio_area_t *out_area) { - int16_t swap = out_area->x1; - out_area->x1 = out_area->y1; - out_area->y1 = swap; - swap = out_area->x2; - out_area->x2 = out_area->y2; - out_area->y2 = swap; -} - - -inline __attribute__((always_inline)) -static void _get_shape_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) { - VECTORIO_SHAPE_DEBUG("%p get_area\n", self); - self->ishape.get_area(self->ishape.shape, out_area); -} - - inline __attribute__((always_inline)) static void _get_screen_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) { - VECTORIO_SHAPE_DEBUG("%p get_screen_area\n", self); + VECTORIO_SHAPE_DEBUG("%p get_screen_area tform:{x:%d y:%d dx:%d dy:%d scl:%d w:%d h:%d mx:%d my:%d tr:%d}", self, + self->absolute_transform->x, self->absolute_transform->y, self->absolute_transform->dx, self->absolute_transform->dy, self->absolute_transform->scale, + self->absolute_transform->width, self->absolute_transform->height, self->absolute_transform->mirror_x, self->absolute_transform->mirror_y, self->absolute_transform->transpose_xy + ); self->ishape.get_area(self->ishape.shape, out_area); + VECTORIO_SHAPE_DEBUG(" in:{(%5d,%5d), (%5d,%5d)}", out_area->x1, out_area->y1, out_area->x2, out_area->y2); if (self->absolute_transform->transpose_xy) { - _transpose_area(out_area); - displayio_area_shift(out_area, self->y, self->x); + int16_t swap = out_area->x1; + out_area->x1 = (out_area->y1 + self->y) * self->absolute_transform->dx + self->absolute_transform->x; + out_area->y1 = (swap + self->x) * self->absolute_transform->dy + self->absolute_transform->y; + swap = out_area->x2; + out_area->x2 = (out_area->y2 + self->y) * self->absolute_transform->dx + self->absolute_transform->x; + out_area->y2 = (swap + self->x) * self->absolute_transform->dy + self->absolute_transform->y; } else { - displayio_area_shift(out_area, self->x, self->y); + out_area->x1 = (out_area->x1 + self->x) * self->absolute_transform->dx + self->absolute_transform->x; + out_area->y1 = (out_area->y1 + self->y) * self->absolute_transform->dy + self->absolute_transform->y; + out_area->x2 = (out_area->x2 + self->x) * self->absolute_transform->dx + self->absolute_transform->x; + out_area->y2 = (out_area->y2 + self->y) * self->absolute_transform->dy + self->absolute_transform->y; + } + // We might have mirrored due to dx + if (out_area->x2 < out_area->x1) { + int16_t swap = out_area->x1; + out_area->x1 = out_area->x2; + out_area->x2 = swap; + } + if (out_area->y2 < out_area->y1) { + int16_t swap = out_area->y1; + out_area->y1 = out_area->y2; + out_area->y2 = swap; } + VECTORIO_SHAPE_DEBUG(" out:{(%5d,%5d), (%5d,%5d)}\n", out_area->x1, out_area->y1, out_area->x2, out_area->y2); } @@ -79,8 +84,8 @@ void common_hal_vectorio_vector_shape_set_dirty(void *vector_shape) { static displayio_buffer_transform_t null_transform = { .x = 0, .y = 0, - .dx = 0, - .dy = 0, + .dx = 1, + .dy = 1, .scale = 1, .width = 0, .height = 0, @@ -99,9 +104,9 @@ void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self, self->pixel_shader = pixel_shader; self->ishape = ishape; self->dirty = true; + self->absolute_transform = &null_transform; // Critical to have a valid transform before getting screen area. _get_screen_area(self, &self->ephemeral_dirty_area); self->ephemeral_dirty_area.next = NULL; - self->absolute_transform = &null_transform; } @@ -156,9 +161,7 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ // Pixels are drawn on the screen_area (shifted) coordinate space, while pixels are _determined_ from // the shape_area (unshifted) space. displayio_area_t overlap; - displayio_area_t shape_area; - _get_shape_area(self, &shape_area); - VECTORIO_SHAPE_DEBUG("%p fill_area dirty:%d fill: {(%3d,%3d), (%3d,%3d)} dirty: {(%3d,%3d), (%3d,%3d)}", + VECTORIO_SHAPE_DEBUG("%p fill_area dirty:%d fill: {(%5d,%5d), (%5d,%5d)} dirty: {(%5d,%5d), (%5d,%5d)}", self, self->dirty, area->x1, area->y1, area->x2, area->y2, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2 @@ -200,11 +203,11 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ int16_t pixel_to_get_x; int16_t pixel_to_get_y; if (self->absolute_transform->transpose_xy) { - pixel_to_get_x = input_pixel.y - self->x; - pixel_to_get_y = input_pixel.x - self->y; + pixel_to_get_x = (input_pixel.y - self->absolute_transform->dy * self->x - self->absolute_transform->y) / self->absolute_transform->dy; + pixel_to_get_y = (input_pixel.x - self->absolute_transform->dx * self->y - self->absolute_transform->x) / self->absolute_transform->dx; } else { - pixel_to_get_x = input_pixel.x - self->x; - pixel_to_get_y = input_pixel.y - self->y; + pixel_to_get_x = (input_pixel.x - self->absolute_transform->dx * self->x) / self->absolute_transform->dx; + pixel_to_get_y = (input_pixel.y - self->absolute_transform->dy * self->y) / self->absolute_transform->dy; } VECTORIO_SHAPE_PIXEL_DEBUG(" get_pixel %p (%3d, %3d) -> ( %3d, %3d )", self->ishape.shape, input_pixel.x, input_pixel.y, pixel_to_get_x, pixel_to_get_y); input_pixel.pixel = self->ishape.get_pixel(self->ishape.shape, pixel_to_get_x, pixel_to_get_y); @@ -289,7 +292,7 @@ displayio_area_t* vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_ } void vectorio_vector_shape_update_transform(vectorio_vector_shape_t *self, displayio_buffer_transform_t *group_transform) { - self->absolute_transform = group_transform; + self->absolute_transform = group_transform == NULL ? &null_transform : group_transform; common_hal_vectorio_vector_shape_set_dirty(self); } -- cgit v1.2.3 From 58c8e00745064bd290fc9ec18a838960b27c1eea Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Sun, 10 May 2020 15:36:23 -0700 Subject: vectorio: clean up after group removal VectorShape tells the Group to redraw whatever it left behind when it is removed now. --- shared-module/displayio/Group.c | 2 ++ shared-module/vectorio/VectorShape.c | 7 +++++++ shared-module/vectorio/VectorShape.h | 4 +++- 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'shared-module') diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index d6df01f3d..15cf5b8e4 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -250,6 +250,8 @@ static void _remove_layer(displayio_group_t* self, size_t index) { bool rendered_last_frame = false; #if CIRCUITPY_VECTORIO if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + bool has_dirty_area = vectorio_vector_shape_get_dirty_area(layer, &layer_area); + rendered_last_frame = has_dirty_area; vectorio_vector_shape_update_transform(layer, NULL); } else diff --git a/shared-module/vectorio/VectorShape.c b/shared-module/vectorio/VectorShape.c index b23baa4cf..f57739047 100644 --- a/shared-module/vectorio/VectorShape.c +++ b/shared-module/vectorio/VectorShape.c @@ -64,6 +64,13 @@ static void _get_screen_area(vectorio_vector_shape_t *self, displayio_area_t *ou } +// For use by Group to know where it needs to redraw on layer removal. +bool vectorio_vector_shape_get_dirty_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) { + displayio_area_copy(&self->ephemeral_dirty_area, out_area); + return true; // For now just always redraw. +} + + // This must be invoked each time a shape changes its position or its shape in any way. void common_hal_vectorio_vector_shape_set_dirty(void *vector_shape) { vectorio_vector_shape_t *self = vector_shape; diff --git a/shared-module/vectorio/VectorShape.h b/shared-module/vectorio/VectorShape.h index 8fc698dbc..56eb3d8a5 100644 --- a/shared-module/vectorio/VectorShape.h +++ b/shared-module/vectorio/VectorShape.h @@ -38,7 +38,9 @@ typedef struct { displayio_area_t ephemeral_dirty_area; } vectorio_vector_shape_t; -displayio_area_t* vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_t *self, displayio_area_t* tail); +displayio_area_t* vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_t *self, displayio_area_t *tail); + +bool vectorio_vector_shape_get_dirty_area(vectorio_vector_shape_t *self, displayio_area_t *current_dirty_area); // Area is always in absolute screen coordinates. bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer); -- cgit v1.2.3 From 32f85f7a44b6ce753070168b4f008a9499d70883 Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Sun, 10 May 2020 16:21:07 -0700 Subject: vectorio: fix up Rectangle * Fix drawing 1 pixel too large * Need to pad dirty area to ensure removed shapes are fully removed --- shared-module/vectorio/Rectangle.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'shared-module') diff --git a/shared-module/vectorio/Rectangle.c b/shared-module/vectorio/Rectangle.c index dfad58a4d..7fabe6ff9 100644 --- a/shared-module/vectorio/Rectangle.c +++ b/shared-module/vectorio/Rectangle.c @@ -12,7 +12,7 @@ void common_hal_vectorio_rectangle_construct(vectorio_rectangle_t *self, uint32_ uint32_t common_hal_vectorio_rectangle_get_pixel(void *obj, int16_t x, int16_t y) { vectorio_rectangle_t *self = obj; - if (x < 0 || x >= self->width || y >= self->height || y < 0) { + if (x < 0 || x > self->width || y > self->height || y < 0) { return 0; } return 1; @@ -21,8 +21,8 @@ uint32_t common_hal_vectorio_rectangle_get_pixel(void *obj, int16_t x, int16_t y void common_hal_vectorio_rectangle_get_area(void *rectangle, displayio_area_t *out_area) { vectorio_rectangle_t *self = rectangle; - out_area->x1 = 0; - out_area->y1 = 0; + out_area->x1 = -1; + out_area->y1 = -1; out_area->x2 = self->width; out_area->y2 = self->height; } -- cgit v1.2.3 From 6aa9709d98c578bf9de342117137ef6595251960 Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Tue, 12 May 2020 11:47:19 -0700 Subject: make Circle return explicit --- shared-module/vectorio/Circle.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'shared-module') diff --git a/shared-module/vectorio/Circle.c b/shared-module/vectorio/Circle.c index e6e3268e7..999d62512 100644 --- a/shared-module/vectorio/Circle.c +++ b/shared-module/vectorio/Circle.c @@ -28,7 +28,8 @@ uint32_t common_hal_vectorio_circle_get_pixel(void *obj, int16_t x, int16_t y) { if (x+y <= radius) return 1; if (x > radius) return 0; if (y > radius) return 0; - return (int32_t)x*x + (int32_t)y*y <= (int32_t)radius*radius; + const bool pythagorasSmallerThanRadius = (int32_t)x*x + (int32_t)y*y <= (int32_t)radius*radius; + return pythagorasSmallerThanRadius ? 1 : 0; } -- cgit v1.2.3 From fd94c08cf45d1b0669c276ba61bbad1b501ee13f Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Tue, 12 May 2020 11:49:15 -0700 Subject: add performance measurement to VectorShape --- shared-module/vectorio/VectorShape.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'shared-module') diff --git a/shared-module/vectorio/VectorShape.c b/shared-module/vectorio/VectorShape.c index f57739047..4fb353aae 100644 --- a/shared-module/vectorio/VectorShape.c +++ b/shared-module/vectorio/VectorShape.c @@ -5,6 +5,7 @@ #include "shared-bindings/vectorio/VectorShape.h" #include "py/runtime.h" +#include "shared-bindings/time/__init__.h" #include "shared-bindings/displayio/ColorConverter.h" #include "shared-bindings/displayio/Palette.h" @@ -17,6 +18,10 @@ // #define VECTORIO_SHAPE_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) +// Used in both logging and ifdefs, for extra variables +// #define VECTORIO_PERF(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) + + // Really verbose. #define VECTORIO_SHAPE_PIXEL_DEBUG(...) (void)0 // #define VECTORIO_SHAPE_PIXEL_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) @@ -167,6 +172,10 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ // To make it relative to the VectorShape position, we must shift it. // Pixels are drawn on the screen_area (shifted) coordinate space, while pixels are _determined_ from // the shape_area (unshifted) space. +#ifdef VECTORIO_PERF + uint64_t start = common_hal_time_monotonic_ns(); + uint64_t pixel_time = 0; +#endif displayio_area_t overlap; VECTORIO_SHAPE_DEBUG("%p fill_area dirty:%d fill: {(%5d,%5d), (%5d,%5d)} dirty: {(%5d,%5d), (%5d,%5d)}", self, self->dirty, @@ -186,7 +195,8 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ uint32_t linestride_px = displayio_area_width(area); uint32_t line_dirty_offset_px = (overlap.y1 - area->y1) * linestride_px; uint32_t column_dirty_offset_px = overlap.x1 - area->x1; - VECTORIO_SHAPE_DEBUG(", linestride:%3d line_offset:%3d col_offset:%3d depth:%2d ppb:%2d\n", linestride_px, line_dirty_offset_px, column_dirty_offset_px, colorspace->depth, pixels_per_byte); + VECTORIO_SHAPE_DEBUG(", linestride:%3d line_offset:%3d col_offset:%3d depth:%2d ppb:%2d shape:%s", + linestride_px, line_dirty_offset_px, column_dirty_offset_px, colorspace->depth, pixels_per_byte, mp_obj_get_type_str(self->ishape.shape)); displayio_input_pixel_t input_pixel; displayio_output_pixel_t output_pixel; @@ -217,7 +227,14 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ pixel_to_get_y = (input_pixel.y - self->absolute_transform->dy * self->y) / self->absolute_transform->dy; } VECTORIO_SHAPE_PIXEL_DEBUG(" get_pixel %p (%3d, %3d) -> ( %3d, %3d )", self->ishape.shape, input_pixel.x, input_pixel.y, pixel_to_get_x, pixel_to_get_y); +#ifdef VECTORIO_PERF + uint64_t pre_pixel = common_hal_time_monotonic_ns(); +#endif input_pixel.pixel = self->ishape.get_pixel(self->ishape.shape, pixel_to_get_x, pixel_to_get_y); +#ifdef VECTORIO_PERF + uint64_t post_pixel = common_hal_time_monotonic_ns(); + pixel_time += post_pixel - pre_pixel; +#endif VECTORIO_SHAPE_PIXEL_DEBUG(" -> %d", input_pixel.pixel); output_pixel.opaque = true; @@ -259,6 +276,19 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ } mask_start_px += linestride_px - column_dirty_offset_px; } +#ifdef VECTORIO_PERF + uint64_t end = common_hal_time_monotonic_ns(); + uint32_t pixels = (overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1); + VECTORIO_PERF("draw %16s -> shape:{%4dpx, %4.1fms,%9.1fpps fill} shape_pixels:{%6.1fus total, %4.1fus/px}\n", + mp_obj_get_type_str(self->ishape.shape), + (overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1), + (double)((end - start) / 1000000.0), + (double)(max(1, pixels * (1000000000.0 / (end - start)))), + (double)(pixel_time / 1000.0), + (double)(pixel_time / 1000.0 / pixels) + ); +#endif + VECTORIO_SHAPE_DEBUG(" -> pixels:%4d\n"); return full_coverage; } -- cgit v1.2.3 From fbf052bb2755b2a3afb85b94ea9625ab29da4ab0 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 13 May 2020 10:04:19 -0500 Subject: Gamepad & GamepadShift: Enable ticks while object exists Otherwise, button presses might not be noticed. --- shared-bindings/gamepad/GamePad.c | 4 ++++ shared-bindings/gamepadshift/GamePadShift.c | 4 ++++ shared-module/gamepad/GamePad.c | 2 ++ shared-module/gamepad/__init__.c | 4 ++++ shared-module/gamepadshift/GamePadShift.c | 2 ++ 5 files changed, 16 insertions(+) (limited to 'shared-module') diff --git a/shared-bindings/gamepad/GamePad.c b/shared-bindings/gamepad/GamePad.c index d3c29019a..88f072f3e 100644 --- a/shared-bindings/gamepad/GamePad.c +++ b/shared-bindings/gamepad/GamePad.c @@ -33,6 +33,7 @@ #include "shared-bindings/gamepad/__init__.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "supervisor/shared/translate.h" +#include "supervisor/shared/tick.h" //| .. currentmodule:: gamepad @@ -108,6 +109,9 @@ STATIC mp_obj_t gamepad_make_new(const mp_obj_type_t *type, size_t n_args, gamepad_singleton = m_new_obj(gamepad_obj_t); gamepad_singleton->base.type = &gamepad_type; gamepad_singleton = gc_make_long_lived(gamepad_singleton); + if (!MP_STATE_VM(gamepad_singleton)) { + supervisor_enable_tick(); + } MP_STATE_VM(gamepad_singleton) = gamepad_singleton; } common_hal_gamepad_gamepad_init(gamepad_singleton, args, n_args); diff --git a/shared-bindings/gamepadshift/GamePadShift.c b/shared-bindings/gamepadshift/GamePadShift.c index 91203ad20..76f054aae 100644 --- a/shared-bindings/gamepadshift/GamePadShift.c +++ b/shared-bindings/gamepadshift/GamePadShift.c @@ -31,6 +31,7 @@ #include "shared-bindings/gamepadshift/GamePadShift.h" #include "shared-bindings/gamepadshift/__init__.h" #include "supervisor/shared/translate.h" +#include "supervisor/shared/tick.h" //| .. currentmodule:: gamepadshift //| @@ -75,6 +76,9 @@ STATIC mp_obj_t gamepadshift_make_new(const mp_obj_type_t *type, size_t n_args, gamepad_singleton = m_new_obj(gamepadshift_obj_t); gamepad_singleton->base.type = &gamepadshift_type; gamepad_singleton = gc_make_long_lived(gamepad_singleton); + if (!MP_STATE_VM(gamepad_singleton)) { + supervisor_enable_tick(); + } MP_STATE_VM(gamepad_singleton) = gamepad_singleton; } common_hal_gamepadshift_gamepadshift_init(gamepad_singleton, clock_pin, data_pin, latch_pin); diff --git a/shared-module/gamepad/GamePad.c b/shared-module/gamepad/GamePad.c index fdce0caab..7b4108eb2 100644 --- a/shared-module/gamepad/GamePad.c +++ b/shared-module/gamepad/GamePad.c @@ -27,6 +27,7 @@ #include "py/mpstate.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/gamepad/GamePad.h" +#include "supervisor/shared/tick.h" void common_hal_gamepad_gamepad_init(gamepad_obj_t *gamepad, const mp_obj_t pins[], size_t n_pins) { @@ -54,4 +55,5 @@ void common_hal_gamepad_gamepad_init(gamepad_obj_t *gamepad, void common_hal_gamepad_gamepad_deinit(gamepad_obj_t *self) { MP_STATE_VM(gamepad_singleton) = NULL; + supervisor_disable_tick(); } diff --git a/shared-module/gamepad/__init__.c b/shared-module/gamepad/__init__.c index 3e17a0fb5..9874b2752 100644 --- a/shared-module/gamepad/__init__.c +++ b/shared-module/gamepad/__init__.c @@ -29,6 +29,7 @@ #include "py/mpstate.h" #include "shared-bindings/gamepad/__init__.h" #include "shared-bindings/gamepad/GamePad.h" +#include "supervisor/shared/tick.h" #include "shared-bindings/digitalio/DigitalInOut.h" @@ -59,5 +60,8 @@ void gamepad_tick(void) { } void gamepad_reset(void) { + if (MP_STATE_VM(gamepad_singleton)) { + supervisor_disable_tick(); + } MP_STATE_VM(gamepad_singleton) = NULL; } diff --git a/shared-module/gamepadshift/GamePadShift.c b/shared-module/gamepadshift/GamePadShift.c index b8fb4d3f4..51da61702 100644 --- a/shared-module/gamepadshift/GamePadShift.c +++ b/shared-module/gamepadshift/GamePadShift.c @@ -27,6 +27,7 @@ #include "py/mpstate.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-module/gamepadshift/GamePadShift.h" +#include "supervisor/shared/tick.h" void common_hal_gamepadshift_gamepadshift_init(gamepadshift_obj_t *gamepadshift, digitalio_digitalinout_obj_t *clock_pin, @@ -46,4 +47,5 @@ void common_hal_gamepadshift_gamepadshift_init(gamepadshift_obj_t *gamepadshift, void common_hal_gamepadshift_gamepadshift_deinit(gamepadshift_obj_t *gamepadshift) { MP_STATE_VM(gamepad_singleton) = NULL; + supervisor_disable_tick(); } -- cgit v1.2.3 From 4bbb80e31af4c5626d118995e36ade470935fafe Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Tue, 12 May 2020 21:55:18 -0700 Subject: vectorio: speed up polygon This change takes polygon from 126k pixels per second fill to 240k pps fill on a reference 5 point star 50x66px polygon, updating both location and shape at 10hz. Tested on an m4 express feather. As a curiosity, the flat-out fill rate of a shape whose get_pixel is `return 0;` fills just shy of 375k pixels per second. --- shared-bindings/vectorio/Polygon.c | 42 +------------------- shared-module/vectorio/Polygon.c | 79 +++++++++++++++++++++++++++++--------- shared-module/vectorio/Polygon.h | 5 ++- 3 files changed, 65 insertions(+), 61 deletions(-) (limited to 'shared-module') diff --git a/shared-bindings/vectorio/Polygon.c b/shared-bindings/vectorio/Polygon.c index 5f4d85e21..e9e90942a 100644 --- a/shared-bindings/vectorio/Polygon.c +++ b/shared-bindings/vectorio/Polygon.c @@ -15,41 +15,6 @@ // #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) -// Converts a list of points tuples to a flat list of ints for speedier internal use. -// Also validates the points. -static mp_obj_t _to_points_list(mp_obj_t points_tuple_list) { - size_t len = 0; - mp_obj_t *items; - mp_obj_list_get(points_tuple_list, &len, &items); - VECTORIO_POLYGON_DEBUG("polygon_points_list len: %d\n", len); - - if ( len == 0 ) { - mp_raise_TypeError_varg(translate("empty %q list"), MP_QSTR_point); - } - - mp_obj_t points_list = mp_obj_new_list(0, NULL); - - for ( size_t i = 0; i < len; ++i) { - size_t tuple_len = 0; - mp_obj_t *tuple_items; - mp_obj_tuple_get(items[i], &tuple_len, &tuple_items); - - if (tuple_len != 2) { - mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point); - } - int value; - if (!mp_obj_get_int_maybe(tuple_items[0], &value)) { - mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); - } - mp_obj_list_append(points_list, MP_OBJ_NEW_SMALL_INT(value)); - if (!mp_obj_get_int_maybe(tuple_items[1], &value)) { - mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); - } - mp_obj_list_append(points_list, MP_OBJ_NEW_SMALL_INT(value)); - } - return points_list; -} -//| from typing import List, Tuple //| //| class Polygon: //| def __init__(self, points: List[ Tuple[ x, y ], ... ] ): @@ -68,12 +33,11 @@ static mp_obj_t vectorio_polygon_make_new(const mp_obj_type_t *type, size_t n_ar if (!MP_OBJ_IS_TYPE(args[ARG_points_list].u_obj, &mp_type_list)) { mp_raise_TypeError_varg(translate("%q list must be a list"), MP_QSTR_point); } - mp_obj_t points_list = _to_points_list(args[ARG_points_list].u_obj); vectorio_polygon_t *self = m_new_obj(vectorio_polygon_t); self->base.type = &vectorio_polygon_type; - common_hal_vectorio_polygon_construct(self, points_list); + common_hal_vectorio_polygon_construct(self, args[ARG_points_list].u_obj); return MP_OBJ_FROM_PTR(self); } @@ -104,9 +68,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(vectorio_polygon_get_points_obj, vectorio_polygon_obj_ STATIC mp_obj_t vectorio_polygon_obj_set_points(mp_obj_t self_in, mp_obj_t points) { vectorio_polygon_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_t points_list = _to_points_list(points); - - common_hal_vectorio_polygon_set_points(self, points_list); + common_hal_vectorio_polygon_set_points(self, points); return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_2(vectorio_polygon_set_points_obj, vectorio_polygon_obj_set_points); diff --git a/shared-module/vectorio/Polygon.c b/shared-module/vectorio/Polygon.c index 6722912c2..7e3a3acfe 100644 --- a/shared-module/vectorio/Polygon.c +++ b/shared-module/vectorio/Polygon.c @@ -4,6 +4,8 @@ #include "shared-module/displayio/area.h" #include "py/runtime.h" +#include "py/gc.h" + #include "stdlib.h" #include @@ -12,10 +14,57 @@ // #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) +// Converts a list of points tuples to a flat list of ints for speedier internal use. +// Also validates the points. +static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple_list) { + size_t len = 0; + mp_obj_t *items; + mp_obj_list_get(points_tuple_list, &len, &items); + VECTORIO_POLYGON_DEBUG("polygon_points_list len: %d\n", len); + + if ( len < 3 ) { + mp_raise_TypeError_varg(translate("Polygon needs at least 3 points")); + } + + if ( self->len < 2*len ) { + if ( self->points_list != NULL ) { + gc_free( self->points_list ); + } + self->points_list = gc_alloc( 2 * len * sizeof(int), false, false ); + } + self->len = 2*len; + + for ( size_t i = 0; i < len; ++i) { + size_t tuple_len = 0; + mp_obj_t *tuple_items; + mp_obj_tuple_get(items[i], &tuple_len, &tuple_items); + + if (tuple_len != 2) { + mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point); + } + if (!mp_obj_get_int_maybe(tuple_items[0], &self->points_list[2*i])) { + self->len = 0; + gc_free( self->points_list ); + self->points_list = NULL; + mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); + } + if (!mp_obj_get_int_maybe(tuple_items[1], &self->points_list[2*i + 1])) { + self->len = 0; + gc_free( self->points_list ); + self->points_list = NULL; + mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); + } + } +} + + + void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t points_list) { VECTORIO_POLYGON_DEBUG("%p polygon_construct\n", self); - self->points_list = points_list; + self->points_list = NULL; + self->len = 0; self->on_dirty.obj = NULL; + _clobber_points_list( self, points_list ); } @@ -23,7 +72,7 @@ mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self) { return self->points_list; } void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list) { - self->points_list = points_list; + _clobber_points_list( self, points_list ); if (self->on_dirty.obj != NULL) { self->on_dirty.event(self->on_dirty.obj); } @@ -38,21 +87,16 @@ void common_hal_vectorio_polygon_set_on_dirty(vectorio_polygon_t *self, vectorio void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *area) { - VECTORIO_POLYGON_DEBUG("%p polygon get_area", polygon); vectorio_polygon_t *self = polygon; - size_t len; - mp_obj_t *points; - mp_obj_list_get(self->points_list, &len, &points); - VECTORIO_POLYGON_DEBUG(" len: %2d, points: %d\n", len, len/2); area->x1 = SHRT_MAX; area->y1 = SHRT_MAX; area->x2 = SHRT_MIN; area->y2 = SHRT_MIN; - for (size_t i=0; i < len; ++i) { - mp_int_t x = mp_obj_get_int(points[i]); + for (size_t i=0; i < self->len; ++i) { + int x = self->points_list[i]; ++i; - mp_int_t y = mp_obj_get_int(points[i]); + int y = self->points_list[i]; if (x <= area->x1) area->x1 = x-1; if (y <= area->y1) area->y1 = y-1; if (x >= area->x2) area->x2 = x+1; @@ -73,22 +117,19 @@ __attribute__((always_inline)) static inline int line_side( mp_int_t x1, mp_int_ uint32_t common_hal_vectorio_polygon_get_pixel(void *obj, int16_t x, int16_t y) { VECTORIO_POLYGON_DEBUG("%p polygon get_pixel %d, %d\n", obj, x, y); vectorio_polygon_t *self = obj; - size_t len; - mp_obj_t *points; - mp_obj_list_get(self->points_list, &len, &points); - if (len == 0) { + if (self->len == 0) { return 0; } int winding_number = 0; - mp_int_t x1 = mp_obj_get_int(points[0]); - mp_int_t y1 = mp_obj_get_int(points[1]); - for (size_t i=2; i <= len + 1; ++i) { + int x1 = self->points_list[0]; + int y1 = self->points_list[1]; + for (size_t i=2; i <= self->len + 1; ++i) { VECTORIO_POLYGON_DEBUG(" {(%3d, %3d),", x1, y1); - mp_int_t x2 = mp_obj_get_int(points[i % len]); + int x2 = self->points_list[i % self->len]; ++i; - mp_int_t y2 = mp_obj_get_int(points[i % len]); + int y2 = self->points_list[i % self->len]; VECTORIO_POLYGON_DEBUG(" (%3d, %3d)}\n", x2, y2); if ( y1 <= y ) { if ( y2 > y && line_side(x1, y1, x2, y2, x, y) > 0 ) { diff --git a/shared-module/vectorio/Polygon.h b/shared-module/vectorio/Polygon.h index 1aef854a7..70de9036d 100644 --- a/shared-module/vectorio/Polygon.h +++ b/shared-module/vectorio/Polygon.h @@ -8,8 +8,9 @@ typedef struct { mp_obj_base_t base; - // A micropython List[ x, y, ... ] - mp_obj_t points_list; + // An int array[ x, y, ... ] + int *points_list; + size_t len; vectorio_event_t on_dirty; } vectorio_polygon_t; -- cgit v1.2.3 From dde66cadde44e478d7b11358f768b1debc0b03c7 Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Thu, 14 May 2020 11:44:36 -0700 Subject: remove a duplication in polygon.c --- shared-bindings/vectorio/Polygon.c | 1 + shared-module/vectorio/Polygon.c | 10 +++------- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'shared-module') diff --git a/shared-bindings/vectorio/Polygon.c b/shared-bindings/vectorio/Polygon.c index e9e90942a..01045d098 100644 --- a/shared-bindings/vectorio/Polygon.c +++ b/shared-bindings/vectorio/Polygon.c @@ -15,6 +15,7 @@ // #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) +//| from typing import List, Tuple //| //| class Polygon: //| def __init__(self, points: List[ Tuple[ x, y ], ... ] ): diff --git a/shared-module/vectorio/Polygon.c b/shared-module/vectorio/Polygon.c index 7e3a3acfe..aadf47911 100644 --- a/shared-module/vectorio/Polygon.c +++ b/shared-module/vectorio/Polygon.c @@ -42,13 +42,9 @@ static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple if (tuple_len != 2) { mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point); } - if (!mp_obj_get_int_maybe(tuple_items[0], &self->points_list[2*i])) { - self->len = 0; - gc_free( self->points_list ); - self->points_list = NULL; - mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); - } - if (!mp_obj_get_int_maybe(tuple_items[1], &self->points_list[2*i + 1])) { + if ( !mp_obj_get_int_maybe(tuple_items[ 0 ], &self->points_list[2*i ]) + || !mp_obj_get_int_maybe(tuple_items[ 1 ], &self->points_list[2*i + 1]) + ) { self->len = 0; gc_free( self->points_list ); self->points_list = NULL; -- cgit v1.2.3 From 007c92ee6ab8da342dacb8967ef999b6d83596ec Mon Sep 17 00:00:00 2001 From: Mark Olsson Date: Mon, 18 May 2020 14:04:55 +0200 Subject: Enable showing the console on a debug uart --- main.c | 3 ++ ports/atmel-samd/common-hal/busio/UART.c | 3 +- ports/cxd56/common-hal/busio/UART.c | 3 +- ports/mimxrt10xx/common-hal/busio/UART.c | 3 +- ports/nrf/common-hal/busio/UART.c | 3 +- ports/stm/boards/nucleo_f746zg/mpconfigboard.h | 3 ++ ports/stm/boards/nucleo_f746zg/pins.c | 5 ++- ports/stm/common-hal/busio/UART.c | 52 ++++++++++++++++++++++---- ports/stm/common-hal/busio/UART.h | 2 + shared-bindings/busio/UART.c | 2 +- shared-bindings/busio/UART.h | 6 ++- shared-module/board/__init__.c | 2 +- supervisor/serial.h | 1 + supervisor/shared/serial.c | 52 ++++++++++++++++++++++++++ supervisor/stub/serial.c | 4 ++ 15 files changed, 128 insertions(+), 16 deletions(-) (limited to 'shared-module') diff --git a/main.c b/main.c index c60cded16..a3bf49fc3 100755 --- a/main.c +++ b/main.c @@ -430,6 +430,9 @@ int __attribute__((used)) main(void) { // displays init after filesystem, since they could share the flash SPI board_init(); + // Start the debug serial + serial_early_init(); + // Reset everything and prep MicroPython to run boot.py. reset_port(); reset_board(); diff --git a/ports/atmel-samd/common-hal/busio/UART.c b/ports/atmel-samd/common-hal/busio/UART.c index 1f6b75f97..30a618cca 100644 --- a/ports/atmel-samd/common-hal/busio/UART.c +++ b/ports/atmel-samd/common-hal/busio/UART.c @@ -56,7 +56,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, - mp_float_t timeout, uint16_t receiver_buffer_size) { + mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, + bool sigint_enabled) { Sercom* sercom = NULL; uint8_t sercom_index = 255; // Unset index diff --git a/ports/cxd56/common-hal/busio/UART.c b/ports/cxd56/common-hal/busio/UART.c index 3bca240e0..e455b0568 100644 --- a/ports/cxd56/common-hal/busio/UART.c +++ b/ports/cxd56/common-hal/busio/UART.c @@ -57,7 +57,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, - mp_float_t timeout, uint16_t receiver_buffer_size) { + mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, + bool sigint_enabled) { struct termios tio; if ((rts != NULL) || (cts != NULL) || (rs485_dir != NULL) || (rs485_invert)) { diff --git a/ports/mimxrt10xx/common-hal/busio/UART.c b/ports/mimxrt10xx/common-hal/busio/UART.c index e3642daf5..fbb45eeea 100644 --- a/ports/mimxrt10xx/common-hal/busio/UART.c +++ b/ports/mimxrt10xx/common-hal/busio/UART.c @@ -76,7 +76,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, - mp_float_t timeout, uint16_t receiver_buffer_size) { + mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, + bool sigint_enabled) { // TODO: Allow none rx or tx diff --git a/ports/nrf/common-hal/busio/UART.c b/ports/nrf/common-hal/busio/UART.c index dbf40ea51..89f3c9f32 100644 --- a/ports/nrf/common-hal/busio/UART.c +++ b/ports/nrf/common-hal/busio/UART.c @@ -134,7 +134,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, - mp_float_t timeout, uint16_t receiver_buffer_size) { + mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, + bool sigint_enabled) { if ((rts != NULL) || (cts != NULL) || (rs485_dir != NULL) || (rs485_invert)) { mp_raise_ValueError(translate("RTS/CTS/RS485 Not yet supported on this device")); diff --git a/ports/stm/boards/nucleo_f746zg/mpconfigboard.h b/ports/stm/boards/nucleo_f746zg/mpconfigboard.h index 1d8614919..b1abd140f 100644 --- a/ports/stm/boards/nucleo_f746zg/mpconfigboard.h +++ b/ports/stm/boards/nucleo_f746zg/mpconfigboard.h @@ -34,3 +34,6 @@ #define FLASH_PAGE_SIZE (0x4000) #define BOARD_OSC_DIV (8) + +#define DEBUG_UART_TX (&pin_PD08) +#define DEBUG_UART_RX (&pin_PD09) diff --git a/ports/stm/boards/nucleo_f746zg/pins.c b/ports/stm/boards/nucleo_f746zg/pins.c index 56b6a745f..7c6a075e2 100644 --- a/ports/stm/boards/nucleo_f746zg/pins.c +++ b/ports/stm/boards/nucleo_f746zg/pins.c @@ -58,8 +58,9 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_USB_ID), MP_ROM_PTR(&pin_PA10) }, { MP_ROM_QSTR(MP_QSTR_USB_DM), MP_ROM_PTR(&pin_PA11) }, { MP_ROM_QSTR(MP_QSTR_USB_DP), MP_ROM_PTR(&pin_PA12) }, -{ MP_ROM_QSTR(MP_QSTR_VCP_TX), MP_ROM_PTR(&pin_PD08) }, -{ MP_ROM_QSTR(MP_QSTR_VCP_RX), MP_ROM_PTR(&pin_PD09) }, +// As we use these for the debug_console, we won't enable them here. +// { MP_ROM_QSTR(MP_QSTR_VCP_TX), MP_ROM_PTR(&pin_PD08) }, +// { MP_ROM_QSTR(MP_QSTR_VCP_RX), MP_ROM_PTR(&pin_PD09) }, { MP_ROM_QSTR(MP_QSTR_UART2_TX), MP_ROM_PTR(&pin_PD05) }, { MP_ROM_QSTR(MP_QSTR_UART2_RX), MP_ROM_PTR(&pin_PD06) }, { MP_ROM_QSTR(MP_QSTR_UART2_RTS), MP_ROM_PTR(&pin_PD04) }, diff --git a/ports/stm/common-hal/busio/UART.c b/ports/stm/common-hal/busio/UART.c index 54ac68d73..c83479126 100644 --- a/ports/stm/common-hal/busio/UART.c +++ b/ports/stm/common-hal/busio/UART.c @@ -28,6 +28,7 @@ #include "shared-bindings/busio/UART.h" #include "mpconfigport.h" +#include "lib/mp-readline/readline.h" #include "lib/utils/interrupt_char.h" #include "py/gc.h" #include "py/mperrno.h" @@ -39,6 +40,7 @@ //arrays use 0 based numbering: UART1 is stored at index 0 STATIC bool reserved_uart[MAX_UART]; +STATIC bool never_reset_uart[MAX_UART]; int errflag; //Used to restart read halts STATIC void uart_clock_enable(uint16_t mask); @@ -61,11 +63,16 @@ STATIC USART_TypeDef * assign_uart_or_throw(busio_uart_obj_t* self, bool pin_eva } void uart_reset(void) { + uint16_t never_reset_mask = 0x00; for (uint8_t i = 0; i < MAX_UART; i++) { - reserved_uart[i] = false; - MP_STATE_PORT(cpy_uart_obj_all)[i] = NULL; + if (!never_reset_uart[i]) { + reserved_uart[i] = false; + MP_STATE_PORT(cpy_uart_obj_all)[i] = NULL; + } else { + never_reset_mask |= 1 << i; + } } - uart_clock_disable(ALL_UARTS); + uart_clock_disable(ALL_UARTS & ~(never_reset_mask)); } void common_hal_busio_uart_construct(busio_uart_obj_t *self, @@ -73,7 +80,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, - mp_float_t timeout, uint16_t receiver_buffer_size) { + mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, + bool sigint_enabled) { //match pins to UART objects USART_TypeDef * USARTx; @@ -209,8 +217,12 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, // Init buffer for rx and claim pins if (self->rx != NULL) { - if (!ringbuf_alloc(&self->ringbuf, receiver_buffer_size, true)) { - mp_raise_ValueError(translate("UART Buffer allocation error")); + if (receiver_buffer != NULL) { + self->ringbuf = (ringbuf_t){ receiver_buffer, receiver_buffer_size }; + } else { + if (!ringbuf_alloc(&self->ringbuf, receiver_buffer_size, true)) { + mp_raise_ValueError(translate("UART Buffer allocation error")); + } } claim_pin(rx); } @@ -219,6 +231,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, } self->baudrate = baudrate; self->timeout_ms = timeout * 1000; + self->sigint_enabled = sigint_enabled; //start the interrupt series if ((HAL_UART_GetState(&self->handle) & HAL_UART_STATE_BUSY_RX) == HAL_UART_STATE_BUSY_RX) { @@ -234,6 +247,17 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, errflag = HAL_OK; } +void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) { + for (size_t i = 0; i < MP_ARRAY_SIZE(mcu_uart_banks); i++) { + if (mcu_uart_banks[i] == self->handle.Instance) { + never_reset_uart[i] = true; + never_reset_pin_number(self->tx->pin->port, self->tx->pin->number); + never_reset_pin_number(self->rx->pin->port, self->rx->pin->number); + break; + } + } +} + bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { return self->tx->pin == NULL; } @@ -241,6 +265,13 @@ bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { if (common_hal_busio_uart_deinited(self)) return; + for (size_t i = 0; i < MP_ARRAY_SIZE(mcu_uart_banks); i++) { + if (mcu_uart_banks[i] == self->handle.Instance) { + never_reset_uart[i] = false; + break; + } + } + reset_pin_number(self->tx->pin->port,self->tx->pin->number); reset_pin_number(self->rx->pin->port,self->rx->pin->number); self->tx = NULL; @@ -289,7 +320,8 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, bool write_err = false; //write error shouldn't disable interrupts HAL_NVIC_DisableIRQ(self->irq); - if (HAL_UART_Transmit(&self->handle, (uint8_t*)data, len, HAL_MAX_DELAY) != HAL_OK) { + HAL_StatusTypeDef ret = HAL_UART_Transmit(&self->handle, (uint8_t*)data, len, HAL_MAX_DELAY); + if (ret != HAL_OK) { write_err = true; } HAL_UART_Receive_IT(&self->handle, &self->rx_char, 1); @@ -313,6 +345,12 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *handle) } ringbuf_put_n(&context->ringbuf, &context->rx_char, 1); errflag = HAL_UART_Receive_IT(handle, &context->rx_char, 1); + if (context->sigint_enabled) { + if (context->rx_char == CHAR_CTRL_C) { + common_hal_busio_uart_clear_rx_buffer(context); + mp_keyboard_interrupt(); + } + } return; } diff --git a/ports/stm/common-hal/busio/UART.h b/ports/stm/common-hal/busio/UART.h index d1971ce27..a6a69a592 100644 --- a/ports/stm/common-hal/busio/UART.h +++ b/ports/stm/common-hal/busio/UART.h @@ -52,6 +52,8 @@ typedef struct { uint32_t baudrate; uint32_t timeout_ms; + + bool sigint_enabled; } busio_uart_obj_t; void uart_reset(void); diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index f231924d5..a448abb78 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -140,7 +140,7 @@ STATIC mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, co common_hal_busio_uart_construct(self, tx, rx, rts, cts, rs485_dir, rs485_invert, args[ARG_baudrate].u_int, bits, parity, stop, timeout, - args[ARG_receiver_buffer_size].u_int); + args[ARG_receiver_buffer_size].u_int, NULL, false); return (mp_obj_t)self; } diff --git a/shared-bindings/busio/UART.h b/shared-bindings/busio/UART.h index fe71e8668..3aed4e534 100644 --- a/shared-bindings/busio/UART.h +++ b/shared-bindings/busio/UART.h @@ -29,6 +29,7 @@ #include "common-hal/microcontroller/Pin.h" #include "common-hal/busio/UART.h" +#include "py/ringbuf.h" extern const mp_obj_type_t busio_uart_type; @@ -44,7 +45,8 @@ extern void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts, const mcu_pin_obj_t * rs485_dir, bool rs485_invert, uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop, - mp_float_t timeout, uint16_t receiver_buffer_size); + mp_float_t timeout, uint16_t receiver_buffer_size, byte* receiver_buffer, + bool sigint_enabled); extern void common_hal_busio_uart_deinit(busio_uart_obj_t *self); extern bool common_hal_busio_uart_deinited(busio_uart_obj_t *self); @@ -66,4 +68,6 @@ extern uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t * extern void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self); extern bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self); +extern void common_hal_busio_uart_never_reset(busio_uart_obj_t *self); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_UART_H diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index 789318a05..0fbf916cd 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -123,7 +123,7 @@ mp_obj_t common_hal_board_create_uart(void) { #endif common_hal_busio_uart_construct(self, tx, rx, rts, cts, rs485_dir, rs485_invert, - 9600, 8, PARITY_NONE, 1, 1.0f, 64); + 9600, 8, PARITY_NONE, 1, 1.0f, 64, NULL, false); MP_STATE_VM(shared_uart_bus) = MP_OBJ_FROM_PTR(self); return MP_STATE_VM(shared_uart_bus); } diff --git a/supervisor/serial.h b/supervisor/serial.h index 84b3062a3..ef88ad346 100644 --- a/supervisor/serial.h +++ b/supervisor/serial.h @@ -38,6 +38,7 @@ FIL* boot_output_file; #endif +void serial_early_init(void); void serial_init(void); void serial_write(const char* text); // Only writes up to given length. Does not check for null termination at all. diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index a61899576..513022667 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -32,26 +32,73 @@ #include "shared-bindings/terminalio/Terminal.h" #include "supervisor/serial.h" #include "supervisor/usb.h" +#include "shared-bindings/microcontroller/Pin.h" #include "tusb.h" +/* + * Note: DEBUG_UART currently only works on STM32, + * enabling on another platform will cause a crash. + */ + +#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) +#include "shared-bindings/busio/UART.h" +busio_uart_obj_t debug_uart; +byte buf_array[64]; +#endif + +void serial_early_init(void) { +#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) + debug_uart.base.type = &busio_uart_type; + + const mcu_pin_obj_t* rx = MP_OBJ_TO_PTR(DEBUG_UART_RX); + const mcu_pin_obj_t* tx = MP_OBJ_TO_PTR(DEBUG_UART_TX); + + common_hal_busio_uart_construct(&debug_uart, tx, rx, NULL, NULL, NULL, + false, 115200, 8, PARITY_NONE, 1, 1.0f, 64, + buf_array, true); + common_hal_busio_uart_never_reset(&debug_uart); +#endif +} + void serial_init(void) { usb_init(); } bool serial_connected(void) { +#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) + return true; +#else return tud_cdc_connected(); +#endif } char serial_read(void) { +#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) + if (tud_cdc_connected() && tud_cdc_available() > 0) { + return (char) tud_cdc_read_char(); + } + int uart_errcode; + char text; + common_hal_busio_uart_read(&debug_uart, (uint8_t*) &text, 1, &uart_errcode); + return text; +#else return (char) tud_cdc_read_char(); +#endif } bool serial_bytes_available(void) { +#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) + return common_hal_busio_uart_rx_characters_available(&debug_uart) || (tud_cdc_available() > 0); +#else return tud_cdc_available() > 0; +#endif } void serial_write_substring(const char* text, uint32_t length) { + if (length == 0) { + return; + } #if CIRCUITPY_DISPLAYIO int errcode; common_hal_terminalio_terminal_write(&supervisor_terminal, (const uint8_t*) text, length, &errcode); @@ -62,6 +109,11 @@ void serial_write_substring(const char* text, uint32_t length) { count += tud_cdc_write(text + count, length - count); usb_background(); } + +#if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) + int uart_errcode; + common_hal_busio_uart_write(&debug_uart, (const uint8_t*) text, length, &uart_errcode); +#endif } void serial_write(const char* text) { diff --git a/supervisor/stub/serial.c b/supervisor/stub/serial.c index 956551914..34b433e53 100644 --- a/supervisor/stub/serial.c +++ b/supervisor/stub/serial.c @@ -26,6 +26,10 @@ #include "supervisor/serial.h" +void serial_early_init(void) { + +} + void serial_init(void) { } -- cgit v1.2.3