improve transpose and mirror

* add heuristic to avoid drawing area unnecessarily
* fix Polygon.points
* fix transpose
* fix mirror x and y

Known broken:
Polygon with negative Y coordinates does not work right.
This commit is contained in:
Kenny 2021-08-07 17:47:57 -07:00
parent 6be952d3ba
commit b5837b157d
3 changed files with 185 additions and 96 deletions

View File

@ -10,7 +10,7 @@
#define VECTORIO_POLYGON_DEBUG(...) (void)0
// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)
// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__)
// Converts a list of points tuples to a flat list of ints for speedier internal use.
@ -30,12 +30,12 @@ static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple
VECTORIO_POLYGON_DEBUG("free(%d), ", sizeof(self->points_list));
gc_free(self->points_list);
}
self->points_list = gc_alloc(2 * len * sizeof(int), false, false);
VECTORIO_POLYGON_DEBUG("alloc(%p, %d)", self->points_list, 2 * len * sizeof(int));
self->points_list = gc_alloc(2 * len * sizeof(uint16_t), false, false);
VECTORIO_POLYGON_DEBUG("alloc(%p, %d)", self->points_list, 2 * len * sizeof(uint16_t));
}
self->len = 2 * len;
for (size_t i = 0; i < len; ++i) {
for (uint16_t i = 0; i < len; ++i) {
size_t tuple_len = 0;
mp_obj_t *tuple_items;
mp_obj_tuple_get(items[i], &tuple_len, &tuple_items);
@ -43,14 +43,19 @@ static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple
if (tuple_len != 2) {
mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point);
}
if (!mp_obj_get_int_maybe(tuple_items[ 0 ], &self->points_list[2 * i ])
|| !mp_obj_get_int_maybe(tuple_items[ 1 ], &self->points_list[2 * i + 1])
mp_int_t x;
mp_int_t y;
if (!mp_obj_get_int_maybe(tuple_items[ 0 ], &x)
|| !mp_obj_get_int_maybe(tuple_items[ 1 ], &y)
|| x < SHRT_MIN || x > SHRT_MAX || y < SHRT_MIN || y > SHRT_MAX
) {
self->len = 0;
gc_free(self->points_list);
self->points_list = NULL;
mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point);
self->len = 0;
}
self->points_list[2 * i ] = (int16_t)x;
self->points_list[2 * i + 1] = (int16_t)y;
}
}
@ -68,16 +73,23 @@ void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t po
mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self) {
VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_get_points {len: %d, points_list: %p}\n", self, self->len, self->points_list);
mp_obj_t list = mp_obj_new_list(self->len / 2, NULL);
mp_obj_list_t *list = MP_OBJ_TO_PTR(mp_obj_new_list(0, NULL));
VECTORIO_POLYGON_DEBUG(" >points\n");
for (uint16_t i = 0; i < self->len; i += 2) {
VECTORIO_POLYGON_DEBUG(" (%4d, %4d)\n", self->points_list[i], self->points_list[i + 1]);
mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
pair->items[0] = mp_obj_new_int((mp_int_t) self->points_list[i ]);
pair->items[1] = mp_obj_new_int((mp_int_t) self->points_list[i + 1]);
for (size_t i = 0; i < self->len; i += 2) {
mp_obj_t tuple[] = { mp_obj_new_int(self->points_list[i]), mp_obj_new_int(self->points_list[i + 1]) };
mp_obj_list_append(
list,
mp_obj_new_tuple(2, tuple)
pair
);
}
return list;
VECTORIO_POLYGON_DEBUG(" <points\n");
return MP_OBJ_FROM_PTR(list);
}
void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list) {
VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_set_points: ", self);
@ -98,25 +110,30 @@ 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_t *self = polygon;
VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_get_area\n");
area->x1 = SHRT_MAX;
area->y1 = SHRT_MAX;
area->x2 = SHRT_MIN;
area->y2 = SHRT_MIN;
for (size_t i = 0; i < self->len; ++i) {
int x = self->points_list[i];
for (uint16_t i = 0; i < self->len; ++i) {
int16_t x = self->points_list[i];
++i;
int y = self->points_list[i];
int16_t y = self->points_list[i];
if (x < area->x1) {
VECTORIO_POLYGON_DEBUG(" x1: %d\n", x);
area->x1 = x;
}
if (y < area->y1) {
VECTORIO_POLYGON_DEBUG(" y1: %d\n", y);
area->y1 = y;
}
if (x > area->x2) {
VECTORIO_POLYGON_DEBUG(" x2: %d\n", x);
area->x2 = x;
}
if (y > area->y2) {
VECTORIO_POLYGON_DEBUG(" y2: %d\n", y);
area->y2 = y;
}
}
@ -126,7 +143,7 @@ void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *area)
// <0 if the point is to the left of the line vector
// 0 if the point is on the line
// >0 if the point is to the right of the line vector
__attribute__((always_inline)) static inline int line_side(mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, int16_t px, int16_t py) {
__attribute__((always_inline)) static inline int line_side(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t px, int16_t py) {
return (px - x1) * (y2 - y1)
- (py - y1) * (x2 - x1);
}
@ -140,14 +157,14 @@ uint32_t common_hal_vectorio_polygon_get_pixel(void *obj, int16_t x, int16_t y)
return 0;
}
int winding_number = 0;
int x1 = self->points_list[0];
int y1 = self->points_list[1];
for (size_t i = 2; i <= self->len + 1; ++i) {
int16_t winding_number = 0;
int16_t x1 = self->points_list[0];
int16_t y1 = self->points_list[1];
for (uint16_t i = 2; i <= self->len + 1; ++i) {
VECTORIO_POLYGON_DEBUG(" {(%3d, %3d),", x1, y1);
int x2 = self->points_list[i % self->len];
int16_t x2 = self->points_list[i % self->len];
++i;
int y2 = self->points_list[i % self->len];
int16_t y2 = self->points_list[i % self->len];
VECTORIO_POLYGON_DEBUG(" (%3d, %3d)}\n", x2, y2);
if (y1 <= y) {
if (y2 > y && line_side(x1, y1, x2, y2, x, y) < 0) {

View File

@ -9,8 +9,8 @@
typedef struct {
mp_obj_base_t base;
// An int array[ x, y, ... ]
int *points_list;
size_t len;
int16_t *points_list;
uint16_t len;
vectorio_event_t on_dirty;
mp_obj_t draw_protocol_instance;
} vectorio_polygon_t;

View File

@ -14,8 +14,8 @@
#include "shared-bindings/vectorio/Rectangle.h"
// Lifecycle actions.
#define VECTORIO_SHAPE_DEBUG(...) (void)0
// #define VECTORIO_SHAPE_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__)
// #define VECTORIO_SHAPE_DEBUG(...) (void)0
#define VECTORIO_SHAPE_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__)
// Used in both logging and ifdefs, for extra variables
@ -23,8 +23,43 @@
// Really verbose.
#define VECTORIO_SHAPE_PIXEL_DEBUG(...) (void)0
// #define VECTORIO_SHAPE_PIXEL_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__)
// #define VECTORIO_SHAPE_PIXEL_DEBUG(...) (void)0
#define VECTORIO_SHAPE_PIXEL_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__)
#define U32_TO_BINARY_FMT "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"
#define U32_TO_BINARY(u32) \
(u32 & 0x80000000 ? '1' : '0'), \
(u32 & 0x40000000 ? '1' : '0'), \
(u32 & 0x20000000 ? '1' : '0'), \
(u32 & 0x10000000 ? '1' : '0'), \
(u32 & 0x8000000 ? '1' : '0'), \
(u32 & 0x4000000 ? '1' : '0'), \
(u32 & 0x2000000 ? '1' : '0'), \
(u32 & 0x1000000 ? '1' : '0'), \
(u32 & 0x800000 ? '1' : '0'), \
(u32 & 0x400000 ? '1' : '0'), \
(u32 & 0x200000 ? '1' : '0'), \
(u32 & 0x100000 ? '1' : '0'), \
(u32 & 0x80000 ? '1' : '0'), \
(u32 & 0x40000 ? '1' : '0'), \
(u32 & 0x20000 ? '1' : '0'), \
(u32 & 0x10000 ? '1' : '0'), \
(u32 & 0x8000 ? '1' : '0'), \
(u32 & 0x4000 ? '1' : '0'), \
(u32 & 0x2000 ? '1' : '0'), \
(u32 & 0x1000 ? '1' : '0'), \
(u32 & 0x800 ? '1' : '0'), \
(u32 & 0x400 ? '1' : '0'), \
(u32 & 0x200 ? '1' : '0'), \
(u32 & 0x100 ? '1' : '0'), \
(u32 & 0x80 ? '1' : '0'), \
(u32 & 0x40 ? '1' : '0'), \
(u32 & 0x20 ? '1' : '0'), \
(u32 & 0x10 ? '1' : '0'), \
(u32 & 0x8 ? '1' : '0'), \
(u32 & 0x4 ? '1' : '0'), \
(u32 & 0x2 ? '1' : '0'), \
(u32 & 0x1 ? '1' : '0')
inline __attribute__((always_inline))
@ -33,39 +68,41 @@ static int32_t max(int32_t a, int32_t b) {
}
inline __attribute__((always_inline))
static int32_t min(int32_t a, int32_t b) {
static uint32_t min(uint32_t a, uint32_t b) {
return a < b ? a : b;
}
inline __attribute__((always_inline))
static void area_transpose(displayio_area_t *to_transpose) {
int16_t swap = to_transpose->y1;
to_transpose->y1 = to_transpose->x1;
to_transpose->x1 = swap;
swap = to_transpose->y2;
to_transpose->y2 = to_transpose->x2;
to_transpose->x2 = swap;
}
inline __attribute__((always_inline))
static void _get_screen_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) {
VECTORIO_SHAPE_DEBUG("%p get_screen_area tform:{x:%d y:%d dx:%d dy:%d scl:%d w:%d h:%d mx:%d my:%d tr:%d}", self,
VECTORIO_SHAPE_DEBUG("%p get_screen_area (%3d,%3d) tform:{x:%d y:%d dx:%d dy:%d scl:%d w:%d h:%d mx:%d my:%d tr:%d}", self, self->x, self->y,
self->absolute_transform->x, self->absolute_transform->y, self->absolute_transform->dx, self->absolute_transform->dy, self->absolute_transform->scale,
self->absolute_transform->width, self->absolute_transform->height, self->absolute_transform->mirror_x, self->absolute_transform->mirror_y, self->absolute_transform->transpose_xy
);
displayio_area_t shape_area;
self->ishape.get_area(self->ishape.shape, &shape_area);
VECTORIO_SHAPE_DEBUG(" in:{(%5d,%5d), (%5d,%5d)}", shape_area.x1, shape_area.y1, shape_area.x2, shape_area.y2);
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);
displayio_area_shift(
&shape_area,
self->x * self->absolute_transform->dx + min(0, self->absolute_transform->dx * displayio_area_width(&shape_area)),
self->y * self->absolute_transform->dy + min(0, self->absolute_transform->dy * displayio_area_height(&shape_area))
);
displayio_area_transform_within(
false,
false,
self->absolute_transform->transpose_xy,
&shape_area, &shape_area, out_area
);
displayio_area_shift(
out_area,
self->absolute_transform->x,
self->absolute_transform->y
);
int16_t x;
int16_t y;
if (self->absolute_transform->transpose_xy) {
x = self->absolute_transform->x + self->absolute_transform->dx * self->y;
y = self->absolute_transform->y + self->absolute_transform->dy * self->x;
area_transpose(out_area);
displayio_area_canon(out_area);
} else {
x = self->absolute_transform->x + self->absolute_transform->dx * self->x;
y = self->absolute_transform->y + self->absolute_transform->dy * self->y;
}
displayio_area_shift(out_area, x, y);
VECTORIO_SHAPE_DEBUG(" out:{(%5d,%5d), (%5d,%5d)}\n", out_area->x1, out_area->y1, out_area->x2, out_area->y2);
}
@ -198,43 +235,34 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ
self->absolute_transform->width, self->absolute_transform->height, self->absolute_transform->mirror_x, self->absolute_transform->mirror_y, self->absolute_transform->transpose_xy
);
uint32_t linestride_px = displayio_area_width(area);
uint32_t line_dirty_offset_px = (overlap.y1 - area->y1) * linestride_px;
uint32_t column_dirty_offset_px = overlap.x1 - area->x1;
uint16_t linestride_px = displayio_area_width(area);
uint16_t line_dirty_offset_px = (overlap.y1 - area->y1) * linestride_px;
uint16_t column_dirty_offset_px = overlap.x1 - area->x1;
VECTORIO_SHAPE_DEBUG(", linestride:%3d line_offset:%3d col_offset:%3d depth:%2d ppb:%2d shape:%s",
linestride_px, line_dirty_offset_px, column_dirty_offset_px, colorspace->depth, pixels_per_byte, mp_obj_get_type_str(self->ishape.shape));
displayio_input_pixel_t input_pixel;
displayio_output_pixel_t output_pixel;
int16_t math_transform_offset_x;
int16_t math_transform_offset_y;
int16_t math_shape_offset_x;
int16_t math_shape_offset_y;
uint16_t width_px_indices;
uint16_t height_px_indices;
if (self->absolute_transform->transpose_xy) {
math_transform_offset_x = self->absolute_transform->dy * self->y;
math_transform_offset_y = self->absolute_transform->dx * self->x;
math_shape_offset_x = min(0, self->absolute_transform->dy * displayio_area_width(&self->current_area));
math_shape_offset_y = min(0, self->absolute_transform->dx * displayio_area_height(&self->current_area));
width_px_indices = displayio_area_height(&self->current_area) - 1;
height_px_indices = displayio_area_width(&self->current_area) - 1;
} else {
math_transform_offset_x = self->absolute_transform->dx * self->x;
math_transform_offset_y = self->absolute_transform->dy * self->y;
math_shape_offset_x = min(0, self->absolute_transform->dx * displayio_area_width(&self->current_area));
math_shape_offset_y = min(0, self->absolute_transform->dy * displayio_area_height(&self->current_area));
width_px_indices = displayio_area_width(&self->current_area) - 1;
height_px_indices = displayio_area_height(&self->current_area) - 1;
}
VECTORIO_SHAPE_DEBUG(", transform_offset: (%3d,%3d), shape_offset: (%3d,%3d)", math_transform_offset_x, math_transform_offset_y, math_shape_offset_x, math_shape_offset_y);
uint32_t mask_start_px = line_dirty_offset_px;
uint16_t mask_start_px = line_dirty_offset_px;
for (input_pixel.y = overlap.y1; input_pixel.y < overlap.y2; ++input_pixel.y) {
mask_start_px += column_dirty_offset_px;
for (input_pixel.x = overlap.x1; input_pixel.x < overlap.x2; ++input_pixel.x) {
// Check the mask first to see if the pixel has already been set.
uint32_t pixel_index = mask_start_px + (input_pixel.x - overlap.x1);
uint16_t pixel_index = mask_start_px + (input_pixel.x - overlap.x1);
uint32_t *mask_doubleword = &(mask[pixel_index / 32]);
uint8_t mask_bit = pixel_index % 32;
VECTORIO_SHAPE_PIXEL_DEBUG("\n%p pixel_index: %5u mask_bit: %2u", self, pixel_index, mask_bit);
VECTORIO_SHAPE_PIXEL_DEBUG("\n%p pixel_index: %5u mask_bit: %2u mask: "U32_TO_BINARY_FMT, self, pixel_index, mask_bit, U32_TO_BINARY(*mask_doubleword));
if ((*mask_doubleword & (1u << mask_bit)) != 0) {
VECTORIO_SHAPE_PIXEL_DEBUG(" masked");
continue;
@ -245,12 +273,27 @@ 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 - math_transform_offset_y - self->absolute_transform->y) - math_shape_offset_y;
pixel_to_get_y = (input_pixel.x - math_transform_offset_x - self->absolute_transform->x) - math_shape_offset_x;
pixel_to_get_x = input_pixel.y - self->absolute_transform->y - self->absolute_transform->dy * self->x;
pixel_to_get_y = input_pixel.x - self->absolute_transform->x - self->absolute_transform->dx * self->y;
if (self->absolute_transform->mirror_x) {
pixel_to_get_y = height_px_indices - pixel_to_get_y;
}
if (self->absolute_transform->mirror_y) {
pixel_to_get_x = width_px_indices - pixel_to_get_x;
}
} else {
pixel_to_get_x = (input_pixel.x - math_transform_offset_x - self->absolute_transform->x) - math_shape_offset_x;
pixel_to_get_y = (input_pixel.y - math_transform_offset_y - self->absolute_transform->y) - math_shape_offset_y;
pixel_to_get_x = input_pixel.x - self->absolute_transform->x - self->absolute_transform->dx * self->x;
pixel_to_get_y = input_pixel.y - self->absolute_transform->y - self->absolute_transform->dy * self->y;
if (self->absolute_transform->mirror_x) {
pixel_to_get_x = width_px_indices - pixel_to_get_x;
}
if (self->absolute_transform->mirror_y) {
pixel_to_get_y = height_px_indices - pixel_to_get_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);
#ifdef VECTORIO_PERF
uint64_t pre_pixel = common_hal_time_monotonic_ns();
@ -296,10 +339,9 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ
} else if (colorspace->depth < 8) {
// Reorder the offsets to pack multiple rows into a byte (meaning they share a column).
if (!colorspace->pixels_in_byte_share_row) {
uint16_t width = linestride_px;
uint16_t row = pixel_index / width;
uint16_t col = pixel_index % width;
pixel_index = col * pixels_per_byte + (row / pixels_per_byte) * pixels_per_byte * width + row % pixels_per_byte;
uint16_t row = pixel_index / linestride_px;
uint16_t col = pixel_index % linestride_px;
pixel_index = col * pixels_per_byte + (row / pixels_per_byte) * pixels_per_byte * linestride_px + row % pixels_per_byte;
}
uint8_t shift = (pixel_index % pixels_per_byte) * colorspace->depth;
if (colorspace->reverse_pixels_in_byte) {
@ -354,27 +396,57 @@ void vectorio_vector_shape_finish_refresh(vectorio_vector_shape_t *self) {
// Assembles a singly linked list of dirty areas from all components on the display.
displayio_area_t *vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_t *self, displayio_area_t *tail) {
displayio_area_t *new_tail = tail;
if (!displayio_area_empty(&self->ephemeral_dirty_area)) {
// vector.add_to_head
self->ephemeral_dirty_area.next = tail;
new_tail = &self->ephemeral_dirty_area;
VECTORIO_SHAPE_DEBUG("%p get_refresh_area dirty: {(%3d,%3d), (%3d,%3d)}", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2);
}
if (self->current_area_dirty
|| (mp_obj_is_type(self->pixel_shader, &displayio_palette_type) && displayio_palette_needs_refresh(self->pixel_shader))
|| (mp_obj_is_type(self->pixel_shader, &displayio_colorconverter_type) && displayio_colorconverter_needs_refresh(self->pixel_shader))
) {
self->current_area.next = new_tail;
new_tail = &self->current_area;
VECTORIO_SHAPE_DEBUG(" redrawing current: {(%3d,%3d), (%3d,%3d)}", self->current_area.x1, self->current_area.y1, self->current_area.x2, self->current_area.y2);
if (!displayio_area_empty(&self->ephemeral_dirty_area)) {
// Both are dirty, check if we should combine the areas or draw separately
// Draws as few pixels as possible both when animations move short distances and large distances.
// The display core implementation currently doesn't combine areas to reduce redrawing of masked areas. If it does,
// this could be simplified to just return the 2 possibly overlapping areas.
displayio_area_t area_swap;
displayio_area_compute_overlap(&self->ephemeral_dirty_area, &self->current_area, &area_swap);
uint32_t overlap_size = displayio_area_size(&area_swap);
displayio_area_union(&self->ephemeral_dirty_area, &self->current_area, &area_swap); // Leave area_swap as the union area for later.
uint32_t union_size = displayio_area_size(&area_swap);
uint32_t current_size = displayio_area_size(&self->current_area);
uint32_t dirty_size = displayio_area_size(&self->ephemeral_dirty_area);
VECTORIO_SHAPE_DEBUG("%p get_refresh_area: dirty{(%3d,%3d), (%3d,%3d)} + current{(%3d,%3d), (%3d,%3d)} = union{(%3d,%3d), (%3d,%3d)}: union%d - dirty%d - curr%d + overlap%d = excluded%d : ", self,
self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2,
self->current_area.x1, self->current_area.y1, self->current_area.x2, self->current_area.y2,
area_swap.x1, area_swap.y1, area_swap.x2, area_swap.y2,
union_size, dirty_size, current_size, overlap_size, (int32_t)union_size - dirty_size - current_size + overlap_size
);
if ((int32_t)union_size - dirty_size - current_size + overlap_size <= min(dirty_size, current_size)) {
// The excluded / non-overlapping area from the disjoint dirty and current areas is smaller
// than the smallest area we need to draw. Redrawing the overlapping area would cost more
// than just drawing the union disjoint area once.
VECTORIO_SHAPE_DEBUG("combining to take disjoint area\n");
displayio_area_copy(&area_swap, &self->ephemeral_dirty_area);
} else {
// The excluded area between the 2 dirty areas is larger than the smallest dirty area. It would be
// more costly to combine these areas than possibly redraw some overlap.
VECTORIO_SHAPE_DEBUG("excluded area too large, drawing separate area\n");
self->current_area.next = tail;
tail = &self->current_area;
}
self->ephemeral_dirty_area.next = tail;
tail = &self->ephemeral_dirty_area;
} else {
self->current_area.next = tail;
tail = &self->current_area;
VECTORIO_SHAPE_DEBUG("%p get_refresh_area: redrawing current: {(%3d,%3d), (%3d,%3d)}\n", self, self->current_area.x1, self->current_area.y1, self->current_area.x2, self->current_area.y2);
}
} else if (!displayio_area_empty(&self->ephemeral_dirty_area)) {
self->ephemeral_dirty_area.next = tail;
tail = &self->ephemeral_dirty_area;
VECTORIO_SHAPE_DEBUG("%p get_refresh_area redrawing dirty: {(%3d,%3d), (%3d,%3d)}\n", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2);
}
#ifdef VECTORIO_SHAPE_DEBUG
if (new_tail != tail) {
VECTORIO_SHAPE_DEBUG("\n");
}
#endif
return new_tail;
return tail;
}
void vectorio_vector_shape_update_transform(vectorio_vector_shape_t *self, displayio_buffer_transform_t *group_transform) {