Skip to content

Add function for drawing polygons to bitmaptools #7471

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions locale/circuitpython.pot
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,14 @@ msgid ""
"connection."
msgstr ""

#: shared-bindings/bitmaptools/__init__.c
msgid "Coordinate arrays have different lengths"
msgstr ""

#: shared-bindings/bitmaptools/__init__.c
msgid "Coordinate arrays types have different sizes"
msgstr ""

#: py/persistentcode.c
msgid "Corrupt .mpy file"
msgstr ""
Expand Down
96 changes: 96 additions & 0 deletions shared-bindings/bitmaptools/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,101 @@ STATIC mp_obj_t bitmaptools_obj_draw_line(size_t n_args, const mp_obj_t *pos_arg
MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_line_obj, 0, bitmaptools_obj_draw_line);
// requires all 6 arguments

//| def draw_polygon(
//| dest_bitmap: displayio.Bitmap,
//| xs: ReadableBuffer,
//| ys: ReadableBuffer,
//| value: int,
//| close: Optional[bool] = True,
//| ) -> None:
//| """Draw a polygon conecting points on provided bitmap with provided value
//|
//| :param bitmap dest_bitmap: Destination bitmap that will be written into
//| :param ReadableBuffer xs: x-pixel position of the polygon's vertices
//| :param ReadableBuffer ys: y-pixel position of the polygon's vertices
//| :param int value: Bitmap palette index that will be written into the
//| line in the destination bitmap
//| :param bool close: (Optional) Wether to connect first and last point. (True)
//|
//| .. code-block:: Python
//|
//| import board
//| import displayio
//| import bitmaptools
//|
//| display = board.DISPLAY
//| main_group = displayio.Group()
//| display.root_group = main_group
//|
//| palette = displayio.Palette(3)
//| palette[0] = 0xffffff
//| palette[1] = 0x0000ff
//| palette[2] = 0xff0000
//|
//| bmp = displayio.Bitmap(128,128, 3)
//| bmp.fill(0)
//|
//| xs = bytes([4, 101, 101, 19])
//| ys = bytes([4, 19, 121, 101])
//| bitmaptools.draw_polygon(bmp, xs, ys, 1)
//|
//| xs = bytes([14, 60, 110])
//| ys = bytes([14, 24, 90])
//| bitmaptools.draw_polygon(bmp, xs, ys, 2)
//|
//| tilegrid = displayio.TileGrid(bitmap=bmp, pixel_shader=palette)
//| main_group.append(tilegrid)
//|
//| while True:
//| pass
//| """
//| ...
//|
STATIC mp_obj_t bitmaptools_obj_draw_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum {ARG_dest_bitmap, ARG_xs, ARG_ys, ARG_value, ARG_close};

static const mp_arg_t allowed_args[] = {
{MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
{MP_QSTR_xs, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
{MP_QSTR_ys, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
{MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}},
{MP_QSTR_close, MP_ARG_BOOL, {.u_bool = true}},
};
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);

displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap

mp_buffer_info_t xs_buf, ys_buf;
mp_get_buffer_raise(args[ARG_xs].u_obj, &xs_buf, MP_BUFFER_READ);
mp_get_buffer_raise(args[ARG_ys].u_obj, &ys_buf, MP_BUFFER_READ);
size_t xs_size = mp_binary_get_size('@', xs_buf.typecode, NULL);
size_t ys_size = mp_binary_get_size('@', ys_buf.typecode, NULL);
size_t xs_len = xs_buf.len / xs_size;
size_t ys_len = ys_buf.len / ys_size;
if (xs_size != ys_size) {
mp_raise_ValueError(translate("Coordinate arrays types have different sizes"));
}
if (xs_len != ys_len) {
mp_raise_ValueError(translate("Coordinate arrays have different lengths"));
}

uint32_t value, color_depth;
value = args[ARG_value].u_int;
color_depth = (1 << destination->bits_per_value);
if (color_depth <= value) {
mp_raise_ValueError(translate("out of range of target"));
}

bool close = args[ARG_close].u_bool;

common_hal_bitmaptools_draw_polygon(destination, xs_buf.buf, ys_buf.buf, xs_len, xs_size, value, close);

return mp_const_none;
}

MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_polygon_obj, 0, bitmaptools_obj_draw_polygon);

//| def arrayblit(
//| bitmap: displayio.Bitmap,
//| data: ReadableBuffer,
Expand Down Expand Up @@ -783,6 +878,7 @@ STATIC const mp_rom_map_elem_t bitmaptools_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_fill_region), MP_ROM_PTR(&bitmaptools_fill_region_obj) },
{ MP_ROM_QSTR(MP_QSTR_boundary_fill), MP_ROM_PTR(&bitmaptools_boundary_fill_obj) },
{ MP_ROM_QSTR(MP_QSTR_draw_line), MP_ROM_PTR(&bitmaptools_draw_line_obj) },
{ MP_ROM_QSTR(MP_QSTR_draw_polygon), MP_ROM_PTR(&bitmaptools_draw_polygon_obj) },
{ MP_ROM_QSTR(MP_QSTR_dither), MP_ROM_PTR(&bitmaptools_dither_obj) },
{ MP_ROM_QSTR(MP_QSTR_DitherAlgorithm), MP_ROM_PTR(&bitmaptools_dither_algorithm_type) },
};
Expand Down
1 change: 1 addition & 0 deletions shared-bindings/bitmaptools/__init__.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination,
int16_t x1, int16_t y1,
uint32_t value);

void common_hal_bitmaptools_draw_polygon(displayio_bitmap_t *destination, void *xs, void *ys, size_t points_len, int point_size, uint32_t value, bool close);
void common_hal_bitmaptools_readinto(displayio_bitmap_t *self, mp_obj_t *file, int element_size, int bits_per_pixel, bool reverse_pixels_in_word, bool swap_bytes, bool reverse_rows);
void common_hal_bitmaptools_arrayblit(displayio_bitmap_t *self, void *data, int element_size, int x1, int y1, int x2, int y2, bool skip_specified, uint32_t skip_index);
void common_hal_bitmaptools_dither(displayio_bitmap_t *dest_bitmap, displayio_bitmap_t *source_bitmap, displayio_colorspace_t colorspace, bitmaptools_dither_algorithm_t algorithm);
Expand Down
106 changes: 79 additions & 27 deletions shared-module/bitmaptools/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -384,37 +384,11 @@ void common_hal_bitmaptools_boundary_fill(displayio_bitmap_t *destination,

}

void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination,
STATIC void draw_line(displayio_bitmap_t *destination,
int16_t x0, int16_t y0,
int16_t x1, int16_t y1,
uint32_t value) {

//
// adapted from Adafruit_CircuitPython_Display_Shapes.Polygon._line
//

// update the dirty rectangle
int16_t xbb0, xbb1, ybb0, ybb1;
if (x0 < x1) {
xbb0 = x0;
xbb1 = x1 + 1;
} else {
xbb0 = x1;
xbb1 = x0 + 1;
}
if (y0 < y1) {
ybb0 = y0;
ybb1 = y1 + 1;
} else {
ybb0 = y1;
ybb1 = y0 + 1;
}
displayio_area_t area = { xbb0, ybb0, xbb1, ybb1, NULL };
displayio_area_t bitmap_area = { 0, 0, destination->width, destination->height, NULL };
displayio_area_compute_overlap(&area, &bitmap_area, &area);

displayio_bitmap_set_dirty_area(destination, &area);

int16_t temp, x, y;

if (x0 == x1) { // vertical line
Expand Down Expand Up @@ -488,6 +462,84 @@ void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination,
}
}

void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination,
int16_t x0, int16_t y0,
int16_t x1, int16_t y1,
uint32_t value) {

//
// adapted from Adafruit_CircuitPython_Display_Shapes.Polygon._line
//

// update the dirty rectangle
int16_t xbb0, xbb1, ybb0, ybb1;
if (x0 < x1) {
xbb0 = x0;
xbb1 = x1 + 1;
} else {
xbb0 = x1;
xbb1 = x0 + 1;
}
if (y0 < y1) {
ybb0 = y0;
ybb1 = y1 + 1;
} else {
ybb0 = y1;
ybb1 = y0 + 1;
}
displayio_area_t area = { xbb0, ybb0, xbb1, ybb1, NULL };
displayio_area_t bitmap_area = { 0, 0, destination->width, destination->height, NULL };
displayio_area_compute_overlap(&area, &bitmap_area, &area);

displayio_bitmap_set_dirty_area(destination, &area);

draw_line(destination, x0, y0, x1, y1, value);
}

STATIC int32_t ith(void *data, size_t i, int element_size) {
switch (element_size) {
default:
case 1:
return *((int8_t *)data + i);
case 2:
return *((int16_t *)data + i);
case 4:
return *((int32_t *)data + i);
}
}

void common_hal_bitmaptools_draw_polygon(displayio_bitmap_t *destination, void *xs, void *ys, size_t points_len, int point_size, uint32_t value, bool close) {
int16_t x0, y0, xmin, xmax, ymin, ymax, xprev, yprev, x, y;
x0 = ith(xs, 0, point_size);
xmin = x0;
xmax = x0;
xprev = x0;
y0 = ith(ys, 0, point_size);
ymin = y0;
ymax = y0;
yprev = y0;

for (size_t i = 1; i < points_len; i++) {
x = ith(xs, i, point_size);
y = ith(ys, i, point_size);
draw_line(destination, xprev, yprev, x, y, value);
xprev = x;
yprev = y;
xmin = MIN(xmin, x);
xmax = MAX(xmax, x);
ymin = MIN(ymin, y);
ymax = MAX(ymax, y);
}
if (close) {
draw_line(destination, xprev, yprev, x0, y0, value);
}

displayio_area_t area = { xmin, ymin, xmax, ymax, NULL };
displayio_area_t bitmap_area = { 0, 0, destination->width, destination->height, NULL };
displayio_area_compute_overlap(&area, &bitmap_area, &area);
displayio_bitmap_set_dirty_area(destination, &area);
}

void common_hal_bitmaptools_arrayblit(displayio_bitmap_t *self, void *data, int element_size, int x1, int y1, int x2, int y2, bool skip_specified, uint32_t skip_value) {
uint32_t mask = (1 << common_hal_displayio_bitmap_get_bits_per_value(self)) - 1;

Expand Down