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
This commit is contained in:
parent
15141ec19a
commit
3b14224881
|
@ -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. */
|
||||
|
|
|
@ -1615,12 +1615,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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue