Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
# SPDX-FileCopyrightText: 2012 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
set(INC
../include
../../makesrna
)
set(INC_SYS
)
set(SRC
mask_add.cc
mask_draw.cc
mask_edit.cc
mask_editaction.cc
mask_ops.cc
mask_query.cc
mask_relationships.cc
mask_select.cc
mask_shapekey.cc
mask_intern.hh
)
set(LIB
PRIVATE bf::blenkernel
PRIVATE bf::blenlib
PRIVATE bf::blentranslation
PRIVATE bf::depsgraph
PRIVATE bf::dna
PRIVATE bf::gpu
PRIVATE bf::intern::guardedalloc
PRIVATE bf::animrig
PRIVATE bf::windowmanager
)
blender_add_lib(bf_editor_mask "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@@ -0,0 +1,936 @@
/* SPDX-FileCopyrightText: 2012 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edmask
*/
#include <algorithm>
#include "MEM_guardedalloc.h"
#include "BKE_context.hh"
#include "BKE_curve.hh"
#include "BKE_mask.hh"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.h"
#include "DEG_depsgraph.hh"
#include "DNA_mask_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_mask.hh" /* own include */
#include "ED_select_utils.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "mask_intern.hh" /* own include */
namespace blender {
/* -------------------------------------------------------------------- */
/** \name Add Vertex
* \{ */
static void setup_vertex_point(Mask *mask,
MaskSpline *spline,
MaskSplinePoint *new_point,
const float point_co[2],
const float u,
const float ctime,
const MaskSplinePoint *reference_point,
const bool reference_adjacent)
{
const MaskSplinePoint *reference_parent_point = nullptr;
BezTriple *bezt;
float co[3];
copy_v2_v2(co, point_co);
co[2] = 0.0f;
/* point coordinate */
bezt = &new_point->bezt;
bezt->h1 = bezt->h2 = HD_ALIGN;
if (reference_point) {
if (reference_point->bezt.h1 == HD_VECT && reference_point->bezt.h2 == HD_VECT) {
/* If the reference point is sharp try using some smooth point as reference
* for handles.
*/
int point_index = reference_point - spline->points;
int delta = new_point == spline->points ? 1 : -1;
for (int i = 0; i < spline->tot_point - 1; i++) {
MaskSplinePoint *current_point;
point_index += delta;
if (point_index == -1 || point_index >= spline->tot_point) {
if (spline->flag & MASK_SPLINE_CYCLIC) {
if (point_index == -1) {
point_index = spline->tot_point - 1;
}
else if (point_index >= spline->tot_point) {
point_index = 0;
}
}
else {
break;
}
}
current_point = &spline->points[point_index];
if (current_point->bezt.h1 != HD_VECT || current_point->bezt.h2 != HD_VECT) {
bezt->h1 = bezt->h2 = std::max(current_point->bezt.h2, current_point->bezt.h1);
break;
}
}
}
else {
bezt->h1 = bezt->h2 = std::max(reference_point->bezt.h2, reference_point->bezt.h1);
}
reference_parent_point = reference_point;
}
else if (reference_adjacent) {
if (spline->tot_point != 1) {
MaskSplinePoint *prev_point, *next_point, *close_point;
const int index = int(new_point - spline->points);
if (spline->flag & MASK_SPLINE_CYCLIC) {
prev_point = &spline->points[mod_i(index - 1, spline->tot_point)];
next_point = &spline->points[mod_i(index + 1, spline->tot_point)];
}
else {
prev_point = (index != 0) ? &spline->points[index - 1] : nullptr;
next_point = (index != spline->tot_point - 1) ? &spline->points[index + 1] : nullptr;
}
if (prev_point && next_point) {
close_point = (len_squared_v2v2(new_point->bezt.vec[1], prev_point->bezt.vec[1]) <
len_squared_v2v2(new_point->bezt.vec[1], next_point->bezt.vec[1])) ?
prev_point :
next_point;
}
else {
close_point = prev_point ? prev_point : next_point;
}
/* handle type */
eBezTriple_Handle handle_type = HD_FREE;
if (prev_point) {
handle_type = prev_point->bezt.h2;
}
if (next_point) {
handle_type = std::max(next_point->bezt.h2, handle_type);
}
bezt->h1 = bezt->h2 = handle_type;
/* parent */
reference_parent_point = close_point;
/* NOTE: we may want to copy other attributes later, radius? pressure? color? */
}
}
copy_v3_v3(bezt->vec[0], co);
copy_v3_v3(bezt->vec[1], co);
copy_v3_v3(bezt->vec[2], co);
if (reference_parent_point) {
new_point->parent = reference_parent_point->parent;
if (new_point->parent.id) {
float parent_matrix[3][3];
BKE_mask_point_parent_matrix_get(new_point, ctime, parent_matrix);
invert_m3(parent_matrix);
mul_m3_v2(parent_matrix, new_point->bezt.vec[1]);
}
}
else {
BKE_mask_parent_init(&new_point->parent);
}
if (spline->tot_point != 1) {
BKE_mask_calc_handle_adjacent_interp(spline, new_point, u);
}
/* select new point */
BKE_mask_point_select_handles(new_point);
ED_mask_select_flush_all(mask);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Add Extrude Vertex
* \{ */
static void finSelectedSplinePoint(MaskLayer *mask_layer,
MaskSpline **spline,
MaskSplinePoint **point,
bool check_active)
{
MaskSpline *cur_spline = static_cast<MaskSpline *>(mask_layer->splines.first);
*spline = nullptr;
*point = nullptr;
if (check_active) {
/* TODO: having an active point but no active spline is possible, why? */
if (mask_layer->act_spline && mask_layer->act_point &&
BKE_mask_point_selected(mask_layer->act_point))
{
*spline = mask_layer->act_spline;
*point = mask_layer->act_point;
return;
}
}
while (cur_spline) {
for (int i = 0; i < cur_spline->tot_point; i++) {
MaskSplinePoint *cur_point = &cur_spline->points[i];
if (BKE_mask_point_selected(cur_point)) {
if (!ELEM(*spline, nullptr, cur_spline)) {
*spline = nullptr;
*point = nullptr;
return;
}
if (*point) {
*point = nullptr;
}
else {
*spline = cur_spline;
*point = cur_point;
}
}
}
cur_spline = cur_spline->next;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Add Subdivide Vertex
* \{ */
static void mask_spline_add_point_at_index(MaskSpline *spline, int point_index)
{
MaskSplinePoint *new_point_array;
new_point_array = MEM_new_array<MaskSplinePoint>(spline->tot_point + 1, "add mask vert points");
memcpy(new_point_array, spline->points, sizeof(MaskSplinePoint) * (point_index + 1));
memcpy(new_point_array + point_index + 2,
spline->points + point_index + 1,
sizeof(MaskSplinePoint) * (spline->tot_point - point_index - 1));
MEM_delete(spline->points);
spline->points = new_point_array;
spline->tot_point++;
}
static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2])
{
MaskLayer *mask_layer;
MaskSpline *spline;
MaskSplinePoint *point = nullptr;
const float threshold = 12;
float tangent[2];
float u;
if (ED_mask_find_nearest_diff_point(C,
mask,
co,
threshold,
false,
tangent,
true,
true,
&mask_layer,
&spline,
&point,
&u,
nullptr))
{
Scene *scene = CTX_data_scene(C);
const float ctime = scene->r.cfra;
MaskSplinePoint *new_point;
int point_index = point - spline->points;
ED_mask_select_toggle_all(mask, SEL_DESELECT);
mask_spline_add_point_at_index(spline, point_index);
new_point = &spline->points[point_index + 1];
setup_vertex_point(mask, spline, new_point, co, u, ctime, nullptr, true);
/* TODO: we could pass the spline! */
BKE_mask_layer_shape_changed_add(mask_layer,
BKE_mask_layer_shape_spline_to_index(mask_layer, spline) +
point_index + 1,
true,
true);
mask_layer->act_spline = spline;
mask_layer->act_point = new_point;
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return true;
}
return false;
}
static bool add_vertex_extrude(const bContext *C,
Mask *mask,
MaskLayer *mask_layer,
const float co[2])
{
Scene *scene = CTX_data_scene(C);
const float ctime = scene->r.cfra;
MaskSpline *spline;
MaskSplinePoint *point;
MaskSplinePoint *new_point = nullptr, *ref_point = nullptr;
/* check on which side we want to add the point */
int point_index;
float tangent_point[2];
float tangent_co[2];
bool do_cyclic_correct = false;
bool do_prev; /* use prev point rather than next?? */
if (!mask_layer) {
return false;
}
finSelectedSplinePoint(mask_layer, &spline, &point, true);
ED_mask_select_toggle_all(mask, SEL_DESELECT);
point_index = (point - spline->points);
BKE_mask_point_deselect_handles(point);
if ((spline->flag & MASK_SPLINE_CYCLIC) ||
(point_index > 0 && point_index != spline->tot_point - 1))
{
BKE_mask_calc_tangent_polyline(spline, point, tangent_point);
sub_v2_v2v2(tangent_co, co, point->bezt.vec[1]);
if (dot_v2v2(tangent_point, tangent_co) < 0.0f) {
do_prev = true;
}
else {
do_prev = false;
}
}
else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == 0)) {
do_prev = true;
}
else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == spline->tot_point - 1)) {
do_prev = false;
}
else {
do_prev = false; /* quiet warning */
/* should never get here */
BLI_assert(0);
}
/* use the point before the active one */
if (do_prev) {
point_index--;
if (point_index < 0) {
point_index += spline->tot_point; /* wrap index */
if ((spline->flag & MASK_SPLINE_CYCLIC) == 0) {
do_cyclic_correct = true;
point_index = 0;
}
}
}
#if 0
print_v2("", tangent_point);
printf("%d\n", point_index);
#endif
mask_spline_add_point_at_index(spline, point_index);
if (do_cyclic_correct) {
ref_point = &spline->points[point_index + 1];
new_point = &spline->points[point_index];
*ref_point = *new_point;
*new_point = MaskSplinePoint{};
}
else {
ref_point = &spline->points[point_index];
new_point = &spline->points[point_index + 1];
}
mask_layer->act_point = new_point;
setup_vertex_point(mask, spline, new_point, co, 0.5f, ctime, ref_point, false);
if (mask_layer->splines_shapes.first) {
point_index = ((int(new_point - spline->points) + 0) % spline->tot_point);
BKE_mask_layer_shape_changed_add(mask_layer,
BKE_mask_layer_shape_spline_to_index(mask_layer, spline) +
point_index,
true,
true);
}
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return true;
}
static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *mask_layer, const float co[2])
{
Scene *scene = CTX_data_scene(C);
const float ctime = scene->r.cfra;
MaskSpline *spline;
MaskSplinePoint *new_point = nullptr, *ref_point = nullptr;
if (!mask_layer) {
/* If there's no mask layer currently operating on, create new one. */
mask_layer = BKE_mask_layer_new(mask, "");
mask->masklay_act = mask->masklay_tot - 1;
}
ED_mask_select_toggle_all(mask, SEL_DESELECT);
spline = BKE_mask_spline_add(mask_layer);
mask_layer->act_spline = spline;
new_point = spline->points;
mask_layer->act_point = new_point;
setup_vertex_point(mask, spline, new_point, co, 0.5f, ctime, ref_point, false);
{
int point_index = ((int(new_point - spline->points) + 0) % spline->tot_point);
BKE_mask_layer_shape_changed_add(mask_layer,
BKE_mask_layer_shape_spline_to_index(mask_layer, spline) +
point_index,
true,
true);
}
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return true;
}
/* Convert coordinate from normalized space to pixel one.
* TODO(sergey): Make the function more generally available. */
static void mask_point_make_pixel_space(bContext *C,
const float point_normalized[2],
float point_pixel[2])
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
float scalex, scaley;
ED_mask_pixelspace_factor(area, region, &scalex, &scaley);
point_pixel[0] = point_normalized[0] * scalex;
point_pixel[1] = point_normalized[1] * scaley;
}
static wmOperatorStatus add_vertex_handle_cyclic_at_point(bContext *C,
Mask *mask,
MaskSpline *spline,
MaskSplinePoint *active_point,
MaskSplinePoint *other_point,
float co[2])
{
const float tolerance_in_pixels_squared = 4 * 4;
if (spline->flag & MASK_SPLINE_CYCLIC) {
/* The spline is already cyclic, so there is no need to handle anything here.
* Return PASS_THROUGH so that it's possible to add vertices close to the endpoints of the
* cyclic spline. */
return OPERATOR_PASS_THROUGH;
}
float co_pixel[2];
mask_point_make_pixel_space(C, co, co_pixel);
float point_pixel[2];
mask_point_make_pixel_space(C, other_point->bezt.vec[1], point_pixel);
const float dist_squared = len_squared_v2v2(co_pixel, point_pixel);
if (dist_squared > tolerance_in_pixels_squared) {
return OPERATOR_PASS_THROUGH;
}
spline->flag |= MASK_SPLINE_CYCLIC;
/* TODO: update keyframes in time. */
BKE_mask_calc_handle_point_auto(spline, active_point, false);
BKE_mask_calc_handle_point_auto(spline, other_point, false);
DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return OPERATOR_FINISHED;
}
static wmOperatorStatus add_vertex_handle_cyclic(
bContext *C, Mask *mask, MaskSpline *spline, MaskSplinePoint *active_point, float co[2])
{
MaskSplinePoint *first_point = &spline->points[0];
MaskSplinePoint *last_point = &spline->points[spline->tot_point - 1];
const bool is_first_point_active = (active_point == first_point);
const bool is_last_point_active = (active_point == last_point);
if (is_last_point_active) {
return add_vertex_handle_cyclic_at_point(C, mask, spline, active_point, first_point, co);
}
if (is_first_point_active) {
return add_vertex_handle_cyclic_at_point(C, mask, spline, active_point, last_point, co);
}
return OPERATOR_PASS_THROUGH;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Add Vertex Operator
* \{ */
static wmOperatorStatus add_vertex_exec(bContext *C, wmOperator *op)
{
MaskViewLockState lock_state;
ED_mask_view_lock_state_store(C, &lock_state);
Mask *mask = CTX_data_edit_mask(C);
if (mask == nullptr) {
/* if there's no active mask, create one */
mask = ED_mask_new(C, nullptr);
}
MaskLayer *mask_layer = BKE_mask_layer_active(mask);
if (mask_layer && mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
mask_layer = nullptr;
}
float co[2];
RNA_float_get_array(op->ptr, "location", co);
/* TODO: having an active point but no active spline is possible, why? */
if (mask_layer && mask_layer->act_spline && mask_layer->act_point &&
BKE_mask_point_selected(mask_layer->act_point))
{
MaskSpline *spline = mask_layer->act_spline;
MaskSplinePoint *active_point = mask_layer->act_point;
const wmOperatorStatus cyclic_result = add_vertex_handle_cyclic(
C, mask, spline, active_point, co);
if (cyclic_result != OPERATOR_PASS_THROUGH) {
return cyclic_result;
}
if (!add_vertex_subdivide(C, mask, co)) {
if (!add_vertex_extrude(C, mask, mask_layer, co)) {
return OPERATOR_CANCELLED;
}
}
}
else {
if (!add_vertex_subdivide(C, mask, co)) {
if (!add_vertex_new(C, mask, mask_layer, co)) {
return OPERATOR_CANCELLED;
}
}
}
DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY);
ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
return OPERATOR_FINISHED;
}
static wmOperatorStatus add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
float co[2];
ED_mask_mouse_pos(area, region, event->mval, co);
RNA_float_set_array(op->ptr, "location", co);
return add_vertex_exec(C, op);
}
void MASK_OT_add_vertex(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Vertex";
ot->description = "Add vertex to active spline";
ot->idname = "MASK_OT_add_vertex";
/* API callbacks. */
ot->exec = add_vertex_exec;
ot->invoke = add_vertex_invoke;
ot->poll = ED_maskedit_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_float_vector(ot->srna,
"location",
2,
nullptr,
-FLT_MAX,
FLT_MAX,
"Location",
"Location of vertex in normalized space",
-1.0f,
1.0f);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Add Feather Vertex Operator
* \{ */
static wmOperatorStatus add_feather_vertex_exec(bContext *C, wmOperator *op)
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *mask_layer;
MaskSpline *spline;
MaskSplinePoint *point = nullptr;
const float threshold = 12;
float co[2], u;
RNA_float_get_array(op->ptr, "location", co);
point = ED_mask_point_find_nearest(C, mask, co, threshold, nullptr, nullptr, nullptr, nullptr);
if (point) {
return OPERATOR_FINISHED;
}
if (ED_mask_find_nearest_diff_point(C,
mask,
co,
threshold,
true,
nullptr,
true,
true,
&mask_layer,
&spline,
&point,
&u,
nullptr))
{
float w = BKE_mask_point_weight(spline, point, u);
float weight_scalar = BKE_mask_point_weight_scalar(spline, point, u);
if (weight_scalar != 0.0f) {
w = w / weight_scalar;
}
BKE_mask_point_add_uw(point, u, w);
DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static wmOperatorStatus add_feather_vertex_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
float co[2];
ED_mask_mouse_pos(area, region, event->mval, co);
RNA_float_set_array(op->ptr, "location", co);
return add_feather_vertex_exec(C, op);
}
void MASK_OT_add_feather_vertex(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Feather Vertex";
ot->description = "Add vertex to feather";
ot->idname = "MASK_OT_add_feather_vertex";
/* API callbacks. */
ot->exec = add_feather_vertex_exec;
ot->invoke = add_feather_vertex_invoke;
ot->poll = ED_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_float_vector(ot->srna,
"location",
2,
nullptr,
-FLT_MAX,
FLT_MAX,
"Location",
"Location of vertex in normalized space",
-1.0f,
1.0f);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Common Primitive Functions
* \{ */
static BezTriple *points_to_bezier(const float (*points)[2],
const int num_points,
const eBezTriple_Handle handle_type,
const float scale,
const float location[2])
{
BezTriple *bezier_points = MEM_new_array_zeroed<BezTriple>(num_points, __func__);
for (int i = 0; i < num_points; i++) {
copy_v2_v2(bezier_points[i].vec[1], points[i]);
mul_v2_fl(bezier_points[i].vec[1], scale);
add_v2_v2(bezier_points[i].vec[1], location);
bezier_points[i].h1 = handle_type;
bezier_points[i].h2 = handle_type;
}
for (int i = 0; i < num_points; i++) {
BKE_nurb_handle_calc(&bezier_points[i],
&bezier_points[(i - 1 + num_points) % num_points],
&bezier_points[(i + 1) % num_points],
false,
false);
}
return bezier_points;
}
static int create_primitive_from_points(bContext *C,
wmOperator *op,
const float (*points)[2],
int num_points,
eBezTriple_Handle handle_type)
{
MaskViewLockState lock_state;
ED_mask_view_lock_state_store(C, &lock_state);
ScrArea *area = CTX_wm_area(C);
int size = RNA_float_get(op->ptr, "size");
int width, height;
ED_mask_get_size(area, &width, &height);
float scale = float(size) / max_ii(width, height);
/* Get location in mask space. */
float frame_size[2];
frame_size[0] = width;
frame_size[1] = height;
float location[2];
RNA_float_get_array(op->ptr, "location", location);
location[0] /= width;
location[1] /= height;
BKE_mask_coord_from_frame(location, location, frame_size);
/* Make it so new primitive is centered to mouse location. */
location[0] -= 0.5f * scale;
location[1] -= 0.5f * scale;
bool added_mask = false;
MaskLayer *mask_layer = ED_mask_layer_ensure(C, &added_mask);
Mask *mask = CTX_data_edit_mask(C);
ED_mask_select_toggle_all(mask, SEL_DESELECT);
MaskSpline *new_spline = BKE_mask_spline_add(mask_layer);
new_spline->flag = MASK_SPLINE_CYCLIC | MASK_SPLINE_SELECT;
new_spline->points = static_cast<MaskSplinePoint *>(
MEM_realloc_zeroed(new_spline->points, sizeof(MaskSplinePoint) * num_points));
mask_layer->act_spline = new_spline;
mask_layer->act_point = nullptr;
const int spline_index = BKE_mask_layer_shape_spline_to_index(mask_layer, new_spline);
BezTriple *bezier_points = points_to_bezier(points, num_points, handle_type, scale, location);
for (int i = 0; i < num_points; i++) {
new_spline->tot_point = i + 1;
MaskSplinePoint *new_point = &new_spline->points[i];
BKE_mask_parent_init(&new_point->parent);
new_point->bezt = bezier_points[i];
BKE_mask_point_select_set(new_point, true);
if (mask_layer->splines_shapes.first) {
BKE_mask_layer_shape_changed_add(mask_layer, spline_index + i, true, false);
}
}
MEM_delete(bezier_points);
if (added_mask) {
WM_event_add_notifier(C, NC_MASK | NA_ADDED, nullptr);
}
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY);
ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
return OPERATOR_FINISHED;
}
static wmOperatorStatus primitive_add_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
{
ScrArea *area = CTX_wm_area(C);
float cursor[2];
int width, height;
ED_mask_get_size(area, &width, &height);
ED_mask_cursor_location_get(area, cursor);
cursor[0] *= width;
cursor[1] *= height;
RNA_float_set_array(op->ptr, "location", cursor);
return op->type->exec(C, op);
}
static void define_primitive_add_properties(wmOperatorType *ot)
{
RNA_def_float(ot->srna,
"size",
100,
-FLT_MAX,
FLT_MAX,
"Size",
"Size of new primitive",
-FLT_MAX,
FLT_MAX);
RNA_def_float_vector(ot->srna,
"location",
2,
nullptr,
-FLT_MAX,
FLT_MAX,
"Location",
"Location of new primitive",
-FLT_MAX,
FLT_MAX);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Primitive Add Circle Operator
* \{ */
static wmOperatorStatus primitive_circle_add_exec(bContext *C, wmOperator *op)
{
const float points[4][2] = {{0.0f, 0.5f}, {0.5f, 1.0f}, {1.0f, 0.5f}, {0.5f, 0.0f}};
int num_points = ARRAY_SIZE(points);
create_primitive_from_points(C, op, points, num_points, HD_AUTO);
return OPERATOR_FINISHED;
}
void MASK_OT_primitive_circle_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Circle";
ot->description = "Add new circle-shaped spline";
ot->idname = "MASK_OT_primitive_circle_add";
/* API callbacks. */
ot->exec = primitive_circle_add_exec;
ot->invoke = primitive_add_invoke;
ot->poll = ED_maskedit_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
define_primitive_add_properties(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Primitive Add Square Operator
* \{ */
static wmOperatorStatus primitive_square_add_exec(bContext *C, wmOperator *op)
{
const float points[4][2] = {{0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f}};
int num_points = ARRAY_SIZE(points);
create_primitive_from_points(C, op, points, num_points, HD_VECT);
return OPERATOR_FINISHED;
}
void MASK_OT_primitive_square_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Square";
ot->description = "Add new square-shaped spline";
ot->idname = "MASK_OT_primitive_square_add";
/* API callbacks. */
ot->exec = primitive_square_add_exec;
ot->invoke = primitive_add_invoke;
ot->poll = ED_maskedit_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
define_primitive_add_properties(ot);
}
/** \} */
} // namespace blender

View File

@@ -0,0 +1,833 @@
/* SPDX-FileCopyrightText: 2012 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edmask
*/
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math_color.h"
#include "BLI_math_vector.h"
#include "BLI_rect.h"
#include "BLI_utildefines.h"
#include "BKE_context.hh"
#include "BKE_mask.hh"
#include "DNA_mask_types.h"
#include "DNA_object_types.h" /* SELECT */
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "ED_clip.hh"
#include "ED_mask.hh" /* own include */
#include "ED_screen.hh"
#include "ED_space_api.hh"
#include "BIF_glutil.hh"
#include "GPU_immediate.hh"
#include "GPU_matrix.hh"
#include "GPU_shader.hh"
#include "GPU_state.hh"
#include "UI_resources.hh"
#include "UI_view2d.hh"
#include "DEG_depsgraph_query.hh"
namespace blender {
static void mask_spline_color_get(MaskLayer *mask_layer,
MaskSpline *spline,
const bool is_sel,
uchar r_rgb[4])
{
if (is_sel) {
if (mask_layer->act_spline == spline) {
r_rgb[0] = r_rgb[1] = r_rgb[2] = 255;
}
else {
r_rgb[0] = 255;
r_rgb[1] = r_rgb[2] = 0;
}
}
else {
r_rgb[0] = 128;
r_rgb[1] = r_rgb[2] = 0;
}
r_rgb[3] = 255;
}
static void mask_spline_feather_color_get(MaskLayer * /*mask_layer*/,
MaskSpline * /*spline*/,
const bool is_sel,
uchar r_rgb[4])
{
if (is_sel) {
r_rgb[1] = 255;
r_rgb[0] = r_rgb[2] = 0;
}
else {
r_rgb[1] = 128;
r_rgb[0] = r_rgb[2] = 0;
}
r_rgb[3] = 255;
}
static void mask_point_undistort_pos(SpaceClip *sc, float r_co[2], const float co[2])
{
BKE_mask_coord_to_movieclip(sc->clip, &sc->user, r_co, co);
ED_clip_point_undistorted_pos(sc, r_co, r_co);
BKE_mask_coord_from_movieclip(sc->clip, &sc->user, r_co, r_co);
}
static void draw_single_handle(const MaskLayer *mask_layer,
const MaskSplinePoint *point,
const eMaskWhichHandle which_handle,
const int draw_type,
const float handle_size,
const float point_pos[2],
const float handle_pos[2])
{
const BezTriple *bezt = &point->bezt;
char handle_type;
if (ELEM(which_handle, MASK_WHICH_HANDLE_STICK, MASK_WHICH_HANDLE_LEFT)) {
handle_type = bezt->h1;
}
else {
handle_type = bezt->h2;
}
if (handle_type == HD_VECT) {
return;
}
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", gpu::VertAttrType::SFLOAT_32_32);
const uchar rgb_gray[4] = {0x60, 0x60, 0x60, 0xff};
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3ubv(rgb_gray);
/* this could be split into its own loop */
if (draw_type == MASK_DT_OUTLINE) {
GPU_line_width(3.0f);
immBegin(GPU_PRIM_LINES, 2);
immVertex2fv(pos, point_pos);
immVertex2fv(pos, handle_pos);
immEnd();
}
switch (handle_type) {
case HD_FREE:
immUniformThemeColor3(TH_HANDLE_FREE);
break;
case HD_AUTO:
immUniformThemeColor3(TH_HANDLE_AUTO);
break;
case HD_ALIGN:
case HD_ALIGN_DOUBLESIDE:
immUniformThemeColor3(TH_HANDLE_ALIGN);
break;
}
GPU_line_width(1.0f);
immBegin(GPU_PRIM_LINES, 2);
immVertex2fv(pos, point_pos);
immVertex2fv(pos, handle_pos);
immEnd();
immUnbindProgram();
/* draw handle points */
immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA);
immUniform1f("size", handle_size);
immUniform1f("outlineWidth", 1.5f);
float point_color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; /* active color by default */
if (BKE_mask_point_is_handle_selected(point, which_handle)) {
if (point != mask_layer->act_point) {
ui::theme::get_color_3fv(TH_HANDLE_VERTEX_SELECT, point_color);
}
}
else {
ui::theme::get_color_3fv(TH_HANDLE_VERTEX, point_color);
}
immUniform4fv("outlineColor", point_color);
immUniformColor3fvAlpha(point_color, 0.25f);
immBegin(GPU_PRIM_POINTS, 1);
immVertex2fv(pos, handle_pos);
immEnd();
immUnbindProgram();
}
/* return non-zero if spline is selected */
static void draw_spline_points(const bContext *C,
MaskLayer *mask_layer,
MaskSpline *spline,
const MaskDrawType draw_type)
{
const bool is_spline_sel = (spline->flag & SELECT) &&
(mask_layer->visibility_flag & MASK_HIDE_SELECT) == 0;
uchar rgb_spline[4];
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
SpaceClip *sc = CTX_wm_space_clip(C);
bool undistort = false;
int tot_feather_point;
float (*feather_points)[2], (*fp)[2];
float min[2], max[2];
if (!spline->tot_point) {
return;
}
if (sc) {
undistort = sc->clip && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT);
}
/* TODO: add this to sequence editor. */
float handle_size = 2.0f * ui::theme::get_value_f(TH_HANDLE_VERTEX_SIZE) * U.pixelsize;
mask_spline_color_get(mask_layer, spline, is_spline_sel, rgb_spline);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", gpu::VertAttrType::SFLOAT_32_32);
immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
immUniform1f("size", 0.7f * handle_size);
/* feather points */
feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
for (int i = 0; i < spline->tot_point; i++) {
/* watch it! this is intentionally not the deform array, only check for sel */
MaskSplinePoint *point = &spline->points[i];
for (int j = 0; j <= point->tot_uw; j++) {
float feather_point[2];
bool sel = false;
copy_v2_v2(feather_point, *fp);
if (undistort) {
mask_point_undistort_pos(sc, feather_point, feather_point);
}
if (j == 0) {
sel = BKE_mask_point_selected(point);
}
else {
sel = (point->uw[j - 1].flag & SELECT) != 0;
}
if (sel) {
if (point == mask_layer->act_point) {
immUniformColor3f(1.0f, 1.0f, 1.0f);
}
else {
immUniformThemeColorShadeAlpha(TH_HANDLE_VERTEX_SELECT, 0, 255);
}
}
else {
immUniformThemeColorShadeAlpha(TH_HANDLE_VERTEX, 0, 255);
}
immBegin(GPU_PRIM_POINTS, 1);
immVertex2fv(pos, feather_point);
immEnd();
fp++;
}
}
MEM_delete(feather_points);
immUnbindProgram();
GPU_line_smooth(true);
/* control points */
INIT_MINMAX2(min, max);
for (int i = 0; i < spline->tot_point; i++) {
/* watch it! this is intentionally not the deform array, only check for sel */
MaskSplinePoint *point = &spline->points[i];
MaskSplinePoint *point_deform = &points_array[i];
BezTriple *bezt = &point_deform->bezt;
float vert[2];
copy_v2_v2(vert, bezt->vec[1]);
if (undistort) {
mask_point_undistort_pos(sc, vert, vert);
}
/* draw handle segment */
if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) {
float handle[2];
BKE_mask_point_handle(point_deform, MASK_WHICH_HANDLE_STICK, handle);
if (undistort) {
mask_point_undistort_pos(sc, handle, handle);
}
draw_single_handle(
mask_layer, point, MASK_WHICH_HANDLE_STICK, draw_type, handle_size, vert, handle);
}
else {
float handle_left[2], handle_right[2];
BKE_mask_point_handle(point_deform, MASK_WHICH_HANDLE_LEFT, handle_left);
BKE_mask_point_handle(point_deform, MASK_WHICH_HANDLE_RIGHT, handle_right);
if (undistort) {
mask_point_undistort_pos(sc, handle_left, handle_left);
mask_point_undistort_pos(sc, handle_left, handle_left);
}
draw_single_handle(
mask_layer, point, MASK_WHICH_HANDLE_LEFT, draw_type, handle_size, vert, handle_left);
draw_single_handle(
mask_layer, point, MASK_WHICH_HANDLE_RIGHT, draw_type, handle_size, vert, handle_right);
}
/* bind program in loop so it does not interfere with draw_single_handle */
immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
/* draw CV point */
if (BKE_mask_point_selected_knot(point)) {
if (point == mask_layer->act_point) {
immUniformColor3f(1.0f, 1.0f, 1.0f);
}
else {
immUniformThemeColorShadeAlpha(TH_HANDLE_VERTEX_SELECT, 0, 255);
}
}
else {
immUniformThemeColorShadeAlpha(TH_HANDLE_VERTEX, 0, 255);
}
immBegin(GPU_PRIM_POINTS, 1);
immVertex2fv(pos, vert);
immEnd();
immUnbindProgram();
minmax_v2v2_v2(min, max, vert);
}
GPU_line_smooth(false);
if (is_spline_sel) {
float x = (min[0] + max[0]) * 0.5f;
float y = (min[1] + max[1]) * 0.5f;
immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA);
immUniform1f("outlineWidth", 1.5f);
if (mask_layer->act_spline == spline) {
immUniformColor3f(1.0f, 1.0f, 1.0f);
}
else {
immUniformColor3f(1.0f, 1.0f, 0.0f);
}
immUniform4f("outlineColor", 0.0f, 0.0f, 0.0f, 1.0f);
immUniform1f("size", 12.0f);
immBegin(GPU_PRIM_POINTS, 1);
immVertex2f(pos, x, y);
immEnd();
immUnbindProgram();
}
}
static void mask_color_active_tint(uchar r_rgb[4], const uchar rgb[4], const bool is_active)
{
if (!is_active) {
r_rgb[0] = uchar((int(rgb[0]) + 128) / 2);
r_rgb[1] = uchar((int(rgb[1]) + 128) / 2);
r_rgb[2] = uchar((int(rgb[2]) + 128) / 2);
r_rgb[3] = rgb[3];
}
else {
*reinterpret_cast<uint *>(r_rgb) = *reinterpret_cast<const uint *>(rgb);
}
}
static void mask_draw_array(uint pos,
GPUPrimType prim_type,
const float (*points)[2],
uint vertex_len)
{
immBegin(prim_type, vertex_len);
for (uint i = 0; i < vertex_len; i++) {
immVertex2fv(pos, points[i]);
}
immEnd();
}
static void mask_draw_curve_type(const bContext *C,
MaskSpline *spline,
float (*orig_points)[2],
int tot_point,
const bool is_feather,
const bool is_active,
const uchar rgb_spline[4],
const MaskDrawType draw_type)
{
const GPUPrimType draw_method = (spline->flag & MASK_SPLINE_CYCLIC) ? GPU_PRIM_LINE_LOOP :
GPU_PRIM_LINE_STRIP;
const uchar rgb_black[4] = {0x00, 0x00, 0x00, 0xff};
uchar rgb_tmp[4];
SpaceClip *sc = CTX_wm_space_clip(C);
float (*points)[2] = orig_points;
if (sc) {
const bool undistort = sc->clip && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT);
if (undistort) {
points = MEM_new_array_zeroed<float[2]>(tot_point, "undistorthed mask curve");
for (int i = 0; i < tot_point; i++) {
mask_point_undistort_pos(sc, points[i], orig_points[i]);
}
}
}
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", gpu::VertAttrType::SFLOAT_32_32);
switch (draw_type) {
case MASK_DT_OUTLINE:
/* TODO(merwin): use fancy line shader here
* probably better with geometry shader (after core profile switch)
*/
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_line_width(3.0f);
mask_color_active_tint(rgb_tmp, rgb_black, is_active);
immUniformColor4ubv(rgb_tmp);
mask_draw_array(pos, draw_method, points, tot_point);
GPU_line_width(1.0f);
mask_color_active_tint(rgb_tmp, rgb_spline, is_active);
immUniformColor4ubv(rgb_tmp);
mask_draw_array(pos, draw_method, points, tot_point);
immUnbindProgram();
break;
case MASK_DT_BLACK:
case MASK_DT_WHITE:
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_line_width(1.0f);
if (draw_type == MASK_DT_BLACK) {
rgb_tmp[0] = rgb_tmp[1] = rgb_tmp[2] = 0;
}
else {
rgb_tmp[0] = rgb_tmp[1] = rgb_tmp[2] = 255;
}
/* alpha values seem too low but gl draws many points that compensate for it */
if (is_feather) {
rgb_tmp[3] = 64;
}
else {
rgb_tmp[3] = 128;
}
if (is_feather) {
rgb_tmp[0] = uchar((short(rgb_tmp[0]) + short(rgb_spline[0])) / 2);
rgb_tmp[1] = uchar((short(rgb_tmp[1]) + short(rgb_spline[1])) / 2);
rgb_tmp[2] = uchar((short(rgb_tmp[2]) + short(rgb_spline[2])) / 2);
}
mask_color_active_tint(rgb_tmp, rgb_tmp, is_active);
immUniformColor4ubv(rgb_tmp);
mask_draw_array(pos, draw_method, points, tot_point);
immUnbindProgram();
break;
case MASK_DT_DASH: {
float colors[2][4];
mask_color_active_tint(rgb_tmp, rgb_spline, is_active);
rgba_uchar_to_float(colors[0], rgb_tmp);
mask_color_active_tint(rgb_tmp, rgb_black, is_active);
rgba_uchar_to_float(colors[1], rgb_tmp);
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
immUniform2f(
"viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
immUniform1i("colors_len", 2); /* "advanced" mode */
immUniform4fv("color", colors[0]);
immUniform4fv("color2", colors[1]);
immUniform1f("dash_width", 4.0f);
immUniform1f("udash_factor", 0.5f);
GPU_line_width(1.0f);
mask_draw_array(pos, draw_method, points, tot_point);
immUnbindProgram();
break;
}
default:
BLI_assert(false);
}
if (points != orig_points) {
MEM_delete(points);
}
}
static void draw_spline_curve(const bContext *C,
MaskLayer *mask_layer,
MaskSpline *spline,
const MaskDrawType draw_type,
const bool is_active,
const int width,
const int height)
{
const uint resol = max_ii(BKE_mask_spline_feather_resolution(spline, width, height),
BKE_mask_spline_resolution(spline, width, height));
uchar rgb_tmp[4];
const bool is_spline_sel = (spline->flag & SELECT) &&
(mask_layer->visibility_flag & MASK_HIDE_SELECT) == 0;
const bool is_fill = (spline->flag & MASK_SPLINE_NOFILL) == 0;
uint tot_diff_point;
float (*diff_points)[2];
uint tot_feather_point;
float (*feather_points)[2];
diff_points = BKE_mask_spline_differentiate_with_resolution(spline, resol, &tot_diff_point);
if (!diff_points) {
return;
}
GPU_line_smooth(true);
feather_points = BKE_mask_spline_feather_differentiated_points_with_resolution(
spline, resol, (is_fill != false), &tot_feather_point);
/* draw feather */
mask_spline_feather_color_get(mask_layer, spline, is_spline_sel, rgb_tmp);
mask_draw_curve_type(
C, spline, feather_points, tot_feather_point, true, is_active, rgb_tmp, draw_type);
if (!is_fill) {
const float *fp = &diff_points[0][0];
float *fp_feather = &feather_points[0][0];
BLI_assert(tot_diff_point == tot_feather_point);
for (int i = 0; i < tot_diff_point; i++, fp += 2, fp_feather += 2) {
float tvec[2];
sub_v2_v2v2(tvec, fp, fp_feather);
add_v2_v2v2(fp_feather, fp, tvec);
}
/* same as above */
mask_draw_curve_type(
C, spline, feather_points, tot_feather_point, true, is_active, rgb_tmp, draw_type);
}
MEM_delete(feather_points);
/* draw main curve */
mask_spline_color_get(mask_layer, spline, is_spline_sel, rgb_tmp);
mask_draw_curve_type(
C, spline, diff_points, tot_diff_point, false, is_active, rgb_tmp, draw_type);
MEM_delete(diff_points);
GPU_line_smooth(false);
}
static void draw_layer_splines(const bContext *C,
MaskLayer *layer,
const MaskDrawType draw_type,
const int width,
const int height,
const bool is_active)
{
for (MaskSpline &spline : layer->splines) {
/* draw curve itself first... */
draw_spline_curve(C, layer, &spline, draw_type, is_active, width, height);
if (!(layer->visibility_flag & MASK_HIDE_SELECT)) {
/* ...and then handles over the curve so they're nicely visible */
draw_spline_points(C, layer, &spline, draw_type);
}
/* show undeform for testing */
if (false) {
MaskSplinePoint *back = spline.points_deform;
spline.points_deform = nullptr;
draw_spline_curve(C, layer, &spline, draw_type, is_active, width, height);
draw_spline_points(C, layer, &spline, draw_type);
spline.points_deform = back;
}
}
}
static void draw_mask_layers(
const bContext *C, Mask *mask, const MaskDrawType draw_type, const int width, const int height)
{
GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
MaskLayer *active = nullptr;
for (const auto [i, mask_layer] : mask->masklayers.enumerate()) {
const bool is_active = (i == mask->masklay_act);
if (mask_layer.visibility_flag & MASK_HIDE_VIEW) {
continue;
}
if (is_active) {
active = &mask_layer;
continue;
}
draw_layer_splines(C, &mask_layer, draw_type, width, height, is_active);
}
if (active != nullptr) {
draw_layer_splines(C, active, draw_type, width, height, true);
}
GPU_program_point_size(false);
GPU_blend(GPU_BLEND_NONE);
}
static float *mask_rasterize(Mask *mask, const int width, const int height)
{
MaskRasterHandle *handle;
float *buffer = MEM_new_array_zeroed<float>(height * width, "rasterized mask buffer");
/* Initialize rasterization handle. */
handle = BKE_maskrasterize_handle_new();
BKE_maskrasterize_handle_init(handle, mask, width, height, true, true, true);
BKE_maskrasterize_buffer(handle, width, height, buffer);
/* Free memory. */
BKE_maskrasterize_handle_free(handle);
return buffer;
}
void ED_mask_draw_region(
Depsgraph *depsgraph,
Mask *mask_,
ARegion *region,
const bool show_overlays,
const MaskDrawFlag draw_flag,
const MaskDrawType draw_type,
const MaskOverlayMode overlay_mode,
const float blend_factor,
/* convert directly into aspect corrected vars */
const int width_i,
const int height_i,
const float aspx,
const float aspy,
const bool do_scale_applied,
const bool do_draw_cb,
/* optional - only used by clip */
float stabmat[4][4],
/* optional - only used when do_post_draw is set or called from clip editor */
const bContext *C)
{
View2D *v2d = &region->v2d;
Mask *mask_eval = DEG_get_evaluated(depsgraph, mask_);
/* aspect always scales vertically in movie and image spaces */
const float width = width_i, height = float(height_i) * (aspy / aspx);
int x, y;
// int w, h;
float zoomx, zoomy;
/* Frame image. */
float maxdim;
float xofs, yofs;
/* find window pixel coordinates of origin */
ui::view2d_view_to_region(&region->v2d, 0.0f, 0.0f, &x, &y);
// w = BLI_rctf_size_x(&v2d->tot);
// h = BLI_rctf_size_y(&v2d->tot);
zoomx = float(BLI_rcti_size_x(&region->winrct) + 1) / BLI_rctf_size_x(&region->v2d.cur);
zoomy = float(BLI_rcti_size_y(&region->winrct) + 1) / BLI_rctf_size_y(&region->v2d.cur);
if (do_scale_applied) {
zoomx /= width;
zoomy /= height;
}
x += v2d->tot.xmin * zoomx;
y += v2d->tot.ymin * zoomy;
/* frame the image */
maxdim = max_ff(width, height);
if (width == height) {
xofs = yofs = 0;
}
else if (width < height) {
xofs = ((height - width) / -2.0f) * zoomx;
yofs = 0.0f;
}
else { /* (width > height) */
xofs = 0.0f;
yofs = ((width - height) / -2.0f) * zoomy;
}
if (show_overlays && draw_flag & MASK_DRAWFLAG_OVERLAY) {
float buf_col[4] = {1.0f, 0.0f, 0.0f, 0.0f};
const float *buffer = mask_rasterize(mask_eval, width, height);
if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) {
/* More blending types could be supported in the future. */
GPU_blend(GPU_BLEND_ALPHA);
buf_col[0] = -1.0f;
buf_col[3] = 1.0f;
}
GPU_matrix_push();
GPU_matrix_translate_2f(x, y);
GPU_matrix_scale_2f(zoomx, zoomy);
if (stabmat) {
GPU_matrix_mul(stabmat);
}
PixelBitmapDrawer drawer(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
GPU_shader_uniform_float_ex(drawer.shader_get(),
GPU_shader_get_uniform(drawer.shader_get(), "shuffle"),
4,
1,
buf_col);
if (overlay_mode == MASK_OVERLAY_COMBINED) {
const float blend_col[4] = {0.0f, 0.0f, 0.0f, blend_factor};
drawer.draw(0.0f,
0.0f,
width,
height,
gpu::TextureFormat::SFLOAT_16,
false,
buffer,
1.0f,
1.0f,
blend_col);
}
else {
drawer.draw(0.0f,
0.0f,
width,
height,
gpu::TextureFormat::SFLOAT_16,
false,
buffer,
1.0f,
1.0f,
nullptr);
}
GPU_matrix_pop();
if (overlay_mode != MASK_OVERLAY_ALPHACHANNEL) {
GPU_blend(GPU_BLEND_NONE);
}
MEM_delete(buffer);
}
/* apply transformation so mask editing tools will assume drawing from the
* origin in normalized space */
GPU_matrix_push();
GPU_matrix_translate_2f(x + xofs, y + yofs);
GPU_matrix_scale_2f(zoomx, zoomy);
if (stabmat) {
GPU_matrix_mul(stabmat);
}
GPU_matrix_scale_2f(maxdim, maxdim);
if (do_draw_cb) {
ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW);
}
/* draw! */
if (show_overlays && draw_flag & MASK_DRAWFLAG_SPLINE) {
draw_mask_layers(C, mask_eval, draw_type, width, height);
}
if (do_draw_cb) {
ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW);
}
GPU_matrix_pop();
}
void ED_mask_draw_frames(
Mask *mask, ARegion *region, const int cfra, const int sfra, const int efra)
{
const float framelen = region->winx / float(efra - sfra + 1);
MaskLayer *mask_layer = BKE_mask_layer_active(mask);
if (mask_layer == nullptr) {
return;
}
uint num_lines = mask_layer->splines_shapes.count();
if (num_lines == 0) {
return;
}
/* Local coordinate visible rect inside region, to accommodate overlapping ui. */
const rcti *rect_visible = ED_region_visible_rect(region);
const int region_bottom = rect_visible->ymin;
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", gpu::VertAttrType::SFLOAT_32_32);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ub(255, 175, 0, 255);
immBegin(GPU_PRIM_LINES, 2 * num_lines);
for (MaskLayerShape &mask_layer_shape : mask_layer->splines_shapes) {
int frame = mask_layer_shape.frame;
// draw_keyframe(i, scene->r.cfra, sfra, framelen, 1);
int height = (frame == cfra) ? 22 : 10;
int x = (frame - sfra) * framelen;
immVertex2f(pos, x, region_bottom);
immVertex2f(pos, x, region_bottom + height * UI_SCALE_FAC);
}
immEnd();
immUnbindProgram();
}
} // namespace blender

View File

@@ -0,0 +1,241 @@
/* SPDX-FileCopyrightText: 2012 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edmask
*/
#include "BKE_context.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_clip.hh"
#include "ED_image.hh"
#include "ED_mask.hh" /* own include */
#include "ED_sequencer.hh"
#include "RNA_access.hh"
#include "mask_intern.hh" /* own include */
namespace blender {
/* -------------------------------------------------------------------- */
/** \name Poll Functions
* \{ */
bool ED_maskedit_poll(bContext *C)
{
ScrArea *area = CTX_wm_area(C);
if (area) {
switch (area->spacetype) {
case SPACE_CLIP:
return ED_space_clip_maskedit_poll(C);
case SPACE_SEQ:
return ed::vse::maskedit_poll(C);
case SPACE_IMAGE:
return ED_space_image_maskedit_poll(C);
}
}
return false;
}
bool ED_maskedit_visible_splines_poll(bContext *C)
{
ScrArea *area = CTX_wm_area(C);
if (area) {
switch (area->spacetype) {
case SPACE_CLIP:
return ED_space_clip_maskedit_visible_splines_poll(C);
case SPACE_SEQ:
return ed::vse::maskedit_poll(C);
case SPACE_IMAGE:
return ED_space_image_maskedit_visible_splines_poll(C);
}
}
return false;
}
bool ED_maskedit_mask_poll(bContext *C)
{
ScrArea *area = CTX_wm_area(C);
if (area) {
switch (area->spacetype) {
case SPACE_CLIP:
return ED_space_clip_maskedit_mask_poll(C);
case SPACE_SEQ:
return ed::vse::maskedit_mask_poll(C);
case SPACE_IMAGE:
return ED_space_image_maskedit_mask_poll(C);
}
}
return false;
}
bool ED_maskedit_mask_visible_splines_poll(bContext *C)
{
const ScrArea *area = CTX_wm_area(C);
if (area) {
switch (area->spacetype) {
case SPACE_CLIP:
return ED_space_clip_maskedit_mask_visible_splines_poll(C);
case SPACE_SEQ:
return ed::vse::maskedit_mask_poll(C);
case SPACE_IMAGE:
return ED_space_image_maskedit_mask_visible_splines_poll(C);
}
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Registration
* \{ */
void ED_operatortypes_mask()
{
WM_operatortype_append(MASK_OT_new);
/* mask layers */
WM_operatortype_append(MASK_OT_layer_new);
WM_operatortype_append(MASK_OT_layer_remove);
/* add */
WM_operatortype_append(MASK_OT_add_vertex);
WM_operatortype_append(MASK_OT_add_feather_vertex);
WM_operatortype_append(MASK_OT_primitive_circle_add);
WM_operatortype_append(MASK_OT_primitive_square_add);
/* geometry */
WM_operatortype_append(MASK_OT_switch_direction);
WM_operatortype_append(MASK_OT_normals_make_consistent);
WM_operatortype_append(MASK_OT_delete);
/* select */
WM_operatortype_append(MASK_OT_select);
WM_operatortype_append(MASK_OT_select_all);
WM_operatortype_append(MASK_OT_select_box);
WM_operatortype_append(MASK_OT_select_lasso);
WM_operatortype_append(MASK_OT_select_circle);
WM_operatortype_append(MASK_OT_select_linked_pick);
WM_operatortype_append(MASK_OT_select_linked);
WM_operatortype_append(MASK_OT_select_more);
WM_operatortype_append(MASK_OT_select_less);
/* hide/reveal */
WM_operatortype_append(MASK_OT_hide_view_clear);
WM_operatortype_append(MASK_OT_hide_view_set);
/* feather */
WM_operatortype_append(MASK_OT_feather_weight_clear);
/* shape */
WM_operatortype_append(MASK_OT_slide_point);
WM_operatortype_append(MASK_OT_slide_spline_curvature);
WM_operatortype_append(MASK_OT_cyclic_toggle);
WM_operatortype_append(MASK_OT_handle_type_set);
/* relationships */
WM_operatortype_append(MASK_OT_parent_set);
WM_operatortype_append(MASK_OT_parent_clear);
/* Shape-keys. */
WM_operatortype_append(MASK_OT_shape_key_insert);
WM_operatortype_append(MASK_OT_shape_key_clear);
WM_operatortype_append(MASK_OT_shape_key_feather_reset);
WM_operatortype_append(MASK_OT_shape_key_rekey);
/* layers */
WM_operatortype_append(MASK_OT_layer_move);
WM_operatortype_append(MASK_OT_move_to_layer);
/* duplicate */
WM_operatortype_append(MASK_OT_duplicate);
/* clipboard */
WM_operatortype_append(MASK_OT_copy_splines);
WM_operatortype_append(MASK_OT_paste_splines);
}
void ED_keymap_mask(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Mask Editing", SPACE_EMPTY, RGN_TYPE_WINDOW);
keymap->poll = ED_maskedit_poll;
}
void ED_operatormacros_mask()
{
wmOperatorType *ot;
wmOperatorTypeMacro *otmacro;
ot = WM_operatortype_append_macro("MASK_OT_add_vertex_slide",
"Add Vertex and Slide",
"Add new vertex and slide it",
OPTYPE_UNDO | OPTYPE_REGISTER);
ot->description = "Add new vertex and slide it";
WM_operatortype_macro_define(ot, "MASK_OT_add_vertex");
otmacro = WM_operatortype_macro_define(ot, "MASK_OT_slide_point");
RNA_boolean_set(otmacro->ptr, "is_new_point", true);
ot = WM_operatortype_append_macro("MASK_OT_add_feather_vertex_slide",
"Add Feather Vertex and Slide",
"Add new vertex to feather and slide it",
OPTYPE_UNDO | OPTYPE_REGISTER);
ot->description = "Add new feather vertex and slide it";
WM_operatortype_macro_define(ot, "MASK_OT_add_feather_vertex");
otmacro = WM_operatortype_macro_define(ot, "MASK_OT_slide_point");
RNA_boolean_set(otmacro->ptr, "slide_feather", true);
ot = WM_operatortype_append_macro("MASK_OT_duplicate_move",
"Add Duplicate",
"Duplicate mask and move",
OPTYPE_UNDO | OPTYPE_REGISTER);
WM_operatortype_macro_define(ot, "MASK_OT_duplicate");
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
RNA_boolean_set(otmacro->ptr, "mirror", false);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Lock-to-selection viewport preservation
* \{ */
void ED_mask_view_lock_state_store(const bContext *C, MaskViewLockState *state)
{
SpaceClip *space_clip = CTX_wm_space_clip(C);
if (space_clip != nullptr) {
ED_clip_view_lock_state_store(C, &state->space_clip_state);
}
}
void ED_mask_view_lock_state_restore_no_jump(const bContext *C, const MaskViewLockState *state)
{
SpaceClip *space_clip = CTX_wm_space_clip(C);
if (space_clip != nullptr) {
if ((space_clip->flag & SC_LOCK_SELECTION) == 0) {
/* Early output if the editor is not locked to selection.
* Avoids forced dependency graph evaluation here. */
return;
}
/* Mask's lock-to-selection requires deformed splines to be evaluated to calculate bounds of
* points after animation has been evaluated. The restore-no-jump type of function does
* calculation of new offset for the view for an updated state of mask to cancel the offset out
* by modifying locked offset. In order to do such calculation mask needs to be evaluated after
* modification by an operator. */
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
(void)depsgraph;
ED_clip_view_lock_state_restore_no_jump(C, &state->space_clip_state);
}
}
/** \} */
} // namespace blender

View File

@@ -0,0 +1,320 @@
/* SPDX-FileCopyrightText: 2008 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edmask
*/
#include <cmath>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "DNA_mask_types.h"
#include "DNA_scene_types.h"
#include "BKE_mask.hh"
#include "ED_keyframes_edit.hh"
#include "ED_markers.hh"
#include "ED_mask.hh" /* own include */
namespace blender {
/* ***************************************** */
/* NOTE ABOUT THIS FILE:
* This file contains code for editing Mask data in the Action Editor
* as a 'keyframes', so that a user can adjust the timing of Mask shape-keys.
* Therefore, this file mostly contains functions for selecting Mask frames (shape-keys).
*/
/* ***************************************** */
/* Generics - Loopers */
bool ED_masklayer_frames_looper(MaskLayer *mask_layer,
Scene *scene,
bool (*mask_layer_shape_cb)(MaskLayerShape *, Scene *))
{
/* error checker */
if (mask_layer == nullptr) {
return false;
}
/* do loop */
for (MaskLayerShape &mask_layer_shape : mask_layer->splines_shapes) {
/* execute callback */
if (mask_layer_shape_cb(&mask_layer_shape, scene)) {
return true;
}
}
/* nothing to return */
return false;
}
/* ****************************************** */
/* Data Conversion Tools */
void ED_masklayer_make_cfra_list(MaskLayer *mask_layer, ListBaseT<CfraElem> *elems, bool onlysel)
{
/* error checking */
if (ELEM(nullptr, mask_layer, elems)) {
return;
}
/* loop through mask-frames, adding */
for (MaskLayerShape &mask_layer_shape : mask_layer->splines_shapes) {
if ((onlysel == false) || (mask_layer_shape.flag & MASK_SHAPE_SELECT)) {
CfraElem *ce = MEM_new_zeroed<CfraElem>("CfraElem");
ce->cfra = float(mask_layer_shape.frame);
ce->sel = (mask_layer_shape.flag & MASK_SHAPE_SELECT) ? 1 : 0;
BLI_addtail(elems, ce);
}
}
}
/* ***************************************** */
/* Selection Tools */
bool ED_masklayer_frame_select_check(const MaskLayer *mask_layer)
{
/* error checking */
if (mask_layer == nullptr) {
return false;
}
/* stop at the first one found */
for (MaskLayerShape &mask_layer_shape : mask_layer->splines_shapes) {
if (mask_layer_shape.flag & MASK_SHAPE_SELECT) {
return true;
}
}
/* not found */
return false;
}
/* helper function - select mask-frame based on SELECT_* mode */
static void mask_layer_shape_select(MaskLayerShape *mask_layer_shape, short select_mode)
{
if (mask_layer_shape == nullptr) {
return;
}
switch (select_mode) {
case SELECT_ADD:
mask_layer_shape->flag |= MASK_SHAPE_SELECT;
break;
case SELECT_SUBTRACT:
mask_layer_shape->flag &= ~MASK_SHAPE_SELECT;
break;
case SELECT_INVERT:
mask_layer_shape->flag ^= MASK_SHAPE_SELECT;
break;
}
}
void ED_mask_select_frames(MaskLayer *mask_layer, short select_mode)
{
/* error checking */
if (mask_layer == nullptr) {
return;
}
/* handle according to mode */
for (MaskLayerShape &mask_layer_shape : mask_layer->splines_shapes) {
mask_layer_shape_select(&mask_layer_shape, select_mode);
}
}
void ED_masklayer_frame_select_set(MaskLayer *mask_layer, short mode)
{
/* error checking */
if (mask_layer == nullptr) {
return;
}
/* now call the standard function */
ED_mask_select_frames(mask_layer, mode);
}
void ED_mask_select_frame(MaskLayer *mask_layer, int selx, short select_mode)
{
MaskLayerShape *mask_layer_shape;
if (mask_layer == nullptr) {
return;
}
mask_layer_shape = BKE_mask_layer_shape_find_frame(mask_layer, selx);
if (mask_layer_shape) {
mask_layer_shape_select(mask_layer_shape, select_mode);
}
}
void ED_masklayer_frames_select_box(MaskLayer *mask_layer, float min, float max, short select_mode)
{
if (mask_layer == nullptr) {
return;
}
/* only select those frames which are in bounds */
for (MaskLayerShape &mask_layer_shape : mask_layer->splines_shapes) {
if (IN_RANGE(mask_layer_shape.frame, min, max)) {
mask_layer_shape_select(&mask_layer_shape, select_mode);
}
}
}
void ED_masklayer_frames_select_region(KeyframeEditData *ked,
MaskLayer *mask_layer,
short tool,
short select_mode)
{
if (mask_layer == nullptr) {
return;
}
/* only select frames which are within the region */
for (MaskLayerShape &mask_layer_shape : mask_layer->splines_shapes) {
/* construct a dummy point coordinate to do this testing with */
float pt[2] = {0};
pt[0] = mask_layer_shape.frame;
pt[1] = ked->channel_y;
/* check the necessary regions */
if (tool == BEZT_OK_CHANNEL_LASSO) {
/* Lasso */
if (keyframe_region_lasso_test(static_cast<KeyframeEdit_LassoData *>(ked->data), pt)) {
mask_layer_shape_select(&mask_layer_shape, select_mode);
}
}
else if (tool == BEZT_OK_CHANNEL_CIRCLE) {
/* Circle */
if (keyframe_region_circle_test(static_cast<KeyframeEdit_CircleData *>(ked->data), pt)) {
mask_layer_shape_select(&mask_layer_shape, select_mode);
}
}
}
}
/* ***************************************** */
/* Frame Editing Tools */
bool ED_masklayer_frames_delete(MaskLayer *mask_layer)
{
bool changed = false;
/* error checking */
if (mask_layer == nullptr) {
return false;
}
/* check for frames to delete */
for (MaskLayerShape &mask_layer_shape : mask_layer->splines_shapes.items_mutable()) {
if (mask_layer_shape.flag & MASK_SHAPE_SELECT) {
BKE_mask_layer_shape_unlink(mask_layer, &mask_layer_shape);
changed = true;
}
}
return changed;
}
bool ED_masklayer_frames_duplicate(MaskLayer *mask_layer)
{
bool changed = false;
/* Error checking. */
if (mask_layer == nullptr) {
return changed;
}
/* Duplicate selected frames. */
for (MaskLayerShape &mask_layer_shape : mask_layer->splines_shapes.items_mutable()) {
/* Duplicate this frame. */
if (mask_layer_shape.flag & MASK_SHAPE_SELECT) {
MaskLayerShape *mask_shape_dupe;
/* Duplicate frame, and deselect self. */
mask_shape_dupe = BKE_mask_layer_shape_duplicate(&mask_layer_shape);
mask_layer_shape.flag &= ~MASK_SHAPE_SELECT;
/* XXX: how to handle duplicate frames? */
BLI_insertlinkafter(&mask_layer->splines_shapes, &mask_layer_shape, mask_shape_dupe);
changed = true;
}
}
return changed;
}
/* -------------------------------------- */
/* Snap Tools */
static bool snap_mask_layer_nearest(MaskLayerShape *mask_layer_shape, Scene * /*scene*/)
{
if (mask_layer_shape->flag & MASK_SHAPE_SELECT) {
mask_layer_shape->frame = int(floor(mask_layer_shape->frame + 0.5));
}
return false;
}
static bool snap_mask_layer_nearestsec(MaskLayerShape *mask_layer_shape, Scene *scene)
{
float secf = float(scene->frames_per_second());
if (mask_layer_shape->flag & MASK_SHAPE_SELECT) {
mask_layer_shape->frame = int(floorf(mask_layer_shape->frame / secf + 0.5f) * secf);
}
return false;
}
static bool snap_mask_layer_cframe(MaskLayerShape *mask_layer_shape, Scene *scene)
{
if (mask_layer_shape->flag & MASK_SHAPE_SELECT) {
mask_layer_shape->frame = scene->r.cfra;
}
return false;
}
static bool snap_mask_layer_nearmarker(MaskLayerShape *mask_layer_shape, Scene *scene)
{
if (mask_layer_shape->flag & MASK_SHAPE_SELECT) {
mask_layer_shape->frame = ED_markers_find_nearest_marker_time(&scene->markers,
float(mask_layer_shape->frame));
}
return false;
}
void ED_masklayer_snap_frames(MaskLayer *mask_layer, Scene *scene, short mode)
{
switch (mode) {
case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
ED_masklayer_frames_looper(mask_layer, scene, snap_mask_layer_nearest);
break;
case SNAP_KEYS_CURFRAME: /* snap to current frame */
ED_masklayer_frames_looper(mask_layer, scene, snap_mask_layer_cframe);
break;
case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
ED_masklayer_frames_looper(mask_layer, scene, snap_mask_layer_nearmarker);
break;
case SNAP_KEYS_NEARSEC: /* snap to nearest second */
ED_masklayer_frames_looper(mask_layer, scene, snap_mask_layer_nearestsec);
break;
default: /* just in case */
break;
}
}
} // namespace blender

View File

@@ -0,0 +1,151 @@
/* SPDX-FileCopyrightText: 2011 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup spclip
*/
#pragma once
#include "BKE_mask.hh"
#include "ED_clip.hh"
namespace blender {
struct Mask;
struct MaskLayer;
struct MaskSpline;
struct MaskSplinePoint;
struct MaskSplinePointUW;
struct bContext;
struct wmOperatorType;
/* internal exports only */
/* `mask_add.cc` */
void MASK_OT_add_vertex(wmOperatorType *ot);
void MASK_OT_add_feather_vertex(wmOperatorType *ot);
void MASK_OT_primitive_circle_add(wmOperatorType *ot);
void MASK_OT_primitive_square_add(wmOperatorType *ot);
/* `mask_ops.cc` */
Mask *ED_mask_new(bContext *C, const char *name);
/**
* Get active layer. Will create mask/layer to be sure there's an active layer.
*/
MaskLayer *ED_mask_layer_ensure(bContext *C, bool *r_added_mask);
void MASK_OT_new(wmOperatorType *ot);
void MASK_OT_layer_new(wmOperatorType *ot);
void MASK_OT_layer_remove(wmOperatorType *ot);
void MASK_OT_cyclic_toggle(wmOperatorType *ot);
void MASK_OT_slide_point(wmOperatorType *ot);
void MASK_OT_slide_spline_curvature(wmOperatorType *ot);
void MASK_OT_delete(wmOperatorType *ot);
void MASK_OT_hide_view_clear(wmOperatorType *ot);
void MASK_OT_hide_view_set(wmOperatorType *ot);
void MASK_OT_feather_weight_clear(wmOperatorType *ot);
void MASK_OT_switch_direction(wmOperatorType *ot);
/* Named to match mesh recalculate normals. */
void MASK_OT_normals_make_consistent(wmOperatorType *ot);
void MASK_OT_handle_type_set(wmOperatorType *ot);
void MASK_OT_layer_move(wmOperatorType *ot);
void MASK_OT_move_to_layer(wmOperatorType *ot);
void MASK_OT_duplicate(wmOperatorType *ot);
void MASK_OT_copy_splines(wmOperatorType *ot);
void MASK_OT_paste_splines(wmOperatorType *ot);
/* `mask_relationships.cc` */
/** based on #OBJECT_OT_parent_set */
void MASK_OT_parent_set(wmOperatorType *ot);
void MASK_OT_parent_clear(wmOperatorType *ot);
/* `mask_select.cc` */
void MASK_OT_select(wmOperatorType *ot);
void MASK_OT_select_all(wmOperatorType *ot);
void MASK_OT_select_box(wmOperatorType *ot);
void MASK_OT_select_lasso(wmOperatorType *ot);
void MASK_OT_select_circle(wmOperatorType *ot);
void MASK_OT_select_linked_pick(wmOperatorType *ot);
void MASK_OT_select_linked(wmOperatorType *ot);
void MASK_OT_select_more(wmOperatorType *ot);
void MASK_OT_select_less(wmOperatorType *ot);
/* 'check' select */
bool ED_mask_spline_select_check(const MaskSpline *spline);
bool ED_mask_layer_select_check(const MaskLayer *mask_layer);
bool ED_mask_select_check(const Mask *mask);
void ED_mask_spline_select_set(MaskSpline *spline, bool do_select);
void ED_mask_layer_select_set(MaskLayer *mask_layer, bool do_select);
void ED_mask_select_toggle_all(Mask *mask, int action);
void ED_mask_select_flush_all(Mask *mask);
/* mask_edit.cc */
/* Generalized solution for preserving editor viewport when making changes while lock-to-selection
* is enabled.
* Any mask operator can use this API, without worrying that some editors do not have an idea of
* lock-to-selection. */
struct MaskViewLockState {
ClipViewLockState space_clip_state;
};
void ED_mask_view_lock_state_store(const bContext *C, MaskViewLockState *state);
void ED_mask_view_lock_state_restore_no_jump(const bContext *C, const MaskViewLockState *state);
/* `mask_query.cc` */
bool ED_mask_find_nearest_diff_point(const bContext *C,
Mask *mask_orig,
const float normal_co[2],
int threshold,
bool feather,
float tangent[2],
bool use_deform,
bool use_project,
MaskLayer **r_mask_layer,
MaskSpline **r_spline,
MaskSplinePoint **r_point,
float *r_u,
float *r_score);
bool ED_mask_feather_find_nearest(const bContext *C,
Mask *mask_orig,
const float normal_co[2],
float threshold,
MaskLayer **r_mask_layer,
MaskSpline **r_spline,
MaskSplinePoint **r_point,
MaskSplinePointUW **r_uw,
float *r_score);
MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C,
Mask *mask_orig,
const float normal_co[2],
float threshold,
MaskLayer **r_mask_layer,
MaskSpline **r_spline,
eMaskWhichHandle *r_which_handle,
float *r_score);
/* `mask_shapekey.cc` */
void MASK_OT_shape_key_insert(wmOperatorType *ot);
void MASK_OT_shape_key_clear(wmOperatorType *ot);
void MASK_OT_shape_key_feather_reset(wmOperatorType *ot);
void MASK_OT_shape_key_rekey(wmOperatorType *ot);
} // namespace blender

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,881 @@
/* SPDX-FileCopyrightText: 2012 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edmask
*/
#include "MEM_guardedalloc.h"
#include "BKE_context.hh"
#include "BKE_mask.hh"
#include "BLI_listbase.h"
#include "BLI_math_geom.h"
#include "BLI_math_vector.h"
#include "DEG_depsgraph_query.hh"
#include "DNA_mask_types.h"
#include "DNA_screen_types.h"
#include "ED_clip.hh"
#include "ED_image.hh"
#include "ED_mask.hh" /* own include */
#include "UI_view2d.hh"
#include "mask_intern.hh" /* own include */
namespace blender {
/* -------------------------------------------------------------------- */
/** \name Spatial Queries
* \{ */
bool ED_mask_find_nearest_diff_point(const bContext *C,
Mask *mask_orig,
const float normal_co[2],
int threshold,
bool feather,
float tangent[2],
const bool use_deform,
const bool use_project,
MaskLayer **r_mask_layer,
MaskSpline **r_spline,
MaskSplinePoint **r_point,
float *r_u,
float *r_score)
{
const float threshold_sq = threshold * threshold;
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
MaskLayer *point_mask_layer;
MaskSpline *point_spline;
MaskSplinePoint *point = nullptr;
float dist_best_sq = FLT_MAX, co[2];
int width, height;
float u = 0.0f;
float scalex, scaley;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Mask *mask_eval = DEG_get_evaluated(depsgraph, mask_orig);
ED_mask_get_size(area, &width, &height);
ED_mask_pixelspace_factor(area, region, &scalex, &scaley);
co[0] = normal_co[0] * scalex;
co[1] = normal_co[1] * scaley;
for (MaskLayer *mask_layer_orig = static_cast<MaskLayer *>(mask_orig->masklayers.first),
*mask_layer_eval = static_cast<MaskLayer *>(mask_eval->masklayers.first);
mask_layer_orig != nullptr;
mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next)
{
if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
for (MaskSpline *spline_orig = static_cast<MaskSpline *>(mask_layer_orig->splines.first),
*spline_eval = static_cast<MaskSpline *>(mask_layer_eval->splines.first);
spline_orig != nullptr;
spline_orig = spline_orig->next, spline_eval = spline_eval->next)
{
int i;
MaskSplinePoint *cur_point_eval;
for (i = 0, cur_point_eval = use_deform ? spline_eval->points_deform : spline_eval->points;
i < spline_eval->tot_point;
i++, cur_point_eval++)
{
uint tot_diff_point;
float *diff_points = BKE_mask_point_segment_diff(
spline_eval, cur_point_eval, width, height, &tot_diff_point);
if (diff_points) {
int j, tot_point;
uint tot_feather_point;
float *feather_points = nullptr, *points;
if (feather) {
feather_points = BKE_mask_point_segment_feather_diff(
spline_eval, cur_point_eval, width, height, &tot_feather_point);
points = feather_points;
tot_point = tot_feather_point;
}
else {
points = diff_points;
tot_point = tot_diff_point;
}
for (j = 0; j < tot_point - 1; j++) {
float dist_sq, a[2], b[2];
a[0] = points[2 * j] * scalex;
a[1] = points[2 * j + 1] * scaley;
b[0] = points[2 * j + 2] * scalex;
b[1] = points[2 * j + 3] * scaley;
dist_sq = dist_squared_to_line_segment_v2(co, a, b);
if (dist_sq < dist_best_sq) {
if (tangent) {
sub_v2_v2v2(tangent, &diff_points[2 * j + 2], &diff_points[2 * j]);
}
point_mask_layer = mask_layer_orig;
point_spline = spline_orig;
point = use_deform ?
&spline_orig->points[(cur_point_eval - spline_eval->points_deform)] :
&spline_orig->points[(cur_point_eval - spline_eval->points)];
dist_best_sq = dist_sq;
u = float(j) / tot_point;
}
}
if (feather_points != nullptr) {
MEM_delete(feather_points);
}
MEM_delete(diff_points);
}
}
}
}
if (point && dist_best_sq < threshold_sq) {
if (r_mask_layer) {
*r_mask_layer = point_mask_layer;
}
if (r_spline) {
*r_spline = point_spline;
}
if (r_point) {
*r_point = point;
}
if (r_u) {
/* TODO(sergey): Projection fails in some weirdo cases. */
if (use_project) {
u = BKE_mask_spline_project_co(point_spline, point, u, normal_co, MASK_PROJ_ANY);
}
*r_u = u;
}
if (r_score) {
*r_score = dist_best_sq;
}
return true;
}
if (r_mask_layer) {
*r_mask_layer = nullptr;
}
if (r_spline) {
*r_spline = nullptr;
}
if (r_point) {
*r_point = nullptr;
}
return false;
}
static void mask_point_scaled_handle(const MaskSplinePoint *point,
const eMaskWhichHandle which_handle,
const float scalex,
const float scaley,
float handle[2])
{
BKE_mask_point_handle(point, which_handle, handle);
handle[0] *= scalex;
handle[1] *= scaley;
}
MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C,
Mask *mask_orig,
const float normal_co[2],
const float threshold,
MaskLayer **r_mask_layer,
MaskSpline **r_spline,
eMaskWhichHandle *r_which_handle,
float *r_score)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
MaskLayer *point_mask_layer = nullptr;
MaskSpline *point_spline = nullptr;
MaskSplinePoint *point = nullptr;
float co[2];
const float threshold_sq = threshold * threshold;
float len_sq = FLT_MAX, scalex, scaley;
eMaskWhichHandle which_handle = MASK_WHICH_HANDLE_NONE;
int width, height;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Mask *mask_eval = DEG_get_evaluated(depsgraph, mask_orig);
ED_mask_get_size(area, &width, &height);
ED_mask_pixelspace_factor(area, region, &scalex, &scaley);
co[0] = normal_co[0] * scalex;
co[1] = normal_co[1] * scaley;
for (MaskLayer *mask_layer_orig = static_cast<MaskLayer *>(mask_orig->masklayers.first),
*mask_layer_eval = static_cast<MaskLayer *>(mask_eval->masklayers.first);
mask_layer_orig != nullptr;
mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next)
{
if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
for (MaskSpline *spline_orig = static_cast<MaskSpline *>(mask_layer_orig->splines.first),
*spline_eval = static_cast<MaskSpline *>(mask_layer_eval->splines.first);
spline_orig != nullptr;
spline_orig = spline_orig->next, spline_eval = spline_eval->next)
{
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval);
for (int i = 0; i < spline_orig->tot_point; i++) {
MaskSplinePoint *cur_point_orig = &spline_orig->points[i];
const MaskSplinePoint *cur_point_deform_eval = &points_array[i];
eMaskWhichHandle cur_which_handle = MASK_WHICH_HANDLE_NONE;
const BezTriple *bezt = &cur_point_deform_eval->bezt;
float cur_len_sq, vec[2];
vec[0] = bezt->vec[1][0] * scalex;
vec[1] = bezt->vec[1][1] * scaley;
cur_len_sq = len_squared_v2v2(co, vec);
if (cur_len_sq < len_sq) {
point_spline = spline_orig;
point_mask_layer = mask_layer_orig;
point = cur_point_orig;
len_sq = cur_len_sq;
which_handle = MASK_WHICH_HANDLE_NONE;
}
if (BKE_mask_point_handles_mode_get(cur_point_deform_eval) == MASK_HANDLE_MODE_STICK) {
float handle[2];
mask_point_scaled_handle(
cur_point_deform_eval, MASK_WHICH_HANDLE_STICK, scalex, scaley, handle);
cur_len_sq = len_squared_v2v2(co, handle);
cur_which_handle = MASK_WHICH_HANDLE_STICK;
}
else {
float handle_left[2], handle_right[2];
float len_left_sq, len_right_sq;
mask_point_scaled_handle(
cur_point_deform_eval, MASK_WHICH_HANDLE_LEFT, scalex, scaley, handle_left);
mask_point_scaled_handle(
cur_point_deform_eval, MASK_WHICH_HANDLE_RIGHT, scalex, scaley, handle_right);
len_left_sq = len_squared_v2v2(co, handle_left);
len_right_sq = len_squared_v2v2(co, handle_right);
if (i == 0) {
if (len_left_sq <= len_right_sq) {
if (bezt->h1 != HD_VECT) {
cur_which_handle = MASK_WHICH_HANDLE_LEFT;
cur_len_sq = len_left_sq;
}
}
else if (bezt->h2 != HD_VECT) {
cur_which_handle = MASK_WHICH_HANDLE_RIGHT;
cur_len_sq = len_right_sq;
}
}
else {
if (len_right_sq <= len_left_sq) {
if (bezt->h2 != HD_VECT) {
cur_which_handle = MASK_WHICH_HANDLE_RIGHT;
cur_len_sq = len_right_sq;
}
}
else if (bezt->h1 != HD_VECT) {
cur_which_handle = MASK_WHICH_HANDLE_LEFT;
cur_len_sq = len_left_sq;
}
}
}
if (cur_len_sq <= len_sq && cur_which_handle != MASK_WHICH_HANDLE_NONE) {
point_mask_layer = mask_layer_orig;
point_spline = spline_orig;
point = cur_point_orig;
len_sq = cur_len_sq;
which_handle = cur_which_handle;
}
}
}
}
if (len_sq < threshold_sq) {
if (r_mask_layer) {
*r_mask_layer = point_mask_layer;
}
if (r_spline) {
*r_spline = point_spline;
}
if (r_which_handle) {
*r_which_handle = which_handle;
}
if (r_score) {
*r_score = sqrtf(len_sq);
}
return point;
}
if (r_mask_layer) {
*r_mask_layer = nullptr;
}
if (r_spline) {
*r_spline = nullptr;
}
if (r_which_handle) {
*r_which_handle = MASK_WHICH_HANDLE_NONE;
}
return nullptr;
}
bool ED_mask_feather_find_nearest(const bContext *C,
Mask *mask_orig,
const float normal_co[2],
const float threshold,
MaskLayer **r_mask_layer,
MaskSpline **r_spline,
MaskSplinePoint **r_point,
MaskSplinePointUW **r_uw,
float *r_score)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
MaskLayer *point_mask_layer = nullptr;
MaskSpline *point_spline = nullptr;
MaskSplinePoint *point = nullptr;
MaskSplinePointUW *uw = nullptr;
const float threshold_sq = threshold * threshold;
float len = FLT_MAX, co[2];
float scalex, scaley;
int width, height;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Mask *mask_eval = DEG_get_evaluated(depsgraph, mask_orig);
ED_mask_get_size(area, &width, &height);
ED_mask_pixelspace_factor(area, region, &scalex, &scaley);
co[0] = normal_co[0] * scalex;
co[1] = normal_co[1] * scaley;
for (MaskLayer *mask_layer_orig = static_cast<MaskLayer *>(mask_orig->masklayers.first),
*mask_layer_eval = static_cast<MaskLayer *>(mask_eval->masklayers.first);
mask_layer_orig != nullptr;
mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next)
{
for (MaskSpline *spline_orig = static_cast<MaskSpline *>(mask_layer_orig->splines.first),
*spline_eval = static_cast<MaskSpline *>(mask_layer_eval->splines.first);
spline_orig != nullptr;
spline_orig = spline_orig->next, spline_eval = spline_eval->next)
{
// MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
int i, tot_feather_point;
float (*feather_points)[2], (*fp)[2];
if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
feather_points = fp = BKE_mask_spline_feather_points(spline_eval, &tot_feather_point);
for (i = 0; i < spline_orig->tot_point; i++) {
int j;
MaskSplinePoint *cur_point_orig = &spline_orig->points[i];
MaskSplinePoint *cur_point_eval = &spline_eval->points[i];
for (j = 0; j <= cur_point_eval->tot_uw; j++) {
float cur_len_sq, vec[2];
vec[0] = (*fp)[0] * scalex;
vec[1] = (*fp)[1] * scaley;
cur_len_sq = len_squared_v2v2(vec, co);
if (point == nullptr || cur_len_sq < len) {
if (j == 0) {
uw = nullptr;
}
else {
uw = &cur_point_orig->uw[j - 1];
}
point_mask_layer = mask_layer_orig;
point_spline = spline_orig;
point = cur_point_orig;
len = cur_len_sq;
}
fp++;
}
}
MEM_delete(feather_points);
}
}
if (len < threshold_sq) {
if (r_mask_layer) {
*r_mask_layer = point_mask_layer;
}
if (r_spline) {
*r_spline = point_spline;
}
if (r_point) {
*r_point = point;
}
if (r_uw) {
*r_uw = uw;
}
if (r_score) {
*r_score = sqrtf(len);
}
return true;
}
if (r_mask_layer) {
*r_mask_layer = nullptr;
}
if (r_spline) {
*r_spline = nullptr;
}
if (r_point) {
*r_point = nullptr;
}
return false;
}
void ED_mask_mouse_pos(ScrArea *area, ARegion *region, const int mval[2], float r_co[2])
{
if (area) {
switch (area->spacetype) {
case SPACE_CLIP: {
SpaceClip *sc = static_cast<SpaceClip *>(area->spacedata.first);
ED_clip_mouse_pos(sc, region, mval, r_co);
BKE_mask_coord_from_movieclip(sc->clip, &sc->user, r_co, r_co);
break;
}
case SPACE_SEQ: {
ui::view2d_region_to_view(&region->v2d, mval[0], mval[1], &r_co[0], &r_co[1]);
break;
}
case SPACE_IMAGE: {
SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first);
ED_image_mouse_pos(sima, region, mval, r_co);
BKE_mask_coord_from_image(sima->image, &sima->iuser, r_co, r_co);
break;
}
default:
/* possible other spaces from which mask editing is available */
BLI_assert(0);
zero_v2(r_co);
break;
}
}
else {
BLI_assert(0);
zero_v2(r_co);
}
}
void ED_mask_point_pos(ScrArea *area, ARegion *region, float x, float y, float *r_x, float *r_y)
{
float co[2];
if (area) {
switch (area->spacetype) {
case SPACE_CLIP: {
SpaceClip *sc = static_cast<SpaceClip *>(area->spacedata.first);
ED_clip_point_stable_pos(sc, region, x, y, &co[0], &co[1]);
BKE_mask_coord_from_movieclip(sc->clip, &sc->user, co, co);
break;
}
case SPACE_SEQ:
zero_v2(co); /* MASKTODO */
break;
case SPACE_IMAGE: {
SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first);
ED_image_point_pos(sima, region, x, y, &co[0], &co[1]);
BKE_mask_coord_from_image(sima->image, &sima->iuser, co, co);
break;
}
default:
/* possible other spaces from which mask editing is available */
BLI_assert(0);
zero_v2(co);
break;
}
}
else {
BLI_assert(0);
zero_v2(co);
}
*r_x = co[0];
*r_y = co[1];
}
void ED_mask_point_pos__reverse(
ScrArea *area, ARegion *region, float x, float y, float *r_x, float *r_y)
{
float co[2];
if (area) {
switch (area->spacetype) {
case SPACE_CLIP: {
SpaceClip *sc = static_cast<SpaceClip *>(area->spacedata.first);
co[0] = x;
co[1] = y;
BKE_mask_coord_to_movieclip(sc->clip, &sc->user, co, co);
ED_clip_point_stable_pos__reverse(sc, region, co, co);
break;
}
case SPACE_SEQ:
zero_v2(co); /* MASKTODO */
break;
case SPACE_IMAGE: {
SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first);
co[0] = x;
co[1] = y;
BKE_mask_coord_to_image(sima->image, &sima->iuser, co, co);
ED_image_point_pos__reverse(sima, region, co, co);
break;
}
default:
/* possible other spaces from which mask editing is available */
BLI_assert(0);
zero_v2(co);
break;
}
}
else {
BLI_assert(0);
zero_v2(co);
}
*r_x = co[0];
*r_y = co[1];
}
static void handle_position_for_minmax(const MaskSplinePoint *point,
eMaskWhichHandle which_handle,
bool handles_as_knot,
float r_handle[2])
{
if (handles_as_knot) {
copy_v2_v2(r_handle, point->bezt.vec[1]);
return;
}
BKE_mask_point_handle(point, which_handle, r_handle);
}
bool ED_mask_selected_minmax(const bContext *C,
float min[2],
float max[2],
const bool handles_as_knot,
const bool handles_as_knot_selected_only)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Mask *mask = CTX_data_edit_mask(C);
bool ok = false;
if (mask == nullptr) {
return ok;
}
/* Use evaluated mask to take animation into account.
* The animation of splines is not "flushed" back to original, so need to explicitly
* use evaluated data-block here. */
Mask *mask_eval = DEG_get_evaluated(depsgraph, mask);
INIT_MINMAX2(min, max);
for (MaskLayer &mask_layer : mask_eval->masklayers) {
if (mask_layer.visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
for (MaskSpline &spline : mask_layer.splines) {
MaskSplinePoint *points_array = BKE_mask_spline_point_array(&spline);
for (int i = 0; i < spline.tot_point; i++) {
const MaskSplinePoint *point = &spline.points[i];
const MaskSplinePoint *deform_point = &points_array[i];
const BezTriple *bezt = &point->bezt;
float handle[2];
if (!BKE_mask_point_selected(point)) {
continue;
}
if (bezt->f2 & SELECT || handles_as_knot_selected_only) {
minmax_v2v2_v2(min, max, deform_point->bezt.vec[1]);
ok = true;
continue;
}
if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) {
handle_position_for_minmax(
deform_point, MASK_WHICH_HANDLE_STICK, handles_as_knot, handle);
minmax_v2v2_v2(min, max, handle);
ok = true;
}
else {
if ((bezt->f1 & SELECT) && (bezt->h1 != HD_VECT)) {
handle_position_for_minmax(
deform_point, MASK_WHICH_HANDLE_LEFT, handles_as_knot, handle);
minmax_v2v2_v2(min, max, handle);
ok = true;
}
if ((bezt->f3 & SELECT) && (bezt->h2 != HD_VECT)) {
handle_position_for_minmax(
deform_point, MASK_WHICH_HANDLE_RIGHT, handles_as_knot, handle);
minmax_v2v2_v2(min, max, handle);
ok = true;
}
}
}
}
}
return ok;
}
void ED_mask_center_from_pivot_ex(const bContext *C,
ScrArea *area,
const char mode,
const bool handles_as_knot_selected_only,
float r_center[2],
bool *r_has_select)
{
float min[2], max[2];
const bool mask_selected = ED_mask_selected_minmax(
C, min, max, false, handles_as_knot_selected_only);
switch (mode) {
case V3D_AROUND_CURSOR:
ED_mask_cursor_location_get(area, r_center);
break;
default:
mid_v2_v2v2(r_center, min, max);
break;
}
if (r_has_select != nullptr) {
*r_has_select = mask_selected;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Generic 2D View Queries
* \{ */
void ED_mask_get_size(ScrArea *area, int *r_width, int *r_height)
{
if (area && area->spacedata.first) {
switch (area->spacetype) {
case SPACE_CLIP: {
SpaceClip *sc = static_cast<SpaceClip *>(area->spacedata.first);
ED_space_clip_get_size(sc, r_width, r_height);
break;
}
case SPACE_SEQ: {
// Scene *scene = CTX_data_scene(C);
// BKE_render_resolution(&scene->r, false, r_width, r_height);
break;
}
case SPACE_IMAGE: {
SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first);
ED_space_image_get_size(sima, r_width, r_height);
break;
}
default:
/* possible other spaces from which mask editing is available */
BLI_assert(0);
*r_width = 0;
*r_height = 0;
break;
}
}
else {
BLI_assert(0);
*r_width = 0;
*r_height = 0;
}
}
void ED_mask_zoom(ScrArea *area, ARegion *region, float *r_zoomx, float *r_zoomy)
{
if (area && area->spacedata.first) {
switch (area->spacetype) {
case SPACE_CLIP: {
SpaceClip *sc = static_cast<SpaceClip *>(area->spacedata.first);
ED_space_clip_get_zoom(sc, region, r_zoomx, r_zoomy);
break;
}
case SPACE_SEQ: {
*r_zoomx = *r_zoomy = 1.0f;
break;
}
case SPACE_IMAGE: {
SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first);
ED_space_image_get_zoom(sima, region, r_zoomx, r_zoomy);
break;
}
default:
/* possible other spaces from which mask editing is available */
BLI_assert(0);
*r_zoomx = *r_zoomy = 1.0f;
break;
}
}
else {
BLI_assert(0);
*r_zoomx = *r_zoomy = 1.0f;
}
}
void ED_mask_get_aspect(ScrArea *area, ARegion * /*region*/, float *r_aspx, float *r_aspy)
{
if (area && area->spacedata.first) {
switch (area->spacetype) {
case SPACE_CLIP: {
SpaceClip *sc = static_cast<SpaceClip *>(area->spacedata.first);
ED_space_clip_get_aspect(sc, r_aspx, r_aspy);
break;
}
case SPACE_SEQ: {
*r_aspx = *r_aspy = 1.0f; /* MASKTODO - render aspect? */
break;
}
case SPACE_IMAGE: {
SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first);
ED_space_image_get_aspect(sima, r_aspx, r_aspy);
break;
}
default:
/* possible other spaces from which mask editing is available */
BLI_assert(0);
*r_aspx = *r_aspy = 1.0f;
break;
}
}
else {
BLI_assert(0);
*r_aspx = *r_aspy = 1.0f;
}
}
void ED_mask_pixelspace_factor(ScrArea *area, ARegion *region, float *r_scalex, float *r_scaley)
{
if (area && area->spacedata.first) {
switch (area->spacetype) {
case SPACE_CLIP: {
SpaceClip *sc = static_cast<SpaceClip *>(area->spacedata.first);
float aspx, aspy;
ui::view2d_scale_get(&region->v2d, r_scalex, r_scaley);
ED_space_clip_get_aspect(sc, &aspx, &aspy);
*r_scalex *= aspx;
*r_scaley *= aspy;
break;
}
case SPACE_SEQ: {
*r_scalex = *r_scaley = 1.0f; /* MASKTODO? */
break;
}
case SPACE_IMAGE: {
SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first);
float aspx, aspy;
ui::view2d_scale_get(&region->v2d, r_scalex, r_scaley);
ED_space_image_get_aspect(sima, &aspx, &aspy);
*r_scalex *= aspx;
*r_scaley *= aspy;
break;
}
default:
/* possible other spaces from which mask editing is available */
BLI_assert(0);
*r_scalex = *r_scaley = 1.0f;
break;
}
}
else {
BLI_assert(0);
*r_scalex = *r_scaley = 1.0f;
}
}
void ED_mask_cursor_location_get(ScrArea *area, float cursor[2])
{
if (area) {
switch (area->spacetype) {
case SPACE_CLIP: {
SpaceClip *space_clip = static_cast<SpaceClip *>(area->spacedata.first);
copy_v2_v2(cursor, space_clip->cursor);
break;
}
case SPACE_SEQ: {
zero_v2(cursor);
break;
}
case SPACE_IMAGE: {
SpaceImage *space_image = static_cast<SpaceImage *>(area->spacedata.first);
copy_v2_v2(cursor, space_image->cursor);
break;
}
default:
/* possible other spaces from which mask editing is available */
BLI_assert(0);
zero_v2(cursor);
break;
}
}
else {
BLI_assert(0);
zero_v2(cursor);
}
}
/** \} */
} // namespace blender

View File

@@ -0,0 +1,175 @@
/* SPDX-FileCopyrightText: 2012 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edmask
*/
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_string_utf8.h"
#include "BKE_context.hh"
#include "BKE_mask.hh"
#include "BKE_tracking.hh"
#include "DEG_depsgraph.hh"
#include "DNA_mask_types.h"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_clip.hh" /* frame remapping functions */
#include "ED_mask.hh"
#include "ED_screen.hh"
#include "mask_intern.hh" /* own include */
namespace blender {
static wmOperatorStatus mask_parent_clear_exec(bContext *C, wmOperator * /*op*/)
{
Mask *mask = CTX_data_edit_mask(C);
for (MaskLayer &mask_layer : mask->masklayers) {
if (mask_layer.visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
for (MaskSpline &spline : mask_layer.splines) {
for (int i = 0; i < spline.tot_point; i++) {
MaskSplinePoint *point = &spline.points[i];
if (BKE_mask_point_selected(point)) {
point->parent.id = nullptr;
}
}
}
}
WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
DEG_id_tag_update(&mask->id, 0);
return OPERATOR_FINISHED;
}
void MASK_OT_parent_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Parent";
ot->description = "Clear the mask's parenting";
ot->idname = "MASK_OT_parent_clear";
/* API callbacks. */
ot->exec = mask_parent_clear_exec;
ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus mask_parent_set_exec(bContext *C, wmOperator * /*op*/)
{
Mask *mask = CTX_data_edit_mask(C);
/* parent info */
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
MovieTrackingTrack *track;
MovieTrackingPlaneTrack *plane_track;
MovieTrackingObject *tracking_object;
/* done */
MaskParentType parent_type;
int framenr;
float parmask_pos[2], orig_corners[4][2];
const char *sub_parent_name;
if (ELEM(nullptr, sc, clip)) {
return OPERATOR_CANCELLED;
}
framenr = ED_space_clip_get_clip_frame_number(sc);
tracking_object = BKE_tracking_object_get_active(&clip->tracking);
if (tracking_object == nullptr) {
return OPERATOR_CANCELLED;
}
if ((track = tracking_object->active_track) != nullptr) {
MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);
float marker_pos_ofs[2];
add_v2_v2v2(marker_pos_ofs, marker->pos, track->offset);
BKE_mask_coord_from_movieclip(clip, &sc->user, parmask_pos, marker_pos_ofs);
sub_parent_name = track->name;
parent_type = MASK_PARENT_POINT_TRACK;
memset(orig_corners, 0, sizeof(orig_corners));
}
else if ((plane_track = tracking_object->active_plane_track) != nullptr) {
MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_get(plane_track, framenr);
zero_v2(parmask_pos);
sub_parent_name = plane_track->name;
parent_type = MASK_PARENT_PLANE_TRACK;
memcpy(orig_corners, plane_marker->corners, sizeof(orig_corners));
}
else {
return OPERATOR_CANCELLED;
}
for (MaskLayer &mask_layer : mask->masklayers) {
if (mask_layer.visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
for (MaskSpline &spline : mask_layer.splines) {
for (int i = 0; i < spline.tot_point; i++) {
MaskSplinePoint *point = &spline.points[i];
if (BKE_mask_point_selected(point)) {
point->parent.id_type = ID_MC;
point->parent.id = &clip->id;
point->parent.type = parent_type;
STRNCPY_UTF8(point->parent.parent, tracking_object->name);
STRNCPY_UTF8(point->parent.sub_parent, sub_parent_name);
copy_v2_v2(point->parent.parent_orig, parmask_pos);
memcpy(point->parent.parent_corners_orig,
orig_corners,
sizeof(point->parent.parent_corners_orig));
}
}
}
}
WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
DEG_id_tag_update(&mask->id, 0);
return OPERATOR_FINISHED;
}
void MASK_OT_parent_set(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Make Parent";
ot->description = "Set the mask's parenting";
ot->idname = "MASK_OT_parent_set";
/* API callbacks. */
// ot->invoke = mask_parent_set_invoke;
ot->exec = mask_parent_set_exec;
ot->poll = ED_space_clip_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
} // namespace blender

View File

@@ -0,0 +1,999 @@
/* SPDX-FileCopyrightText: 2012 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edmask
*/
#include "BLI_lasso_2d.hh"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_math_vector_types.hh"
#include "BLI_rect.h"
#include "BKE_context.hh"
#include "BKE_mask.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_mask_types.h"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_mask.hh" /* own include */
#include "ED_select_utils.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "mask_intern.hh" /* own include */
namespace blender {
/* -------------------------------------------------------------------- */
/** \name Public Mask Selection API
* \{ */
bool ED_mask_spline_select_check(const MaskSpline *spline)
{
for (int i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
if (BKE_mask_point_selected(point)) {
return true;
}
}
return false;
}
bool ED_mask_layer_select_check(const MaskLayer *mask_layer)
{
if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
return false;
}
for (const MaskSpline &spline : mask_layer->splines) {
if (ED_mask_spline_select_check(&spline)) {
return true;
}
}
return false;
}
bool ED_mask_select_check(const Mask *mask)
{
for (const MaskLayer &mask_layer : mask->masklayers) {
if (ED_mask_layer_select_check(&mask_layer)) {
return true;
}
}
return false;
}
void ED_mask_spline_select_set(MaskSpline *spline, const bool do_select)
{
if (do_select) {
spline->flag |= MASK_SPLINE_SELECT;
}
else {
spline->flag &= ~MASK_SPLINE_SELECT;
}
for (int i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
BKE_mask_point_select_set(point, do_select);
}
}
void ED_mask_layer_select_set(MaskLayer *mask_layer, const bool do_select)
{
if (mask_layer->visibility_flag & MASK_HIDE_SELECT) {
if (do_select == true) {
return;
}
}
for (MaskSpline &spline : mask_layer->splines) {
ED_mask_spline_select_set(&spline, do_select);
}
}
void ED_mask_select_toggle_all(Mask *mask, int action)
{
if (action == SEL_TOGGLE) {
if (ED_mask_select_check(mask)) {
action = SEL_DESELECT;
}
else {
action = SEL_SELECT;
}
}
for (MaskLayer &mask_layer : mask->masklayers) {
if (mask_layer.visibility_flag & MASK_HIDE_VIEW) {
continue;
}
if (action == SEL_INVERT) {
/* we don't have generic functions for this, its restricted to this operator
* if one day we need to re-use such functionality, they can be split out */
if (mask_layer.visibility_flag & MASK_HIDE_SELECT) {
continue;
}
for (MaskSpline &spline : mask_layer.splines) {
for (int i = 0; i < spline.tot_point; i++) {
MaskSplinePoint *point = &spline.points[i];
BKE_mask_point_select_set(point, !BKE_mask_point_selected(point));
}
}
}
else {
ED_mask_layer_select_set(&mask_layer, (action == SEL_SELECT) ? true : false);
}
}
}
void ED_mask_select_flush_all(Mask *mask)
{
for (MaskLayer &mask_layer : mask->masklayers) {
for (MaskSpline &spline : mask_layer.splines) {
spline.flag &= ~MASK_SPLINE_SELECT;
/* Intentionally *don't* do this in the mask layer loop
* so we clear flags on all splines. */
if (mask_layer.visibility_flag & MASK_HIDE_VIEW) {
continue;
}
for (int i = 0; i < spline.tot_point; i++) {
MaskSplinePoint *cur_point = &spline.points[i];
if (BKE_mask_point_selected(cur_point)) {
spline.flag |= MASK_SPLINE_SELECT;
}
else {
int j;
for (j = 0; j < cur_point->tot_uw; j++) {
if (cur_point->uw[j].flag & SELECT) {
spline.flag |= MASK_SPLINE_SELECT;
break;
}
}
}
}
}
}
}
void ED_mask_deselect_all(const bContext *C)
{
Mask *mask = CTX_data_edit_mask(C);
if (mask) {
ED_mask_select_toggle_all(mask, SEL_DESELECT);
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name (De)select All Operator
* \{ */
static wmOperatorStatus select_all_exec(bContext *C, wmOperator *op)
{
Mask *mask = CTX_data_edit_mask(C);
int action = RNA_enum_get(op->ptr, "action");
MaskViewLockState lock_state;
ED_mask_view_lock_state_store(C, &lock_state);
ED_mask_select_toggle_all(mask, action);
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
return OPERATOR_FINISHED;
}
void MASK_OT_select_all(wmOperatorType *ot)
{
/* identifiers */
ot->name = "(De)select All";
ot->description = "Change selection of all curve points";
ot->idname = "MASK_OT_select_all";
/* API callbacks. */
ot->exec = select_all_exec;
ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
WM_operator_properties_select_all(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select (Cursor Pick) Operator
* \{ */
static wmOperatorStatus select_exec(bContext *C, wmOperator *op)
{
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *mask_layer;
MaskSpline *spline;
MaskSplinePoint *point = nullptr;
float co[2];
bool extend = RNA_boolean_get(op->ptr, "extend");
bool deselect = RNA_boolean_get(op->ptr, "deselect");
bool toggle = RNA_boolean_get(op->ptr, "toggle");
const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
eMaskWhichHandle which_handle;
const float threshold = 19;
MaskViewLockState lock_state;
ED_mask_view_lock_state_store(C, &lock_state);
RNA_float_get_array(op->ptr, "location", co);
point = ED_mask_point_find_nearest(
C, mask, co, threshold, &mask_layer, &spline, &which_handle, nullptr);
if (extend == false && deselect == false && toggle == false) {
ED_mask_select_toggle_all(mask, SEL_DESELECT);
}
if (point) {
if (which_handle != MASK_WHICH_HANDLE_NONE) {
if (extend) {
mask_layer->act_spline = spline;
mask_layer->act_point = point;
BKE_mask_point_select_set_handle(point, which_handle, true);
}
else if (deselect) {
BKE_mask_point_select_set_handle(point, which_handle, false);
}
else {
mask_layer->act_spline = spline;
mask_layer->act_point = point;
if (!BKE_mask_point_is_handle_selected(point, which_handle)) {
BKE_mask_point_select_set_handle(point, which_handle, true);
}
else if (toggle) {
BKE_mask_point_select_set_handle(point, which_handle, false);
}
}
}
else {
if (extend) {
mask_layer->act_spline = spline;
mask_layer->act_point = point;
BKE_mask_point_select_set(point, true);
}
else if (deselect) {
BKE_mask_point_select_set(point, false);
}
else {
mask_layer->act_spline = spline;
mask_layer->act_point = point;
if (!BKE_mask_point_selected(point)) {
BKE_mask_point_select_set(point, true);
}
else if (toggle) {
BKE_mask_point_select_set(point, false);
}
}
}
mask_layer->act_spline = spline;
mask_layer->act_point = point;
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
}
MaskSplinePointUW *uw;
if (ED_mask_feather_find_nearest(
C, mask, co, threshold, &mask_layer, &spline, &point, &uw, nullptr))
{
if (extend) {
mask_layer->act_spline = spline;
mask_layer->act_point = point;
if (uw) {
uw->flag |= SELECT;
}
}
else if (deselect) {
if (uw) {
uw->flag &= ~SELECT;
}
}
else {
mask_layer->act_spline = spline;
mask_layer->act_point = point;
if (uw) {
if (!(uw->flag & SELECT)) {
uw->flag |= SELECT;
}
else if (toggle) {
uw->flag &= ~SELECT;
}
}
}
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
}
if (deselect_all) {
ED_mask_deselect_all(C);
ED_mask_view_lock_state_restore_no_jump(C, &lock_state);
return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
}
return OPERATOR_PASS_THROUGH;
}
static wmOperatorStatus select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
float co[2];
ED_mask_mouse_pos(area, region, event->mval, co);
RNA_float_set_array(op->ptr, "location", co);
const wmOperatorStatus retval = select_exec(C, op);
return WM_operator_flag_only_pass_through_on_press(retval, event);
}
void MASK_OT_select(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select";
ot->description = "Select spline points";
ot->idname = "MASK_OT_select";
/* API callbacks. */
ot->exec = select_exec;
ot->invoke = select_invoke;
ot->poll = ED_maskedit_mask_visible_splines_poll;
ot->get_name = ED_select_pick_get_name;
/* flags */
ot->flag = OPTYPE_UNDO;
/* properties */
WM_operator_properties_mouse_select(ot);
RNA_def_float_vector(ot->srna,
"location",
2,
nullptr,
-FLT_MAX,
FLT_MAX,
"Location",
"Location of vertex in normalized space",
-1.0f,
1.0f);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Box Select Operator
* \{ */
static wmOperatorStatus box_select_exec(bContext *C, wmOperator *op)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Mask *mask_orig = CTX_data_edit_mask(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Mask *mask_eval = DEG_get_evaluated(depsgraph, mask_orig);
rcti rect;
rctf rectf;
bool changed = false;
const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
ED_mask_select_toggle_all(mask_orig, SEL_DESELECT);
changed = true;
}
/* get rectangle from operator */
WM_operator_properties_border_to_rcti(op, &rect);
ED_mask_point_pos(area, region, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
ED_mask_point_pos(area, region, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
/* do actual selection */
for (MaskLayer *mask_layer_orig = static_cast<MaskLayer *>(mask_orig->masklayers.first),
*mask_layer_eval = static_cast<MaskLayer *>(mask_eval->masklayers.first);
mask_layer_orig != nullptr;
mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next)
{
if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
for (MaskSpline *spline_orig = static_cast<MaskSpline *>(mask_layer_orig->splines.first),
*spline_eval = static_cast<MaskSpline *>(mask_layer_eval->splines.first);
spline_orig != nullptr;
spline_orig = spline_orig->next, spline_eval = spline_eval->next)
{
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval);
for (int i = 0; i < spline_orig->tot_point; i++) {
MaskSplinePoint *point = &spline_orig->points[i];
MaskSplinePoint *point_deform = &points_array[i];
/* TODO: handles? */
/* TODO: uw? */
if (BLI_rctf_isect_pt_v(&rectf, point_deform->bezt.vec[1])) {
BKE_mask_point_select_set(point, select);
BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, select);
changed = true;
}
}
}
}
if (changed) {
ED_mask_select_flush_all(mask_orig);
DEG_id_tag_update(&mask_orig->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask_orig);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_select_box(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Box Select";
ot->description = "Select curve points using box selection";
ot->idname = "MASK_OT_select_box";
/* API callbacks. */
ot->invoke = WM_gesture_box_invoke;
ot->exec = box_select_exec;
ot->modal = WM_gesture_box_modal;
ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_UNDO;
/* properties */
WM_operator_properties_gesture_box(ot);
WM_operator_properties_select_operation_simple(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Lasso Select Operator
* \{ */
static bool do_lasso_select_mask(bContext *C, const Span<int2> mcoords, const eSelectOp sel_op)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Mask *mask_orig = CTX_data_edit_mask(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Mask *mask_eval = DEG_get_evaluated(depsgraph, mask_orig);
rcti rect;
bool changed = false;
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
ED_mask_select_toggle_all(mask_orig, SEL_DESELECT);
changed = true;
}
/* get rectangle from operator */
BLI_lasso_boundbox(&rect, mcoords);
/* do actual selection */
for (MaskLayer *mask_layer_orig = static_cast<MaskLayer *>(mask_orig->masklayers.first),
*mask_layer_eval = static_cast<MaskLayer *>(mask_eval->masklayers.first);
mask_layer_orig != nullptr;
mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next)
{
if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
for (MaskSpline *spline_orig = static_cast<MaskSpline *>(mask_layer_orig->splines.first),
*spline_eval = static_cast<MaskSpline *>(mask_layer_eval->splines.first);
spline_orig != nullptr;
spline_orig = spline_orig->next, spline_eval = spline_eval->next)
{
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval);
for (int i = 0; i < spline_orig->tot_point; i++) {
MaskSplinePoint *point = &spline_orig->points[i];
MaskSplinePoint *point_deform = &points_array[i];
/* TODO: handles? */
/* TODO: uw? */
if (BKE_mask_point_selected(point) && select) {
continue;
}
float screen_co[2];
/* point in screen coords */
ED_mask_point_pos__reverse(area,
region,
point_deform->bezt.vec[1][0],
point_deform->bezt.vec[1][1],
&screen_co[0],
&screen_co[1]);
if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
BLI_lasso_is_point_inside(mcoords, screen_co[0], screen_co[1], INT_MAX))
{
BKE_mask_point_select_set(point, select);
BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, select);
changed = true;
}
}
}
}
if (changed) {
ED_mask_select_flush_all(mask_orig);
DEG_id_tag_update(&mask_orig->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask_orig);
}
return changed;
}
static wmOperatorStatus clip_lasso_select_exec(bContext *C, wmOperator *op)
{
const Array<int2> mcoords = WM_gesture_lasso_path_to_array(C, op);
if (mcoords.is_empty()) {
return OPERATOR_PASS_THROUGH;
}
const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
do_lasso_select_mask(C, mcoords, sel_op);
return OPERATOR_FINISHED;
}
void MASK_OT_select_lasso(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Lasso Select";
ot->description = "Select curve points using lasso selection";
ot->idname = "MASK_OT_select_lasso";
/* API callbacks. */
ot->invoke = WM_gesture_lasso_invoke;
ot->modal = WM_gesture_lasso_modal;
ot->exec = clip_lasso_select_exec;
ot->poll = ED_maskedit_mask_visible_splines_poll;
ot->cancel = WM_gesture_lasso_cancel;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_DEPENDS_ON_CURSOR;
/* properties */
WM_operator_properties_gesture_lasso(ot);
WM_operator_properties_select_operation_simple(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Circle Select Operator
* \{ */
static int mask_spline_point_inside_ellipse(BezTriple *bezt,
const float offset[2],
const float ellipse[2])
{
/* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
float x, y;
x = (bezt->vec[1][0] - offset[0]) * ellipse[0];
y = (bezt->vec[1][1] - offset[1]) * ellipse[1];
return x * x + y * y < 1.0f;
}
static wmOperatorStatus circle_select_exec(bContext *C, wmOperator *op)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Mask *mask_orig = CTX_data_edit_mask(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Mask *mask_eval = DEG_get_evaluated(depsgraph, mask_orig);
float zoomx, zoomy, offset[2], ellipse[2];
int width, height;
bool changed = false;
/* get operator properties */
const int x = RNA_int_get(op->ptr, "x");
const int y = RNA_int_get(op->ptr, "y");
const int radius = RNA_int_get(op->ptr, "radius");
/* compute ellipse and position in unified coordinates */
ED_mask_get_size(area, &width, &height);
ED_mask_zoom(area, region, &zoomx, &zoomy);
width = height = max_ii(width, height);
ellipse[0] = width * zoomx / radius;
ellipse[1] = height * zoomy / radius;
ED_mask_point_pos(area, region, x, y, &offset[0], &offset[1]);
const eSelectOp sel_op = ED_select_op_modal(
eSelectOp(RNA_enum_get(op->ptr, "mode")),
WM_gesture_is_modal_first(static_cast<wmGesture *>(op->customdata)));
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
ED_mask_select_toggle_all(mask_orig, SEL_DESELECT);
changed = true;
}
/* do actual selection */
for (MaskLayer *mask_layer_orig = static_cast<MaskLayer *>(mask_orig->masklayers.first),
*mask_layer_eval = static_cast<MaskLayer *>(mask_eval->masklayers.first);
mask_layer_orig != nullptr;
mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next)
{
if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
for (MaskSpline *spline_orig = static_cast<MaskSpline *>(mask_layer_orig->splines.first),
*spline_eval = static_cast<MaskSpline *>(mask_layer_eval->splines.first);
spline_orig != nullptr;
spline_orig = spline_orig->next, spline_eval = spline_eval->next)
{
MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval);
for (int i = 0; i < spline_orig->tot_point; i++) {
MaskSplinePoint *point = &spline_orig->points[i];
MaskSplinePoint *point_deform = &points_array[i];
if (mask_spline_point_inside_ellipse(&point_deform->bezt, offset, ellipse)) {
BKE_mask_point_select_set(point, select);
BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, select);
changed = true;
}
}
}
}
if (changed) {
ED_mask_select_flush_all(mask_orig);
DEG_id_tag_update(&mask_orig->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask_orig);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_select_circle(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Circle Select";
ot->description = "Select curve points using circle selection";
ot->idname = "MASK_OT_select_circle";
/* API callbacks. */
ot->invoke = WM_gesture_circle_invoke;
ot->modal = WM_gesture_circle_modal;
ot->exec = circle_select_exec;
ot->poll = ED_maskedit_mask_visible_splines_poll;
ot->get_name = ED_select_circle_get_name;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
WM_operator_properties_gesture_circle(ot);
WM_operator_properties_select_operation_simple(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select Linked (Cursor Pick) Operator
* \{ */
static wmOperatorStatus mask_select_linked_pick_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *mask_layer;
MaskSpline *spline;
MaskSplinePoint *point = nullptr;
float co[2];
bool do_select = !RNA_boolean_get(op->ptr, "deselect");
const float threshold = 19;
bool changed = false;
ED_mask_mouse_pos(area, region, event->mval, co);
point = ED_mask_point_find_nearest(
C, mask, co, threshold, &mask_layer, &spline, nullptr, nullptr);
if (point) {
ED_mask_spline_select_set(spline, do_select);
mask_layer->act_spline = spline;
mask_layer->act_point = point;
changed = true;
}
if (changed) {
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_select_linked_pick(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Linked";
ot->idname = "MASK_OT_select_linked_pick";
ot->description = "(De)select all points linked to the curve under the mouse cursor";
/* API callbacks. */
ot->invoke = mask_select_linked_pick_invoke;
ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select Linked Operator
* \{ */
static wmOperatorStatus mask_select_linked_exec(bContext *C, wmOperator * /*op*/)
{
Mask *mask = CTX_data_edit_mask(C);
bool changed = false;
/* do actual selection */
for (MaskLayer &mask_layer : mask->masklayers) {
if (mask_layer.visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
for (MaskSpline &spline : mask_layer.splines) {
if (ED_mask_spline_select_check(&spline)) {
ED_mask_spline_select_set(&spline, true);
changed = true;
}
}
}
if (changed) {
ED_mask_select_flush_all(mask);
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_select_linked(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Linked All";
ot->idname = "MASK_OT_select_linked";
ot->description = "Select all curve points linked to already selected ones";
/* API callbacks. */
ot->exec = mask_select_linked_exec;
ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select More/Less Operators
* \{ */
static wmOperatorStatus mask_select_more_less(bContext *C, bool more)
{
Mask *mask = CTX_data_edit_mask(C);
for (MaskLayer &mask_layer : mask->masklayers) {
if (mask_layer.visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
for (MaskSpline &spline : mask_layer.splines) {
const bool cyclic = (spline.flag & MASK_SPLINE_CYCLIC) != 0;
bool start_sel, end_sel, prev_sel, cur_sel;
/* Re-select point if any handle is selected to make the result more predictable. */
for (int i = 0; i < spline.tot_point; i++) {
BKE_mask_point_select_set(spline.points + i, BKE_mask_point_selected(spline.points + i));
}
/* select more/less does not affect empty/single point splines */
if (spline.tot_point < 2) {
continue;
}
if (cyclic) {
start_sel = BKE_mask_point_selected_knot(spline.points);
end_sel = BKE_mask_point_selected_knot(&spline.points[spline.tot_point - 1]);
}
else {
start_sel = false;
end_sel = false;
}
for (int i = 0; i < spline.tot_point; i++) {
if (i == 0 && !cyclic) {
continue;
}
prev_sel = (i > 0) ? BKE_mask_point_selected_knot(&spline.points[i - 1]) : end_sel;
cur_sel = BKE_mask_point_selected_knot(&spline.points[i]);
if (cur_sel != more) {
if (prev_sel == more) {
BKE_mask_point_select_set(&spline.points[i], more);
}
i++;
}
}
for (int i = spline.tot_point - 1; i >= 0; i--) {
if (i == spline.tot_point - 1 && !cyclic) {
continue;
}
prev_sel = (i < spline.tot_point - 1) ?
BKE_mask_point_selected_knot(&spline.points[i + 1]) :
start_sel;
cur_sel = BKE_mask_point_selected_knot(&spline.points[i]);
if (cur_sel != more) {
if (prev_sel == more) {
BKE_mask_point_select_set(&spline.points[i], more);
}
i--;
}
}
}
}
DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
return OPERATOR_FINISHED;
}
static wmOperatorStatus mask_select_more_exec(bContext *C, wmOperator * /*op*/)
{
return mask_select_more_less(C, true);
}
void MASK_OT_select_more(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select More";
ot->idname = "MASK_OT_select_more";
ot->description = "Select more spline points connected to initial selection";
/* API callbacks. */
ot->exec = mask_select_more_exec;
ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus mask_select_less_exec(bContext *C, wmOperator * /*op*/)
{
return mask_select_more_less(C, false);
}
void MASK_OT_select_less(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Less";
ot->idname = "MASK_OT_select_less";
ot->description = "Deselect spline points at the boundary of each selection region";
/* API callbacks. */
ot->exec = mask_select_less_exec;
ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
} // namespace blender

View File

@@ -0,0 +1,429 @@
/* SPDX-FileCopyrightText: 2012 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edmask
*/
#include <cstdlib>
#include "BLI_listbase.h"
#include "BKE_context.hh"
#include "BKE_mask.hh"
#include "DNA_mask_types.h"
#include "DNA_scene_types.h"
#include "DEG_depsgraph.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_mask.hh" /* own include */
#include "mask_intern.hh" /* own include */
namespace blender {
static void select_only_layer_shape(MaskLayer *mask_layer, MaskLayerShape *mask_layer_shape)
{
for (MaskLayerShape &mask_layer_shape_iter : mask_layer->splines_shapes) {
mask_layer_shape_iter.flag &= ~MASK_SHAPE_SELECT;
}
mask_layer_shape->flag |= MASK_SHAPE_SELECT;
}
static wmOperatorStatus mask_shape_key_insert_exec(bContext *C, wmOperator * /*op*/)
{
Scene *scene = CTX_data_scene(C);
const int frame = scene->r.cfra;
Mask *mask = CTX_data_edit_mask(C);
bool changed = false;
for (MaskLayer &mask_layer : mask->masklayers) {
MaskLayerShape *mask_layer_shape;
if (!ED_mask_layer_select_check(&mask_layer)) {
continue;
}
mask_layer_shape = BKE_mask_layer_shape_verify_frame(&mask_layer, frame);
BKE_mask_layer_shape_from_mask(&mask_layer, mask_layer_shape);
changed = true;
select_only_layer_shape(&mask_layer, mask_layer_shape);
}
if (changed) {
WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
DEG_id_tag_update(&mask->id, 0);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_shape_key_insert(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Insert Shape Key";
ot->description = "Insert mask shape keyframe for active mask layer at the current frame";
ot->idname = "MASK_OT_shape_key_insert";
/* API callbacks. */
ot->exec = mask_shape_key_insert_exec;
ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus mask_shape_key_clear_exec(bContext *C, wmOperator * /*op*/)
{
Scene *scene = CTX_data_scene(C);
const int frame = scene->r.cfra;
Mask *mask = CTX_data_edit_mask(C);
bool changed = false;
for (MaskLayer &mask_layer : mask->masklayers) {
MaskLayerShape *mask_layer_shape;
if (!ED_mask_layer_select_check(&mask_layer)) {
continue;
}
mask_layer_shape = BKE_mask_layer_shape_find_frame(&mask_layer, frame);
if (mask_layer_shape) {
BKE_mask_layer_shape_unlink(&mask_layer, mask_layer_shape);
changed = true;
}
}
if (changed) {
WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
DEG_id_tag_update(&mask->id, ID_RECALC_GEOMETRY);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_shape_key_clear(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Clear Shape Key";
ot->description = "Remove mask shape keyframe for active mask layer at the current frame";
ot->idname = "MASK_OT_shape_key_clear";
/* API callbacks. */
ot->exec = mask_shape_key_clear_exec;
ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus mask_shape_key_feather_reset_exec(bContext *C, wmOperator * /*op*/)
{
Scene *scene = CTX_data_scene(C);
const int frame = scene->r.cfra;
Mask *mask = CTX_data_edit_mask(C);
bool changed = false;
for (MaskLayer &mask_layer : mask->masklayers) {
if (mask_layer.visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
if (mask_layer.splines_shapes.first) {
MaskLayerShape *mask_layer_shape_reset;
/* Get the shape-key of the current state. */
mask_layer_shape_reset = BKE_mask_layer_shape_alloc(&mask_layer, frame);
/* Initialize from mask - as if inserting a keyframe. */
BKE_mask_layer_shape_from_mask(&mask_layer, mask_layer_shape_reset);
for (MaskLayerShape &mask_layer_shape : mask_layer.splines_shapes) {
if (mask_layer_shape_reset->tot_vert == mask_layer_shape.tot_vert) {
int i_abs = 0;
MaskLayerShapeElem *shape_ele_src;
MaskLayerShapeElem *shape_ele_dst;
shape_ele_src = reinterpret_cast<MaskLayerShapeElem *>(mask_layer_shape_reset->data);
shape_ele_dst = reinterpret_cast<MaskLayerShapeElem *>(mask_layer_shape.data);
for (MaskSpline &spline : mask_layer.splines) {
for (int i = 0; i < spline.tot_point; i++) {
MaskSplinePoint *point = &spline.points[i];
if (BKE_mask_point_selected(point)) {
shape_ele_dst->weight = shape_ele_src->weight;
}
shape_ele_src++;
shape_ele_dst++;
i_abs++;
}
}
(void)i_abs; /* Quiet set-but-unused warning (may be removed). */
}
else {
// printf("%s: skipping\n", __func__);
}
changed = true;
}
BKE_mask_layer_shape_free(mask_layer_shape_reset);
}
}
if (changed) {
WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
DEG_id_tag_update(&mask->id, 0);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_shape_key_feather_reset(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Feather Reset Animation";
ot->description = "Reset feather weights on all selected points animation values";
ot->idname = "MASK_OT_shape_key_feather_reset";
/* API callbacks. */
ot->exec = mask_shape_key_feather_reset_exec;
ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/*
* - loop over selected shape-keys.
* - find first-selected/last-selected pairs.
* - move these into a temp list.
* - re-key all the original shapes.
* - copy unselected values back from the original.
* - free the original.
*/
static wmOperatorStatus mask_shape_key_rekey_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
const int frame = scene->r.cfra;
Mask *mask = CTX_data_edit_mask(C);
bool changed = false;
const bool do_feather = RNA_boolean_get(op->ptr, "feather");
const bool do_location = RNA_boolean_get(op->ptr, "location");
for (MaskLayer &mask_layer : mask->masklayers) {
if (mask_layer.visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
/* we need at least one point selected here to bother re-interpolating */
if (!ED_mask_layer_select_check(&mask_layer)) {
continue;
}
if (mask_layer.splines_shapes.first) {
MaskLayerShape *mask_layer_shape, *mask_layer_shape_next;
MaskLayerShape *mask_layer_shape_lastsel = nullptr;
for (mask_layer_shape = static_cast<MaskLayerShape *>(mask_layer.splines_shapes.first);
mask_layer_shape;
mask_layer_shape = mask_layer_shape_next)
{
MaskLayerShape *mask_layer_shape_a = nullptr;
MaskLayerShape *mask_layer_shape_b = nullptr;
mask_layer_shape_next = mask_layer_shape->next;
/* find contiguous selections */
if (mask_layer_shape->flag & MASK_SHAPE_SELECT) {
if (mask_layer_shape_lastsel == nullptr) {
mask_layer_shape_lastsel = mask_layer_shape;
}
if ((mask_layer_shape->next == nullptr) ||
((static_cast<MaskLayerShape *>(mask_layer_shape->next))->flag &
MASK_SHAPE_SELECT) == 0)
{
mask_layer_shape_a = mask_layer_shape_lastsel;
mask_layer_shape_b = mask_layer_shape;
mask_layer_shape_lastsel = nullptr;
/* this will be freed below, step over selection */
mask_layer_shape_next = mask_layer_shape->next;
}
}
/* we have a from<>to? - re-interpolate! */
if (mask_layer_shape_a && mask_layer_shape_b) {
ListBaseT<MaskLayerShape> shapes_tmp = {nullptr, nullptr};
MaskLayerShape *mask_layer_shape_tmp;
MaskLayerShape *mask_layer_shape_tmp_next;
MaskLayerShape *mask_layer_shape_tmp_last = mask_layer_shape_b->next;
MaskLayerShape *mask_layer_shape_tmp_rekey;
/* move keys */
for (mask_layer_shape_tmp = mask_layer_shape_a;
mask_layer_shape_tmp && (mask_layer_shape_tmp != mask_layer_shape_tmp_last);
mask_layer_shape_tmp = mask_layer_shape_tmp_next)
{
mask_layer_shape_tmp_next = mask_layer_shape_tmp->next;
BLI_remlink(&mask_layer.splines_shapes, mask_layer_shape_tmp);
BLI_addtail(&shapes_tmp, mask_layer_shape_tmp);
}
/* re-key, NOTE: can't modify the keys here since it messes up. */
for (mask_layer_shape_tmp = static_cast<MaskLayerShape *>(shapes_tmp.first);
mask_layer_shape_tmp;
mask_layer_shape_tmp = mask_layer_shape_tmp->next)
{
BKE_mask_layer_evaluate(&mask_layer, mask_layer_shape_tmp->frame, true);
mask_layer_shape_tmp_rekey = BKE_mask_layer_shape_verify_frame(
&mask_layer, mask_layer_shape_tmp->frame);
BKE_mask_layer_shape_from_mask(&mask_layer, mask_layer_shape_tmp_rekey);
mask_layer_shape_tmp_rekey->flag = mask_layer_shape_tmp->flag & MASK_SHAPE_SELECT;
}
/* restore unselected points and free copies */
for (mask_layer_shape_tmp = static_cast<MaskLayerShape *>(shapes_tmp.first);
mask_layer_shape_tmp;
mask_layer_shape_tmp = mask_layer_shape_tmp_next)
{
/* restore */
int i_abs = 0;
MaskLayerShapeElem *shape_ele_src;
MaskLayerShapeElem *shape_ele_dst;
mask_layer_shape_tmp_next = mask_layer_shape_tmp->next;
/* we know this exists, added above */
mask_layer_shape_tmp_rekey = BKE_mask_layer_shape_find_frame(
&mask_layer, mask_layer_shape_tmp->frame);
shape_ele_src = reinterpret_cast<MaskLayerShapeElem *>(mask_layer_shape_tmp->data);
shape_ele_dst = reinterpret_cast<MaskLayerShapeElem *>(
mask_layer_shape_tmp_rekey->data);
for (MaskSpline &spline : mask_layer.splines) {
for (int i = 0; i < spline.tot_point; i++) {
MaskSplinePoint *point = &spline.points[i];
/* not especially efficient but makes this easier to follow */
std::swap(*shape_ele_src, *shape_ele_dst);
if (BKE_mask_point_selected(point)) {
if (do_location) {
memcpy(
shape_ele_dst->point, shape_ele_src->point, sizeof(shape_ele_src->point));
}
if (do_feather) {
shape_ele_dst->weight = shape_ele_src->weight;
}
}
shape_ele_src++;
shape_ele_dst++;
i_abs++;
}
}
(void)i_abs; /* Quiet set-but-unused warning (may be removed). */
BKE_mask_layer_shape_free(mask_layer_shape_tmp);
}
changed = true;
}
}
/* re-evaluate */
BKE_mask_layer_evaluate(&mask_layer, frame, true);
}
}
if (changed) {
WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
DEG_id_tag_update(&mask->id, 0);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void MASK_OT_shape_key_rekey(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Re-Key Points of Selected Shapes";
ot->description =
"Recalculate animation data on selected points for frames selected in the dopesheet";
ot->idname = "MASK_OT_shape_key_rekey";
/* API callbacks. */
ot->exec = mask_shape_key_rekey_exec;
ot->poll = ED_maskedit_mask_visible_splines_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_boolean(ot->srna, "location", true, "Location", "");
RNA_def_boolean(ot->srna, "feather", true, "Feather", "");
}
/* *** Shape Key Utils *** */
void ED_mask_layer_shape_auto_key(MaskLayer *mask_layer, const int frame)
{
MaskLayerShape *mask_layer_shape;
mask_layer_shape = BKE_mask_layer_shape_verify_frame(mask_layer, frame);
BKE_mask_layer_shape_from_mask(mask_layer, mask_layer_shape);
select_only_layer_shape(mask_layer, mask_layer_shape);
}
bool ED_mask_layer_shape_auto_key_all(Mask *mask, const int frame)
{
bool changed = false;
for (MaskLayer &mask_layer : mask->masklayers) {
ED_mask_layer_shape_auto_key(&mask_layer, frame);
changed = true;
}
return changed;
}
bool ED_mask_layer_shape_auto_key_select(Mask *mask, const int frame)
{
bool changed = false;
for (MaskLayer &mask_layer : mask->masklayers) {
if (!ED_mask_layer_select_check(&mask_layer)) {
continue;
}
ED_mask_layer_shape_auto_key(&mask_layer, frame);
changed = true;
}
return changed;
}
} // namespace blender