diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 010fa33db2..c4dfb4e944 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -2575,6 +2575,10 @@ msgstr "" msgid "axis too long" msgstr "" +#: shared-bindings/bitmaptools/__init__.c +msgid "background value out of range of target" +msgstr "" + #: py/builtinevex.c msgid "bad compile mode" msgstr "" @@ -3926,6 +3930,7 @@ msgstr "" #: ports/esp32s2/boards/gravitech_cucumber_rs/mpconfigboard.h #: ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/mpconfigboard.h #: ports/esp32s2/boards/microdev_micro_s2/mpconfigboard.h +#: ports/esp32s2/boards/morpheans_morphesp-240/mpconfigboard.h #: ports/esp32s2/boards/muselab_nanoesp32_s2_wroom/mpconfigboard.h #: ports/esp32s2/boards/muselab_nanoesp32_s2_wrover/mpconfigboard.h #: ports/esp32s2/boards/targett_module_clip_wroom/mpconfigboard.h @@ -4367,6 +4372,10 @@ msgstr "" msgid "value must fit in %d byte(s)" msgstr "" +#: shared-bindings/bitmaptools/__init__.c +msgid "value out of range of target" +msgstr "" + #: shared-bindings/displayio/Bitmap.c msgid "value_count must be > 0" msgstr "" diff --git a/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk b/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk index d1a627569c..4ff181c7f3 100644 --- a/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk +++ b/ports/stm/boards/pyb_nano_v2/mpconfigboard.mk @@ -19,3 +19,4 @@ CIRCUITPY_AUDIOPWMIO = 0 CIRCUITPY_KEYPAD = 0 CIRCUITPY_MIDI = 0 CIRCUITPY_MSGPACK = 0 +CIRCUITPY_BITMAPTOOLS = 0 diff --git a/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk b/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk index c8dc80a70c..d5368fa84c 100644 --- a/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk +++ b/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk @@ -18,3 +18,4 @@ CIRCUITPY_AUDIOPWMIO = 0 CIRCUITPY_KEYPAD = 0 CIRCUITPY_MIDI = 0 CIRCUITPY_MSGPACK = 0 +CIRCUITPY_BITMAPTOOLS = 0 diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index 2a5195d1a4..4956c61a1f 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -296,6 +296,68 @@ STATIC mp_obj_t bitmaptools_obj_fill_region(size_t n_args, const mp_obj_t *pos_a } MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_fill_region_obj, 0, bitmaptools_obj_fill_region); +//| +//| def boundary_fill( +//| dest_bitmap: displayio.Bitmap, +//| x: int, y: int, +//| fill_color_value: int, replaced_color_value: int) -> None: +//| """Draws the color value into the destination bitmap enclosed +//| area of pixels of the background_value color. Like "Paint Bucket" +//| fill tool. +//| +//| :param bitmap dest_bitmap: Destination bitmap that will be written into +//| :param int x: x-pixel position of the first pixel to check and fill if needed +//| :param int y: y-pixel position of the first pixel to check and fill if needed +//| :param int fill_color_value: Bitmap palette index that will be written into the +//| enclosed area in the destination bitmap +//| :param int replaced_color_value: Bitmap palette index that will filled with the +//| value color in the enclosed area in the destination bitmap""" +//| ... +//| +STATIC mp_obj_t bitmaptools_obj_boundary_fill(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_dest_bitmap, ARG_x, ARG_y, ARG_fill_color_value, ARG_replaced_color_value}; + + static const mp_arg_t allowed_args[] = { + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_fill_color_value, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_replaced_color_value, MP_ARG_INT, {.u_int = INT_MAX} }, + }; + 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(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap + + uint32_t fill_color_value, color_depth; + fill_color_value = args[ARG_fill_color_value].u_int; + color_depth = (1 << destination->bits_per_value); + if (color_depth <= fill_color_value) { + mp_raise_ValueError(translate("value out of range of target")); + } + + uint32_t replaced_color_value; + replaced_color_value = args[ARG_replaced_color_value].u_int; + if (replaced_color_value != INT_MAX && color_depth <= replaced_color_value) { + mp_raise_ValueError(translate("background value out of range of target")); + } + + int16_t x = args[ARG_x].u_int; + int16_t y = args[ARG_y].u_int; + + if (x < 0 || x >= destination->width) { + mp_raise_ValueError(translate("out of range of target")); + } + if (y < 0 || y >= destination->height) { + mp_raise_ValueError(translate("out of range of target")); + } + + common_hal_bitmaptools_boundary_fill(destination, x, y, fill_color_value, replaced_color_value); + + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_boundary_fill_obj, 0, bitmaptools_obj_boundary_fill); // requires all 6 arguments //| @@ -520,6 +582,7 @@ STATIC const mp_rom_map_elem_t bitmaptools_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_rotozoom), MP_ROM_PTR(&bitmaptools_rotozoom_obj) }, { MP_ROM_QSTR(MP_QSTR_arrayblit), MP_ROM_PTR(&bitmaptools_arrayblit_obj) }, { 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) }, }; STATIC MP_DEFINE_CONST_DICT(bitmaptools_module_globals, bitmaptools_module_globals_table); diff --git a/shared-bindings/bitmaptools/__init__.h b/shared-bindings/bitmaptools/__init__.h index fc1eb59068..6415b20258 100644 --- a/shared-bindings/bitmaptools/__init__.h +++ b/shared-bindings/bitmaptools/__init__.h @@ -46,6 +46,10 @@ void common_hal_bitmaptools_fill_region(displayio_bitmap_t *destination, int16_t x2, int16_t y2, uint32_t value); +void common_hal_bitmaptools_boundary_fill(displayio_bitmap_t *destination, + int16_t x, int16_t y, + uint32_t fill_color_value, uint32_t replaced_color_value); + void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination, int16_t x0, int16_t y0, int16_t x1, int16_t y1, diff --git a/shared-module/bitmaptools/__init__.c b/shared-module/bitmaptools/__init__.c index 4c8b887200..2bb062239a 100644 --- a/shared-module/bitmaptools/__init__.c +++ b/shared-module/bitmaptools/__init__.c @@ -24,7 +24,6 @@ * THE SOFTWARE. */ - #include "shared-bindings/bitmaptools/__init__.h" #include "shared-bindings/displayio/Bitmap.h" #include "shared-module/displayio/Bitmap.h" @@ -252,6 +251,145 @@ void common_hal_bitmaptools_fill_region(displayio_bitmap_t *destination, } } +void common_hal_bitmaptools_boundary_fill(displayio_bitmap_t *destination, + int16_t x, int16_t y, + uint32_t fill_color_value, uint32_t replaced_color_value) { + + if (fill_color_value == replaced_color_value) { + // There is nothing to do + return; + } + + uint32_t current_point_color_value; + + // the list of points that we'll check + mp_obj_t fill_area = mp_obj_new_list(0, NULL); + + // first point is the one user passed in + mp_obj_t point[] = { mp_obj_new_int(x), mp_obj_new_int(y) }; + mp_obj_list_append( + fill_area, + mp_obj_new_tuple(2, point) + ); + + int16_t minx = x; + int16_t miny = y; + int16_t maxx = x; + int16_t maxy = y; + + if (replaced_color_value == INT_MAX) { + current_point_color_value = common_hal_displayio_bitmap_get_pixel( + destination, + mp_obj_get_int(point[0]), + mp_obj_get_int(point[1])); + replaced_color_value = (uint32_t)current_point_color_value; + } + + mp_obj_t *fill_points; + size_t list_length = 0; + mp_obj_list_get(fill_area, &list_length, &fill_points); + + mp_obj_t current_point; + + size_t tuple_len = 0; + mp_obj_t *tuple_items; + + int cur_x, cur_y; + + // while there are still points to check + while (list_length > 0) { + mp_obj_list_get(fill_area, &list_length, &fill_points); + current_point = mp_obj_list_pop(fill_area, 0); + mp_obj_tuple_get(current_point, &tuple_len, &tuple_items); + current_point_color_value = common_hal_displayio_bitmap_get_pixel( + destination, + mp_obj_get_int(tuple_items[0]), + mp_obj_get_int(tuple_items[1])); + + // if the current point is not background color ignore it + if (current_point_color_value != replaced_color_value) { + mp_obj_list_get(fill_area, &list_length, &fill_points); + continue; + } + + cur_x = mp_obj_int_get_checked(tuple_items[0]); + cur_y = mp_obj_int_get_checked(tuple_items[1]); + + if (cur_x < minx) { + minx = (int16_t)cur_x; + } + if (cur_x > maxx) { + maxx = (int16_t)cur_x; + } + if (cur_y < miny) { + miny = (int16_t)cur_y; + } + if (cur_y > maxy) { + maxy = (int16_t)cur_y; + } + + // fill the current point with fill color + displayio_bitmap_write_pixel( + destination, + mp_obj_get_int(tuple_items[0]), + mp_obj_get_int(tuple_items[1]), + fill_color_value); + + // add all 4 surrounding points to the list to check + + // ignore points outside of the bitmap + if (mp_obj_int_get_checked(tuple_items[1]) - 1 >= 0) { + mp_obj_t above_point[] = { + tuple_items[0], + MP_OBJ_NEW_SMALL_INT(mp_obj_int_get_checked(tuple_items[1]) - 1) + }; + mp_obj_list_append( + fill_area, + mp_obj_new_tuple(2, above_point)); + } + + // ignore points outside of the bitmap + if (mp_obj_int_get_checked(tuple_items[0]) - 1 >= 0) { + mp_obj_t left_point[] = { + MP_OBJ_NEW_SMALL_INT(mp_obj_int_get_checked(tuple_items[0]) - 1), + tuple_items[1] + }; + mp_obj_list_append( + fill_area, + mp_obj_new_tuple(2, left_point)); + } + + // ignore points outside of the bitmap + if (mp_obj_int_get_checked(tuple_items[0]) + 1 < destination->width) { + mp_obj_t right_point[] = { + MP_OBJ_NEW_SMALL_INT(mp_obj_int_get_checked(tuple_items[0]) + 1), + tuple_items[1] + }; + mp_obj_list_append( + fill_area, + mp_obj_new_tuple(2, right_point)); + } + + // ignore points outside of the bitmap + if (mp_obj_int_get_checked(tuple_items[1]) + 1 < destination->height) { + mp_obj_t below_point[] = { + tuple_items[0], + MP_OBJ_NEW_SMALL_INT(mp_obj_int_get_checked(tuple_items[1]) + 1) + }; + mp_obj_list_append( + fill_area, + mp_obj_new_tuple(2, below_point)); + } + + mp_obj_list_get(fill_area, &list_length, &fill_points); + } + + // set dirty the area so displayio will draw + displayio_area_t area = { minx, miny, maxx + 1, maxy + 1}; + displayio_bitmap_set_dirty_area(destination, &area); + +} + void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination, int16_t x0, int16_t y0, int16_t x1, int16_t y1,