diff options
Diffstat (limited to 'source/blender/editors/gpencil/gpencil_fill.c')
-rw-r--r-- | source/blender/editors/gpencil/gpencil_fill.c | 205 |
1 files changed, 182 insertions, 23 deletions
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index d7525c3c6a5..2855f2cc0d7 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -104,6 +104,8 @@ typedef struct tGPDfill { struct bGPdata *gpd; /** current material */ struct Material *mat; + /** current brush */ + struct Brush *brush; /** layer */ struct bGPDlayer *gpl; /** frame */ @@ -233,6 +235,8 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) Object *ob = tgpf->ob; bGPdata *gpd = tgpf->gpd; + Brush *brush = tgpf->brush; + BrushGpencilSettings *brush_settings = brush->gpencil_settings; tGPDdraw tgpw; tgpw.rv3d = tgpf->rv3d; @@ -249,6 +253,12 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) GPU_blend(true); + bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd); + BLI_assert(gpl_active != NULL); + + const int gpl_active_index = BLI_findindex(&gpd->layers, gpl_active); + BLI_assert(gpl_active_index >= 0); + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { /* calculate parent position */ BKE_gpencil_parent_matrix_get(tgpw.depsgraph, ob, gpl, tgpw.diff_mat); @@ -258,6 +268,44 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) continue; } + /* Decide if layer is included or not depending of the layer mode. */ + const int gpl_index = BLI_findindex(&gpd->layers, gpl); + switch (brush_settings->fill_layer_mode) { + case GP_FILL_GPLMODE_ACTIVE: { + if (gpl_index != gpl_active_index) { + continue; + } + break; + } + case GP_FILL_GPLMODE_ABOVE: { + if (gpl_index != gpl_active_index + 1) { + continue; + } + break; + } + case GP_FILL_GPLMODE_BELOW: { + if (gpl_index != gpl_active_index - 1) { + continue; + } + break; + } + case GP_FILL_GPLMODE_ALL_ABOVE: { + if (gpl_index <= gpl_active_index) { + continue; + } + break; + } + case GP_FILL_GPLMODE_ALL_BELOW: { + if (gpl_index >= gpl_active_index) { + continue; + } + break; + } + case GP_FILL_GPLMODE_VISIBLE: + default: + break; + } + /* if active layer and no keyframe, create a new one */ if (gpl == tgpf->gpl) { if ((gpl->actframe == NULL) || (gpl->actframe->framenum != tgpf->active_cfra)) { @@ -408,7 +456,7 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) GPU_matrix_set(tgpf->rv3d->viewmat); /* draw strokes */ - float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f}; + const float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f}; gpencil_draw_datablock(tgpf, ink); GPU_matrix_pop_projection(); @@ -702,6 +750,92 @@ static void gpencil_set_borders(tGPDfill *tgpf, const bool transparent) tgpf->ima->id.tag |= LIB_TAG_DOIT; } +/* Invert image to paint invese area. */ +static void gpencil_invert_image(tGPDfill *tgpf) +{ + ImBuf *ibuf; + void *lock; + const float fill_col[3][4] = { + {1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}}; + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + + const int maxpixel = (ibuf->x * ibuf->y) - 1; + + for (int v = maxpixel; v != 0; v--) { + float color[4]; + get_pixel(ibuf, v, color); + /* Green. */ + if (color[1] == 1.0f) { + set_pixel(ibuf, v, fill_col[0]); + } + else if (color[0] == 1.0f) { + set_pixel(ibuf, v, fill_col[1]); + } + else { + set_pixel(ibuf, v, fill_col[2]); + } + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } + + tgpf->ima->id.tag |= LIB_TAG_DOIT; +} + +/* Mark and clear processed areas. */ +static void gpencil_erase_processed_area(tGPDfill *tgpf) +{ + ImBuf *ibuf; + void *lock; + const float blue_col[4] = {0.0f, 0.0f, 1.0f, 1.0f}; + const float clear_col[4] = {1.0f, 0.0f, 0.0f, 1.0f}; + tGPspoint *point2D; + + if (tgpf->sbuffer_used == 0) { + return; + } + + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + point2D = (tGPspoint *)tgpf->sbuffer; + + /* First set in blue the perimeter. */ + for (int i = 0; i < tgpf->sbuffer_used && point2D; i++, point2D++) { + int image_idx = ibuf->x * (int)point2D->y + (int)point2D->x; + set_pixel(ibuf, image_idx, blue_col); + } + + /* Second, clean by lines any pixel between blue pixels. */ + float rgba[4]; + + for (int idy = 0; idy < ibuf->y; idy++) { + bool clear = false; + for (int idx = 0; idx < ibuf->x; idx++) { + int image_idx = ibuf->x * idy + idx; + get_pixel(ibuf, image_idx, rgba); + /* Blue. */ + if (rgba[2] == 1.0f) { + clear = true; + } + /* Red. */ + else if (rgba[0] == 1.0f) { + clear = false; + } + if (clear) { + set_pixel(ibuf, image_idx, clear_col); + } + } + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } + + tgpf->ima->id.tag |= LIB_TAG_DOIT; +} + /* Naive dilate * * Expand green areas into enclosing red areas. @@ -713,7 +847,7 @@ static void gpencil_set_borders(tGPDfill *tgpf, const bool transparent) * XXXX * ----------- */ -static void dilate(ImBuf *ibuf) +static void dilate_shape(ImBuf *ibuf) { BLI_Stack *stack = BLI_stack_new(sizeof(int), __func__); const float green[4] = {0.0f, 1.0f, 0.0f, 1.0f}; @@ -812,7 +946,7 @@ static void dilate(ImBuf *ibuf) * This is a Blender customized version of the general algorithm described * in https://en.wikipedia.org/wiki/Moore_neighborhood */ -static void gpencil_get_outline_points(tGPDfill *tgpf) +static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate) { ImBuf *ibuf; float rgba[4]; @@ -844,8 +978,10 @@ static void gpencil_get_outline_points(tGPDfill *tgpf) ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); int imagesize = ibuf->x * ibuf->y; - /* dilate */ - dilate(ibuf); + /* Dilate. */ + if (dilate) { + dilate_shape(ibuf); + } /* find the initial point to start outline analysis */ for (int idx = imagesize - 1; idx != 0; idx--) { @@ -979,12 +1115,12 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) } /* create array of points using stack as source */ -static void gpencil_points_from_stack(tGPDfill *tgpf) +static int gpencil_points_from_stack(tGPDfill *tgpf) { tGPspoint *point2D; int totpoints = BLI_stack_count(tgpf->stack); if (totpoints == 0) { - return; + return 0; } tgpf->sbuffer_used = (short)totpoints; @@ -1002,6 +1138,8 @@ static void gpencil_points_from_stack(tGPDfill *tgpf) point2D->time = 0.0f; point2D++; } + + return totpoints; } /* create a grease pencil stroke using points in buffer */ @@ -1247,6 +1385,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *UNUSED(op)) /* save filling parameters */ Brush *brush = BKE_paint_brush(&ts->gp_paint->paint); + tgpf->brush = brush; tgpf->flag = brush->gpencil_settings->flag; tgpf->fill_leak = brush->gpencil_settings->fill_leak; tgpf->fill_threshold = brush->gpencil_settings->fill_threshold; @@ -1412,6 +1551,10 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) { tGPDfill *tgpf = op->customdata; Scene *scene = tgpf->scene; + Brush *brush = tgpf->brush; + BrushGpencilSettings *brush_settings = brush->gpencil_settings; + const bool is_brush_inv = brush_settings->fill_direction == BRUSH_DIR_IN; + const bool is_inverted = (is_brush_inv && !event->ctrl) || (!is_brush_inv && event->ctrl); int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through */ @@ -1440,6 +1583,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) tgpf->active_cfra = CFRA; /* render screen to temp image */ + int totpoints = 1; if (gpencil_render_offscreen(tgpf)) { /* Set red borders to create a external limit. */ @@ -1448,30 +1592,45 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) /* apply boundary fill */ gpencil_boundaryfill_area(tgpf); + /* Invert direction if press Ctrl. */ + if (is_inverted) { + gpencil_invert_image(tgpf); + } + /* Clean borders to avoid infinite loops. */ gpencil_set_borders(tgpf, false); - /* analyze outline */ - gpencil_get_outline_points(tgpf); + while (totpoints > 0) { + /* analyze outline */ + gpencil_get_outline_points(tgpf, (totpoints == 1) ? true : false); - /* create array of points from stack */ - gpencil_points_from_stack(tgpf); + /* create array of points from stack */ + totpoints = gpencil_points_from_stack(tgpf); - /* create z-depth array for reproject */ - gpencil_get_depth_array(tgpf); + /* create z-depth array for reproject */ + gpencil_get_depth_array(tgpf); - /* create stroke and reproject */ - gpencil_stroke_from_buffer(tgpf); - } + /* create stroke and reproject */ + gpencil_stroke_from_buffer(tgpf); - /* free temp stack data */ - if (tgpf->stack) { - BLI_stack_free(tgpf->stack); - } + if (is_inverted) { + gpencil_erase_processed_area(tgpf); + } + else { + /* Exit of the loop. */ + totpoints = 0; + } - /* Free memory. */ - MEM_SAFE_FREE(tgpf->sbuffer); - MEM_SAFE_FREE(tgpf->depth_arr); + /* free temp stack data */ + if (tgpf->stack) { + BLI_stack_free(tgpf->stack); + } + + /* Free memory. */ + MEM_SAFE_FREE(tgpf->sbuffer); + MEM_SAFE_FREE(tgpf->depth_arr); + } + } /* restore size */ tgpf->region->winx = (short)tgpf->bwinx; |