Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntonio Vazquez <blendergit@gmail.com>2021-02-08 18:28:42 +0300
committerAntonio Vazquez <blendergit@gmail.com>2021-02-09 18:00:36 +0300
commit1352d81b174726639bbfb6f7aa32dbadf188a8dd (patch)
treeb7bc33114a93c86313b774d8fa05e1214c6844c3 /source/blender/editors/gpencil
parent5213b18eb2d57153e0e394190af1624401fed74b (diff)
GPencil: Fill tool refactor and Multiframe in Draw mode
This commit is a refactor of the fill tool to solve several problems we had since the first version of the tool. Changes: * The filling speed has been improved for each step of the process with the optimization of each algorithm/function. * New `AutoFit` option to fill areas outside of the viewport. When enable, the total size of the frame is calculated to fit the filling area. * New support multiframe filling. Now it is possible to fill multiple similar frames in one go. * New `Stroke Extension` option to create temporary closing strokes. These strokes can be displayed and adjusted dynamically using wheel mouse or PageUp/Down keys. * Parameter `Resolution` now is named `Precision` and has been moved to topbar. * `Resolution` now has decimals and can be lower than 1 to allow quick filling in storyboarding workflows. Maximum value has been set as 5. * Parameter `Simplify` has been moved to Advanced panel. * Improved fill outline detection. In some cases, the outline penetrated the area to be filled with unexpected results. * Fixes some corner case bugs with infinite loops. As a result of this refactor, also these new functionalities has been added. * New support for multiframe in `Draw` mode. Any drawing in active frame is duplicated to all selected frame. * New multiframe display mode. Keyframes before or after of the active frame are displayed using onion colors. This can be disable using Onion overlay options.
Diffstat (limited to 'source/blender/editors/gpencil')
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c11
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c10
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c115
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c1078
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c77
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c14
7 files changed, 943 insertions, 368 deletions
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 4e2951c3571..751f8333aaa 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -171,7 +171,8 @@ static void gpencil_draw_stroke_3d(tGPDdraw *tgpw,
int totpoints = tgpw->gps->totpoints;
const float viewport[2] = {(float)tgpw->winx, (float)tgpw->winy};
- float curpressure = points[0].pressure;
+ const float min_thickness = 0.05f;
+
float fpt[3];
/* if cyclic needs more vertex */
@@ -205,7 +206,6 @@ static void gpencil_draw_stroke_3d(tGPDdraw *tgpw,
immUniform1i("fill_stroke", (int)tgpw->is_fill_stroke);
/* draw stroke curve */
- GPU_line_width(max_ff(curpressure * thickness, 1.0f));
immBeginAtMost(GPU_PRIM_LINE_STRIP_ADJ, totpoints + cyclic_add + 2);
const bGPDspoint *pt = points;
@@ -215,18 +215,19 @@ static void gpencil_draw_stroke_3d(tGPDdraw *tgpw,
gpencil_set_point_varying_color(points, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
if ((cyclic) && (totpoints > 2)) {
- immAttr1f(attr_id.thickness, max_ff((points + totpoints - 1)->pressure * thickness, 1.0f));
+ immAttr1f(attr_id.thickness,
+ max_ff((points + totpoints - 1)->pressure * thickness, min_thickness));
mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 1)->x);
}
else {
- immAttr1f(attr_id.thickness, max_ff((points + 1)->pressure * thickness, 1.0f));
+ immAttr1f(attr_id.thickness, max_ff((points + 1)->pressure * thickness, min_thickness));
mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x);
}
immVertex3fv(attr_id.pos, fpt);
}
/* set point */
gpencil_set_point_varying_color(pt, ink, attr_id.color, (bool)tgpw->is_fill_stroke);
- immAttr1f(attr_id.thickness, max_ff(pt->pressure * thickness, 1.0f));
+ immAttr1f(attr_id.thickness, max_ff(pt->pressure * thickness, min_thickness));
mul_v3_m4v3(fpt, tgpw->diff_mat, &pt->x);
immVertex3fv(attr_id.pos, fpt);
}
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index e297a806895..3be913f342d 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -466,9 +466,15 @@ static int gpencil_layer_copy_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* make copy of layer, and add it immediately after the existing layer */
+ /* Make copy of layer, and add it immediately after or before the existing layer. */
new_layer = BKE_gpencil_layer_duplicate(gpl, true, dup_strokes);
- BLI_insertlinkafter(&gpd->layers, gpl, new_layer);
+ if (dup_strokes) {
+ BLI_insertlinkafter(&gpd->layers, gpl, new_layer);
+ }
+ else {
+ /* For empty strokes is better add below. */
+ BLI_insertlinkbefore(&gpd->layers, gpl, new_layer);
+ }
/* ensure new layer has a unique name, and is now the active layer */
BLI_uniquename(&gpd->layers,
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index b5269bbfacf..aeff2acb04d 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -4948,11 +4948,14 @@ static int gpencil_cutter_lasso_select(bContext *C,
GPencilTestFn is_inside_fn,
void *user_data)
{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Object *obact = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
ScrArea *area = CTX_wm_area(C);
ToolSettings *ts = CTX_data_tool_settings(C);
const float scale = ts->gp_sculpt.isect_threshold;
const bool flat_caps = RNA_boolean_get(op->ptr, "flat_caps");
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
bGPDspoint *pt;
GP_SpaceConversion gsc = {NULL};
@@ -4979,57 +4982,87 @@ static int gpencil_cutter_lasso_select(bContext *C,
}
CTX_DATA_END;
- /* select points */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- int tot_inside = 0;
- const int oldtot = gps->totpoints;
- for (int i = 0; i < gps->totpoints; i++) {
- pt = &gps->points[i];
- if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) {
- continue;
- }
- /* convert point coords to screen-space */
- const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data);
- if (is_inside) {
- tot_inside++;
- changed = true;
- pt->flag |= GP_SPOINT_SELECT;
- gps->flag |= GP_STROKE_SELECT;
- float r_hita[3], r_hitb[3];
- if (gps->totpoints > 1) {
- ED_gpencil_select_stroke_segment(gpd, gpl, gps, pt, true, true, scale, r_hita, r_hitb);
+ /* Select points */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if ((gpl->flag & GP_LAYER_LOCKED) || ((gpl->flag & GP_LAYER_HIDE))) {
+ continue;
+ }
+
+ float diff_mat[4][4];
+ BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
+
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf == NULL) {
+ continue;
}
- /* avoid infinite loops */
- if (gps->totpoints > oldtot) {
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ } /* check if the color is editable */
+ if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
+ continue;
+ }
+ int tot_inside = 0;
+ const int oldtot = gps->totpoints;
+ for (int i = 0; i < gps->totpoints; i++) {
+ pt = &gps->points[i];
+ if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) {
+ continue;
+ }
+ /* convert point coords to screen-space */
+ const bool is_inside = is_inside_fn(gps, pt, &gsc, diff_mat, user_data);
+ if (is_inside) {
+ tot_inside++;
+ changed = true;
+ pt->flag |= GP_SPOINT_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
+ float r_hita[3], r_hitb[3];
+ if (gps->totpoints > 1) {
+ ED_gpencil_select_stroke_segment(
+ gpd, gpl, gps, pt, true, true, scale, r_hita, r_hitb);
+ }
+ /* avoid infinite loops */
+ if (gps->totpoints > oldtot) {
+ break;
+ }
+ }
+ }
+ /* if mark all points inside lasso set to remove all stroke */
+ if ((tot_inside == oldtot) || ((tot_inside == 1) && (oldtot == 2))) {
+ for (int i = 0; i < gps->totpoints; i++) {
+ pt = &gps->points[i];
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+ }
+ }
+ /* if not multiedit, exit loop. */
+ if (!is_multiedit) {
break;
}
}
}
- /* if mark all points inside lasso set to remove all stroke */
- if ((tot_inside == oldtot) || ((tot_inside == 1) && (oldtot == 2))) {
- for (int i = 0; i < gps->totpoints; i++) {
- pt = &gps->points[i];
- pt->flag |= GP_SPOINT_SELECT;
- }
- }
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
- /* dissolve selected points */
+ /* Dissolve selected points. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- if (gpl->flag & GP_LAYER_LOCKED) {
- continue;
- }
-
- bGPDframe *gpf = gpl->actframe;
- if (gpf == NULL) {
- continue;
- }
- LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- gpencil_cutter_dissolve(gpd, gpl, gps, flat_caps);
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ bGPDframe *gpf_act = gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ gpl->actframe = gpf;
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ gpencil_cutter_dissolve(gpd, gpl, gps, flat_caps);
+ }
+ }
+ /* if not multiedit, exit loop. */
+ if (!is_multiedit) {
+ break;
}
}
+ gpl->actframe = gpf_act;
}
/* updates */
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index e3e73d5deb5..f595d0f5b35 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -26,6 +26,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_stack.h"
#include "BLI_utildefines.h"
@@ -83,6 +84,15 @@
#define LEAK_VERT 1
#define MIN_WINDOW_SIZE 128
+/* Set to 1 to debug filling internal image. By default, the value must be 0. */
+#define FILL_DEBUG 0
+
+/* Duplicated: etempFlags */
+const enum {
+ GP_DRAWFILLS_NOSTATUS = (1 << 0), /* don't draw status info */
+ GP_DRAWFILLS_ONLY3D = (1 << 1), /* only draw 3d-strokes */
+};
+
/* Temporary fill operation data (op->customdata) */
typedef struct tGPDfill {
bContext *C;
@@ -112,6 +122,8 @@ typedef struct tGPDfill {
struct bGPDlayer *gpl;
/** frame */
struct bGPDframe *gpf;
+ /** Temp mouse position stroke. */
+ struct bGPDstroke *gps_mouse;
/** flags */
short flag;
@@ -119,9 +131,12 @@ typedef struct tGPDfill {
short oldkey;
/** send to back stroke */
bool on_back;
-
+ /** Flag for render mode */
+ bool is_render;
+ /** Flag to check something was done. */
+ bool done;
/** mouse fill center position */
- int center[2];
+ int mouse[2];
/** windows width */
int sizex;
/** window height */
@@ -163,8 +178,178 @@ typedef struct tGPDfill {
int bwiny;
rcti brect;
+ /* Space Conversion Data */
+ GP_SpaceConversion gsc;
+
+ /** Zoom factor. */
+ float zoom;
+
+ /** Factor of extension. */
+ float fill_extend_fac;
+
} tGPDfill;
+bool skip_layer_check(short fill_layer_mode, int gpl_active_index, int gpl_index);
+static void gpencil_draw_boundary_lines(const struct bContext *UNUSED(C), struct tGPDfill *tgpf);
+
+/* Delete any temporary stroke. */
+static void gpencil_delete_temp_stroke_extension(tGPDfill *tgpf, const bool all_frames)
+{
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &tgpf->gpd->layers) {
+ if (gpl->flag & GP_LAYER_HIDE) {
+ continue;
+ }
+
+ bGPDframe *init_gpf = (all_frames) ? gpl->frames.first :
+ BKE_gpencil_layer_frame_get(
+ gpl, tgpf->active_cfra, GP_GETFRAME_USE_PREV);
+ if (init_gpf == NULL) {
+ continue;
+ }
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ /* free stroke */
+ if ((gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG)) {
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+ }
+ }
+ if (!all_frames) {
+ break;
+ }
+ }
+ }
+}
+
+static void extrapolate_points_by_length(bGPDspoint *a,
+ bGPDspoint *b,
+ float length,
+ float r_point[3])
+{
+ float ab[3];
+ sub_v3_v3v3(ab, &b->x, &a->x);
+ normalize_v3(ab);
+ mul_v3_fl(ab, length);
+ add_v3_v3v3(r_point, &b->x, ab);
+}
+
+/* Loop all layers create stroke extensions. */
+static void gpencil_create_extensions(tGPDfill *tgpf)
+{
+ Object *ob = tgpf->ob;
+ bGPdata *gpd = tgpf->gpd;
+ Brush *brush = tgpf->brush;
+ BrushGpencilSettings *brush_settings = brush->gpencil_settings;
+
+ 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) {
+ if (gpl->flag & GP_LAYER_HIDE) {
+ continue;
+ }
+
+ /* Decide if the strokes of layers are included or not depending on the layer mode. */
+ const int gpl_index = BLI_findindex(&gpd->layers, gpl);
+ bool skip = skip_layer_check(brush_settings->fill_layer_mode, gpl_active_index, gpl_index);
+ if (skip) {
+ continue;
+ }
+
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_USE_PREV);
+ if (gpf == NULL) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* Check if stroke can be drawn. */
+ if ((gps->points == NULL) || (gps->totpoints < 2)) {
+ continue;
+ }
+ if (gps->flag & (GP_STROKE_NOFILL | GP_STROKE_TAG)) {
+ continue;
+ }
+ /* Check if the color is visible. */
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ if ((gp_style == NULL) || (gp_style->flag & GP_MATERIAL_HIDE)) {
+ continue;
+ }
+
+ /* Extend start. */
+ bGPDspoint *pt0 = &gps->points[1];
+ bGPDspoint *pt1 = &gps->points[0];
+ bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
+ gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
+ BLI_addtail(&gpf->strokes, gps_new);
+
+ bGPDspoint *pt = &gps_new->points[0];
+ copy_v3_v3(&pt->x, &pt1->x);
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+
+ pt = &gps_new->points[1];
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+ extrapolate_points_by_length(pt0, pt1, tgpf->fill_extend_fac * 0.1f, &pt->x);
+
+ /* Extend end. */
+ pt0 = &gps->points[gps->totpoints - 2];
+ pt1 = &gps->points[gps->totpoints - 1];
+ gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
+ gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
+ BLI_addtail(&gpf->strokes, gps_new);
+
+ pt = &gps_new->points[0];
+ copy_v3_v3(&pt->x, &pt1->x);
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+
+ pt = &gps_new->points[1];
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+ extrapolate_points_by_length(pt0, pt1, tgpf->fill_extend_fac * 0.1f, &pt->x);
+ }
+ }
+}
+
+static void gpencil_update_extend(tGPDfill *tgpf)
+{
+ gpencil_delete_temp_stroke_extension(tgpf, false);
+
+ if (tgpf->fill_extend_fac > 0.0f) {
+ gpencil_create_extensions(tgpf);
+ }
+ WM_event_add_notifier(tgpf->C, NC_GPENCIL | NA_EDITED, NULL);
+}
+
+static bool gpencil_stroke_is_drawable(tGPDfill *tgpf, bGPDstroke *gps)
+{
+ if (tgpf->is_render) {
+ return true;
+ }
+
+ const bool show_help = (tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) != 0;
+ const bool show_extend = (tgpf->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES) != 0;
+ const bool is_extend = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG);
+
+ if ((!show_help) && (show_extend)) {
+ if (!is_extend) {
+ return false;
+ }
+ }
+
+ if ((show_help) && (!show_extend)) {
+ if (is_extend) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
/* draw a given stroke using same thickness and color for all points */
static void gpencil_draw_basic_stroke(tGPDfill *tgpf,
bGPDstroke *gps,
@@ -172,7 +357,8 @@ static void gpencil_draw_basic_stroke(tGPDfill *tgpf,
const bool cyclic,
const float ink[4],
const int flag,
- const float thershold)
+ const float thershold,
+ const float thickness)
{
bGPDspoint *points = gps->points;
@@ -182,9 +368,19 @@ static void gpencil_draw_basic_stroke(tGPDfill *tgpf,
int totpoints = gps->totpoints;
float fpt[3];
float col[4];
+ const float extend_col[4] = {0.0f, 1.0f, 1.0f, 1.0f};
+ const bool is_extend = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG);
- copy_v4_v4(col, ink);
+ if (!gpencil_stroke_is_drawable(tgpf, gps)) {
+ return;
+ }
+ if ((is_extend) && (!tgpf->is_render)) {
+ copy_v4_v4(col, extend_col);
+ }
+ else {
+ copy_v4_v4(col, ink);
+ }
/* if cyclic needs more vertex */
int cyclic_add = (cyclic) ? 1 : 0;
@@ -195,7 +391,7 @@ static void gpencil_draw_basic_stroke(tGPDfill *tgpf,
immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
/* draw stroke curve */
- GPU_line_width(1.0f);
+ GPU_line_width((!is_extend) ? thickness : thickness * 2.0f);
immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add);
const bGPDspoint *pt = points;
@@ -226,15 +422,77 @@ static void gpencil_draw_basic_stroke(tGPDfill *tgpf,
immUnbindProgram();
}
-/* loop all layers */
-static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
+static void draw_mouse_position(tGPDfill *tgpf)
{
- /* duplicated: etempFlags */
- enum {
- GP_DRAWFILLS_NOSTATUS = (1 << 0), /* don't draw status info */
- GP_DRAWFILLS_ONLY3D = (1 << 1), /* only draw 3d-strokes */
- };
+ if (tgpf->gps_mouse == NULL) {
+ return;
+ }
+ uchar mouse_color[4] = {0, 0, 255, 255};
+
+ bGPDspoint *pt = &tgpf->gps_mouse->points[0];
+ float point_size = (tgpf->zoom == 1.0f) ? 4.0f * tgpf->fill_factor :
+ (0.5f * tgpf->zoom) + tgpf->fill_factor;
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+
+ /* Draw mouse click position in Blue. */
+ immBindBuiltinProgram(GPU_SHADER_3D_POINT_FIXED_SIZE_VARYING_COLOR);
+ GPU_point_size(point_size);
+ immBegin(GPU_PRIM_POINTS, 1);
+ immAttr4ubv(col, mouse_color);
+ immVertex3fv(pos, &pt->x);
+ immEnd();
+ immUnbindProgram();
+}
+
+/* Helper: Check if must skip the layer */
+bool skip_layer_check(short fill_layer_mode, int gpl_active_index, int gpl_index)
+{
+ bool skip = false;
+
+ switch (fill_layer_mode) {
+ case GP_FILL_GPLMODE_ACTIVE: {
+ if (gpl_index != gpl_active_index) {
+ skip = true;
+ }
+ break;
+ }
+ case GP_FILL_GPLMODE_ABOVE: {
+ if (gpl_index != gpl_active_index + 1) {
+ skip = true;
+ }
+ break;
+ }
+ case GP_FILL_GPLMODE_BELOW: {
+ if (gpl_index != gpl_active_index - 1) {
+ skip = true;
+ }
+ break;
+ }
+ case GP_FILL_GPLMODE_ALL_ABOVE: {
+ if (gpl_index <= gpl_active_index) {
+ skip = true;
+ }
+ break;
+ }
+ case GP_FILL_GPLMODE_ALL_BELOW: {
+ if (gpl_index >= gpl_active_index) {
+ skip = true;
+ }
+ break;
+ }
+ case GP_FILL_GPLMODE_VISIBLE:
+ default:
+ break;
+ }
+
+ return skip;
+}
+/* Loop all layers to draw strokes. */
+static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
+{
Object *ob = tgpf->ob;
bGPdata *gpd = tgpf->gpd;
Brush *brush = tgpf->brush;
@@ -248,8 +506,8 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
tgpw.gpd = gpd;
tgpw.offsx = 0;
tgpw.offsy = 0;
- tgpw.winx = tgpf->region->winx;
- tgpw.winy = tgpf->region->winy;
+ tgpw.winx = tgpf->sizex;
+ tgpw.winy = tgpf->sizey;
tgpw.dflag = 0;
tgpw.disable_fill = 1;
tgpw.dflag |= (GP_DRAWFILLS_ONLY3D | GP_DRAWFILLS_NOSTATUS);
@@ -262,6 +520,9 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
const int gpl_active_index = BLI_findindex(&gpd->layers, gpl_active);
BLI_assert(gpl_active_index >= 0);
+ /* Draw blue point where click with mouse. */
+ draw_mouse_position(tgpf);
+
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* do not draw layer if hidden */
if (gpl->flag & GP_LAYER_HIDE) {
@@ -273,43 +534,8 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
/* Decide if the strokes of layers are included or not depending on the layer mode.
* Cannot skip the layer because it can use boundary strokes and must be used. */
- bool skip = false;
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) {
- skip = true;
- }
- break;
- }
- case GP_FILL_GPLMODE_ABOVE: {
- if (gpl_index != gpl_active_index + 1) {
- skip = true;
- }
- break;
- }
- case GP_FILL_GPLMODE_BELOW: {
- if (gpl_index != gpl_active_index - 1) {
- skip = true;
- }
- break;
- }
- case GP_FILL_GPLMODE_ALL_ABOVE: {
- if (gpl_index <= gpl_active_index) {
- skip = true;
- }
- break;
- }
- case GP_FILL_GPLMODE_ALL_BELOW: {
- if (gpl_index >= gpl_active_index) {
- skip = true;
- }
- break;
- }
- case GP_FILL_GPLMODE_VISIBLE:
- default:
- break;
- }
+ bool skip = skip_layer_check(brush_settings->fill_layer_mode, gpl_active_index, gpl_index);
/* if active layer and no keyframe, create a new one */
if (gpl == tgpf->gpl) {
@@ -352,17 +578,19 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
tgpw.gpf = gpf;
tgpw.t_gpf = gpf;
- /* reduce thickness to avoid gaps */
tgpw.is_fill_stroke = (tgpf->fill_draw_mode == GP_FILL_DMODE_CONTROL) ? false : true;
+ /* Reduce thickness to avoid gaps. */
tgpw.lthick = gpl->line_change;
tgpw.opacity = 1.0;
copy_v4_v4(tgpw.tintcolor, ink);
tgpw.onion = true;
tgpw.custonion = true;
- /* normal strokes */
+ /* Normal strokes. */
if (ELEM(tgpf->fill_draw_mode, GP_FILL_DMODE_STROKE, GP_FILL_DMODE_BOTH)) {
- ED_gpencil_draw_fill(&tgpw);
+ if (gpencil_stroke_is_drawable(tgpf, gps) && ((gps->flag & GP_STROKE_TAG) == 0)) {
+ ED_gpencil_draw_fill(&tgpw);
+ }
}
/* 3D Lines with basic shapes and invisible lines */
@@ -373,7 +601,8 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
gps->flag & GP_STROKE_CYCLIC,
ink,
tgpf->flag,
- tgpf->fill_threshold);
+ tgpf->fill_threshold,
+ 1.0f);
}
}
}
@@ -392,8 +621,8 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf)
}
/* set temporary new size */
- tgpf->bwinx = tgpf->region->winx;
- tgpf->bwiny = tgpf->region->winy;
+ tgpf->bwinx = tgpf->region->sizex;
+ tgpf->bwiny = tgpf->region->sizey;
tgpf->brect = tgpf->region->winrct;
/* resize region */
@@ -408,12 +637,6 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf)
tgpf->sizex = (int)tgpf->region->winx;
tgpf->sizey = (int)tgpf->region->winy;
- /* adjust center */
- float center[2];
- center[0] = (float)tgpf->center[0] * ((float)tgpf->region->winx / (float)tgpf->bwinx);
- center[1] = (float)tgpf->center[1] * ((float)tgpf->region->winy / (float)tgpf->bwiny);
- round_v2i_v2fl(tgpf->center, center);
-
char err_out[256] = "unknown";
GPUOffScreen *offscreen = GPU_offscreen_create(tgpf->sizex, tgpf->sizey, true, false, err_out);
if (offscreen == NULL) {
@@ -422,7 +645,7 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf)
}
GPU_offscreen_bind(offscreen, true);
- uint flag = IB_rect | IB_rectfloat;
+ uint flag = IB_rectfloat;
ImBuf *ibuf = IMB_allocImBuf(tgpf->sizex, tgpf->sizey, 32, flag);
rctf viewplane;
@@ -437,6 +660,21 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf)
&clip_start,
&clip_end,
NULL);
+
+ /* Rescale viewplane to fit all strokes. */
+ float width = viewplane.xmax - viewplane.xmin;
+ float height = viewplane.ymax - viewplane.ymin;
+
+ float width_new = width * tgpf->zoom;
+ float height_new = height * tgpf->zoom;
+ float scale_x = (width_new - width) / 2.0f;
+ float scale_y = (height_new - height) / 2.0f;
+
+ viewplane.xmin -= scale_x;
+ viewplane.xmax += scale_x;
+ viewplane.ymin -= scale_y;
+ viewplane.ymax += scale_y;
+
if (is_ortho) {
orthographic_m4(winmat,
viewplane.xmin,
@@ -506,35 +744,23 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf)
/* return pixel data (rgba) at index */
static void get_pixel(const ImBuf *ibuf, const int idx, float r_col[4])
{
- if (ibuf->rect_float) {
- const float *frgba = &ibuf->rect_float[idx * 4];
- copy_v4_v4(r_col, frgba);
- }
- else {
- /* XXX: This case probably doesn't happen, as we only write to the float buffer,
- * but we get compiler warnings about uninitialized vars otherwise
- */
- BLI_assert(!"gpencil_fill.c - get_pixel() non-float case is used!");
- zero_v4(r_col);
- }
+ BLI_assert(ibuf->rect_float != NULL);
+ memcpy(r_col, &ibuf->rect_float[idx * 4], sizeof(float[4]));
}
/* set pixel data (rgba) at index */
static void set_pixel(ImBuf *ibuf, int idx, const float col[4])
{
- // BLI_assert(idx <= ibuf->x * ibuf->y);
- if (ibuf->rect) {
- uint *rrect = &ibuf->rect[idx];
- uchar ccol[4];
-
- rgba_float_to_uchar(ccol, col);
- *rrect = *((uint *)ccol);
- }
+ BLI_assert(ibuf->rect_float != NULL);
+ float *rrectf = &ibuf->rect_float[idx * 4];
+ copy_v4_v4(rrectf, col);
+}
- if (ibuf->rect_float) {
- float *rrectf = &ibuf->rect_float[idx * 4];
- copy_v4_v4(rrectf, col);
- }
+/* Helper: Check if one image row is empty. */
+static bool is_row_filled(const ImBuf *ibuf, const int row_index)
+{
+ float *row = &ibuf->rect_float[ibuf->x * 4 * row_index];
+ return (row[0] == 0.0f && memcmp(row, row + 1, ((ibuf->x * 4) - 1) * sizeof(float)) != 0);
}
/**
@@ -542,6 +768,9 @@ static void set_pixel(ImBuf *ibuf, int idx, const float col[4])
* this is used for strokes with small gaps between them to get a full fill
* and do not get a full screen fill.
*
+ * This function assumes that if the furthest pixel is occupied,
+ * the other pixels are occupied.
+ *
* \param ibuf: Image pixel data.
* \param maxpixel: Maximum index.
* \param limit: Limit of pixels to analyze.
@@ -551,10 +780,10 @@ static void set_pixel(ImBuf *ibuf, int idx, const float col[4])
static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index, int type)
{
float rgba[4];
- int i;
int pt;
bool t_a = false;
bool t_b = false;
+ const int extreme = limit - 1;
/* Horizontal leak (check vertical pixels)
* X
@@ -565,37 +794,29 @@ static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index
*/
if (type == LEAK_HORZ) {
/* pixels on top */
- for (i = 1; i <= limit; i++) {
- pt = index + (ibuf->x * i);
- if (pt <= maxpixel) {
- get_pixel(ibuf, pt, rgba);
- if (rgba[0] == 1.0f) {
- t_a = true;
- break;
- }
- }
- else {
- /* edge of image*/
+ pt = index + (ibuf->x * extreme);
+ if (pt <= maxpixel) {
+ get_pixel(ibuf, pt, rgba);
+ if (rgba[0] == 1.0f) {
t_a = true;
- break;
}
}
+ else {
+ /* edge of image*/
+ t_a = true;
+ }
/* pixels on bottom */
- for (i = 1; i <= limit; i++) {
- pt = index - (ibuf->x * i);
- if (pt >= 0) {
- get_pixel(ibuf, pt, rgba);
- if (rgba[0] == 1.0f) {
- t_b = true;
- break;
- }
- }
- else {
- /* edge of image*/
+ pt = index - (ibuf->x * extreme);
+ if (pt >= 0) {
+ get_pixel(ibuf, pt, rgba);
+ if (rgba[0] == 1.0f) {
t_b = true;
- break;
}
}
+ else {
+ /* edge of image*/
+ t_b = true;
+ }
}
/* Vertical leak (check horizontal pixels)
@@ -609,35 +830,27 @@ static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index
int higpix = lowpix + ibuf->x - 1;
/* pixels to right */
- for (i = 0; i < limit; i++) {
- pt = index - (limit - i);
- if (pt >= lowpix) {
- get_pixel(ibuf, pt, rgba);
- if (rgba[0] == 1.0f) {
- t_a = true;
- break;
- }
- }
- else {
- t_a = true; /* edge of image*/
- break;
+ pt = index - extreme;
+ if (pt >= lowpix) {
+ get_pixel(ibuf, pt, rgba);
+ if (rgba[0] == 1.0f) {
+ t_a = true;
}
}
+ else {
+ t_a = true; /* edge of image*/
+ }
/* pixels to left */
- for (i = 0; i < limit; i++) {
- pt = index + (limit - i);
- if (pt <= higpix) {
- get_pixel(ibuf, pt, rgba);
- if (rgba[0] == 1.0f) {
- t_b = true;
- break;
- }
- }
- else {
- t_b = true; /* edge of image */
- break;
+ pt = index + extreme;
+ if (pt <= higpix) {
+ get_pixel(ibuf, pt, rgba);
+ if (rgba[0] == 1.0f) {
+ t_b = true;
}
}
+ else {
+ t_b = true; /* edge of image */
+ }
}
return (bool)(t_a && t_b);
}
@@ -660,10 +873,21 @@ static void gpencil_boundaryfill_area(tGPDfill *tgpf)
BLI_Stack *stack = BLI_stack_new(sizeof(int), __func__);
- /* calculate index of the seed point using the position of the mouse */
- int index = (tgpf->sizex * tgpf->center[1]) + tgpf->center[0];
+ /* Calculate index of the seed point using the position of the mouse looking
+ * for a blue pixel. */
+ int index = -1;
+ for (int i = 0; i < maxpixel; i++) {
+ get_pixel(ibuf, i, rgba);
+ if (rgba[2] == 1.0f) {
+ index = i;
+ break;
+ }
+ }
+
if ((index >= 0) && (index <= maxpixel)) {
- BLI_stack_push(stack, &index);
+ if (!FILL_DEBUG) {
+ BLI_stack_push(stack, &index);
+ }
}
/* the fill use a stack to save the pixel list instead of the common recursive
@@ -774,18 +998,24 @@ 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];
get_pixel(ibuf, v, color);
- /* Green. */
+ /* Green->Red. */
if (color[1] == 1.0f) {
set_pixel(ibuf, v, fill_col[0]);
}
+ /* 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. */
+ int offset = (v % ibuf->x < center) ? 1 : -1;
+ set_pixel(ibuf, v + offset, fill_col[1]);
}
else {
+ /* Set to Transparent. */
set_pixel(ibuf, v, fill_col[2]);
}
}
@@ -822,21 +1052,37 @@ static void gpencil_erase_processed_area(tGPDfill *tgpf)
float rgba[4];
for (int idy = 0; idy < ibuf->y; idy++) {
- bool clear = false;
+ int init = -1;
+ int end = -1;
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;
+ if (init < 0) {
+ init = image_idx;
+ }
+ else {
+ end = image_idx;
+ }
}
/* Red. */
else if (rgba[0] == 1.0f) {
- clear = false;
+ if (init > -1) {
+ for (int i = init; i <= max_ii(init, end); i++) {
+ set_pixel(ibuf, i, clear_col);
+ }
+ init = -1;
+ end = -1;
+ }
}
- if (clear) {
- set_pixel(ibuf, image_idx, clear_col);
+ }
+ /* Check last segment. */
+ if (init > -1) {
+ for (int i = init; i <= max_ii(init, end); i++) {
+ set_pixel(ibuf, i, clear_col);
}
+ set_pixel(ibuf, init, clear_col);
}
}
@@ -857,87 +1103,98 @@ static void gpencil_erase_processed_area(tGPDfill *tgpf)
* XXXX
* -----------
*/
-static void dilate_shape(ImBuf *ibuf)
+static bool dilate_shape(ImBuf *ibuf)
{
+ 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 maxpixel = (ibuf->x * ibuf->y) - 1;
/* detect pixels and expand into red areas */
- for (int v = maxpixel; v != 0; v--) {
- float color[4];
- int index;
- int tp = 0;
- int bm = 0;
- int lt = 0;
- int rt = 0;
- get_pixel(ibuf, v, color);
- if (color[1] == 1.0f) {
- /* pixel left */
- if (v - 1 >= 0) {
- index = v - 1;
- get_pixel(ibuf, index, color);
- if (color[0] == 1.0f) {
- BLI_stack_push(stack, &index);
- lt = index;
+ 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];
+ int index;
+ get_pixel(ibuf, v, color);
+ if (color[1] == 1.0f) {
+ int tp = 0;
+ int bm = 0;
+ int lt = 0;
+ int rt = 0;
+
+ /* pixel left */
+ if (v - 1 >= 0) {
+ index = v - 1;
+ get_pixel(ibuf, index, color);
+ if (color[0] == 1.0f) {
+ BLI_stack_push(stack, &index);
+ lt = index;
+ }
}
- }
- /* pixel right */
- if (v + 1 <= maxpixel) {
- index = v + 1;
- get_pixel(ibuf, index, color);
- if (color[0] == 1.0f) {
- BLI_stack_push(stack, &index);
- rt = index;
+ /* pixel right */
+ if (v + 1 <= maxpixel) {
+ index = v + 1;
+ get_pixel(ibuf, index, color);
+ if (color[0] == 1.0f) {
+ BLI_stack_push(stack, &index);
+ rt = index;
+ }
}
- }
- /* pixel top */
- if (v + ibuf->x <= maxpixel) {
- index = v + ibuf->x;
- get_pixel(ibuf, index, color);
- if (color[0] == 1.0f) {
- BLI_stack_push(stack, &index);
- tp = index;
+ /* pixel top */
+ if (v + (ibuf->x * 1) <= maxpixel) {
+ index = v + (ibuf->x * 1);
+ get_pixel(ibuf, index, color);
+ if (color[0] == 1.0f) {
+ BLI_stack_push(stack, &index);
+ tp = index;
+ }
}
- }
- /* pixel bottom */
- if (v - ibuf->x >= 0) {
- index = v - ibuf->x;
- get_pixel(ibuf, index, color);
- if (color[0] == 1.0f) {
- BLI_stack_push(stack, &index);
- bm = index;
+ /* pixel bottom */
+ if (v - (ibuf->x * 1) >= 0) {
+ index = v - (ibuf->x * 1);
+ get_pixel(ibuf, index, color);
+ if (color[0] == 1.0f) {
+ BLI_stack_push(stack, &index);
+ bm = index;
+ }
}
- }
- /* pixel top-left */
- if (tp && lt) {
- index = tp - 1;
- get_pixel(ibuf, index, color);
- if (color[0] == 1.0f) {
- BLI_stack_push(stack, &index);
+ /* pixel top-left */
+ if (tp && lt) {
+ index = tp - 1;
+ get_pixel(ibuf, index, color);
+ if (color[0] == 1.0f) {
+ BLI_stack_push(stack, &index);
+ }
}
- }
- /* pixel top-right */
- if (tp && rt) {
- index = tp + 1;
- get_pixel(ibuf, index, color);
- if (color[0] == 1.0f) {
- BLI_stack_push(stack, &index);
+ /* pixel top-right */
+ if (tp && rt) {
+ index = tp + 1;
+ get_pixel(ibuf, index, color);
+ if (color[0] == 1.0f) {
+ BLI_stack_push(stack, &index);
+ }
}
- }
- /* pixel bottom-left */
- if (bm && lt) {
- index = bm - 1;
- get_pixel(ibuf, index, color);
- if (color[0] == 1.0f) {
- BLI_stack_push(stack, &index);
+ /* pixel bottom-left */
+ if (bm && lt) {
+ index = bm - 1;
+ get_pixel(ibuf, index, color);
+ if (color[0] == 1.0f) {
+ BLI_stack_push(stack, &index);
+ }
}
- }
- /* pixel bottom-right */
- if (bm && rt) {
- index = bm + 1;
- get_pixel(ibuf, index, color);
- if (color[0] == 1.0f) {
- BLI_stack_push(stack, &index);
+ /* pixel bottom-right */
+ if (bm && rt) {
+ index = bm + 1;
+ get_pixel(ibuf, index, color);
+ if (color[0] == 1.0f) {
+ BLI_stack_push(stack, &index);
+ }
}
}
}
@@ -947,8 +1204,11 @@ static void dilate_shape(ImBuf *ibuf)
int v;
BLI_stack_pop(stack, &v);
set_pixel(ibuf, v, green);
+ done = true;
}
BLI_stack_free(stack);
+
+ return done;
}
/* Get the outline points of a shape using Moore Neighborhood algorithm
@@ -964,11 +1224,12 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
int v[2];
int boundary_co[2];
int start_co[2];
+ int first_co[2] = {-1, -1};
int backtracked_co[2];
int current_check_co[2];
int prev_check_co[2];
int backtracked_offset[1][2] = {{0, 0}};
- // bool boundary_found = false;
+ bool first_pixel = false;
bool start_found = false;
const int NEIGHBOR_COUNT = 8;
@@ -993,7 +1254,6 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
dilate_shape(ibuf);
}
- /* find the initial point to start outline analysis */
for (int idx = imagesize - 1; idx != 0; idx--) {
get_pixel(ibuf, idx, rgba);
if (rgba[1] == 1.0f) {
@@ -1046,12 +1306,17 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
cur_back_offset++;
loop++;
}
- /* current pixel is equal to starting pixel */
- if (boundary_co[0] == start_co[0] && boundary_co[1] == start_co[1]) {
+ /* Current pixel is equal to starting or firt pixel. */
+ if ((boundary_co[0] == start_co[0] && boundary_co[1] == start_co[1]) ||
+ (boundary_co[0] == first_co[0] && boundary_co[1] == first_co[1])) {
BLI_stack_pop(tgpf->stack, &v);
- // boundary_found = true;
break;
}
+
+ if (!first_pixel) {
+ first_pixel = true;
+ copy_v2_v2_int(first_co, boundary_co);
+ }
}
/* release ibuf */
@@ -1172,6 +1437,9 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
return;
}
+ /* Set as done. */
+ tgpf->done = true;
+
/* Get frame or create a new one. */
tgpf->gpf = BKE_gpencil_layer_frame_get(tgpf->gpl, tgpf->active_cfra, GP_GETFRAME_ADD_NEW);
@@ -1327,7 +1595,6 @@ static void gpencil_fill_draw_3d(const bContext *C, ARegion *UNUSED(region), voi
if (region != tgpf->region) {
return;
}
-
gpencil_draw_boundary_lines(C, tgpf);
}
@@ -1378,6 +1645,10 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *UNUSED(op))
tgpf->win = CTX_wm_window(C);
tgpf->active_cfra = CFRA;
+ /* Setup space conversions. */
+ gpencil_point_conversion_init(C, &tgpf->gsc);
+ tgpf->zoom = 1.0f;
+
/* set GP datablock */
tgpf->gpd = gpd;
tgpf->gpl = BKE_gpencil_layer_active_get(gpd);
@@ -1388,6 +1659,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *UNUSED(op))
tgpf->lock_axis = ts->gp_sculpt.lock_axis;
tgpf->oldkey = -1;
+ tgpf->is_render = false;
tgpf->sbuffer_used = 0;
tgpf->sbuffer = NULL;
tgpf->depth_arr = NULL;
@@ -1399,8 +1671,9 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *UNUSED(op))
tgpf->fill_threshold = brush->gpencil_settings->fill_threshold;
tgpf->fill_simplylvl = brush->gpencil_settings->fill_simplylvl;
tgpf->fill_draw_mode = brush->gpencil_settings->fill_draw_mode;
+ tgpf->fill_extend_fac = brush->gpencil_settings->fill_extend_fac;
tgpf->fill_factor = max_ff(GPENCIL_MIN_FILL_FAC,
- min_ff(brush->gpencil_settings->fill_factor, 8.0f));
+ min_ff(brush->gpencil_settings->fill_factor, GPENCIL_MAX_FILL_FAC));
tgpf->fill_leak = (int)ceil((float)brush->gpencil_settings->fill_leak * tgpf->fill_factor);
int totcol = tgpf->ob->totcol;
@@ -1426,7 +1699,6 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *UNUSED(op))
/* end operator */
static void gpencil_fill_exit(bContext *C, wmOperator *op)
{
- Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
/* clear undo stack */
@@ -1445,16 +1717,14 @@ static void gpencil_fill_exit(bContext *C, wmOperator *op)
MEM_SAFE_FREE(tgpf->sbuffer);
MEM_SAFE_FREE(tgpf->depth_arr);
+ /* Remove any temp stroke. */
+ gpencil_delete_temp_stroke_extension(tgpf, true);
+
/* remove drawing handler */
if (tgpf->draw_handle_3d) {
ED_region_draw_cb_exit(tgpf->region->type, tgpf->draw_handle_3d);
}
- /* Delete temp image. */
- if (tgpf->ima) {
- BKE_id_free(bmain, tgpf->ima);
- }
-
/* finally, free memory used by temp data */
MEM_freeN(tgpf);
}
@@ -1538,7 +1808,11 @@ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
tgpf = op->customdata;
/* Enable custom drawing handlers to show help lines */
- if (tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) {
+ const bool do_extend = (tgpf->fill_extend_fac > 0.0f);
+ const bool help_lines = ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) ||
+ ((tgpf->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES) && (do_extend)));
+
+ if (help_lines) {
tgpf->draw_handle_3d = ED_region_draw_cb_activate(
tgpf->region->type, gpencil_fill_draw_3d, tgpf, REGION_DRAW_POST_VIEW);
}
@@ -1556,17 +1830,210 @@ static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
return OPERATOR_RUNNING_MODAL;
}
+/* Helper: Calc the maximum bounding box size of strokes to get the zoom level of the viewport.
+ * For each stroke, the 2D projected bounding box is calculated and using this data, the total
+ * object bounding box (all strokes) is calculated. */
+static void gpencil_zoom_level_set(tGPDfill *tgpf)
+{
+ Brush *brush = tgpf->brush;
+ if (brush->gpencil_settings->flag & GP_BRUSH_FILL_FIT_DISABLE) {
+ tgpf->zoom = 1.0f;
+ return;
+ }
+
+ Object *ob = tgpf->ob;
+ bGPdata *gpd = tgpf->gpd;
+ BrushGpencilSettings *brush_settings = tgpf->brush->gpencil_settings;
+ 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);
+
+ /* Init maximum boundbox size. */
+ rctf rect_max;
+ const float winx_half = tgpf->region->winx / 2.0f;
+ const float winy_half = tgpf->region->winy / 2.0f;
+ BLI_rctf_init(&rect_max,
+ 0.0f - winx_half,
+ tgpf->region->winx + winx_half,
+ 0.0f - winy_half,
+ tgpf->region->winy + winy_half);
+
+ float objectbox_min[2], objectbox_max[2];
+ INIT_MINMAX2(objectbox_min, objectbox_max);
+ rctf rect_bound;
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if (gpl->flag & GP_LAYER_HIDE) {
+ continue;
+ }
+ float diff_mat[4][4];
+ /* calculate parent matrix */
+ BKE_gpencil_layer_transform_matrix_get(tgpf->depsgraph, ob, gpl, diff_mat);
+
+ /* Decide if the strokes of layers are included or not depending on the layer mode.
+ * Cannot skip the layer because it can use boundary strokes and must be used. */
+ const int gpl_index = BLI_findindex(&gpd->layers, gpl);
+ bool skip = skip_layer_check(brush_settings->fill_layer_mode, gpl_active_index, gpl_index);
+
+ /* Get frame to check. */
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_USE_PREV);
+ if (gpf == NULL) {
+ continue;
+ }
+
+ /* Read all strokes. */
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* check if stroke can be drawn */
+ if ((gps->points == NULL) || (gps->totpoints < 2)) {
+ continue;
+ }
+ /* check if the color is visible */
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ if ((gp_style == NULL) || (gp_style->flag & GP_MATERIAL_HIDE)) {
+ continue;
+ }
+
+ /* If the layer must be skipped, but the stroke is not boundary, skip stroke. */
+ if ((skip) && ((gps->flag & GP_STROKE_NOFILL) == 0)) {
+ continue;
+ }
+
+ float boundbox_min[2];
+ float boundbox_max[2];
+ ED_gpencil_projected_2d_bound_box(&tgpf->gsc, gps, diff_mat, boundbox_min, boundbox_max);
+ minmax_v2v2_v2(objectbox_min, objectbox_max, boundbox_min);
+ minmax_v2v2_v2(objectbox_min, objectbox_max, boundbox_max);
+ }
+ }
+ /* Clamp max bound box. */
+ BLI_rctf_init(
+ &rect_bound, objectbox_min[0], objectbox_max[0], objectbox_min[1], objectbox_max[1]);
+ float r_xy[2];
+ BLI_rctf_clamp(&rect_bound, &rect_max, r_xy);
+
+ /* Calculate total width used. */
+ float width = tgpf->region->winx;
+ if (rect_bound.xmin < 0.0f) {
+ width -= rect_bound.xmin;
+ }
+ if (rect_bound.xmax > tgpf->region->winx) {
+ width += rect_bound.xmax - tgpf->region->winx;
+ }
+ /* Calculate total height used. */
+ float height = tgpf->region->winy;
+ if (rect_bound.ymin < 0.0f) {
+ height -= rect_bound.ymin;
+ }
+ if (rect_bound.ymax > tgpf->region->winy) {
+ height += rect_bound.ymax - tgpf->region->winy;
+ }
+
+ width = ceilf(width);
+ height = ceilf(height);
+
+ float zoomx = (width > tgpf->region->winx) ? width / (float)tgpf->region->winx : 1.0f;
+ float zoomy = (height > tgpf->region->winy) ? height / (float)tgpf->region->winy : 1.0f;
+ if ((zoomx != 1.0f) || (zoomy != 1.0f)) {
+ tgpf->zoom = min_ff(max_ff(zoomx, zoomy) * 1.5f, 5.0f);
+ }
+}
+
+static bool gpencil_do_frame_fill(tGPDfill *tgpf, const bool is_inverted)
+{
+ wmWindow *win = CTX_wm_window(tgpf->C);
+
+ /* render screen to temp image */
+ int totpoints = 1;
+ if (gpencil_render_offscreen(tgpf)) {
+
+ /* Set red borders to create a external limit. */
+ gpencil_set_borders(tgpf, true);
+
+ /* 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);
+ WM_cursor_time(win, 50);
+ int totpoints_prv = 0;
+ int loop_limit = 0;
+ while (totpoints > 0) {
+ /* analyze outline */
+ gpencil_get_outline_points(tgpf, (totpoints == 1) ? true : false);
+
+ /* create array of points from stack */
+ totpoints = gpencil_points_from_stack(tgpf);
+
+ /* create z-depth array for reproject */
+ gpencil_get_depth_array(tgpf);
+
+ /* create stroke and reproject */
+ gpencil_stroke_from_buffer(tgpf);
+
+ if (is_inverted) {
+ gpencil_erase_processed_area(tgpf);
+ }
+ else {
+ /* Exit of the loop. */
+ totpoints = 0;
+ }
+
+ /* free temp stack data */
+ if (tgpf->stack) {
+ BLI_stack_free(tgpf->stack);
+ }
+ WM_cursor_time(win, 100);
+
+ /* Free memory. */
+ MEM_SAFE_FREE(tgpf->sbuffer);
+ MEM_SAFE_FREE(tgpf->depth_arr);
+
+ /* Limit very small areas. */
+ if (totpoints < 3) {
+ break;
+ }
+ /* Limit infinite loops is some corner cases. */
+ if (totpoints_prv == totpoints) {
+ loop_limit++;
+ if (loop_limit > 3) {
+ break;
+ }
+ }
+ totpoints_prv = totpoints;
+ }
+
+ /* Delete temp image. */
+ if ((tgpf->ima) && (!FILL_DEBUG)) {
+ BKE_id_free(tgpf->bmain, tgpf->ima);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
/* events handling during interactive part of operator */
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;
+ tgpf->on_back = RNA_boolean_get(op->ptr, "on_back");
+
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 */
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(tgpf->gpd);
+ const bool do_extend = (tgpf->fill_extend_fac > 0.0f);
+ const bool help_lines = ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) ||
+ ((tgpf->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES) && (do_extend)));
+ int estate = OPERATOR_RUNNING_MODAL;
switch (event->type) {
case EVT_ESCKEY:
@@ -1574,82 +2041,104 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
estate = OPERATOR_CANCELLED;
break;
case LEFTMOUSE:
- tgpf->on_back = RNA_boolean_get(op->ptr, "on_back");
/* first time the event is not enabled to show help lines. */
- if ((tgpf->oldkey != -1) || ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) == 0)) {
+ if ((tgpf->oldkey != -1) || (!help_lines)) {
ARegion *region = BKE_area_find_region_xy(
CTX_wm_area(C), RGN_TYPE_ANY, event->x, event->y);
if (region) {
bool in_bounds = false;
-
/* Perform bounds check */
in_bounds = BLI_rcti_isect_pt(&region->winrct, event->x, event->y);
if ((in_bounds) && (region->regiontype == RGN_TYPE_WINDOW)) {
- tgpf->center[0] = event->mval[0];
- tgpf->center[1] = event->mval[1];
-
- /* Set active frame as current for filling. */
- tgpf->active_cfra = CFRA;
+ tgpf->mouse[0] = event->mval[0];
+ tgpf->mouse[1] = event->mval[1];
+ tgpf->is_render = true;
+ /* Define Zoom level. */
+ gpencil_zoom_level_set(tgpf);
+
+ /* Create Temp stroke. */
+ tgpf->gps_mouse = BKE_gpencil_stroke_new(0, 1, 10.0f);
+ tGPspoint point2D;
+ bGPDspoint *pt = &tgpf->gps_mouse->points[0];
+ copy_v2fl_v2i(&point2D.x, tgpf->mouse);
+ gpencil_stroke_convertcoords_tpoint(
+ tgpf->scene, tgpf->region, tgpf->ob, &point2D, NULL, &pt->x);
+
+ /* If not multiframe and there is no frame in CFRA for the active layer, create
+ * a new frame before to make the hash function can find something. */
+ if (!is_multiedit) {
+ tgpf->gpf = BKE_gpencil_layer_frame_get(
+ tgpf->gpl, tgpf->active_cfra, GP_GETFRAME_ADD_NEW);
+ tgpf->gpf->flag |= GP_FRAME_SELECT;
+ }
- /* render screen to temp image */
- int totpoints = 1;
- if (gpencil_render_offscreen(tgpf)) {
+ /* Hash of selected frames.*/
+ GHash *frame_list = BLI_ghash_int_new_ex(__func__, 64);
+ BKE_gpencil_frame_selected_hash(tgpf->gpd, frame_list);
- /* Set red borders to create a external limit. */
- gpencil_set_borders(tgpf, true);
+ /* Loop all frames. */
+ wmWindow *win = CTX_wm_window(C);
- /* apply boundary fill */
- gpencil_boundaryfill_area(tgpf);
+ GHashIterator gh_iter;
+ int total = BLI_ghash_len(frame_list);
+ int i = 1;
+ GHASH_ITER (gh_iter, frame_list) {
+ /* Set active frame as current for filling. */
+ tgpf->active_cfra = POINTER_AS_INT(BLI_ghashIterator_getKey(&gh_iter));
+ int step = ((float)i / (float)total) * 100.0f;
+ WM_cursor_time(win, step);
- /* Invert direction if press Ctrl. */
- if (is_inverted) {
- gpencil_invert_image(tgpf);
+ if (do_extend) {
+ gpencil_update_extend(tgpf);
}
- /* Clean borders to avoid infinite loops. */
- gpencil_set_borders(tgpf, false);
-
- while (totpoints > 0) {
- /* analyze outline */
- gpencil_get_outline_points(tgpf, (totpoints == 1) ? true : false);
-
- /* create array of points from stack */
- totpoints = gpencil_points_from_stack(tgpf);
-
- /* create z-depth array for reproject */
- gpencil_get_depth_array(tgpf);
-
- /* create stroke and reproject */
- gpencil_stroke_from_buffer(tgpf);
-
- if (is_inverted) {
- gpencil_erase_processed_area(tgpf);
- }
- else {
- /* Exit of the loop. */
- totpoints = 0;
- }
-
- /* free temp stack data */
- if (tgpf->stack) {
- BLI_stack_free(tgpf->stack);
+ /* Repeat loop until get something. */
+ tgpf->done = false;
+ int loop_limit = 0;
+ while ((!tgpf->done) && (loop_limit < 2)) {
+ WM_cursor_time(win, loop_limit + 1);
+ /* Render screen to temp image and do fill. */
+ gpencil_do_frame_fill(tgpf, is_inverted);
+
+ /* restore size */
+ tgpf->region->winx = (short)tgpf->bwinx;
+ tgpf->region->winy = (short)tgpf->bwiny;
+ tgpf->region->winrct = tgpf->brect;
+ if (!tgpf->done) {
+ /* If the zoom was not set before, avoid a loop. */
+ if (tgpf->zoom == 1.0f) {
+ loop_limit++;
+ }
+ else {
+ tgpf->zoom = 1.0f;
+ tgpf->fill_factor = max_ff(
+ GPENCIL_MIN_FILL_FAC,
+ min_ff(brush->gpencil_settings->fill_factor, GPENCIL_MAX_FILL_FAC));
+ }
}
+ loop_limit++;
+ }
- /* Free memory. */
- MEM_SAFE_FREE(tgpf->sbuffer);
- MEM_SAFE_FREE(tgpf->depth_arr);
+ if (do_extend) {
+ gpencil_delete_temp_stroke_extension(tgpf, true);
}
+
+ i++;
}
+ WM_cursor_modal_restore(win);
+ /* Free hash table. */
+ BLI_ghash_free(frame_list, NULL, NULL);
- /* restore size */
- tgpf->region->winx = (short)tgpf->bwinx;
- tgpf->region->winy = (short)tgpf->bwiny;
- tgpf->region->winrct = tgpf->brect;
+ /* Free temp stroke. */
+ BKE_gpencil_free_stroke(tgpf->gps_mouse);
/* push undo data */
gpencil_undo_push(tgpf->gpd);
+ /* Save extend value for next operation. */
+ brush_settings->fill_extend_fac = tgpf->fill_extend_fac;
+
estate = OPERATOR_FINISHED;
}
else {
@@ -1660,8 +2149,29 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
estate = OPERATOR_CANCELLED;
}
}
+ else if (do_extend) {
+ gpencil_update_extend(tgpf);
+ }
tgpf->oldkey = event->type;
break;
+ case EVT_PAGEUPKEY:
+ case WHEELUPMOUSE:
+ if (tgpf->oldkey == 1) {
+ tgpf->fill_extend_fac -= (event->shift) ? 0.01f : 0.1f;
+ CLAMP_MIN(tgpf->fill_extend_fac, 0.0f);
+ gpencil_update_extend(tgpf);
+ }
+ break;
+ case EVT_PAGEDOWNKEY:
+ case WHEELDOWNMOUSE:
+ if (tgpf->oldkey == 1) {
+ tgpf->fill_extend_fac += (event->shift) ? 0.01f : 0.1f;
+ CLAMP_MAX(tgpf->fill_extend_fac, 100.0f);
+ gpencil_update_extend(tgpf);
+ }
+ break;
+ default:
+ break;
}
/* process last operations before exiting */
switch (estate) {
@@ -1674,7 +2184,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event)
gpencil_fill_exit(C, op);
break;
- case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH:
+ default:
break;
}
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 2d5491e7569..b833125cf34 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -1306,6 +1306,12 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, gps);
+ /* In Multiframe mode, duplicate the stroke in other frames. */
+ if (GPENCIL_MULTIEDIT_SESSIONS_ON(p->gpd)) {
+ const bool tail = (ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK);
+ BKE_gpencil_stroke_copy_to_keyframes(gpd, gpl, p->gpf, gps, tail);
+ }
+
gpencil_stroke_added_enable(p);
}
@@ -1323,10 +1329,8 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3])
}
/* only erase stroke points that are visible */
-static bool gpencil_stroke_eraser_is_occluded(tGPsdata *p,
- const bGPDspoint *pt,
- const int x,
- const int y)
+static bool gpencil_stroke_eraser_is_occluded(
+ tGPsdata *p, bGPDlayer *gpl, const bGPDspoint *pt, const int x, const int y)
{
Object *obact = (Object *)p->ownerPtr.data;
Brush *brush = p->brush;
@@ -1343,7 +1347,6 @@ static bool gpencil_stroke_eraser_is_occluded(tGPsdata *p,
if ((gp_settings != NULL) && (p->area->spacetype == SPACE_VIEW3D) &&
(gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) {
RegionView3D *rv3d = p->region->regiondata;
- bGPDlayer *gpl = p->gpl;
const int mval_i[2] = {x, y};
float mval_3d[3];
@@ -1454,6 +1457,7 @@ static void gpencil_stroke_soft_refine(bGPDstroke *gps)
/* eraser tool - evaluation per stroke */
static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
+ bGPDlayer *gpl,
bGPDframe *gpf,
bGPDstroke *gps,
const float mval[2],
@@ -1579,9 +1583,9 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
* - this assumes that linewidth is irrelevant
*/
if (gpencil_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) {
- if ((gpencil_stroke_eraser_is_occluded(p, pt0, pc0[0], pc0[1]) == false) ||
- (gpencil_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) ||
- (gpencil_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) {
+ if ((gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]) == false) ||
+ (gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]) == false) ||
+ (gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]) == false)) {
/* Point is affected: */
/* Adjust thickness
* - Influence of eraser falls off with distance from the middle of the eraser
@@ -1683,6 +1687,8 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
/* erase strokes which fall under the eraser strokes */
static void gpencil_stroke_doeraser(tGPsdata *p)
{
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(p->gpd);
+
rcti rect;
Brush *brush = p->brush;
Brush *eraser = p->eraser;
@@ -1723,40 +1729,53 @@ static void gpencil_stroke_doeraser(tGPsdata *p)
* on multiple layers...
*/
LISTBASE_FOREACH (bGPDlayer *, gpl, &p->gpd->layers) {
- bGPDframe *gpf = gpl->actframe;
-
/* only affect layer if it's editable (and visible) */
if (BKE_gpencil_layer_is_editable(gpl) == false) {
continue;
}
- if (gpf == NULL) {
+
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ if (init_gpf == NULL) {
continue;
}
- /* calculate difference matrix */
- BKE_gpencil_layer_transform_matrix_get(p->depsgraph, p->ob, gpl, p->diff_mat);
- /* loop over strokes, checking segments for intersections */
- LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(p->ob, gpl, gps) == false) {
- continue;
- }
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf == NULL) {
+ continue;
+ }
+ /* calculate difference matrix */
+ BKE_gpencil_layer_transform_matrix_get(p->depsgraph, p->ob, gpl, p->diff_mat);
+
+ /* loop over strokes, checking segments for intersections */
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_material_editable(p->ob, gpl, gps) == false) {
+ continue;
+ }
- /* Check if the stroke collide with mouse. */
- if (!ED_gpencil_stroke_check_collision(&p->gsc, gps, p->mval, calc_radius, p->diff_mat)) {
- continue;
- }
+ /* Check if the stroke collide with mouse. */
+ if (!ED_gpencil_stroke_check_collision(
+ &p->gsc, gps, p->mval, calc_radius, p->diff_mat)) {
+ continue;
+ }
- /* Not all strokes in the datablock may be valid in the current editor/context
- * (e.g. 2D space strokes in the 3D view, if the same datablock is shared)
- */
- if (ED_gpencil_stroke_can_use_direct(p->area, gps)) {
- gpencil_stroke_eraser_dostroke(p, gpf, gps, p->mval, calc_radius, &rect);
+ /* Not all strokes in the datablock may be valid in the current editor/context
+ * (e.g. 2D space strokes in the 3D view, if the same datablock is shared)
+ */
+ if (ED_gpencil_stroke_can_use_direct(p->area, gps)) {
+ gpencil_stroke_eraser_dostroke(p, gpl, gpf, gps, p->mval, calc_radius, &rect);
+ }
+ }
+
+ /* if not multiedit, exit loop*/
+ if (!is_multiedit) {
+ break;
+ }
}
}
}
}
-
/* ******************************************* */
/* Sketching Operator */
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index d94a02204a2..bcdde49b93d 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -1377,6 +1377,12 @@ static void gpencil_primitive_interaction_end(bContext *C,
BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps);
}
+ /* In Multiframe mode, duplicate the stroke in other frames. */
+ if (GPENCIL_MULTIEDIT_SESSIONS_ON(tgpi->gpd)) {
+ const bool tail = (ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK);
+ BKE_gpencil_stroke_copy_to_keyframes(tgpi->gpd, tgpi->gpl, gpf, gps, tail);
+ }
+
DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_COPY_ON_WRITE);
DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 42e3055ef65..9b12772bc9b 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -3032,12 +3032,12 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph,
}
}
-/* Helper to get the bigger 2D bound box points. */
-static void gpencil_projected_2d_bound_box(GP_SpaceConversion *gsc,
- bGPDstroke *gps,
- const float diff_mat[4][4],
- float r_min[2],
- float r_max[2])
+/* Get the bigger 2D bound box points. */
+void ED_gpencil_projected_2d_bound_box(GP_SpaceConversion *gsc,
+ bGPDstroke *gps,
+ const float diff_mat[4][4],
+ float r_min[2],
+ float r_max[2])
{
float bounds[8][2];
BoundBox bb;
@@ -3082,7 +3082,7 @@ bool ED_gpencil_stroke_check_collision(GP_SpaceConversion *gsc,
BKE_gpencil_stroke_boundingbox_calc(gps);
}
- gpencil_projected_2d_bound_box(gsc, gps, diff_mat, boundbox_min, boundbox_max);
+ ED_gpencil_projected_2d_bound_box(gsc, gps, diff_mat, boundbox_min, boundbox_max);
rcti rect_stroke = {boundbox_min[0], boundbox_max[0], boundbox_min[1], boundbox_max[1]};