Skip to content

Commit 96d3e66

Browse files
committed
refactor bitmap.blit into bitmaptools
1 parent abf1512 commit 96d3e66

File tree

6 files changed

+198
-190
lines changed

6 files changed

+198
-190
lines changed

shared-bindings/bitmaptools/__init__.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,132 @@ STATIC mp_obj_t bitmaptools_obj_draw_circle(size_t n_args, const mp_obj_t *pos_a
949949

950950
MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_circle_obj, 0, bitmaptools_obj_draw_circle);
951951

952+
//| def blit(
953+
//| dest_bitmap: Bitmap,
954+
//| source_bitmap: Bitmap,
955+
//| x: int,
956+
//| y: int,
957+
//| *,
958+
//| x1: int,
959+
//| y1: int,
960+
//| x2: int,
961+
//| y2: int,
962+
//| skip_index: int,
963+
//| skip_dest_index: int
964+
//| ) -> None:
965+
//| """Inserts the source_bitmap region defined by rectangular boundaries
966+
//| (x1,y1) and (x2,y2) into the bitmap at the specified (x,y) location.
967+
//|
968+
//| :param bitmap dest_bitmap: Destination bitmap that the area will be copied into.
969+
//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied
970+
//| :param int x: Horizontal pixel location in bitmap where source_bitmap upper-left
971+
//| corner will be placed
972+
//| :param int y: Vertical pixel location in bitmap where source_bitmap upper-left
973+
//| corner will be placed
974+
//| :param int x1: Minimum x-value for rectangular bounding box to be copied from the source bitmap
975+
//| :param int y1: Minimum y-value for rectangular bounding box to be copied from the source bitmap
976+
//| :param int x2: Maximum x-value (exclusive) for rectangular bounding box to be copied from the source bitmap
977+
//| :param int y2: Maximum y-value (exclusive) for rectangular bounding box to be copied from the source bitmap
978+
//| :param int skip_index: bitmap palette index in the source that will not be copied,
979+
//| set to None to copy all pixels
980+
//| :param int skip_dest_index: bitmap palette index in the destination bitmap that will not get overwritten
981+
//| by the pixels from the source"""
982+
//| ...
983+
//|
984+
STATIC mp_obj_t bitmaptools_obj_blit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
985+
enum {ARG_destination, ARG_source, ARG_x, ARG_y, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_skip_index, ARG_skip_dest_index};
986+
static const mp_arg_t allowed_args[] = {
987+
{MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
988+
{MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
989+
{MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} },
990+
{MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} },
991+
{MP_QSTR_x1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
992+
{MP_QSTR_y1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
993+
{MP_QSTR_x2, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->width
994+
{MP_QSTR_y2, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->height
995+
{MP_QSTR_skip_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
996+
{MP_QSTR_skip_dest_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
997+
};
998+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
999+
// mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
1000+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
1001+
1002+
// displayio_bitmap_t *self = MP_OBJ_TO_PTR(pos_args[0]);
1003+
displayio_bitmap_t *destination = mp_arg_validate_type(args[ARG_destination].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap);
1004+
// check_for_deinit(destination);
1005+
1006+
// Check x,y are within self (target) bitmap boundary
1007+
int16_t x = mp_arg_validate_int_range(args[ARG_x].u_int, 0, MAX(0, destination->width - 1), MP_QSTR_x);
1008+
int16_t y = mp_arg_validate_int_range(args[ARG_y].u_int, 0, MAX(0, destination->height - 1), MP_QSTR_y);
1009+
1010+
1011+
displayio_bitmap_t *source = mp_arg_validate_type(args[ARG_source].u_obj, &displayio_bitmap_type, MP_QSTR_source_bitmap);
1012+
1013+
1014+
// ensure that the target bitmap (self) has at least as many `bits_per_value` as the source
1015+
if (destination->bits_per_value < source->bits_per_value) {
1016+
mp_raise_ValueError(translate("source palette too large"));
1017+
}
1018+
1019+
// Check x1,y1,x2,y2 are within source bitmap boundary
1020+
int16_t x1 = mp_arg_validate_int_range(args[ARG_x1].u_int, 0, MAX(0, source->width - 1), MP_QSTR_x1);
1021+
int16_t y1 = mp_arg_validate_int_range(args[ARG_y1].u_int, 0, MAX(0, source->height - 1), MP_QSTR_y1);
1022+
int16_t x2, y2;
1023+
// if x2 or y2 is None, then set as the maximum size of the source bitmap
1024+
if (args[ARG_x2].u_obj == mp_const_none) {
1025+
x2 = source->width;
1026+
} else {
1027+
x2 = mp_arg_validate_int_range(mp_obj_get_int(args[ARG_x2].u_obj), 0, source->width, MP_QSTR_x2);
1028+
}
1029+
// int16_t y2;
1030+
if (args[ARG_y2].u_obj == mp_const_none) {
1031+
y2 = source->height;
1032+
} else {
1033+
y2 = mp_arg_validate_int_range(mp_obj_get_int(args[ARG_y2].u_obj), 0, source->height, MP_QSTR_y2);
1034+
}
1035+
1036+
// Ensure x1 < x2 and y1 < y2
1037+
if (x1 > x2) {
1038+
int16_t temp = x2;
1039+
x2 = x1;
1040+
x1 = temp;
1041+
}
1042+
if (y1 > y2) {
1043+
int16_t temp = y2;
1044+
y2 = y1;
1045+
y1 = temp;
1046+
}
1047+
1048+
uint32_t skip_index;
1049+
bool skip_index_none; // flag whether skip_value was None
1050+
1051+
if (args[ARG_skip_index].u_obj == mp_const_none) {
1052+
skip_index = 0;
1053+
skip_index_none = true;
1054+
} else {
1055+
skip_index = mp_obj_get_int(args[ARG_skip_index].u_obj);
1056+
skip_index_none = false;
1057+
}
1058+
1059+
uint32_t skip_dest_index;
1060+
bool skip_dest_index_none; // flag whether skip_self_value was None
1061+
1062+
if (args[ARG_skip_dest_index].u_obj == mp_const_none) {
1063+
skip_dest_index = 0;
1064+
skip_dest_index_none = true;
1065+
} else {
1066+
skip_dest_index = mp_obj_get_int(args[ARG_skip_dest_index].u_obj);
1067+
skip_dest_index_none = false;
1068+
}
1069+
1070+
common_hal_bitmaptools_blit(destination, source, x, y, x1, y1, x2, y2, skip_index, skip_index_none, skip_dest_index,
1071+
skip_dest_index_none);
1072+
1073+
return mp_const_none;
1074+
}
1075+
MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_blit_obj, 1, bitmaptools_obj_blit);
1076+
1077+
9521078
STATIC const mp_rom_map_elem_t bitmaptools_module_globals_table[] = {
9531079
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bitmaptools) },
9541080
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&bitmaptools_readinto_obj) },
@@ -960,6 +1086,7 @@ STATIC const mp_rom_map_elem_t bitmaptools_module_globals_table[] = {
9601086
{ MP_ROM_QSTR(MP_QSTR_draw_line), MP_ROM_PTR(&bitmaptools_draw_line_obj) },
9611087
{ MP_ROM_QSTR(MP_QSTR_draw_polygon), MP_ROM_PTR(&bitmaptools_draw_polygon_obj) },
9621088
{ MP_ROM_QSTR(MP_QSTR_draw_circle), MP_ROM_PTR(&bitmaptools_draw_circle_obj) },
1089+
{ MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&bitmaptools_blit_obj) },
9631090
{ MP_ROM_QSTR(MP_QSTR_dither), MP_ROM_PTR(&bitmaptools_dither_obj) },
9641091
{ MP_ROM_QSTR(MP_QSTR_DitherAlgorithm), MP_ROM_PTR(&bitmaptools_dither_algorithm_type) },
9651092
};

shared-bindings/bitmaptools/__init__.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ void common_hal_bitmaptools_draw_circle(displayio_bitmap_t *destination,
6969
int16_t radius,
7070
uint32_t value);
7171

72+
void common_hal_bitmaptools_blit(displayio_bitmap_t *destination, displayio_bitmap_t *source, int16_t x, int16_t y,
73+
int16_t x1, int16_t y1, int16_t x2, int16_t y2,
74+
uint32_t skip_index, bool skip_index_none, uint32_t skip_self_index, bool skip_self_index_none);
75+
7276
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);
7377
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);
7478
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);

shared-bindings/displayio/Bitmap.c

Lines changed: 0 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -186,125 +186,6 @@ STATIC mp_obj_t bitmap_subscr(mp_obj_t self_in, mp_obj_t index_obj, mp_obj_t val
186186
return mp_const_none;
187187
}
188188

189-
//| def blit(
190-
//| self,
191-
//| x: int,
192-
//| y: int,
193-
//| source_bitmap: Bitmap,
194-
//| *,
195-
//| x1: int,
196-
//| y1: int,
197-
//| x2: int,
198-
//| y2: int,
199-
//| skip_index: int,
200-
//| skip_self_index: int
201-
//| ) -> None:
202-
//| """Inserts the source_bitmap region defined by rectangular boundaries
203-
//| (x1,y1) and (x2,y2) into the bitmap at the specified (x,y) location.
204-
//|
205-
//| :param int x: Horizontal pixel location in bitmap where source_bitmap upper-left
206-
//| corner will be placed
207-
//| :param int y: Vertical pixel location in bitmap where source_bitmap upper-left
208-
//| corner will be placed
209-
//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be copied
210-
//| :param int x1: Minimum x-value for rectangular bounding box to be copied from the source bitmap
211-
//| :param int y1: Minimum y-value for rectangular bounding box to be copied from the source bitmap
212-
//| :param int x2: Maximum x-value (exclusive) for rectangular bounding box to be copied from the source bitmap
213-
//| :param int y2: Maximum y-value (exclusive) for rectangular bounding box to be copied from the source bitmap
214-
//| :param int skip_index: bitmap palette index in the source that will not be copied,
215-
//| set to None to copy all pixels
216-
//| :param int skip_self_index: bitmap palette index in the self bitmap that will not get overwritten
217-
//| by the pixels from the source"""
218-
//| ...
219-
STATIC mp_obj_t displayio_bitmap_obj_blit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
220-
enum {ARG_x, ARG_y, ARG_source, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_skip_index, ARG_skip_self_index};
221-
static const mp_arg_t allowed_args[] = {
222-
{MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} },
223-
{MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} },
224-
{MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
225-
{MP_QSTR_x1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
226-
{MP_QSTR_y1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
227-
{MP_QSTR_x2, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->width
228-
{MP_QSTR_y2, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to source->height
229-
{MP_QSTR_skip_index, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} },
230-
{MP_QSTR_skip_self_index, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} },
231-
};
232-
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
233-
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
234-
235-
displayio_bitmap_t *self = MP_OBJ_TO_PTR(pos_args[0]);
236-
check_for_deinit(self);
237-
238-
// Check x,y are within self (target) bitmap boundary
239-
int16_t x = mp_arg_validate_int_range(args[ARG_x].u_int, 0, MAX(0, self->width - 1), MP_QSTR_x);
240-
int16_t y = mp_arg_validate_int_range(args[ARG_y].u_int, 0, MAX(0, self->height - 1), MP_QSTR_y);
241-
242-
displayio_bitmap_t *source = mp_arg_validate_type(args[ARG_source].u_obj, &displayio_bitmap_type, MP_QSTR_source_bitmap);
243-
244-
245-
// ensure that the target bitmap (self) has at least as many `bits_per_value` as the source
246-
if (self->bits_per_value < source->bits_per_value) {
247-
mp_raise_ValueError(translate("source palette too large"));
248-
}
249-
250-
// Check x1,y1,x2,y2 are within source bitmap boundary
251-
int16_t x1 = mp_arg_validate_int_range(args[ARG_x1].u_int, 0, MAX(0, source->width - 1), MP_QSTR_x1);
252-
int16_t y1 = mp_arg_validate_int_range(args[ARG_y1].u_int, 0, MAX(0, source->height - 1), MP_QSTR_y1);
253-
int16_t x2, y2;
254-
// if x2 or y2 is None, then set as the maximum size of the source bitmap
255-
if (args[ARG_x2].u_obj == mp_const_none) {
256-
x2 = source->width;
257-
} else {
258-
x2 = mp_arg_validate_int_range(mp_obj_get_int(args[ARG_x2].u_obj), 0, source->width, MP_QSTR_x2);
259-
}
260-
// int16_t y2;
261-
if (args[ARG_y2].u_obj == mp_const_none) {
262-
y2 = source->height;
263-
} else {
264-
y2 = mp_arg_validate_int_range(mp_obj_get_int(args[ARG_y2].u_obj), 0, source->height, MP_QSTR_y2);
265-
}
266-
267-
// Ensure x1 < x2 and y1 < y2
268-
if (x1 > x2) {
269-
int16_t temp = x2;
270-
x2 = x1;
271-
x1 = temp;
272-
}
273-
if (y1 > y2) {
274-
int16_t temp = y2;
275-
y2 = y1;
276-
y1 = temp;
277-
}
278-
279-
uint32_t skip_index;
280-
bool skip_index_none; // flag whether skip_value was None
281-
282-
if (args[ARG_skip_index].u_obj == mp_const_none) {
283-
skip_index = 0;
284-
skip_index_none = true;
285-
} else {
286-
skip_index = mp_obj_get_int(args[ARG_skip_index].u_obj);
287-
skip_index_none = false;
288-
}
289-
290-
uint32_t skip_self_index;
291-
bool skip_self_index_none; // flag whether skip_self_value was None
292-
293-
if (args[ARG_skip_self_index].u_obj == mp_const_none) {
294-
skip_self_index = 0;
295-
skip_self_index_none = true;
296-
} else {
297-
skip_self_index = mp_obj_get_int(args[ARG_skip_self_index].u_obj);
298-
skip_self_index_none = false;
299-
}
300-
301-
common_hal_displayio_bitmap_blit(self, x, y, source, x1, y1, x2, y2, skip_index, skip_index_none, skip_self_index,
302-
skip_self_index_none);
303-
304-
return mp_const_none;
305-
}
306-
MP_DEFINE_CONST_FUN_OBJ_KW(displayio_bitmap_blit_obj, 1, displayio_bitmap_obj_blit);
307-
308189
//| def fill(self, value: int) -> None:
309190
//| """Fills the bitmap with the supplied palette index value."""
310191
//| ...
@@ -379,7 +260,6 @@ MP_DEFINE_CONST_FUN_OBJ_1(displayio_bitmap_deinit_obj, displayio_bitmap_obj_dein
379260
STATIC const mp_rom_map_elem_t displayio_bitmap_locals_dict_table[] = {
380261
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_bitmap_height_obj) },
381262
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_bitmap_width_obj) },
382-
{ MP_ROM_QSTR(MP_QSTR_blit), MP_ROM_PTR(&displayio_bitmap_blit_obj) },
383263
{ MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&displayio_bitmap_fill_obj) },
384264
{ MP_ROM_QSTR(MP_QSTR_dirty), MP_ROM_PTR(&displayio_bitmap_dirty_obj) },
385265
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&displayio_bitmap_deinit_obj) },

shared-bindings/displayio/Bitmap.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ uint16_t common_hal_displayio_bitmap_get_height(displayio_bitmap_t *self);
4242
uint16_t common_hal_displayio_bitmap_get_width(displayio_bitmap_t *self);
4343
uint32_t common_hal_displayio_bitmap_get_bits_per_value(displayio_bitmap_t *self);
4444
void common_hal_displayio_bitmap_set_pixel(displayio_bitmap_t *bitmap, int16_t x, int16_t y, uint32_t value);
45-
void common_hal_displayio_bitmap_blit(displayio_bitmap_t *self, int16_t x, int16_t y, displayio_bitmap_t *source,
46-
int16_t x1, int16_t y1, int16_t x2, int16_t y2,
47-
uint32_t skip_index, bool skip_index_none, uint32_t skip_self_index, bool skip_self_index_none);
4845
uint32_t common_hal_displayio_bitmap_get_pixel(displayio_bitmap_t *bitmap, int16_t x, int16_t y);
4946
void common_hal_displayio_bitmap_fill(displayio_bitmap_t *bitmap, uint32_t value);
5047
int common_hal_displayio_bitmap_get_buffer(displayio_bitmap_t *self, mp_buffer_info_t *bufinfo, mp_uint_t flags);

shared-module/bitmaptools/__init__.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,3 +982,70 @@ void common_hal_bitmaptools_draw_circle(displayio_bitmap_t *destination,
982982

983983
draw_circle(destination, x, y, radius, value);
984984
}
985+
986+
void common_hal_bitmaptools_blit(displayio_bitmap_t *destination, displayio_bitmap_t *source, int16_t x, int16_t y,
987+
int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint32_t skip_index, bool skip_index_none, uint32_t skip_dest_index,
988+
bool skip_dest_index_none) {
989+
if (destination->read_only) {
990+
mp_raise_RuntimeError(translate("Read-only"));
991+
}
992+
// Copy region of "source" bitmap into "destination" bitmap at location x,y in the "destination"
993+
// If skip_value is encountered in the source bitmap, it will not be copied.
994+
// If skip_value is `None`, then all pixels are copied.
995+
// This function assumes input checks were performed for pixel index entries.
996+
997+
// Update the dirty area
998+
int16_t dirty_x_max = (x + (x2 - x1));
999+
if (dirty_x_max > destination->width) {
1000+
dirty_x_max = destination->width;
1001+
}
1002+
int16_t dirty_y_max = y + (y2 - y1);
1003+
if (dirty_y_max > destination->height) {
1004+
dirty_y_max = destination->height;
1005+
}
1006+
1007+
displayio_area_t a = { x, y, dirty_x_max, dirty_y_max, NULL};
1008+
displayio_bitmap_set_dirty_area(destination, &a);
1009+
1010+
bool x_reverse = false;
1011+
bool y_reverse = false;
1012+
1013+
// Add reverse direction option to protect blitting of destination bitmap back into destination bitmap
1014+
if (x > x1) {
1015+
x_reverse = true;
1016+
}
1017+
if (y > y1) {
1018+
y_reverse = true;
1019+
}
1020+
1021+
// simplest version - use internal functions for get/set pixels
1022+
for (int16_t i = 0; i < (x2 - x1); i++) {
1023+
1024+
const int xs_index = x_reverse ? ((x2) - i - 1) : x1 + i; // x-index into the source bitmap
1025+
const int xd_index = x_reverse ? ((x + (x2 - x1)) - i - 1) : x + i; // x-index into the destination bitmap
1026+
1027+
if ((xd_index >= 0) && (xd_index < destination->width)) {
1028+
for (int16_t j = 0; j < (y2 - y1); j++) {
1029+
1030+
const int ys_index = y_reverse ? ((y2) - j - 1) : y1 + j; // y-index into the source bitmap
1031+
const int yd_index = y_reverse ? ((y + (y2 - y1)) - j - 1) : y + j; // y-index into the destination bitmap
1032+
1033+
if ((yd_index >= 0) && (yd_index < destination->height)) {
1034+
uint32_t value = common_hal_displayio_bitmap_get_pixel(source, xs_index, ys_index);
1035+
if (skip_dest_index_none) { // if skip_dest_index is none, then only check source skip
1036+
if ((skip_index_none) || (value != skip_index)) { // write if skip_value_none is True
1037+
displayio_bitmap_write_pixel(destination, xd_index, yd_index, value);
1038+
}
1039+
} else { // check dest_value index against skip_dest_index and skip if they match
1040+
uint32_t dest_value = common_hal_displayio_bitmap_get_pixel(destination, xd_index, yd_index);
1041+
if (dest_value != skip_dest_index) {
1042+
if ((skip_index_none) || (value != skip_index)) { // write if skip_value_none is True
1043+
displayio_bitmap_write_pixel(destination, xd_index, yd_index, value);
1044+
}
1045+
}
1046+
}
1047+
}
1048+
}
1049+
}
1050+
}
1051+
}

0 commit comments

Comments
 (0)