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

Note: This was committed only in master (3.2) by error.
This commit is contained in:
Antonio Vazquez 2022-02-14 16:30:09 +01:00
parent 60af7a3496
commit 5fd792c1f6
2 changed files with 132 additions and 30 deletions

View File

@ -1020,7 +1020,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];
@ -1032,15 +1031,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. */
@ -1136,11 +1126,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)) {
@ -1153,7 +1146,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;
@ -1163,7 +1156,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;
}
@ -1172,25 +1165,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;
}
@ -1199,7 +1192,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);
}
}
@ -1207,7 +1200,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);
}
}
@ -1215,7 +1208,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);
}
}
@ -1223,7 +1216,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);
}
}
@ -1240,6 +1233,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
@ -1281,10 +1356,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);
}
}
}
@ -1991,6 +2071,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);
@ -2011,6 +2109,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. */

View File

@ -1629,12 +1629,13 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
prop, "Stroke Extension", "Strokes end extension for closing gaps, use zero to disable");
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
/* Number of pixels to dilate fill area. */
/* Number of pixels to dilate fill area. Negative values contract the filled area. */
prop = RNA_def_property(srna, "dilate", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "dilate_pixels");
RNA_def_property_range(prop, 0, 20);
RNA_def_property_range(prop, -40, 40);
RNA_def_property_int_default(prop, 1);
RNA_def_property_ui_text(prop, "Dilate", "Number of pixels to dilate fill area");
RNA_def_property_ui_text(
prop, "Dilate/Contract", "Number of pixels to expand or contract fill area");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);