From 3b14224881958297d88b70050ddae93d19c3f244 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 14 Feb 2022 16:30:09 +0100 Subject: GPencil: Fill Dilate using negative values contract the fill area This is requested by artist for some animation styles where is necessary to fill the area, but create a gap between fill and stroke. Also some code cleanup and fix a bug in dilate for top area. Reviewed By: pepeland, mendio Differential Revision: https://developer.blender.org/D14082 --- source/blender/editors/gpencil/gpencil_fill.c | 155 +++++++++++++++++++++----- 1 file changed, 128 insertions(+), 27 deletions(-) (limited to 'source/blender/editors/gpencil') diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 5eca3a4eb6c..8095cc479bc 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1004,7 +1004,6 @@ static void gpencil_invert_image(tGPDfill *tgpf) ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); const int maxpixel = (ibuf->x * ibuf->y) - 1; - const int center = ibuf->x / 2; for (int v = maxpixel; v != 0; v--) { float color[4]; @@ -1016,15 +1015,6 @@ static void gpencil_invert_image(tGPDfill *tgpf) /* Red->Green */ else if (color[0] == 1.0f) { set_pixel(ibuf, v, fill_col[1]); - /* Add thickness of 2 pixels to avoid too thin lines, but avoid extremes of the pixel line. - */ - int row = v / ibuf->x; - int lowpix = row * ibuf->x; - int highpix = lowpix + ibuf->x - 1; - if ((v > lowpix) && (v < highpix)) { - int offset = (v % ibuf->x < center) ? 1 : -1; - set_pixel(ibuf, v + offset, fill_col[1]); - } } else { /* Set to Transparent. */ @@ -1120,11 +1110,14 @@ static void gpencil_erase_processed_area(tGPDfill *tgpf) */ static bool dilate_shape(ImBuf *ibuf) { +#define IS_RED (color[0] == 1.0f) +#define IS_GREEN (color[1] == 1.0f) + bool done = false; BLI_Stack *stack = BLI_stack_new(sizeof(int), __func__); const float green[4] = {0.0f, 1.0f, 0.0f, 1.0f}; - // const int maxpixel = (ibuf->x * ibuf->y) - 1; + const int max_size = (ibuf->x * ibuf->y) - 1; /* detect pixels and expand into red areas */ for (int row = 0; row < ibuf->y; row++) { if (!is_row_filled(ibuf, row)) { @@ -1137,7 +1130,7 @@ static bool dilate_shape(ImBuf *ibuf) float color[4]; int index; get_pixel(ibuf, v, color); - if (color[1] == 1.0f) { + if (IS_GREEN) { int tp = 0; int bm = 0; int lt = 0; @@ -1147,7 +1140,7 @@ static bool dilate_shape(ImBuf *ibuf) if (v - 1 >= 0) { index = v - 1; get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { + if (IS_RED) { BLI_stack_push(stack, &index); lt = index; } @@ -1156,25 +1149,25 @@ static bool dilate_shape(ImBuf *ibuf) if (v + 1 <= maxpixel) { index = v + 1; get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { + if (IS_RED) { BLI_stack_push(stack, &index); rt = index; } } /* pixel top */ - if (v + (ibuf->x * 1) <= maxpixel) { - index = v + (ibuf->x * 1); + if (v + ibuf->x <= max_size) { + index = v + ibuf->x; get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { + if (IS_RED) { BLI_stack_push(stack, &index); tp = index; } } /* pixel bottom */ - if (v - (ibuf->x * 1) >= 0) { - index = v - (ibuf->x * 1); + if (v - ibuf->x >= 0) { + index = v - ibuf->x; get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { + if (IS_RED) { BLI_stack_push(stack, &index); bm = index; } @@ -1183,7 +1176,7 @@ static bool dilate_shape(ImBuf *ibuf) if (tp && lt) { index = tp - 1; get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { + if (IS_RED) { BLI_stack_push(stack, &index); } } @@ -1191,7 +1184,7 @@ static bool dilate_shape(ImBuf *ibuf) if (tp && rt) { index = tp + 1; get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { + if (IS_RED) { BLI_stack_push(stack, &index); } } @@ -1199,7 +1192,7 @@ static bool dilate_shape(ImBuf *ibuf) if (bm && lt) { index = bm - 1; get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { + if (IS_RED) { BLI_stack_push(stack, &index); } } @@ -1207,7 +1200,7 @@ static bool dilate_shape(ImBuf *ibuf) if (bm && rt) { index = bm + 1; get_pixel(ibuf, index, color); - if (color[0] == 1.0f) { + if (IS_RED) { BLI_stack_push(stack, &index); } } @@ -1224,6 +1217,88 @@ static bool dilate_shape(ImBuf *ibuf) BLI_stack_free(stack); return done; + +#undef IS_RED +#undef IS_GREEN +} + +/** + * Contract + * + * Contract green areas to scale down the size. + * Using stack prevents creep when replacing colors directly. + */ +static bool contract_shape(ImBuf *ibuf) +{ +#define IS_GREEN (color[1] == 1.0f) +#define IS_NOT_GREEN (color[1] != 1.0f) + + bool done = false; + + BLI_Stack *stack = BLI_stack_new(sizeof(int), __func__); + const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + const int max_size = (ibuf->x * ibuf->y) - 1; + + /* detect pixels and expand into red areas */ + for (int row = 0; row < ibuf->y; row++) { + if (!is_row_filled(ibuf, row)) { + continue; + } + int maxpixel = (ibuf->x * (row + 1)) - 1; + int minpixel = ibuf->x * row; + + for (int v = maxpixel; v != minpixel; v--) { + float color[4]; + get_pixel(ibuf, v, color); + if (IS_GREEN) { + /* pixel left */ + if (v - 1 >= 0) { + get_pixel(ibuf, v - 1, color); + if (IS_NOT_GREEN) { + BLI_stack_push(stack, &v); + continue; + } + } + /* pixel right */ + if (v + 1 <= maxpixel) { + get_pixel(ibuf, v + 1, color); + if (IS_NOT_GREEN) { + BLI_stack_push(stack, &v); + continue; + } + } + /* pixel top */ + if (v + ibuf->x <= max_size) { + get_pixel(ibuf, v + ibuf->x, color); + if (IS_NOT_GREEN) { + BLI_stack_push(stack, &v); + continue; + } + } + /* pixel bottom */ + if (v - ibuf->x >= 0) { + get_pixel(ibuf, v - ibuf->x, color); + if (IS_NOT_GREEN) { + BLI_stack_push(stack, &v); + continue; + } + } + } + } + } + /* Clear pixels. */ + while (!BLI_stack_is_empty(stack)) { + int v; + BLI_stack_pop(stack, &v); + set_pixel(ibuf, v, clear); + done = true; + } + BLI_stack_free(stack); + + return done; + +#undef IS_GREEN +#undef IS_NOT_GREEN } /* Get the outline points of a shape using Moore Neighborhood algorithm @@ -1265,10 +1340,15 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate) ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); int imagesize = ibuf->x * ibuf->y; - /* Dilate. */ + /* Dilate or contract. */ if (dilate) { - for (int i = 0; i < brush->gpencil_settings->dilate_pixels; i++) { - dilate_shape(ibuf); + for (int i = 0; i < abs(brush->gpencil_settings->dilate_pixels); i++) { + if (brush->gpencil_settings->dilate_pixels > 0) { + dilate_shape(ibuf); + } + else { + contract_shape(ibuf); + } } } @@ -1975,6 +2055,24 @@ static void gpencil_zoom_level_set(tGPDfill *tgpf) } } +static bool gpencil_find_and_mark_empty_areas(tGPDfill *tgpf) +{ + ImBuf *ibuf; + void *lock; + const float blue_col[4] = {0.0f, 0.0f, 1.0f, 1.0f}; + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + const int maxpixel = (ibuf->x * ibuf->y) - 1; + float rgba[4]; + for (int i = 0; i < maxpixel; i++) { + get_pixel(ibuf, i, rgba); + if (rgba[3] == 0.0f) { + set_pixel(ibuf, i, blue_col); + return true; + } + } + return false; +} + static bool gpencil_do_frame_fill(tGPDfill *tgpf, const bool is_inverted) { wmWindow *win = CTX_wm_window(tgpf->C); @@ -1995,6 +2093,9 @@ static bool gpencil_do_frame_fill(tGPDfill *tgpf, const bool is_inverted) /* Invert direction if press Ctrl. */ if (is_inverted) { gpencil_invert_image(tgpf); + while (gpencil_find_and_mark_empty_areas(tgpf)) { + gpencil_boundaryfill_area(tgpf); + } } /* Clean borders to avoid infinite loops. */ -- cgit v1.2.3