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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorSergey Sharybin <sergey.vfx@gmail.com>2012-04-30 11:43:21 +0400
committerSergey Sharybin <sergey.vfx@gmail.com>2012-04-30 11:43:21 +0400
commit9151d46d7dbab7c978604656c146245aaf8f4ce3 (patch)
tree567c74224cbaec49ed7de0c64a209053fd04c1aa /source
parent16f06d4fe4bc9317a505a15112d7250152aa36d0 (diff)
Tomato: initial commit of mask editing tools into SVN
- Added new dtaablock called Mask which might be re-used in any area. Currently editing of masks happens in clip editor and they might be used in compositor nodes only. - Added new mode to clip clip editor to interact with masks. Implemented basic tools to create shapes, splines and points. Also implemented idea of UW points for feather which means feather points are have got U coordinate along spline (which is measured from 0 to 1) and W is it's weight meaning distance from main spline. - Spline points might be parented to movie tracks. Interface for this isn't best yet. - Rasterisaztion of masks happens in compositor node (Input -> Mask) Input image of this ode is used as reference for mask resolution. Currently all splines of all shapes are rasterizing independently which means shapes with holes are not supported. Also feather rasterization is not implemented. Rasterized was implemented by Pete Larbell, thanks! Do not consider this is something finished, there's still lots of things to be done (especially from interface and usability points of view).
Diffstat (limited to 'source')
-rw-r--r--source/blender/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/BKE_context.h1
-rw-r--r--source/blender/blenkernel/BKE_library.h2
-rw-r--r--source/blender/blenkernel/BKE_main.h1
-rw-r--r--source/blender/blenkernel/BKE_mask.h95
-rw-r--r--source/blender/blenkernel/BKE_node.h1
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/context.c5
-rw-r--r--source/blender/blenkernel/intern/depsgraph.c12
-rw-r--r--source/blender/blenkernel/intern/idcode.c1
-rw-r--r--source/blender/blenkernel/intern/library.c11
-rw-r--r--source/blender/blenkernel/intern/mask.c890
-rw-r--r--source/blender/blenkernel/intern/node.c1
-rw-r--r--source/blender/blenkernel/intern/scene.c6
-rw-r--r--source/blender/blenloader/intern/readfile.c91
-rw-r--r--source/blender/blenloader/intern/writefile.c50
-rw-r--r--source/blender/editors/CMakeLists.txt1
-rw-r--r--source/blender/editors/SConscript1
-rw-r--r--source/blender/editors/include/ED_clip.h9
-rw-r--r--source/blender/editors/include/ED_mask.h44
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/include/ED_transform.h1
-rw-r--r--source/blender/editors/interface/interface_templates.c6
-rw-r--r--source/blender/editors/mask/CMakeLists.txt47
-rw-r--r--source/blender/editors/mask/SConscript9
-rw-r--r--source/blender/editors/mask/mask_draw.c270
-rw-r--r--source/blender/editors/mask/mask_editor.c218
-rw-r--r--source/blender/editors/mask/mask_intern.h69
-rw-r--r--source/blender/editors/mask/mask_ops.c1653
-rw-r--r--source/blender/editors/screen/screen_ops.c8
-rw-r--r--source/blender/editors/space_api/spacetypes.c4
-rw-r--r--source/blender/editors/space_clip/clip_editor.c91
-rw-r--r--source/blender/editors/space_clip/space_clip.c53
-rw-r--r--source/blender/editors/space_node/drawnode.c8
-rw-r--r--source/blender/editors/space_node/space_node.c7
-rw-r--r--source/blender/editors/transform/transform.c61
-rw-r--r--source/blender/editors/transform/transform.h1
-rw-r--r--source/blender/editors/transform/transform_conversions.c207
-rw-r--r--source/blender/editors/transform/transform_generics.c54
-rw-r--r--source/blender/editors/transform/transform_ops.c1
-rw-r--r--source/blender/makesdna/DNA_ID.h1
-rw-r--r--source/blender/makesdna/DNA_mask_types.h108
-rw-r--r--source/blender/makesdna/DNA_space_types.h5
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt1
-rw-r--r--source/blender/makesrna/intern/makesrna.c1
-rw-r--r--source/blender/makesrna/intern/rna_ID.c2
-rw-r--r--source/blender/makesrna/intern/rna_internal.h2
-rw-r--r--source/blender/makesrna/intern/rna_main.c7
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c51
-rw-r--r--source/blender/makesrna/intern/rna_mask.c600
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c12
-rw-r--r--source/blender/makesrna/intern/rna_nodetree_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_space.c16
-rw-r--r--source/blender/nodes/CMakeLists.txt2
-rw-r--r--source/blender/nodes/NOD_composite.h2
-rw-r--r--source/blender/nodes/composite/node_composite_tree.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mask.c124
-rw-r--r--source/blender/windowmanager/WM_types.h1
-rw-r--r--source/blenderplayer/CMakeLists.txt1
-rw-r--r--source/blenderplayer/bad_level_call_stubs/stubs.c1
-rw-r--r--source/creator/CMakeLists.txt2
63 files changed, 4903 insertions, 39 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index cb17a87fd76..dc7e7948088 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -86,6 +86,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_world_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_movieclip_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_tracking_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_mask_types.h
)
add_subdirectory(editors)
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index b2bd840a09a..c170cbb5694 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -260,6 +260,7 @@ struct Image *CTX_data_edit_image(const bContext *C);
struct Text *CTX_data_edit_text(const bContext *C);
struct MovieClip *CTX_data_edit_movieclip(const bContext *C);
+struct Mask *CTX_data_edit_mask(const bContext *C);
int CTX_data_selected_nodes(const bContext *C, ListBase *list);
diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h
index be21996428a..d97e035a547 100644
--- a/source/blender/blenkernel/BKE_library.h
+++ b/source/blender/blenkernel/BKE_library.h
@@ -65,7 +65,7 @@ void id_clear_lib_data(struct Main *bmain, struct ID *id);
struct ListBase *which_libbase(struct Main *mainlib, short type);
-#define MAX_LIBARRAY 40
+#define MAX_LIBARRAY 41
int set_listbasepointers(struct Main *main, struct ListBase **lb);
void free_libblock(struct ListBase *lb, void *idv);
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index ffcbb6e40bc..382ce7c1274 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -86,6 +86,7 @@ typedef struct Main {
ListBase wm;
ListBase gpencil;
ListBase movieclip;
+ ListBase mask;
char id_tag_update[256];
} Main;
diff --git a/source/blender/blenkernel/BKE_mask.h b/source/blender/blenkernel/BKE_mask.h
new file mode 100644
index 00000000000..268b7df0247
--- /dev/null
+++ b/source/blender/blenkernel/BKE_mask.h
@@ -0,0 +1,95 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_MASK_H__
+#define __BKE_MASK_H__
+
+struct Main;
+struct Mask;
+struct MaskParent;
+struct MaskShape;
+struct MaskSpline;
+struct MaskSplinePoint;
+struct MaskSplinePointUW;
+struct Scene;
+
+/* shapes */
+struct MaskShape *BKE_mask_shape_new(struct Mask *mask, const char *name);
+struct MaskShape *BKE_mask_shape_active(struct Mask *mask);
+void BKE_mask_shape_active_set(struct Mask *mask, struct MaskShape *shape);
+void BKE_mask_shape_remove(struct Mask *mask, struct MaskShape *shape);
+
+void BKE_mask_shape_free(struct MaskShape *shape);
+void BKE_mask_spline_free(struct MaskSpline *spline);
+void BKE_mask_point_free(struct MaskSplinePoint *point);
+
+void BKE_mask_shape_unique_name(struct Mask *mask, struct MaskShape *shape);
+
+/* splines */
+struct MaskSpline *BKE_mask_spline_add(struct MaskShape *shape);
+int BKE_mask_spline_resolution(struct MaskSpline *spline);
+float *BKE_mask_spline_differentiate(struct MaskSpline *spline, int *tot_diff_point);
+float *BKE_mask_spline_feather_differentiated_points(struct MaskSpline *spline, float aspx,
+ float aspy, int *tot_feather_point);
+float *BKE_mask_spline_feather_points(struct MaskSpline *spline, float aspx, float aspy, int *tot_feather_point);
+
+/* point */
+int BKE_mask_point_has_handle(struct MaskSplinePoint *point);
+void BKE_mask_point_handle(struct MaskSplinePoint *point, float aspx, float aspy, float handle[2]);
+void BKE_mask_point_set_handle(struct MaskSplinePoint *point, float loc[2], int keep_direction, float aspx, float aspy, float orig_handle[2], float orig_vec[3][3]);
+float *BKE_mask_point_segment_diff(struct MaskSpline *spline, struct MaskSplinePoint *point, int *tot_diff_point);
+float *BKE_mask_point_segment_feather_diff(struct MaskSpline *spline, struct MaskSplinePoint *point,
+ float aspx, float aspy, int *tot_feather_point);
+void BKE_mask_point_segment_co(struct MaskSpline *spline, struct MaskSplinePoint *point, float u, float co[2]);
+void BKE_mask_point_normal(struct MaskSpline *spline, struct MaskSplinePoint *point, float aspx, float aspy, float u, float n[2]);
+float BKE_mask_point_weight(struct MaskSpline *spline, struct MaskSplinePoint *point, float u);
+struct MaskSplinePointUW * BKE_mask_point_sort_uw(struct MaskSplinePoint *point, struct MaskSplinePointUW *uw);
+void BKE_mask_point_add_uw(struct MaskSplinePoint *point, float u, float w);
+
+/* general */
+struct Mask *BKE_mask_new(const char *name);
+
+void BKE_mask_free(struct Mask *mask);
+void BKE_mask_unlink(struct Main *bmain, struct Mask *mask);
+
+/* parenting */
+
+void BKE_mask_evaluate_all_masks(struct Main *bmain, float ctime);
+void BKE_mask_update_scene(struct Main *bmain, struct Scene *scene);
+void BKE_mask_parent_init(struct MaskParent *parent);
+
+#define MASKPOINT_ISSEL(p) ( ((p)->bezt.f1 | (p)->bezt.f2 | (p)->bezt.f2) & SELECT )
+#define MASKPOINT_SEL(p) { (p)->bezt.f1 |= SELECT; (p)->bezt.f2 |= SELECT; (p)->bezt.f3 |= SELECT; }
+#define MASKPOINT_DESEL(p) { (p)->bezt.f1 &= ~SELECT; (p)->bezt.f2 &= ~SELECT; (p)->bezt.f3 &= ~SELECT; }
+#define MASKPOINT_INVSEL(p) { (p)->bezt.f1 ^= SELECT; (p)->bezt.f2 ^= SELECT; (p)->bezt.f3 ^= SELECT; }
+
+#define MASKPOINT_CV_ISSEL(p) ( (p)->bezt.f2 & SELECT )
+
+#define MASKPOINT_HANDLE_ONLY_ISSEL(p) ( (((p)->bezt.f1 | (p)->bezt.f2) & SELECT ) && (((p)->bezt.f2 & SELECT) == 0) )
+#define MASKPOINT_HANDLE_ISSEL(p) ( (((p)->bezt.f1 | (p)->bezt.f2) & SELECT ) )
+#define MASKPOINT_HANDLE_SEL(p) { (p)->bezt.f1 |= SELECT; (p)->bezt.f3 |= SELECT; }
+
+#endif
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index a4eddd0b590..4f3a6d4e174 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -638,6 +638,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMaterial *mat);
#define CMP_NODE_MOVIEDISTORTION 265
#define CMP_NODE_DOUBLEEDGEMASK 266
#define CMP_NODE_OUTPUT_MULTI_FILE__DEPRECATED 267 /* DEPRECATED multi file node has been merged into regular CMP_NODE_OUTPUT_FILE */
+#define CMP_NODE_MASK 268
#define CMP_NODE_GLARE 301
#define CMP_NODE_TONEMAP 302
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index c2a83b5c048..61461044fa7 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -100,6 +100,7 @@ set(SRC
intern/lamp.c
intern/lattice.c
intern/library.c
+ intern/mask.c
intern/material.c
intern/mball.c
intern/mesh.c
@@ -186,6 +187,7 @@ set(SRC
BKE_lamp.h
BKE_lattice.h
BKE_library.h
+ BKE_mask.h
BKE_main.h
BKE_material.h
BKE_mball.h
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index 7a5b4ef9b24..639af36e15f 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -925,6 +925,11 @@ struct MovieClip *CTX_data_edit_movieclip(const bContext *C)
return ctx_data_pointer_get(C, "edit_movieclip");
}
+struct Mask *CTX_data_edit_mask(const bContext *C)
+{
+ return ctx_data_pointer_get(C, "edit_mask");
+}
+
struct EditBone *CTX_data_active_bone(const bContext *C)
{
return ctx_data_pointer_get(C, "active_bone");
diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c
index d8cdd728c16..95ee3b34644 100644
--- a/source/blender/blenkernel/intern/depsgraph.c
+++ b/source/blender/blenkernel/intern/depsgraph.c
@@ -2604,6 +2604,18 @@ static void dag_id_flush_update(Scene *sce, ID *id)
}
}
+ if (idtype == ID_MSK) {
+ if (sce->nodetree) {
+ bNode *node;
+
+ for (node = sce->nodetree->nodes.first; node; node = node->next) {
+ if (node->id == id) {
+ nodeUpdate(sce->nodetree, node);
+ }
+ }
+ }
+ }
+
/* camera's matrix is used to orient reconstructed stuff,
* so it should happen tracking-related constraints recalculation
* when camera is changing (sergey) */
diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c
index cd246681f3c..1f4d9d01fe1 100644
--- a/source/blender/blenkernel/intern/idcode.c
+++ b/source/blender/blenkernel/intern/idcode.c
@@ -79,6 +79,7 @@ static IDType idtypes[]= {
{ ID_WO, "World", "worlds", IDTYPE_FLAGS_ISLINKABLE},
{ ID_WM, "WindowManager", "window_managers", 0},
{ ID_MC, "MovieClip", "movieclips", IDTYPE_FLAGS_ISLINKABLE},
+ { ID_MSK, "Mask", "masks", IDTYPE_FLAGS_ISLINKABLE},
};
static int nidtypes= sizeof(idtypes)/sizeof(idtypes[0]);
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index 2924ea457a8..bd147db34d7 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -66,6 +66,7 @@
#include "DNA_world_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_movieclip_types.h"
+#include "DNA_mask_types.h"
#include "BLI_blenlib.h"
#include "BLI_dynstr.h"
@@ -108,6 +109,7 @@
#include "BKE_speaker.h"
#include "BKE_utildefines.h"
#include "BKE_movieclip.h"
+#include "BKE_mask.h"
#include "RNA_access.h"
@@ -486,6 +488,8 @@ ListBase *which_libbase(Main *mainlib, short type)
return &(mainlib->gpencil);
case ID_MC:
return &(mainlib->movieclip);
+ case ID_MSK:
+ return &(mainlib->mask);
}
return NULL;
}
@@ -569,6 +573,7 @@ int set_listbasepointers(Main *main, ListBase **lb)
lb[a++]= &(main->library);
lb[a++]= &(main->wm);
lb[a++]= &(main->movieclip);
+ lb[a++]= &(main->mask);
lb[a]= NULL;
@@ -680,6 +685,9 @@ static ID *alloc_libblock_notest(short type)
case ID_MC:
id = MEM_callocN(sizeof(MovieClip), "Movie Clip");
break;
+ case ID_MSK:
+ id = MEM_callocN(sizeof(Mask), "Mask");
+ break;
}
return id;
}
@@ -888,6 +896,9 @@ void free_libblock(ListBase *lb, void *idv)
case ID_MC:
BKE_movieclip_free((MovieClip *)id);
break;
+ case ID_MSK:
+ BKE_mask_free((Mask *)id);
+ break;
}
if (id->properties) {
diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c
new file mode 100644
index 00000000000..ad0baa9d63c
--- /dev/null
+++ b/source/blender/blenkernel/intern/mask.c
@@ -0,0 +1,890 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/mask.c
+ * \ingroup bke
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "DNA_mask_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_movieclip_types.h"
+#include "DNA_tracking_types.h"
+
+#include "BKE_curve.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_mask.h"
+#include "BKE_tracking.h"
+#include "BKE_utildefines.h"
+
+/* shapes */
+
+MaskShape *BKE_mask_shape_new(Mask *mask, const char *name)
+{
+ MaskShape *shape = MEM_callocN(sizeof(MaskShape), "new mask shape");
+
+ if (name && name[0])
+ BLI_strncpy(shape->name, name, sizeof(shape->name));
+ else
+ strcpy(shape->name, "Shape");
+
+ BLI_addtail(&mask->shapes, shape);
+
+ BKE_mask_shape_unique_name(mask, shape);
+
+ mask->tot_shape++;
+
+ return shape;
+}
+
+MaskShape *BKE_mask_shape_active(Mask *mask)
+{
+ return BLI_findlink(&mask->shapes, mask->shapenr);
+}
+
+void BKE_mask_shape_active_set(Mask *mask, MaskShape *shape)
+{
+ int index = BLI_findindex(&mask->shapes, shape);
+
+ if (index >= 0)
+ mask->shapenr = index;
+ else
+ mask->shapenr = 0;
+}
+
+void BKE_mask_shape_remove(Mask *mask, MaskShape *shape)
+{
+ BLI_remlink(&mask->shapes, shape);
+ BKE_mask_shape_free(shape);
+
+ mask->tot_shape--;
+
+ if (mask->shapenr >= mask->tot_shape)
+ mask->shapenr = mask->tot_shape - 1;
+}
+
+void BKE_mask_shape_unique_name(Mask *mask, MaskShape *shape)
+{
+ BLI_uniquename(&mask->shapes, shape, "Shape", '.', offsetof(MaskShape, name), sizeof(shape->name));
+}
+
+/* splines */
+
+MaskSpline *BKE_mask_spline_add(MaskShape *shape)
+{
+ MaskSpline *spline;
+
+ spline = MEM_callocN(sizeof(MaskSpline), "new shape spline");
+ BLI_addtail(&shape->splines, spline);
+
+ /* spline shall have one point at least */
+ spline->points = MEM_callocN(sizeof(MaskSplinePoint), "new shape spline point");
+ spline->tot_point = 1;
+
+ /* cyclic shapes are more usually used */
+ spline->flag |= MASK_SPLINE_CYCLIC;
+
+ spline->weight_interp = MASK_SPLINE_INTERP_LINEAR;
+
+ BKE_mask_parent_init(&spline->parent);
+
+ return spline;
+}
+
+int BKE_mask_spline_resolution(MaskSpline *spline)
+{
+ const float max_segment = 0.01;
+ int i, resol = 1;
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+ MaskSplinePoint *next_point;
+ BezTriple *bezt, *next_bezt;
+ float a, b, c, len;
+ int cur_resol;
+
+ if (i == spline->tot_point - 1) {
+ if (spline->flag & MASK_SPLINE_CYCLIC)
+ next_point = &spline->points[0];
+ else
+ break;
+ }
+ else
+ next_point = &spline->points[i + 1];
+
+ bezt = &point->bezt;
+ next_bezt = &next_point->bezt;
+
+ a = len_v3v3(bezt->vec[1], bezt->vec[2]);
+ b = len_v3v3(bezt->vec[2], next_bezt->vec[0]);
+ c = len_v3v3(next_bezt->vec[0], next_bezt->vec[1]);
+
+ len = a + b + c;
+ cur_resol = len / max_segment;
+
+ resol = MAX2(resol, cur_resol);
+ }
+
+ return resol;
+}
+
+int BKE_mask_spline_feather_resolution(MaskSpline *spline)
+{
+ const float max_segment = 0.005;
+ int resol = BKE_mask_spline_resolution(spline);
+ float max_jump = 0.0f;
+ int i;
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+ float prev_u, prev_w;
+ int j;
+
+ prev_u = 0.0f;
+ prev_w = point->bezt.weight;
+
+ for (j = 0; j < point->tot_uw; j++) {
+ float jump = fabsf((point->uw[j].w - prev_w) / (point->uw[j].u - prev_u));
+
+ max_jump = MAX2(max_jump, jump);
+
+ prev_u = point->uw[j].u;
+ prev_w = point->uw[j].w;
+ }
+ }
+
+ resol += max_jump / max_segment;
+
+ return resol;
+}
+
+float *BKE_mask_spline_differentiate(MaskSpline *spline, int *tot_diff_point)
+{
+ MaskSplinePoint *point, *prev;
+ float *diff_points, *fp;
+ int a, len, resol = BKE_mask_spline_resolution(spline);
+
+ if (spline->tot_point <= 1) {
+ /* nothing to differentiate */
+ *tot_diff_point = 0;
+ return NULL;
+ }
+
+ /* count */
+ len = (spline->tot_point - 1) * resol;
+
+ if (spline->flag & MASK_SPLINE_CYCLIC)
+ len += resol;
+ else
+ len++;
+
+ /* len+1 because of 'forward_diff_bezier' function */
+ *tot_diff_point = len;
+ diff_points = fp = MEM_callocN((len + 1)*2*sizeof(float), "mask spline vets");
+
+ a = spline->tot_point - 1;
+ if (spline->flag & MASK_SPLINE_CYCLIC)
+ a++;
+
+ prev = spline->points;
+ point = prev + 1;
+
+ while (a--) {
+ BezTriple *prevbezt;
+ BezTriple *bezt;
+ int j;
+
+ if (a==0 && (spline->flag & MASK_SPLINE_CYCLIC))
+ point = spline->points;
+
+ prevbezt = &prev->bezt;
+ bezt = &point->bezt;
+
+ for (j = 0; j < 2; j++) {
+ BKE_curve_forward_diff_bezier(prevbezt->vec[1][j], prevbezt->vec[2][j],
+ bezt->vec[0][j], bezt->vec[1][j],
+ fp + j, resol, 2 * sizeof(float));
+ }
+
+ fp += 2 * resol;
+
+ if (a==0 && (spline->flag & MASK_SPLINE_CYCLIC)==0) {
+ copy_v2_v2(fp, bezt->vec[1]);
+ }
+
+ prev = point;
+ point++;
+ }
+
+ return diff_points;
+}
+
+float *BKE_mask_spline_feather_differentiated_points(MaskSpline *spline, float aspx, float aspy,
+ int *tot_feather_point)
+{
+ float *feather, *fp;
+ int i, j, tot, resol = BKE_mask_spline_feather_resolution(spline);
+
+ tot = resol * spline->tot_point;
+ feather = fp = MEM_callocN(2 * tot * sizeof(float), "mask spline feather diff points");
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ for (j = 0; j < resol; j++, fp += 2) {
+ float u = (float) j / resol, weight;
+ float co[2], n[2];
+
+ BKE_mask_point_segment_co(spline, point, u, co);
+ BKE_mask_point_normal(spline, point, aspx, aspy, u, n);
+ weight = BKE_mask_point_weight(spline, point, u);
+
+ fp[0] = co[0] + n[0] * weight;
+ fp[1] = co[1] + n[1] * weight;
+ }
+ }
+
+ *tot_feather_point = tot;
+
+ return feather;
+}
+
+float *BKE_mask_spline_feather_points(MaskSpline *spline, float aspx, float aspy, int *tot_feather_point)
+{
+ int i, tot = 0;
+ float *feather, *fp;
+
+ /* count */
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ tot += point->tot_uw + 1;
+ }
+
+ /* create data */
+ feather = fp = MEM_callocN(2 * tot * sizeof(float), "mask spline feather points");
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+ BezTriple *bezt = &point->bezt;
+ float weight, n[2];
+ int j;
+
+ BKE_mask_point_normal(spline, point, aspx, aspy, 0.0f, n);
+ weight = BKE_mask_point_weight(spline, point, 0.0f);
+
+ fp[0] = bezt->vec[1][0] + n[0] * weight;
+ fp[1] = bezt->vec[1][1] + n[1] * weight;
+ fp += 2;
+
+ for (j = 0; j < point->tot_uw; j++) {
+ float u = point->uw[j].u;
+ float co[2];
+
+ BKE_mask_point_segment_co(spline, point, u, co);
+ BKE_mask_point_normal(spline, point, aspx, aspy, u, n);
+ weight = BKE_mask_point_weight(spline, point, u);
+
+ fp[0] = co[0] + n[0] * weight;
+ fp[1] = co[1] + n[1] * weight;
+
+ fp += 2;
+ }
+ }
+
+ *tot_feather_point = tot;
+
+ return feather;
+}
+
+/* point */
+
+int BKE_mask_point_has_handle(MaskSplinePoint *point)
+{
+ BezTriple *bezt = &point->bezt;
+
+ return bezt->h1 == HD_ALIGN;
+}
+
+void BKE_mask_point_handle(MaskSplinePoint *point, float aspx, float aspy, float handle[2])
+{
+ float vec[2];
+
+ sub_v2_v2v2(vec, point->bezt.vec[0], point->bezt.vec[1]);
+
+ vec[0] *= aspx;
+ vec[1] *= aspy;
+
+ handle[0] = (point->bezt.vec[1][0]*aspx + vec[1]) / aspx;
+ handle[1] = (point->bezt.vec[1][1]*aspy - vec[0]) / aspy;
+}
+
+void BKE_mask_point_set_handle(MaskSplinePoint *point, float loc[2], int keep_direction, float aspx, float aspy,
+ float orig_handle[2], float orig_vec[3][3])
+{
+ BezTriple *bezt = &point->bezt;
+ float v1[2], v2[2], vec[2];
+
+ if (keep_direction) {
+ sub_v2_v2v2(v1, loc, orig_vec[1]);
+ sub_v2_v2v2(v2, orig_handle, orig_vec[1]);
+
+ v1[0] *= aspx;
+ v1[1] *= aspy;
+ v2[0] *= aspx;
+ v2[1] *= aspx;
+
+ project_v2_v2v2(vec, v1, v2);
+
+ if (dot_v2v2(v2, vec) > 0) {
+ float len = len_v2(vec);
+
+ sub_v2_v2v2(v1, orig_vec[0], orig_vec[1]);
+
+ v1[0] *= aspx;
+ v1[1] *= aspy;
+
+ mul_v2_fl(v1, len / len_v2(v1));
+
+ v1[0] /= aspx;
+ v1[1] /= aspy;
+
+ add_v2_v2v2(bezt->vec[0], bezt->vec[1], v1);
+ sub_v2_v2v2(bezt->vec[2], bezt->vec[1], v1);
+ }
+ else {
+ copy_v3_v3(bezt->vec[0], bezt->vec[1]);
+ copy_v3_v3(bezt->vec[2], bezt->vec[1]);
+ }
+ } else {
+ sub_v2_v2v2(v1, loc, bezt->vec[1]);
+
+ v2[0] = -v1[1] * aspy / aspx;
+ v2[1] = v1[0] * aspx / aspy;
+
+ add_v2_v2v2(bezt->vec[0], bezt->vec[1], v2);
+ sub_v2_v2v2(bezt->vec[2], bezt->vec[1], v2);
+ }
+}
+
+float *BKE_mask_point_segment_feather_diff(MaskSpline *spline, MaskSplinePoint *point, float aspx, float aspy,
+ int *tot_feather_point)
+{
+ float *feather, *fp;
+ int i, resol = BKE_mask_spline_feather_resolution(spline);
+
+ feather = fp = MEM_callocN(2 * resol * sizeof(float), "mask point spline feather diff points");
+
+ for (i = 0; i < resol; i++, fp += 2) {
+ float u = (float)(i % resol) / resol, weight;
+ float co[2], n[2];
+
+ BKE_mask_point_segment_co(spline, point, u, co);
+ BKE_mask_point_normal(spline, point, aspx, aspy, u, n);
+ weight = BKE_mask_point_weight(spline, point, u);
+
+ fp[0] = co[0] + n[0] * weight;
+ fp[1] = co[1] + n[1] * weight;
+ }
+
+ *tot_feather_point = resol;
+
+ return feather;
+}
+
+float *BKE_mask_point_segment_diff(MaskSpline *spline, MaskSplinePoint *point, int *tot_diff_point)
+{
+ BezTriple *bezt, *next;
+ float *diff_points, *fp;
+ int j, resol = BKE_mask_spline_resolution(spline);
+
+ bezt = &point->bezt;
+
+ if (point == &spline->points[spline->tot_point - 1]) {
+ if (spline->flag & MASK_SPLINE_CYCLIC)
+ next = &(spline->points[0].bezt);
+ else
+ next = NULL;
+ }
+ else next = &((point + 1))->bezt;
+
+ if (!next)
+ return NULL;
+
+ /* resol+1 because of 'forward_diff_bezier' function */
+ *tot_diff_point = resol + 1;
+ diff_points = fp = MEM_callocN((resol + 1)*2*sizeof(float), "mask segment vets");
+
+ for (j = 0; j < 2; j++) {
+ BKE_curve_forward_diff_bezier(bezt->vec[1][j], bezt->vec[2][j],
+ next->vec[0][j], next->vec[1][j],
+ fp + j, resol, 2 * sizeof(float));
+ }
+
+ copy_v2_v2(fp + 2 * resol, next->vec[1]);
+
+ return diff_points;
+}
+
+void BKE_mask_point_segment_co(MaskSpline *spline, MaskSplinePoint *point, float u, float co[2])
+{
+ BezTriple *bezt = &point->bezt, *next;
+ float q0[2], q1[2], q2[2], r0[2], r1[2];
+
+ if (point == &spline->points[spline->tot_point - 1]) {
+ if (spline->flag & MASK_SPLINE_CYCLIC)
+ next = &(spline->points[0].bezt);
+ else
+ next = NULL;
+ }
+ else next = &((point + 1))->bezt;
+
+ if (!next) {
+ copy_v2_v2(co, bezt->vec[1]);
+ return;
+ }
+
+ interp_v2_v2v2(q0, bezt->vec[1], bezt->vec[2], u);
+ interp_v2_v2v2(q1, bezt->vec[2], next->vec[0], u);
+ interp_v2_v2v2(q2, next->vec[0], next->vec[1], u);
+
+ interp_v2_v2v2(r0, q0, q1, u);
+ interp_v2_v2v2(r1, q1, q2, u);
+
+ interp_v2_v2v2(co, r0, r1, u);
+}
+
+void BKE_mask_point_normal(MaskSpline *spline, MaskSplinePoint *point, float aspx, float aspy, float u, float n[2])
+{
+ BezTriple *bezt = &point->bezt, *next;
+ float q0[2], q1[2], q2[2], r0[2], r1[2], vec[2];
+
+ if (point == &spline->points[spline->tot_point - 1]) {
+ if (spline->flag & MASK_SPLINE_CYCLIC)
+ next = &(spline->points[0].bezt);
+ else
+ next = NULL;
+ }
+ else next = &((point + 1))->bezt;
+
+ if (!next) {
+ BKE_mask_point_handle(point, aspx, aspy, vec);
+
+ sub_v2_v2v2(n, vec, bezt->vec[1]);
+
+ n[0] *= aspx;
+ n[1] *= aspy;
+
+ normalize_v2(n);
+
+ n[0] /= aspx;
+ n[1] /= aspy;
+
+ return;
+ }
+
+ interp_v2_v2v2(q0, bezt->vec[1], bezt->vec[2], u);
+ interp_v2_v2v2(q1, bezt->vec[2], next->vec[0], u);
+ interp_v2_v2v2(q2, next->vec[0], next->vec[1], u);
+
+ interp_v2_v2v2(r0, q0, q1, u);
+ interp_v2_v2v2(r1, q1, q2, u);
+
+ sub_v2_v2v2(vec, r1, r0);
+
+ n[0] = -vec[1] * aspy;
+ n[1] = vec[0] * aspx;
+
+ normalize_v2(n);
+
+ n[0] /= aspx;
+ n[1] /= aspy;
+}
+
+float BKE_mask_point_weight(MaskSpline *spline, MaskSplinePoint *point, float u)
+{
+ BezTriple *bezt = &point->bezt, *next;
+ float cur_u, cur_w, next_u, next_w, fac;
+ int i;
+
+ if (point == &spline->points[spline->tot_point - 1]) {
+ if (spline->flag & MASK_SPLINE_CYCLIC)
+ next = &(spline->points[0].bezt);
+ else
+ next = NULL;
+ }
+ else next = &((point + 1))->bezt;
+
+ if (!next)
+ return bezt->weight;
+
+ for (i = 0; i < point->tot_uw + 1; i++) {
+
+ if (i == 0) {
+ cur_u = 0.0f;
+ cur_w = bezt->weight;
+ }
+ else {
+ cur_u = point->uw[i - 1].u;
+ cur_w = point->uw[i - 1].w;
+ }
+
+ if (i == point->tot_uw) {
+ next_u = 1.0f;
+ next_w = next->weight;
+ }
+ else {
+ next_u = point->uw[i].u;
+ next_w = point->uw[i].w;
+ }
+
+ if (u >= cur_u && u <= next_u) {
+ break;
+ }
+ }
+
+ fac = (u - cur_u) / (next_u - cur_u);
+
+ if (spline->weight_interp == MASK_SPLINE_INTERP_EASE)
+ return cur_w + (next_w - cur_w) * (3.0f * fac * fac - 2.0f * fac * fac * fac);
+ else
+ return (1.0f - fac) * cur_w + fac * next_w;
+}
+
+MaskSplinePointUW *BKE_mask_point_sort_uw(MaskSplinePoint *point, MaskSplinePointUW *uw)
+{
+ if (point->tot_uw > 1) {
+ int idx = uw - point->uw;
+
+ if (idx > 0 && point->uw[idx - 1].u > uw->u) {
+ while (idx > 0 && point->uw[idx - 1].u > point->uw[idx].u) {
+ SWAP(MaskSplinePointUW, point->uw[idx - 1], point->uw[idx]);
+ idx--;
+ }
+ }
+
+ if (idx < point->tot_uw - 1 && point->uw[idx + 1].u < uw->u) {
+ while (idx < point->tot_uw - 1 && point->uw[idx + 1].u < point->uw[idx].u) {
+ SWAP(MaskSplinePointUW, point->uw[idx + 1], point->uw[idx]);
+ idx++;
+ }
+ }
+
+ return &point->uw[idx];
+ }
+
+ return uw;
+}
+
+void BKE_mask_point_add_uw(MaskSplinePoint *point, float u, float w)
+{
+ if (!point->uw)
+ point->uw = MEM_callocN(sizeof(*point->uw), "mask point uw");
+ else
+ point->uw = MEM_reallocN(point->uw, (point->tot_uw + 1) * sizeof(*point->uw));
+
+ point->uw[point->tot_uw].u = u;
+ point->uw[point->tot_uw].w = w;
+
+ point->tot_uw++;
+
+ BKE_mask_point_sort_uw(point, &point->uw[point->tot_uw - 1]);
+}
+
+/* only mask block itself */
+static Mask *mask_alloc(const char *name)
+{
+ Mask *mask;
+
+ mask = alloc_libblock(&G.main->mask, ID_MSK, name);
+
+ return mask;
+}
+
+Mask *BKE_mask_new(const char *name)
+{
+ Mask *mask;
+ char mask_name[MAX_ID_NAME - 2];
+
+ if (name && name[0])
+ BLI_strncpy(mask_name, name, sizeof(mask_name));
+ else
+ strcpy(mask_name, "Mask");
+
+ mask = mask_alloc(mask_name);
+
+ return mask;
+}
+
+void BKE_mask_point_free(MaskSplinePoint *point)
+{
+ if (point->uw)
+ MEM_freeN(point->uw);
+}
+
+void BKE_mask_spline_free(MaskSpline *spline)
+{
+ int i = 0;
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ BKE_mask_point_free(point);
+ }
+
+ MEM_freeN(spline->points);
+
+ MEM_freeN(spline);
+}
+
+void BKE_mask_shape_free(MaskShape *shape)
+{
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ MaskSpline *next_spline = spline->next;
+
+ BLI_remlink(&shape->splines, spline);
+ BKE_mask_spline_free(spline);
+
+ spline = next_spline;
+ }
+
+ MEM_freeN(shape);
+}
+
+void BKE_mask_free(Mask *mask)
+{
+ MaskShape *shape = mask->shapes.first;
+
+ while (shape) {
+ MaskShape *next_shape = shape->next;
+
+ BLI_remlink(&mask->shapes, shape);
+ BKE_mask_shape_free(shape);
+
+ shape = next_shape;
+ }
+}
+
+void BKE_mask_unlink(Main *bmain, Mask *mask)
+{
+ bScreen *scr;
+ ScrArea *area;
+ SpaceLink *sl;
+
+ for (scr = bmain->screen.first; scr; scr = scr->id.next) {
+ for (area = scr->areabase.first; area; area = area->next) {
+ for(sl = area->spacedata.first; sl; sl = sl->next) {
+ if(sl->spacetype == SPACE_CLIP) {
+ SpaceClip *sc = (SpaceClip *) sl;
+
+ if(sc->mask == mask)
+ sc->mask = NULL;
+ }
+ }
+ }
+ }
+
+ mask->id.us= 0;
+}
+
+static void evaluate_mask_parent(MaskParent *parent, float ctime, float co[2])
+{
+ if (!parent)
+ return;
+
+ if ((parent->flag & MASK_PARENT_ACTIVE) == 0)
+ return;
+
+ if (parent->id_type == ID_MC) {
+ if (parent->id) {
+ MovieClip *clip = (MovieClip *) parent->id;
+ MovieTracking *tracking = (MovieTracking *) &clip->tracking;
+ MovieTrackingObject *ob = BKE_tracking_named_object(tracking, parent->parent);
+
+ if (ob) {
+ MovieTrackingTrack *track = BKE_tracking_named_track(tracking, ob, parent->sub_parent);
+
+ if (track) {
+ MovieTrackingMarker *marker = BKE_tracking_get_marker(track, ctime);
+
+ copy_v2_v2(co, marker->pos);
+ }
+ }
+ }
+ }
+}
+
+static void mask_calc_point_handle(MaskSplinePoint *point, MaskSplinePoint *prev_point, MaskSplinePoint *next_point)
+{
+ BezTriple *bezt = &point->bezt;
+ BezTriple *prev_bezt = NULL, *next_bezt = NULL;
+ int handle_type = bezt->h1;
+
+ if (prev_point)
+ prev_bezt = &prev_point->bezt;
+
+ if (next_point)
+ next_bezt = &next_point->bezt;
+
+ if (handle_type == HD_VECT) {
+ BKE_nurb_handle_calc(bezt, prev_bezt, next_bezt, 0);
+ }
+ else if (handle_type == HD_AUTO) {
+ BKE_nurb_handle_calc(bezt, prev_bezt, next_bezt, 0);
+ }
+ else if (handle_type == HD_ALIGN) {
+ float v1[3], v2[3];
+ float vec[3], h[3];
+
+ sub_v3_v3v3(v1, bezt->vec[0], bezt->vec[1]);
+ sub_v3_v3v3(v2, bezt->vec[2], bezt->vec[1]);
+ add_v3_v3v3(vec, v1, v2);
+
+ if (len_v3(vec) > 1e-3) {
+ h[0] = vec[1];
+ h[1] = -vec[0];
+ h[2] = 0.0f;
+ }
+ else {
+ copy_v3_v3(h, v1);
+ }
+
+ add_v3_v3v3(bezt->vec[0], bezt->vec[1], h);
+ sub_v3_v3v3(bezt->vec[2], bezt->vec[1], h);
+ }
+}
+
+void BKE_mask_calc_handles(Mask *mask)
+{
+ MaskShape *shape = mask->shapes.first;
+
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+ int i;
+
+ while (spline) {
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+ MaskSplinePoint *prev_point, *next_point;
+
+ if (i == 0) {
+ if (spline->flag & MASK_SPLINE_CYCLIC)
+ prev_point = &spline->points[spline->tot_point - 1];
+ else
+ prev_point = NULL;
+ }
+ else prev_point = point - 1;
+
+ if (i == spline->tot_point - 1) {
+ if (spline->flag & MASK_SPLINE_CYCLIC)
+ next_point = &spline->points[0];
+ else
+ next_point = NULL;
+ }
+ else next_point = point + 1;
+
+ mask_calc_point_handle(point, prev_point, next_point);
+ }
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+}
+
+void BKE_mask_evaluate(Mask *mask, float ctime)
+{
+ MaskShape *shape = mask->shapes.first;
+
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+ int i;
+
+ while (spline) {
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+ BezTriple *bezt = &point->bezt;
+ float co[2], delta[2];
+
+ copy_v2_v2(co, bezt->vec[1]);
+ evaluate_mask_parent(&point->parent, ctime, co);
+ sub_v2_v2v2(delta, co, bezt->vec[1]);
+
+ add_v2_v2(bezt->vec[0], delta);
+ add_v2_v2(bezt->vec[1], delta);
+ add_v2_v2(bezt->vec[2], delta);
+ }
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+
+ BKE_mask_calc_handles(mask);
+}
+
+void BKE_mask_evaluate_all_masks(Main *bmain, float ctime)
+{
+ Mask *mask;
+
+ for (mask = bmain->mask.first; mask; mask = mask->id.next) {
+ BKE_mask_evaluate(mask, ctime);
+ }
+}
+
+void BKE_mask_update_scene(Main *bmain, Scene *scene)
+{
+ Mask *mask;
+
+ for (mask = bmain->mask.first; mask; mask = mask->id.next) {
+ if (mask->id.flag & LIB_ID_RECALC) {
+ BKE_mask_evaluate_all_masks(bmain, CFRA);
+ }
+ }
+}
+
+void BKE_mask_parent_init(MaskParent *parent)
+{
+ parent->id_type = ID_MC;
+}
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index b2a85ad0629..19713f60ec8 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -1930,6 +1930,7 @@ static void registerCompositNodes(bNodeTreeType *ttype)
register_node_type_cmp_transform(ttype);
register_node_type_cmp_stabilize2d(ttype);
register_node_type_cmp_moviedistortion(ttype);
+ register_node_type_cmp_mask(ttype);
}
static void registerShaderNodes(bNodeTreeType *ttype)
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 864260833a6..2a311a871f0 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -63,6 +63,7 @@
#include "BKE_idprop.h"
#include "BKE_library.h"
#include "BKE_main.h"
+#include "BKE_mask.h"
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_paint.h"
@@ -1003,6 +1004,9 @@ static void scene_update_tagged_recursive(Main *bmain, Scene *scene, Scene *scen
/* update sound system animation */
sound_update_scene(scene);
+
+ /* update masking curves */
+ BKE_mask_update_scene(bmain, scene);
}
/* this is called in main loop, doing tagged updates before redraw */
@@ -1073,6 +1077,8 @@ void scene_update_for_newframe(Main *bmain, Scene *sce, unsigned int lay)
* so don't call within 'scene_update_tagged_recursive' */
DAG_scene_update_flags(bmain, sce, lay, TRUE); // only stuff that moves or needs display still
+ BKE_mask_evaluate_all_masks(bmain, ctime);
+
/* All 'standard' (i.e. without any dependencies) animation is handled here,
* with an 'local' to 'macro' order of evaluation. This should ensure that
* settings stored nestled within a hierarchy (i.e. settings in a Texture block
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 993426545ee..753cd93b89a 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -94,6 +94,7 @@
#include "DNA_vfont_types.h"
#include "DNA_world_types.h"
#include "DNA_movieclip_types.h"
+#include "DNA_mask_types.h"
#include "MEM_guardedalloc.h"
@@ -5387,6 +5388,7 @@ static void lib_link_screen(FileData *fd, Main *main)
SpaceClip *sclip= (SpaceClip *)sl;
sclip->clip= newlibadr_us(fd, sc->id.lib, sclip->clip);
+ sclip->mask= newlibadr_us(fd, sc->id.lib, sclip->mask);
sclip->scopes.track_preview = NULL;
sclip->draw_context = NULL;
@@ -5654,6 +5656,7 @@ void lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *curscene)
SpaceClip *sclip= (SpaceClip *)sl;
sclip->clip= restore_pointer_by_name(newmain, (ID *)sclip->clip, 1);
+ sclip->mask= restore_pointer_by_name(newmain, (ID *)sclip->mask, 1);
sclip->scopes.ok = 0;
}
@@ -6238,6 +6241,90 @@ static void lib_link_movieclip(FileData *fd, Main *main)
}
}
+/* ***************** READ MOVIECLIP *************** */
+
+static void direct_link_mask(FileData *fd, Mask *mask)
+{
+ MaskShape *shape;
+
+ mask->adt = newdataadr(fd, mask->adt);
+
+ link_list(fd, &mask->shapes);
+
+ shape = mask->shapes.first;
+ while (shape) {
+ MaskSpline *spline;
+
+ link_list(fd, &shape->splines);
+
+ spline = shape->splines.first;
+ while (spline) {
+ int i;
+
+ spline->points = newdataadr(fd, spline->points);
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ if (point->tot_uw)
+ point->uw = newdataadr(fd, point->uw);
+ }
+
+ spline = spline->next;
+ }
+
+ shape->act_spline = newdataadr(fd, shape->act_spline);
+ shape->act_point = newdataadr(fd, shape->act_point);
+
+ shape = shape->next;
+ }
+}
+
+static void lib_link_mask_parent(FileData *fd, Mask *mask, MaskParent *parent)
+{
+ parent->id = newlibadr_us(fd, mask->id.lib, parent->id);
+}
+
+static void lib_link_mask(FileData *fd, Main *main)
+{
+ Mask *mask;
+
+ mask = main->mask.first;
+ while (mask) {
+ if(mask->id.flag & LIB_NEEDLINK) {
+ MaskShape *shape;
+
+ if (mask->adt)
+ lib_link_animdata(fd, &mask->id, mask->adt);
+
+ shape = mask->shapes.first;
+ while (shape) {
+ MaskSpline *spline;
+
+ spline = shape->splines.first;
+ while (spline) {
+ int i;
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ lib_link_mask_parent(fd, mask, &point->parent);
+ }
+
+ lib_link_mask_parent(fd, mask, &spline->parent);
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+
+ mask->id.flag -= LIB_NEEDLINK;
+ }
+ mask = mask->id.next;
+ }
+}
+
/* ************** GENERAL & MAIN ******************** */
@@ -6444,6 +6531,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID
case ID_MC:
direct_link_movieclip(fd, (MovieClip *)id);
break;
+ case ID_MSK:
+ direct_link_mask(fd, (Mask *)id);
+ break;
}
/*link direct data of ID properties*/
@@ -13344,6 +13434,7 @@ static void lib_link_all(FileData *fd, Main *main)
lib_link_brush(fd, main);
lib_link_particlesettings(fd, main);
lib_link_movieclip(fd, main);
+ lib_link_mask(fd, main);
lib_link_mesh(fd, main); /* as last: tpage images with users at zero */
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 5580c9efc9b..e11e0274b28 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -133,6 +133,7 @@ Any case: direct data is ALWAYS after the lib block
#include "DNA_world_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_movieclip_types.h"
+#include "DNA_mask_types.h"
#include "MEM_guardedalloc.h" // MEM_freeN
#include "BLI_bitmap.h"
@@ -2732,6 +2733,54 @@ static void write_movieclips(WriteData *wd, ListBase *idbase)
mywrite(wd, MYWRITE_FLUSH, 0);
}
+static void write_masks(WriteData *wd, ListBase *idbase)
+{
+ Mask *mask;
+
+ mask = idbase->first;
+ while (mask) {
+ if (mask->id.us > 0 || wd->current) {
+ MaskShape *shape;
+
+ writestruct(wd, ID_MSK, "Mask", 1, mask);
+
+ if (mask->adt)
+ write_animdata(wd, mask->adt);
+
+ shape = mask->shapes.first;
+ while (shape) {
+ MaskSpline *spline;
+
+ writestruct(wd, DATA, "MaskShape", 1, shape);
+
+ spline = shape->splines.first;
+ while (spline) {
+ int i;
+
+ writestruct(wd, DATA, "MaskSpline", 1, spline);
+ writestruct(wd, DATA, "MaskSplinePoint", spline->tot_point, spline->points);
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ if (point->tot_uw)
+ writestruct(wd, DATA, "MaskSplinePointUW", point->tot_uw, point->uw);
+ }
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+ }
+
+ mask = mask->id.next;
+ }
+
+ /* flush helps the compression for undo-save */
+ mywrite(wd, MYWRITE_FLUSH, 0);
+}
+
/* context is usually defined by WM, two cases where no WM is available:
* - for forward compatibility, curscreen has to be saved
* - for undofile, curscene needs to be saved */
@@ -2816,6 +2865,7 @@ static int write_file_handle(Main *mainvar, int handle, MemFile *compare, MemFil
write_screens (wd, &mainvar->screen);
}
write_movieclips (wd, &mainvar->movieclip);
+ write_masks (wd, &mainvar->mask);
write_scenes (wd, &mainvar->scene);
write_curves (wd, &mainvar->curve);
write_mballs (wd, &mainvar->mball);
diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt
index 088376b20ef..67ed77bcc4b 100644
--- a/source/blender/editors/CMakeLists.txt
+++ b/source/blender/editors/CMakeLists.txt
@@ -24,6 +24,7 @@ if(WITH_BLENDER)
add_subdirectory(curve)
add_subdirectory(gpencil)
add_subdirectory(interface)
+ add_subdirectory(mask)
add_subdirectory(mesh)
add_subdirectory(metaball)
add_subdirectory(object)
diff --git a/source/blender/editors/SConscript b/source/blender/editors/SConscript
index ed66a76a324..d08b496f0ef 100644
--- a/source/blender/editors/SConscript
+++ b/source/blender/editors/SConscript
@@ -8,6 +8,7 @@ SConscript(['datafiles/SConscript',
'interface/SConscript',
'animation/SConscript',
'armature/SConscript',
+ 'mask/SConscript',
'mesh/SConscript',
'metaball/SConscript',
'object/SConscript',
diff --git a/source/blender/editors/include/ED_clip.h b/source/blender/editors/include/ED_clip.h
index db6d9bbd013..285f1487a71 100644
--- a/source/blender/editors/include/ED_clip.h
+++ b/source/blender/editors/include/ED_clip.h
@@ -36,6 +36,7 @@ struct bContext;
struct bScreen;
struct ImBuf;
struct Main;
+struct Mask;
struct MovieClip;
struct SpaceClip;
struct wmEvent;
@@ -45,13 +46,19 @@ int ED_space_clip_poll(struct bContext *C);
int ED_space_clip_tracking_poll(struct bContext *C);
int ED_space_clip_tracking_size_poll(struct bContext *C);
int ED_space_clip_tracking_frame_poll(struct bContext *C);
+int ED_space_clip_maskediting_poll(struct bContext *C);
+int ED_space_clip_maskediting_mask_poll(bContext *C);
void ED_space_clip_set(struct bContext *C, struct bScreen *screen, struct SpaceClip *sc, struct MovieClip *clip);
struct MovieClip *ED_space_clip(struct SpaceClip *sc);
+struct Mask *ED_space_clip_mask(struct SpaceClip *sc);
void ED_space_clip_size(struct SpaceClip *sc, int *width, int *height);
void ED_space_clip_zoom(struct SpaceClip *sc, ARegion *ar, float *zoomx, float *zoomy);
void ED_space_clip_aspect(struct SpaceClip *sc, float *aspx, float *aspy);
+void ED_space_clip_mask_size(struct SpaceClip *sc, int *width, int *height);
+void ED_space_clip_mask_aspect(struct SpaceClip *sc, float *aspx, float *aspy);
+
struct ImBuf *ED_space_clip_get_buffer(struct SpaceClip *sc);
struct ImBuf *ED_space_clip_get_stable_buffer(struct SpaceClip *sc, float loc[2], float *scale, float *angle);
@@ -68,6 +75,8 @@ void ED_space_clip_unload_movieclip_buffer(struct SpaceClip *sc);
void ED_space_clip_free_texture_buffer(struct SpaceClip *sc);
int ED_space_clip_show_trackedit(struct SpaceClip *sc);
+int ED_space_clip_show_maskedit(struct SpaceClip *sc);
+void ED_space_clip_set_mask(struct bContext *C, struct SpaceClip *sc, struct Mask *mask);
void ED_space_clip_update_dopesheet(struct SpaceClip *sc);
diff --git a/source/blender/editors/include/ED_mask.h b/source/blender/editors/include/ED_mask.h
new file mode 100644
index 00000000000..5df91b4032a
--- /dev/null
+++ b/source/blender/editors/include/ED_mask.h
@@ -0,0 +1,44 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file ED_mask.h
+ * \ingroup editors
+ */
+
+#ifndef __ED_MASK_H__
+#define __ED_MASK_H__
+
+struct wmKeyConfig;
+
+/* mask_editor.c */
+void ED_operatortypes_mask(void);
+void ED_keymap_mask(struct wmKeyConfig *keyconf);
+void ED_operatormacros_mask(void);
+
+/* mask_draw.c */
+void ED_mask_draw(bContext *C, int width, int height, float zoomx, float zoomy);
+
+#endif /* ED_TEXT_H */
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index f62befdaa31..44a66d436db 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -169,6 +169,7 @@ int ED_operator_editmball(struct bContext *C);
int ED_operator_uvedit(struct bContext *C);
int ED_operator_uvmap(struct bContext *C);
int ED_operator_posemode(struct bContext *C);
+int ED_operator_mask(struct bContext *C);
/* default keymaps, bitflags */
diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h
index f6dee351c29..2d6379ad8a6 100644
--- a/source/blender/editors/include/ED_transform.h
+++ b/source/blender/editors/include/ED_transform.h
@@ -95,6 +95,7 @@ enum {
#define CTX_BMESH 64
#define CTX_NDOF 128
#define CTX_MOVIECLIP 256
+#define CTX_MASK 512
/* Standalone call to get the transformation center corresponding to the current situation
* returns 1 if successful, 0 otherwise (usually means there's no selection)
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index bafd85e9451..c6bc975d981 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -576,8 +576,10 @@ void uiTemplateAnyID(uiLayout *layout, PointerRNA *ptr, const char *propname, co
row = uiLayoutRow(layout, 1);
/* Label - either use the provided text, or will become "ID-Block:" */
- if (text)
- uiItemL(row, text, ICON_NONE);
+ if (text) {
+ if (text[0])
+ uiItemL(row, text, ICON_NONE);
+ }
else
uiItemL(row, "ID-Block:", ICON_NONE);
diff --git a/source/blender/editors/mask/CMakeLists.txt b/source/blender/editors/mask/CMakeLists.txt
new file mode 100644
index 00000000000..fc1d2f4be6b
--- /dev/null
+++ b/source/blender/editors/mask/CMakeLists.txt
@@ -0,0 +1,47 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2012 Blender Foundation.
+#
+# Contributor(s): Blender Foundation,
+# Sergey Sharybin
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ ../include
+ ../../blenkernel
+ ../../blenloader
+ ../../blenlib
+ ../../makesdna
+ ../../makesrna
+ ../../windowmanager
+ ../../../../intern/guardedalloc
+ ${GLEW_INCLUDE_PATH}
+)
+
+set(INC_SYS
+)
+
+set(SRC
+ mask_draw.c
+ mask_editor.c
+ mask_ops.c
+
+ mask_intern.h
+)
+
+blender_add_lib(bf_editor_mask "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/mask/SConscript b/source/blender/editors/mask/SConscript
new file mode 100644
index 00000000000..4af000d038d
--- /dev/null
+++ b/source/blender/editors/mask/SConscript
@@ -0,0 +1,9 @@
+#!/usr/bin/python
+Import ('env')
+
+sources = env.Glob('*.c')
+defs = []
+incs = '../include ../../blenkernel ../../blenloader ../../blenlib ../../windowmanager ../../makesdna'
+incs += ' ../../makesrna #/extern/glew/include #/intern/guardedalloc'
+
+env.BlenderLib ( 'bf_editors_mask', sources, Split(incs), defs, libtype=['core'], priority=[100] )
diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c
new file mode 100644
index 00000000000..d898a1c5232
--- /dev/null
+++ b/source/blender/editors/mask/mask_draw.c
@@ -0,0 +1,270 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_draw.c
+ * \ingroup edmask
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_mask.h"
+
+#include "DNA_mask_types.h"
+#include "DNA_object_types.h" /* SELECT */
+
+#include "ED_mask.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "UI_resources.h"
+
+#include "mask_intern.h" // own include
+
+typedef struct PixelSpaceContext {
+ int width, height;
+ float zoomx, zoomy;
+ float aspx, aspy;
+} PixelSpaceContext;
+
+static void set_spline_color(MaskShape *shape, MaskSpline *spline)
+{
+ if (spline->flag & SELECT) {
+ if (shape->act_spline == spline)
+ glColor3f(1.0f, 1.0f, 1.0f);
+ else
+ glColor3f(1.0f, 0.0f, 0.0f);
+ }
+ else {
+ glColor3f(0.5f, 0.0f, 0.0f);
+ }
+}
+
+/* return non-zero if spline is selected */
+static void draw_spline_points(MaskShape *shape, MaskSpline *spline, PixelSpaceContext *pixelspace)
+{
+ int i, hsize, tot_feather_point;
+ float *feather_points, *fp;
+
+ if (!spline->tot_point)
+ return;
+
+ hsize = UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE);
+
+ glPointSize(hsize);
+
+ /* feather points */
+ feather_points = fp = BKE_mask_spline_feather_points(spline, pixelspace->aspx, pixelspace->aspy, &tot_feather_point);
+ for (i = 0; i < spline->tot_point; i++) {
+ int j;
+ MaskSplinePoint *point = &spline->points[i];
+
+ for (j = 0; j < point->tot_uw + 1; j++) {
+ int sel = FALSE;
+
+ if (j == 0) {
+ sel = MASKPOINT_ISSEL(point);
+ }
+ else {
+ sel = point->uw[j - 1].flag & SELECT;
+ }
+
+ if (sel) {
+ if (point == shape->act_point)
+ glColor3f(1.0f, 1.0f, 1.0f);
+ else
+ glColor3f(1.0f, 1.0f, 0.0f);
+ } else
+ glColor3f(0.5f, 0.5f, 0.0f);
+
+ glBegin(GL_POINTS);
+ glVertex2fv(fp);
+ glEnd();
+
+ fp += 2;
+ }
+ }
+ MEM_freeN(feather_points);
+
+ /* control points */
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+ BezTriple *bezt = &point->bezt;
+ float vert[2], handle[2];
+ int has_handle = BKE_mask_point_has_handle(point);;
+
+ copy_v2_v2(vert, bezt->vec[1]);
+ BKE_mask_point_handle(point, pixelspace->aspx, pixelspace->aspy, handle);
+
+ /* draw handle segment */
+ if (has_handle) {
+ set_spline_color(shape, spline);
+
+ glBegin(GL_LINES);
+ glVertex3fv(vert);
+ glVertex3fv(handle);
+ glEnd();
+ }
+
+ /* draw CV point */
+ if (MASKPOINT_CV_ISSEL(point)) {
+ if (point == shape->act_point)
+ glColor3f(1.0f, 1.0f, 1.0f);
+ else
+ glColor3f(1.0f, 1.0f, 0.0f);
+ } else
+ glColor3f(0.5f, 0.5f, 0.0f);
+
+ glBegin(GL_POINTS);
+ glVertex3fv(vert);
+ glEnd();
+
+ /* draw handle points */
+ if (has_handle) {
+ if (MASKPOINT_HANDLE_ISSEL(point)) {
+ if (point == shape->act_point)
+ glColor3f(1.0f, 1.0f, 1.0f);
+ else
+ glColor3f(1.0f, 1.0f, 0.0f);
+ } else
+ glColor3f(0.5f, 0.5f, 0.0f);
+
+ glBegin(GL_POINTS);
+ glVertex3fv(handle);
+ glEnd();
+ }
+ }
+
+ glPointSize(1.0f);
+}
+
+static void draw_spline_curve_lines(float *points, int tot_point, int closed)
+{
+ int i;
+ float *fp = points;
+
+ if (closed)
+ glBegin(GL_LINE_LOOP);
+ else
+ glBegin(GL_LINE_STRIP);
+
+ for (i = 0; i < tot_point; i++, fp+=2) {
+ glVertex3fv(fp);
+ }
+ glEnd();
+}
+
+static void draw_dashed_curve(MaskSpline *spline, float *points, int tot_point)
+{
+ glEnable(GL_COLOR_LOGIC_OP);
+ glLogicOp(GL_OR);
+
+ draw_spline_curve_lines(points, tot_point, spline->flag & MASK_SPLINE_CYCLIC);
+
+ glDisable(GL_COLOR_LOGIC_OP);
+ glLineStipple(3, 0xaaaa);
+ glEnable(GL_LINE_STIPPLE);
+
+ glColor3f(0.0f, 0.0f, 0.0f);
+ draw_spline_curve_lines(points, tot_point, spline->flag & MASK_SPLINE_CYCLIC);
+
+ glDisable(GL_LINE_STIPPLE);
+}
+
+static void draw_spline_curve(MaskShape *shape, MaskSpline *spline, PixelSpaceContext *pixelspace)
+{
+ float *diff_points, *feather_points;
+ int tot_diff_point, tot_feather_point;
+
+ diff_points = BKE_mask_spline_differentiate(spline, &tot_diff_point);
+
+ if (!diff_points)
+ return;
+
+ feather_points = BKE_mask_spline_feather_differentiated_points(spline, pixelspace->aspx, pixelspace->aspy,
+ &tot_feather_point);
+
+ /* draw feather */
+ if (spline->flag & SELECT)
+ glColor3f(0.0f, 1.0f, 0.0f);
+ else
+ glColor3f(0.0f, 0.5f, 0.0f);
+ draw_dashed_curve(spline, feather_points, tot_feather_point);
+
+ /* draw main curve */
+ set_spline_color(shape, spline);
+ draw_dashed_curve(spline, diff_points, tot_diff_point);
+
+ MEM_freeN(diff_points);
+ MEM_freeN(feather_points);
+}
+
+static void draw_shapes(Mask *mask, PixelSpaceContext *pixelspace)
+{
+ MaskShape *shape = mask->shapes.first;
+
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ /* draw curve itself first... */
+ draw_spline_curve(shape, spline, pixelspace);
+
+ /* ...and then handles over the curve so they're nicely visible */
+ draw_spline_points(shape, spline, pixelspace);
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+}
+
+void ED_mask_draw(bContext *C, int width, int height, float zoomx, float zoomy)
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ PixelSpaceContext pixelspace;
+ float aspx, aspy;
+
+ if (!mask)
+ return;
+
+ ED_mask_aspect(C, &aspx, &aspy);
+
+ pixelspace.width = width;
+ pixelspace.height = height;
+ pixelspace.zoomx = zoomx;
+ pixelspace.zoomy = zoomy;
+ pixelspace.aspx = aspx;
+ pixelspace.aspy = aspy;
+
+ draw_shapes(mask, &pixelspace);
+}
diff --git a/source/blender/editors/mask/mask_editor.c b/source/blender/editors/mask/mask_editor.c
new file mode 100644
index 00000000000..fcdf66ed69d
--- /dev/null
+++ b/source/blender/editors/mask/mask_editor.c
@@ -0,0 +1,218 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_ops.c
+ * \ingroup edmask
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_mask.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_mask.h"
+#include "ED_clip.h"
+#include "ED_transform.h"
+
+#include "RNA_access.h"
+
+#include "mask_intern.h" // own include
+
+/********************** generic poll functions *********************/
+
+int ED_maskediting_poll(bContext *C)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+
+ if (sc) {
+ return ED_space_clip_maskediting_poll(C);
+ }
+
+ return FALSE;
+}
+
+int ED_maskediting_mask_poll(bContext *C)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+
+ if (sc) {
+ return ED_space_clip_maskediting_mask_poll(C);
+ }
+
+ return FALSE;
+}
+
+/********************** registration *********************/
+
+void ED_mask_mouse_pos(bContext *C, wmEvent *event, float co[2])
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+
+ if (sc) {
+ ED_clip_mouse_pos(C, event, co);
+ }
+ else {
+ /* possible other spaces from which mask editing is available */
+ zero_v2(co);
+ }
+}
+
+void ED_mask_size(bContext *C, int *width, int *height)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+
+ if (sc) {
+ ED_space_clip_mask_size(sc, width, height);
+ }
+ else {
+ /* possible other spaces from which mask editing is available */
+ *width = 0;
+ *height = 0;
+ }
+}
+
+void ED_mask_aspect(bContext *C, float *aspx, float *aspy)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+
+ if (sc) {
+ ED_space_clip_mask_aspect(sc, aspx, aspy);
+ }
+ else {
+ /* possible other spaces from which mask editing is available */
+ *aspx = 1.0f;
+ *aspy = 1.0f;
+ }
+}
+
+void ED_mask_pixelspace_factor(bContext *C, float *scalex, float *scaley)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+
+ if (sc) {
+ ARegion *ar = CTX_wm_region(C);
+ int width, height;
+ float zoomx, zoomy, aspx, aspy;
+
+ ED_space_clip_size(sc, &width, &height);
+ ED_space_clip_zoom(sc, ar, &zoomx, &zoomy);
+ ED_space_clip_aspect(sc, &aspx, &aspy);
+
+ *scalex = ((float)width * aspx) * zoomx;
+ *scaley = ((float)height * aspy) * zoomy;
+ }
+ else {
+ /* possible other spaces from which mask editing is available */
+ *scalex = 1.0f;
+ *scaley = 1.0f;
+ }
+}
+
+/********************** registration *********************/
+
+void ED_operatortypes_mask(void)
+{
+ WM_operatortype_append(MASK_OT_new);
+
+ /* shapes */
+ WM_operatortype_append(MASK_OT_shape_new);
+ WM_operatortype_append(MASK_OT_shape_remove);
+
+ /* geometry */
+ WM_operatortype_append(MASK_OT_add_vertex);
+ WM_operatortype_append(MASK_OT_add_feather_vertex);
+ WM_operatortype_append(MASK_OT_delete);
+
+ /* select */
+ WM_operatortype_append(MASK_OT_select);
+ WM_operatortype_append(MASK_OT_select_all);
+
+ /* shape */
+ WM_operatortype_append(MASK_OT_slide_point);
+ WM_operatortype_append(MASK_OT_cyclic_toggle);
+ WM_operatortype_append(MASK_OT_handle_type_set);
+}
+
+void ED_keymap_mask(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap;
+ wmKeyMapItem *kmi;
+
+ keymap = WM_keymap_find(keyconf, "Mask Editor", 0, 0);
+ keymap->poll = ED_maskediting_poll;
+
+ WM_keymap_add_item(keymap, "MASK_OT_new", NKEY, KM_PRESS, KM_ALT, 0);
+
+ /* geometry */
+ WM_keymap_add_item(keymap, "MASK_OT_add_vertex_slide", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "MASK_OT_add_feather_vertex_slide", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
+ WM_keymap_add_item(keymap, "MASK_OT_delete", XKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "MASK_OT_delete", DELKEY, KM_PRESS, 0, 0);
+
+ /* select */
+ WM_keymap_add_item(keymap, "MASK_OT_select", SELECTMOUSE, KM_PRESS, 0, 0);
+ kmi = WM_keymap_add_item(keymap, "MASK_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "extend", TRUE);
+
+ kmi = WM_keymap_add_item(keymap, "MASK_OT_select_all", AKEY, KM_PRESS, 0, 0);
+ RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
+ kmi = WM_keymap_add_item(keymap, "MASK_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
+ RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
+
+ /* shape */
+ WM_keymap_add_item(keymap, "MASK_OT_cyclic_toggle", CKEY, KM_PRESS, KM_ALT, 0);
+ WM_keymap_add_item(keymap, "MASK_OT_slide_point", LEFTMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "MASK_OT_handle_type_set", VKEY, KM_PRESS, 0, 0);
+
+ transform_keymap_for_space(keyconf, keymap, SPACE_CLIP);
+}
+
+void ED_operatormacros_mask(void)
+{
+ /* XXX: just for sample */
+ wmOperatorType *ot;
+ wmOperatorTypeMacro *otmacro;
+
+ ot= WM_operatortype_append_macro("MASK_OT_add_vertex_slide", "Add Vertex and Slide", 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, "TRANSFORM_OT_translate");
+ RNA_boolean_set(otmacro->ptr, "release_confirm", TRUE);
+
+ ot= WM_operatortype_append_macro("MASK_OT_add_feather_vertex_slide", "Add Feather Vertex and Slide", 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);
+}
diff --git a/source/blender/editors/mask/mask_intern.h b/source/blender/editors/mask/mask_intern.h
new file mode 100644
index 00000000000..073eba3efc9
--- /dev/null
+++ b/source/blender/editors/mask/mask_intern.h
@@ -0,0 +1,69 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2011 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_intern.h
+ * \ingroup spclip
+ */
+
+#ifndef __MASK_INTERN_H__
+#define __MASK_INTERN_H__
+
+struct bContext;
+struct wmEvent;
+struct wmOperatorType;
+
+/* internal exports only */
+
+/* mask_ops.c */
+void MASK_OT_new(struct wmOperatorType *ot);
+void MASK_OT_shape_new(struct wmOperatorType *ot);
+void MASK_OT_shape_remove(struct wmOperatorType *ot);
+
+void MASK_OT_add_vertex(struct wmOperatorType *ot);
+void MASK_OT_add_feather_vertex(struct wmOperatorType *ot);
+void MASK_OT_cyclic_toggle(struct wmOperatorType *ot);
+
+void MASK_OT_select(struct wmOperatorType *ot);
+void MASK_OT_select_all(struct wmOperatorType *ot);
+
+void MASK_OT_slide_point(struct wmOperatorType *ot);
+
+void MASK_OT_delete(struct wmOperatorType *ot);
+
+void MASK_OT_handle_type_set(struct wmOperatorType *ot);
+
+/* mask_editor.c */
+int ED_maskediting_poll(struct bContext *C);
+int ED_maskediting_mask_poll(struct bContext *C);
+
+void ED_mask_size(struct bContext *C, int *width, int *height);
+void ED_mask_aspect(struct bContext *C, float *aspx, float *aspy);
+
+void ED_mask_pixelspace_factor(struct bContext *C, float *scalex, float *scaley);
+void ED_mask_mouse_pos(struct bContext *C, struct wmEvent *event, float co[2]);
+
+#endif /* __MASK_INTERN_H__ */
diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c
new file mode 100644
index 00000000000..0fedc2b34dc
--- /dev/null
+++ b/source/blender/editors/mask/mask_ops.c
@@ -0,0 +1,1653 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_ops.c
+ * \ingroup edmask
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_mask.h"
+
+#include "DNA_mask_types.h"
+#include "DNA_object_types.h" /* SELECT */
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_mask.h"
+#include "ED_clip.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "mask_intern.h" // own include
+
+/******************** utility functions *********************/
+
+static void spline_point_select(MaskSplinePoint *point, int action)
+{
+ int i;
+
+ switch (action) {
+ case SEL_SELECT:
+ MASKPOINT_SEL(point);
+ break;
+ case SEL_DESELECT:
+ MASKPOINT_DESEL(point);
+ break;
+ case SEL_INVERT:
+ MASKPOINT_INVSEL(point);
+ break;
+ }
+
+ for (i = 0; i < point->tot_uw; i++) {
+ switch (action) {
+ case SEL_SELECT:
+ point->uw[i].flag |= SELECT;
+ break;
+ case SEL_DESELECT:
+ point->uw[i].flag &= ~SELECT;
+ break;
+ case SEL_INVERT:
+ point->uw[i].flag ^= SELECT;
+ break;
+ }
+ }
+}
+
+static float projection_on_spline(MaskSpline *spline, MaskSplinePoint *point, float aspx, float aspy, float start_u, float co[2])
+{
+ const int N = 1000;
+ float u = -1.0f, du = 1.0f / N, u1 = start_u, u2 = start_u;
+ float ang = -1.0f;
+
+ while (u1 > 0.0f || u2 < 1.0d) {
+ float n1[2], n2[2], co1[2], co2[2];
+ float v1[2], v2[2];
+ float ang1, ang2;
+
+ if (u1 >= 0.0f) {
+ BKE_mask_point_segment_co(spline, point, u1, co1);
+ BKE_mask_point_normal(spline, point, aspx, aspy, u1, n1);
+ sub_v2_v2v2(v1, co, co1);
+
+ if (len_v2(v1) > 1e-3) {
+ ang1 = angle_v2v2(v1, n1);
+ if (ang1 > M_PI / 2.0f)
+ ang1 = M_PI - ang1;
+
+ if (ang < 0.0f || ang1 < ang) {
+ ang = ang1;
+ u = u1;
+ }
+ }
+ else {
+ u = u1;
+ break;
+ }
+ }
+
+ if (u2 <= 1.0f) {
+ BKE_mask_point_segment_co(spline, point, u2, co2);
+ BKE_mask_point_normal(spline, point, aspx, aspy, u2, n2);
+ sub_v2_v2v2(v2, co, co2);
+
+ if (len_v2(v2) > 1e-3) {
+ ang2 = angle_v2v2(v2, n2);
+ if (ang2 > M_PI / 2.0f)
+ ang2 = M_PI - ang2;
+
+ if (ang2 < ang) {
+ ang = ang2;
+ u = u2;
+ }
+ }
+ else {
+ u = u2;
+ break;
+ }
+ }
+
+ u1 -= du;
+ u2 += du;
+ }
+
+ return u;
+}
+
+static int points_has_selection(MaskSplinePoint *points, int tot_point)
+{
+ int i;
+
+ for (i = 0; i < tot_point; i++) {
+ MaskSplinePoint *point = &points[i];
+
+ if (MASKPOINT_ISSEL(point))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int mask_has_selection(Mask *mask)
+{
+ MaskShape *shape = mask->shapes.first;
+
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ if (points_has_selection(spline->points, spline->tot_point))
+ return TRUE;
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+
+ return FALSE;
+}
+
+static void toggle_selection_all(Mask *mask, int action)
+{
+ MaskShape *shape = mask->shapes.first;
+
+ if (action == SEL_TOGGLE) {
+ if (mask_has_selection(mask))
+ action = SEL_DESELECT;
+ else
+ action = SEL_SELECT;
+ }
+
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ int i;
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ spline_point_select(point, action);
+ }
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+}
+
+static MaskSplinePoint *find_nearest_point(bContext *C, Mask *mask, float normal_co[2], int threshold,
+ MaskShape **shape_r, MaskSpline **spline_r, int *is_handle_r,
+ float *score)
+{
+ MaskShape *shape;
+ MaskShape *point_shape = NULL;
+ MaskSpline *point_spline = NULL;
+ MaskSplinePoint *point = NULL;
+ float co[2], aspx, aspy;
+ float len = FLT_MAX, scalex, scaley;
+ int is_handle = FALSE, width, height;
+
+ ED_mask_size(C, &width, &height);
+ ED_mask_aspect(C, &aspx, &aspy);
+ ED_mask_pixelspace_factor(C, &scalex, &scaley);
+
+ co[0] = normal_co[0] * scalex;
+ co[1] = normal_co[1] * scaley;
+
+ shape = mask->shapes.first;
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ int i;
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *cur_point = &spline->points[i];
+ float cur_len, vec[2], handle[2];
+
+ vec[0] = cur_point->bezt.vec[1][0] * scalex;
+ vec[1] = cur_point->bezt.vec[1][1] * scaley;
+
+ if (BKE_mask_point_has_handle(cur_point)) {
+ BKE_mask_point_handle(cur_point, aspx, aspy, handle);
+ handle[0] *= scalex;
+ handle[1] *= scaley;
+
+ cur_len = len_v2v2(co, handle);
+
+ if (cur_len < len) {
+ point_shape = shape;
+ point_spline = spline;
+ point = cur_point;
+ len = cur_len;
+ is_handle = TRUE;
+ }
+ }
+
+ cur_len = len_v2v2(co, vec);
+
+ if (cur_len < len) {
+ point_spline = spline;
+ point_shape = shape;
+ point = cur_point;
+ len = cur_len;
+ is_handle = FALSE;
+ }
+ }
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+
+ if (len < threshold) {
+ if (shape_r)
+ *shape_r = point_shape;
+
+ if (spline_r)
+ *spline_r = point_spline;
+
+ if (is_handle_r)
+ *is_handle_r = is_handle;
+
+ if (score)
+ *score = len;
+
+ return point;
+ }
+
+ if (shape_r)
+ *shape_r = NULL;
+
+ if (spline_r)
+ *spline_r = NULL;
+
+ if (is_handle_r)
+ *is_handle_r = FALSE;
+
+ return NULL;
+}
+
+static int find_nearest_feather(bContext *C, Mask *mask, float normal_co[2], int threshold,
+ MaskShape **shape_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
+ MaskSplinePointUW **uw_r, float *score)
+{
+ MaskShape *shape, *point_shape = NULL;
+ MaskSpline *point_spline = NULL;
+ MaskSplinePoint *point = NULL;
+ MaskSplinePointUW *uw = NULL;
+ float len = FLT_MAX, co[2];
+ float scalex, scaley, aspx, aspy;
+ int width, height;
+
+ ED_mask_size(C, &width, &height);
+ ED_mask_aspect(C, &aspx, &aspy);
+ ED_mask_pixelspace_factor(C, &scalex, &scaley);
+
+ co[0] = normal_co[0] * scalex;
+ co[1] = normal_co[1] * scaley;
+
+ shape = mask->shapes.first;
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ int i, tot_feather_point;
+ float *feather_points, *fp;
+
+ feather_points = fp = BKE_mask_spline_feather_points(spline, aspx, aspy, &tot_feather_point);
+
+ for (i = 0; i < spline->tot_point; i++) {
+ int j;
+ MaskSplinePoint *cur_point = &spline->points[i];
+
+ for (j = 0; j < cur_point->tot_uw + 1; j++) {
+ float cur_len, vec[2];
+
+ vec[0] = fp[0] * scalex;
+ vec[1] = fp[1] * scaley;
+
+ cur_len = len_v2v2(vec, co);
+
+ if (point == NULL || cur_len < len) {
+ if (j == 0)
+ uw = NULL;
+ else
+ uw = &cur_point->uw[j - 1];
+
+ point_shape = shape;
+ point_spline = spline;
+ point = cur_point;
+ len = cur_len;
+ }
+
+ fp += 2;
+ }
+ }
+
+ MEM_freeN(feather_points);
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+
+ if (len < threshold) {
+ if (shape_r)
+ *shape_r = point_shape;
+
+ if (spline_r)
+ *spline_r = point_spline;
+
+ if (point_r)
+ *point_r = point;
+
+ if (uw_r)
+ *uw_r = uw;
+
+ if (score)
+ *score = len;
+
+ return TRUE;
+ }
+
+ if (shape_r)
+ *shape_r = NULL;
+
+ if (spline_r)
+ *spline_r = NULL;
+
+ if (point_r)
+ *point_r = NULL;
+
+ return FALSE;
+}
+
+static int find_nearest_diff_point(bContext *C, Mask *mask, float normal_co[2], int threshold, int feather,
+ MaskShape **shape_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
+ float *u_r, float tangent[2])
+{
+ MaskShape *shape, *point_shape;
+ MaskSpline *point_spline;
+ MaskSplinePoint *point = NULL;
+ float dist, co[2];
+ int width, height;
+ float u;
+ float scalex, scaley, aspx, aspy;
+
+ ED_mask_size(C, &width, &height);
+ ED_mask_aspect(C, &aspx, &aspy);
+ ED_mask_pixelspace_factor(C, &scalex, &scaley);
+
+ co[0] = normal_co[0] * scalex;
+ co[1] = normal_co[1] * scaley;
+
+ shape = mask->shapes.first;
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ int i;
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *cur_point = &spline->points[i];
+ float *diff_points;
+ int tot_diff_point;
+
+ diff_points = BKE_mask_point_segment_diff(spline, cur_point, &tot_diff_point);
+
+ if (diff_points) {
+ int i, tot_feather_point, tot_point;
+ float *feather_points = NULL, *points;
+
+ if (feather) {
+ feather_points = BKE_mask_point_segment_feather_diff(spline, cur_point,
+ aspx, aspy, &tot_feather_point);
+
+ points = feather_points;
+ tot_point = tot_feather_point;
+ }
+ else {
+ points = diff_points;
+ tot_point = tot_diff_point;
+ }
+
+ for (i = 0; i < tot_point - 1; i++) {
+ float cur_dist, a[2], b[2];
+
+ a[0] = points[2 * i] * scalex;
+ a[1] = points[2 * i + 1] * scaley;
+
+ b[0] = points[2 * i + 2] * scalex;
+ b[1] = points[2 * i + 3] * scaley;
+
+ cur_dist = dist_to_line_segment_v2(co, a, b);
+
+ if (point == NULL || cur_dist < dist) {
+ if (tangent)
+ sub_v2_v2v2(tangent, &diff_points[2 * i + 2], &diff_points[2 * i]);
+
+ point_shape = shape;
+ point_spline = spline;
+ point = cur_point;
+ dist = cur_dist;
+ u = (float)i / tot_point;
+
+ }
+ }
+
+ if (feather_points)
+ MEM_freeN(feather_points);
+ }
+
+ MEM_freeN(diff_points);
+ }
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+
+ if (point && dist < threshold) {
+ if (shape_r)
+ *shape_r = point_shape;
+
+ if (spline_r)
+ *spline_r = point_spline;
+
+ if (point_r)
+ *point_r = point;
+
+ if (u_r) {
+ u = projection_on_spline(point_spline, point, aspx, aspy, u, normal_co);
+
+ *u_r = u;
+ }
+
+ return TRUE;
+ }
+
+ if (shape_r)
+ *shape_r = NULL;
+
+ if (spline_r)
+ *spline_r = NULL;
+
+ if (point_r)
+ *point_r = NULL;
+
+ return FALSE;
+}
+
+static void mask_flush_selection(Mask *mask)
+{
+ MaskShape *shape;
+
+ shape = mask->shapes.first;
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ int i;
+
+ spline->flag &= ~SELECT;
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *cur_point = &spline->points[i];
+
+ if (MASKPOINT_ISSEL(cur_point)) {
+ spline->flag |= SELECT;
+ }
+ else {
+ int j;
+
+ for (j = 0; j < cur_point->tot_uw; j++) {
+ if (cur_point->uw[j].flag & SELECT) {
+ spline->flag |= SELECT;
+ break;
+ }
+ }
+ }
+ }
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+}
+
+/******************** create new mask *********************/
+
+static int mask_new_exec(bContext *C, wmOperator *op)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+ Mask *mask;
+ char name[MAX_ID_NAME-2];
+
+ RNA_string_get(op->ptr, "name", name);
+
+ mask = BKE_mask_new(name);
+
+ if (sc)
+ ED_space_clip_set_mask(C, sc, mask);
+
+ return OPERATOR_FINISHED;
+}
+
+void MASK_OT_new(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "New Mask";
+ ot->description = "Create new mask";
+ ot->idname = "MASK_OT_new";
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* api callbacks */
+ ot->exec = mask_new_exec;
+ ot->poll = ED_operator_mask;
+
+ /* properties */
+ RNA_def_string(ot->srna, "name", "", MAX_ID_NAME-2, "Name", "Name of new mask");
+}
+
+/******************** create new shape *********************/
+
+static int shape_new_exec(bContext *C, wmOperator *op)
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ char name[MAX_ID_NAME-2];
+
+ RNA_string_get(op->ptr, "name", name);
+
+ BKE_mask_shape_new(mask, name);
+ mask->shapenr = mask->tot_shape - 1;
+
+ WM_event_add_notifier(C, NC_MASK|NA_EDITED, mask);
+
+ return OPERATOR_FINISHED;
+}
+
+void MASK_OT_shape_new(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Shape";
+ ot->description = "Add new shape for masking";
+ ot->idname = "MASK_OT_shape_new";
+
+ /* api callbacks */
+ ot->exec = shape_new_exec;
+ ot->poll = ED_maskediting_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_string(ot->srna, "name", "", MAX_ID_NAME-2, "Name", "Name of new shape");
+}
+
+/******************** remove shape *********************/
+
+static int shape_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ MaskShape *shape = BKE_mask_shape_active(mask);
+
+ if (shape) {
+ BKE_mask_shape_remove(mask, shape);
+
+ WM_event_add_notifier(C, NC_MASK|NA_EDITED, mask);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void MASK_OT_shape_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Shape";
+ ot->description = "Remove shape used for masking";
+ ot->idname = "MASK_OT_shape_remove";
+
+ /* api callbacks */
+ ot->exec = shape_remove_exec;
+ ot->poll = ED_maskediting_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************** slide *********************/
+
+#define SLIDE_ACTION_NONE 0
+#define SLIDE_ACTION_POINT 1
+#define SLIDE_ACTION_HANDLE 2
+#define SLIDE_ACTION_FEATHER 3
+
+typedef struct SlidePointData {
+ int action;
+
+ float co[2];
+ float vec[3][3];
+
+ Mask *mask;
+ MaskShape *shape;
+ MaskSpline *spline;
+ MaskSplinePoint *point;
+ MaskSplinePointUW *uw;
+ float handle[2], no[2], feather[2];
+ float aspx, aspy;
+ int width, height;
+ float weight;
+
+ short curvature_only, accurate;
+} SlidePointData;
+
+static void *slide_point_customdata(bContext *C, wmOperator *op, wmEvent *event)
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ SlidePointData *customdata = NULL;
+ MaskShape *shape, *cv_shape, *feather_shape;
+ MaskSpline *spline, *cv_spline, *feather_spline;
+ MaskSplinePoint *point, *cv_point, *feather_point;
+ MaskSplinePointUW *uw = NULL;
+ int is_handle = FALSE, width, height, action = SLIDE_ACTION_NONE;
+ int slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
+ float co[2], cv_score, feather_score;
+ const float threshold = 19;
+
+ ED_mask_mouse_pos(C, event, co);
+ ED_mask_size(C, &width, &height);
+
+ cv_point = find_nearest_point(C, mask, co, threshold, &cv_shape, &cv_spline, &is_handle, &cv_score);
+
+ if (find_nearest_feather(C, mask, co, threshold, &feather_shape, &feather_spline, &feather_point, &uw, &feather_score)) {
+ if (slide_feather || !cv_point || feather_score < cv_score) {
+ action = SLIDE_ACTION_FEATHER;
+
+ shape = feather_shape;
+ spline = feather_spline;
+ point = feather_point;
+ }
+ }
+
+ if (cv_point && action == SLIDE_ACTION_NONE) {
+ if (is_handle)
+ action = SLIDE_ACTION_HANDLE;
+ else
+ action = SLIDE_ACTION_POINT;
+
+ shape = cv_shape;
+ spline = cv_spline;
+ point = cv_point;
+ }
+
+ if (action != SLIDE_ACTION_NONE) {
+ customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
+
+ customdata->mask = mask;
+ customdata->shape = shape;
+ customdata->spline = spline;
+ customdata->point = point;
+ customdata->width = width;
+ customdata->height = height;
+ customdata->action = action;
+ customdata->uw = uw;
+
+ ED_mask_aspect(C, &customdata->aspx, &customdata->aspy);
+
+ if (uw) {
+ float co[2];
+
+ customdata->weight = point->bezt.weight;
+
+ customdata->weight = uw->w;
+ BKE_mask_point_segment_co(spline, point, uw->u, co);
+ BKE_mask_point_normal(spline, point, customdata->aspx, customdata->aspy, uw->u, customdata->no);
+
+ customdata->feather[0] = co[0] + customdata->no[0] * uw->w;
+ customdata->feather[1] = co[1] + customdata->no[1] * uw->w;
+ }
+ else {
+ BezTriple *bezt = &point->bezt;
+ BKE_mask_point_normal(spline, point, customdata->aspx, customdata->aspy, 0.0f, customdata->no);
+
+ customdata->feather[0] = bezt->vec[1][0] + customdata->no[0] * bezt->weight;
+ customdata->feather[1] = bezt->vec[1][1] + customdata->no[1] * bezt->weight;
+ }
+
+ copy_m3_m3(customdata->vec, point->bezt.vec);
+ if (BKE_mask_point_has_handle(point))
+ BKE_mask_point_handle(point, customdata->aspx, customdata->aspy, customdata->handle);
+ ED_mask_mouse_pos(C, event, customdata->co);
+ }
+
+ return customdata;
+}
+
+static int slide_point_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ SlidePointData *slidedata = slide_point_customdata(C, op, event);
+
+ if (slidedata) {
+ Mask *mask = CTX_data_edit_mask(C);
+
+ op->customdata = slidedata;
+
+ WM_event_add_modal_handler(C, op);
+
+ if (slidedata->uw) {
+ if ((slidedata->uw->flag & SELECT) == 0) {
+ toggle_selection_all(mask, SEL_DESELECT);
+
+ slidedata->uw->flag |= SELECT;
+
+ mask_flush_selection(mask);
+ }
+ }
+ else if (!MASKPOINT_ISSEL(slidedata->point)) {
+ toggle_selection_all(mask, SEL_DESELECT);
+
+ spline_point_select(slidedata->point, SEL_SELECT);
+
+ mask_flush_selection(mask);
+ }
+
+ slidedata->shape->act_spline = slidedata->spline;
+ slidedata->shape->act_point = slidedata->point;
+
+ WM_event_add_notifier(C, NC_MASK|ND_SELECT, mask);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ return OPERATOR_PASS_THROUGH;
+}
+
+static void cancel_slide_point(SlidePointData *data)
+{
+ /* cancel sliding */
+ if (data->action == SLIDE_ACTION_FEATHER) {
+ if (data->uw)
+ data->uw->w = data->weight;
+ else
+ data->point->bezt.weight = data->weight;
+ }
+ else {
+ copy_m3_m3(data->point->bezt.vec, data->vec);
+ }
+}
+
+static void free_slide_point_data(SlidePointData *data)
+{
+ MEM_freeN(data);
+}
+
+static int slide_point_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ SlidePointData *data = (SlidePointData *)op->customdata;
+ BezTriple *bezt = &data->point->bezt;
+ float co[2], dco[2];
+
+ switch(event->type) {
+ case LEFTCTRLKEY:
+ case RIGHTCTRLKEY:
+ case LEFTSHIFTKEY:
+ case RIGHTSHIFTKEY:
+ if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY))
+ data->curvature_only = event->val==KM_PRESS;
+
+ if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
+ data->accurate = event->val==KM_PRESS;
+
+ /* no break! update CV position */
+
+ case MOUSEMOVE:
+ ED_mask_mouse_pos(C, event, co);
+ sub_v2_v2v2(dco, co, data->co);
+
+ if (data->action == SLIDE_ACTION_HANDLE) {
+ float delta[2], offco[2];
+
+ sub_v2_v2v2(delta, data->handle, data->co);
+
+ sub_v2_v2v2(offco, co, data->co);
+ if (data->accurate)
+ mul_v2_fl(offco, 0.2f);
+ add_v2_v2(offco, data->co);
+ add_v2_v2(offco, delta);
+
+ BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->aspx, data->aspy, data->handle, data->vec);
+ }
+ else if (data->action == SLIDE_ACTION_POINT) {
+ float delta[2];
+
+ copy_v2_v2(delta, dco);
+ if (data->accurate)
+ mul_v2_fl(delta, 0.2f);
+
+ add_v2_v2v2(bezt->vec[0], data->vec[0], delta);
+ add_v2_v2v2(bezt->vec[1], data->vec[1], delta);
+ add_v2_v2v2(bezt->vec[2], data->vec[2], delta);
+ }
+ else if (data->action == SLIDE_ACTION_FEATHER) {
+ float vec[2], no[2], p[2], c[2], w, offco[2];
+ float *weight;
+
+ add_v2_v2v2(offco, data->feather, dco);
+
+ if (data->uw) {
+ float u = projection_on_spline(data->spline, data->point, data->aspx, data->aspy, data->uw->u, offco);
+
+ if (u > 0.0f && u < 1.0f)
+ data->uw->u = u;
+
+ data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
+ weight = &data->uw->w;
+ BKE_mask_point_normal(data->spline, data->point, data->aspx, data->aspy, data->uw->u, no);
+ BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
+ }
+ else {
+ weight = &bezt->weight;
+ copy_v2_v2(no, data->no);
+ copy_v2_v2(p, bezt->vec[1]);
+ }
+
+ sub_v2_v2v2(c, offco, p);
+ project_v2_v2v2(vec, c, no);
+
+ vec[0] *= data->aspx;
+ vec[1] *= data->aspy;
+
+ w = len_v2(vec);
+
+ if (dot_v2v2(no, vec) > 0.0f)
+ *weight = w;
+ else
+ *weight = 0;
+ }
+
+ WM_event_add_notifier(C, NC_MASK|NA_EDITED, data->mask);
+ DAG_id_tag_update(&data->mask->id, 0);
+
+ break;
+
+ case LEFTMOUSE:
+ if(event->val==KM_RELEASE) {
+ free_slide_point_data(op->customdata);
+
+ WM_event_add_notifier(C, NC_MASK|NA_EDITED, data->mask);
+ DAG_id_tag_update(&data->mask->id, 0);
+
+ return OPERATOR_FINISHED;
+ }
+
+ break;
+
+ case ESCKEY:
+ cancel_slide_point(op->customdata);
+
+ free_slide_point_data(op->customdata);
+
+ WM_event_add_notifier(C, NC_MASK|NA_EDITED, data->mask);
+ DAG_id_tag_update(&data->mask->id, 0);
+
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void MASK_OT_slide_point(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Slide Point";
+ ot->description = "Slide control points";
+ ot->idname = "MASK_OT_slide_point";
+
+ /* api callbacks */
+ ot->invoke = slide_point_invoke;
+ ot->modal = slide_point_modal;
+ ot->poll = ED_maskediting_mask_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide slide feather instead of vertex");
+}
+
+/******************** toggle selection *********************/
+
+static int select_all_exec(bContext *C, wmOperator *op)
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ int action = RNA_enum_get(op->ptr, "action");
+
+ toggle_selection_all(mask, action);
+ mask_flush_selection(mask);
+
+ WM_event_add_notifier(C, NC_MASK|ND_SELECT, mask);
+
+ return OPERATOR_FINISHED;
+}
+
+void MASK_OT_select_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select or Deselect 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_maskediting_mask_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_select_all(ot);
+}
+
+/******************** select *********************/
+
+static int select_exec(bContext *C, wmOperator *op)
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ MaskShape *shape;
+ MaskSpline *spline;
+ MaskSplinePoint *point = NULL;
+ float co[2];
+ int extend = RNA_boolean_get(op->ptr, "extend");
+ int is_handle = 0;
+ const float threshold = 19;
+
+ RNA_float_get_array(op->ptr, "location", co);
+
+ point = find_nearest_point(C, mask, co, threshold, &shape, &spline, &is_handle, NULL);
+
+ if (point) {
+ if (!extend)
+ toggle_selection_all(mask, SEL_DESELECT);
+
+ if (is_handle) {
+ MASKPOINT_HANDLE_SEL(point);
+ }
+ else {
+ spline_point_select(point, SEL_SELECT);
+ }
+
+ shape->act_spline = spline;
+ shape->act_point = point;
+
+ mask_flush_selection(mask);
+
+ WM_event_add_notifier(C, NC_MASK|ND_SELECT, mask);
+ }
+ else {
+ MaskSplinePointUW *uw;
+
+ if (find_nearest_feather(C, mask, co, threshold, &shape, &spline, &point, &uw, NULL)) {
+ if (!extend)
+ toggle_selection_all(mask, SEL_DESELECT);
+
+ uw->flag |= SELECT;
+
+ shape->act_spline = spline;
+ shape->act_point = point;
+
+ mask_flush_selection(mask);
+
+ WM_event_add_notifier(C, NC_MASK|ND_SELECT, mask);
+ }
+ }
+
+ return OPERATOR_PASS_THROUGH;
+}
+
+static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ float co[2];
+
+ ED_mask_mouse_pos(C, event, co);
+
+ RNA_float_set_array(op->ptr, "location", co);
+
+ return select_exec(C, op);
+}
+
+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_maskediting_mask_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "extend", 0,
+ "Extend", "Extend selection rather than clearing the existing selection");
+ RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
+ "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
+}
+
+/******************** add vertex *********************/
+
+static void setup_vertex_point(bContext *C, Mask *mask, MaskSpline *spline, MaskSplinePoint *new_point,
+ float point_co[2], float tangent[2])
+{
+ BezTriple *bezt;
+ int width, height;
+ float co[3];
+ const float len = 20.0; /* default length of handle in pixel space */
+
+ copy_v2_v2(co, point_co);
+ co[2] = 0.0f;
+
+ ED_mask_size(C, &width, &height);
+
+ /* point coordinate */
+ bezt = &new_point->bezt;
+
+ bezt->h1 = bezt->h2 = HD_ALIGN;
+
+ copy_v3_v3(bezt->vec[0], co);
+ copy_v3_v3(bezt->vec[1], co);
+ copy_v3_v3(bezt->vec[2], co);
+
+ /* initial offset for handles */
+ if (spline->tot_point == 1) {
+ /* first point of splien is aligned horizontally */
+ bezt->vec[0][0] -= len / width;
+ bezt->vec[2][0] += len / width;
+ }
+ else if (tangent) {
+ float vec[2];
+
+ copy_v2_v2(vec, tangent);
+
+ vec[0] *= width;
+ vec[1] *= height;
+
+ mul_v2_fl(vec, len / len_v2(vec));
+
+ vec[0] /= width;
+ vec[1] /= height;
+
+ sub_v2_v2(bezt->vec[0], vec);
+ add_v2_v2(bezt->vec[2], vec);
+ } else {
+ /* next points are aligning in the direction of previous/next point */
+ MaskSplinePoint *point;
+ float v1[2], v2[2], vec[2];
+ float dir = 1.0f;
+
+ if (new_point == spline->points) {
+ point = new_point + 1;
+ dir = -1.0f;
+ } else
+ point = new_point - 1;
+
+ if (spline->tot_point < 3) {
+ v1[0] = point->bezt.vec[1][0] * width;
+ v1[1] = point->bezt.vec[1][1] * height;
+
+ v2[0] = new_point->bezt.vec[1][0] * width;
+ v2[1] = new_point->bezt.vec[1][1] * height;
+ }
+ else {
+ if (new_point == spline->points) {
+ v1[0] = spline->points[1].bezt.vec[1][0] * width;
+ v1[1] = spline->points[1].bezt.vec[1][1] * height;
+
+ v2[0] = spline->points[spline->tot_point - 1].bezt.vec[1][0] * width;
+ v2[1] = spline->points[spline->tot_point - 1].bezt.vec[1][1] * height;
+ }
+ else {
+ v1[0] = spline->points[0].bezt.vec[1][0] * width;
+ v1[1] = spline->points[0].bezt.vec[1][1] * height;
+
+ v2[0] = spline->points[spline->tot_point - 2].bezt.vec[1][0] * width;
+ v2[1] = spline->points[spline->tot_point - 2].bezt.vec[1][1] * height;
+ }
+ }
+
+ sub_v2_v2v2(vec, v1, v2);
+ mul_v2_fl(vec, len * dir / len_v2(vec));
+
+ vec[0] /= width;
+ vec[1] /= height;
+
+ add_v2_v2(bezt->vec[0], vec);
+ sub_v2_v2(bezt->vec[2], vec);
+ }
+
+ BKE_mask_parent_init(&new_point->parent);
+
+ /* select new point */
+ MASKPOINT_SEL(new_point);
+ mask_flush_selection(mask);
+}
+
+/* **** add subdivide vertex **** */
+
+static int add_vertex_subdivide(bContext *C, Mask *mask, float co[2])
+{
+ MaskShape *shape;
+ MaskSpline *spline;
+ MaskSplinePoint *point = NULL;
+ const float threshold = 9;
+ float tangent[2];
+
+ if (find_nearest_diff_point(C, mask, co, threshold, FALSE, &shape, &spline, &point, NULL, tangent)) {
+ MaskSplinePoint *new_point_array, *new_point;
+ int point_index = point - spline->points;
+
+ toggle_selection_all(mask, SEL_DESELECT);
+
+ new_point_array = MEM_callocN(sizeof(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_freeN(spline->points);
+ spline->points = new_point_array;
+ spline->tot_point++;
+
+ new_point = &new_point_array[point_index + 1];
+
+ setup_vertex_point(C, mask, spline, new_point, co, tangent);
+
+ shape->act_point = new_point;
+
+ WM_event_add_notifier(C, NC_MASK|NA_EDITED, mask);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* **** add extrude vertex **** */
+
+static void finSelectedSplinePoint(MaskShape *shape, MaskSpline **spline, MaskSplinePoint **point)
+{
+ MaskSpline *cur_spline = shape->splines.first;
+
+ *spline = NULL;
+ *point = NULL;
+
+ while (cur_spline) {
+ int i;
+
+ for (i = 0; i < cur_spline->tot_point; i++) {
+ MaskSplinePoint *cur_point = &cur_spline->points[i];
+
+ if (MASKPOINT_ISSEL(cur_point)) {
+ if (*spline != NULL && *spline != cur_spline) {
+ *spline = NULL;
+ *point = NULL;
+ return;
+ }
+ else if (*point) {
+ *point = NULL;
+ }
+ else {
+ *spline = cur_spline;
+ *point = cur_point;
+ }
+ }
+ }
+
+ cur_spline = cur_spline->next;
+ }
+}
+
+static int add_vertex_extrude(bContext *C, Mask *mask, float co[2])
+{
+ MaskShape *shape;
+ MaskSpline *spline;
+ MaskSplinePoint *point, *new_point = NULL;
+
+ shape = BKE_mask_shape_active(mask);
+
+ if (!shape) {
+ /* if there's no shape currently operationg on, create new one */
+ shape = BKE_mask_shape_new(mask, "");
+ mask->shapenr = mask->tot_shape - 1;
+ spline = NULL;
+ point = NULL;
+ }
+ else {
+ finSelectedSplinePoint(shape, &spline, &point);
+ }
+
+ if (!spline) {
+ /* no selected splines in actuve shape, create new spline */
+ spline = BKE_mask_spline_add(shape);
+ shape->act_spline = spline;
+ new_point = spline->points;
+ }
+
+ if (!new_point) {
+ MaskSplinePoint *new_point_array;
+
+ if (point == &spline->points[spline->tot_point - 1]) {
+ MASKPOINT_DESEL(point);
+ new_point_array = MEM_callocN(sizeof(MaskSplinePoint) * (spline->tot_point + 1), "add mask vert points");
+ memcpy(new_point_array, spline->points, sizeof(MaskSplinePoint) * spline->tot_point);
+ MEM_freeN(spline->points);
+ spline->points = new_point_array;
+ spline->tot_point++;
+
+ new_point = &spline->points[spline->tot_point - 1];
+ }
+ else if (point == &spline->points[0]) {
+ MASKPOINT_DESEL(point);
+ new_point_array = MEM_callocN(sizeof(MaskSplinePoint) * (spline->tot_point + 1), "add mask vert points");
+ memcpy(new_point_array + 1, spline->points, sizeof(MaskSplinePoint) * spline->tot_point);
+ MEM_freeN(spline->points);
+ spline->points = new_point_array;
+ spline->tot_point++;
+
+ new_point = &spline->points[0];
+ }
+ else {
+ spline = BKE_mask_spline_add(shape);
+ shape->act_spline = spline;
+ new_point = spline->points;
+ }
+ }
+
+ shape->act_point = new_point;
+
+ setup_vertex_point(C, mask, spline, new_point, co, NULL);
+ WM_event_add_notifier(C, NC_MASK|NA_EDITED, mask);
+
+ return TRUE;
+}
+
+static int add_vertex_exec(bContext *C, wmOperator *op)
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ float co[2];
+
+ RNA_float_get_array(op->ptr, "location", co);
+
+ if (!add_vertex_subdivide(C, mask, co)) {
+ if (!add_vertex_extrude(C, mask, co))
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static int add_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ float co[2];
+
+ ED_mask_mouse_pos(C, event, 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_maskediting_mask_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
+ "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
+}
+
+/******************** add feather vertex *********************/
+
+static int add_feather_vertex_exec(bContext *C, wmOperator *op)
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ MaskShape *shape;
+ MaskSpline *spline;
+ MaskSplinePoint *point = NULL;
+ const float threshold = 9;
+ float co[2], u;
+
+ RNA_float_get_array(op->ptr, "location", co);
+
+ point = find_nearest_point(C, mask, co, threshold, NULL, NULL, NULL, NULL);
+ if (point)
+ return OPERATOR_FINISHED;
+
+ if (find_nearest_diff_point(C, mask, co, threshold, TRUE, &shape, &spline, &point, &u, NULL)) {
+ float w = BKE_mask_point_weight(spline, point, u);
+
+ BKE_mask_point_add_uw(point, u, w);
+
+ WM_event_add_notifier(C, NC_MASK|NA_EDITED, mask);
+
+ return OPERATOR_FINISHED;
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+static int add_feather_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ float co[2];
+
+ ED_mask_mouse_pos(C, event, 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_maskediting_mask_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
+ "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
+}
+
+/******************** toggle cyclic *********************/
+
+static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ MaskShape *shape = mask->shapes.first;
+
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ if (points_has_selection(spline->points, spline->tot_point))
+ spline->flag ^= MASK_SPLINE_CYCLIC;
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+
+ WM_event_add_notifier(C, NC_MASK|NA_EDITED, mask);
+
+ return OPERATOR_FINISHED;
+}
+
+void MASK_OT_cyclic_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Toggle Cyclic";
+ ot->description = "Toggle cyclic for selected splines";
+ ot->idname = "MASK_OT_cyclic_toggle";
+
+ /* api callbacks */
+ ot->exec = cyclic_toggle_exec;
+ ot->poll = ED_maskediting_mask_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************** delete *********************/
+
+static void delete_feather_points(MaskSplinePoint *point)
+{
+ int i, count = 0;
+
+ if (!point->tot_uw)
+ return;
+
+ for (i = 0; i < point->tot_uw; i++) {
+ if ((point->uw[i].flag & SELECT) == 0)
+ count++;
+ }
+
+ if (count == 0) {
+ MEM_freeN(point->uw);
+ point->uw = NULL;
+ point->tot_uw = 0;
+ }
+ else {
+ MaskSplinePointUW *new_uw;
+ int j = 0;
+
+ new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
+
+ for (i = 0; i < point->tot_uw; i++) {
+ if ((point->uw[i].flag & SELECT) == 0) {
+ new_uw[j++] = point->uw[i];
+ }
+ }
+
+ MEM_freeN(point->uw);
+
+ point->uw = new_uw;
+ point->tot_uw = count;
+ }
+}
+
+static int delete_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ MaskShape *shape = mask->shapes.first;
+
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ int i, count = 0;
+ MaskSpline *next_spline = spline->next;
+
+ /* count unselected points */
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ if (!MASKPOINT_ISSEL(point))
+ count++;
+ }
+
+ if (count == 0) {
+ /* delete the whole spline */
+ BLI_remlink(&shape->splines, spline);
+ BKE_mask_spline_free(spline);
+
+ if (spline == shape->act_spline) {
+ shape->act_spline = NULL;
+ shape->act_point = NULL;
+ }
+ }
+ else {
+ MaskSplinePoint *new_points;
+ int j;
+
+ new_points = MEM_callocN(count*sizeof(MaskSplinePoint), "deleteMaskPoints");
+
+ for (i = 0, j = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ if (!MASKPOINT_ISSEL(point)) {
+ if (point == shape->act_point)
+ shape->act_point = &new_points[j];
+
+ delete_feather_points(point);
+
+ new_points[j] = *point;
+ j++;
+ }
+ else {
+ if (point == shape->act_point)
+ shape->act_point = NULL;
+
+ BKE_mask_point_free(point);
+ }
+ }
+
+ MEM_freeN(spline->points);
+ spline->points = new_points;
+ spline->tot_point = j;
+
+ mask_flush_selection(mask);
+ }
+
+ spline = next_spline;
+ }
+
+ shape = shape->next;
+ }
+
+ WM_event_add_notifier(C, NC_MASK|NA_EDITED, mask);
+
+ return OPERATOR_FINISHED;
+}
+
+void MASK_OT_delete(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Delete";
+ ot->description = "Delete selected control points or splines";
+ ot->idname = "MASK_OT_delete";
+
+ /* api callbacks */
+ ot->invoke = WM_operator_confirm;
+ ot->exec = delete_exec;
+ ot->poll = ED_maskediting_mask_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************** set handle type *********************/
+
+static int set_handle_type_exec(bContext *C, wmOperator *op)
+{
+ Mask *mask = CTX_data_edit_mask(C);
+ MaskShape *shape = mask->shapes.first;
+ int handle_type = RNA_enum_get(op->ptr, "type");
+
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+ int i;
+
+ while (spline) {
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ if (MASKPOINT_ISSEL(point)) {
+ BezTriple *bezt = &point->bezt;
+
+ bezt->h1 = bezt->h2 = handle_type;
+ }
+ }
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+
+ WM_event_add_notifier(C, NC_MASK|ND_DATA, mask);
+ DAG_id_tag_update(&mask->id, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void MASK_OT_handle_type_set(wmOperatorType *ot)
+{
+ static EnumPropertyItem editcurve_handle_type_items[]= {
+ {HD_AUTO, "AUTO", 0, "Auto", ""},
+ {HD_VECT, "VECTOR", 0, "Vector", ""},
+ {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
+ {0, NULL, 0, NULL, NULL}};
+
+ /* identifiers */
+ ot->name = "Set Handle Type";
+ ot->description = "Set type of handles for selected control points";
+ ot->idname = "MASK_OT_handle_type_set";
+
+ /* api callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = set_handle_type_exec;
+ ot->poll = ED_maskediting_mask_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
+}
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 49672b77d43..e6fa4d43851 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -69,6 +69,7 @@
#include "ED_screen_types.h"
#include "ED_keyframes_draw.h"
#include "ED_view3d.h"
+#include "ED_clip.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -449,6 +450,13 @@ int ED_operator_editmball(bContext *C)
return 0;
}
+int ED_operator_mask(bContext *C)
+{
+ SpaceClip *sc= CTX_wm_space_clip(C);
+
+ return ED_space_clip_show_maskedit(sc);
+}
+
/* *************************** action zone operator ************************** */
/* operator state vars used:
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index 31243184961..6733d95ad71 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -62,6 +62,7 @@
#include "ED_mball.h"
#include "ED_logic.h"
#include "ED_clip.h"
+#include "ED_mask.h"
/* only call once on startup, storage is global in BKE kernel listbase */
void ED_spacetypes_init(void)
@@ -111,6 +112,7 @@ void ED_spacetypes_init(void)
ED_operatortypes_sound();
ED_operatortypes_render();
ED_operatortypes_logic();
+ ED_operatortypes_mask();
UI_view2d_operatortypes();
UI_buttons_operatortypes();
@@ -133,6 +135,7 @@ void ED_spacetypes_init(void)
ED_operatormacros_action();
ED_operatormacros_clip();
ED_operatormacros_curve();
+ ED_operatormacros_mask();
/* register dropboxes (can use macros) */
spacetypes = BKE_spacetypes_list();
@@ -164,6 +167,7 @@ void ED_spacetypes_keymap(wmKeyConfig *keyconf)
ED_keymap_physics(keyconf);
ED_keymap_metaball(keyconf);
ED_keymap_paint(keyconf);
+ ED_keymap_mask(keyconf);
ED_marker_keymap(keyconf);
UI_view2d_keymap(keyconf);
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
index e61580295fc..2e7de318299 100644
--- a/source/blender/editors/space_clip/clip_editor.c
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -34,10 +34,12 @@
#include "MEM_guardedalloc.h"
#include "BKE_main.h"
+#include "BKE_mask.h"
#include "BKE_movieclip.h"
#include "BKE_context.h"
#include "BKE_tracking.h"
+#include "DNA_mask_types.h"
#include "DNA_object_types.h" /* SELECT */
#include "BLI_utildefines.h"
@@ -116,6 +118,32 @@ int ED_space_clip_tracking_frame_poll(bContext *C)
return FALSE;
}
+int ED_space_clip_maskediting_poll(bContext *C)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+
+ if (sc && sc->clip) {
+ return ED_space_clip_show_maskedit(sc);
+ }
+
+ return FALSE;
+}
+
+int ED_space_clip_maskediting_mask_poll(bContext *C)
+{
+ if (ED_space_clip_maskediting_poll(C)) {
+ MovieClip *clip = CTX_data_edit_movieclip(C);
+
+ if (clip) {
+ SpaceClip *sc= CTX_wm_space_clip(C);
+
+ return sc->mask != NULL;
+ }
+ }
+
+ return FALSE;
+}
+
/* ******** editing functions ******** */
void ED_space_clip_set(bContext *C, bScreen *screen, SpaceClip *sc, MovieClip *clip)
@@ -159,6 +187,11 @@ MovieClip *ED_space_clip(SpaceClip *sc)
return sc->clip;
}
+Mask *ED_space_clip_mask(SpaceClip *sc)
+{
+ return sc->mask;
+}
+
ImBuf *ED_space_clip_get_buffer(SpaceClip *sc)
{
if (sc->clip) {
@@ -203,6 +236,42 @@ void ED_space_clip_size(SpaceClip *sc, int *width, int *height)
}
}
+void ED_space_clip_mask_size(SpaceClip *sc, int *width, int *height)
+{
+ if(!sc->mask) {
+ *width= 0;
+ *height= 0;
+ } else {
+ float aspx, aspy;
+
+ ED_space_clip_size(sc, width, height);
+ ED_space_clip_aspect(sc, &aspx, &aspy);
+
+ *width *= aspx;
+ *height *= aspy;
+ }
+}
+
+void ED_space_clip_mask_aspect(SpaceClip *sc, float *aspx, float *aspy)
+{
+ int w, h;
+
+ ED_space_clip_aspect(sc, aspx, aspy);
+ ED_space_clip_size(sc, &w, &h);
+
+ *aspx *= (float)w;
+ *aspy *= (float)h;
+
+ if(*aspx < *aspy) {
+ *aspy= *aspy / *aspx;
+ *aspx= 1.0f;
+ }
+ else {
+ *aspx= *aspx / *aspy;
+ *aspy= 1.0f;
+ }
+}
+
void ED_space_clip_zoom(SpaceClip *sc, ARegion *ar, float *zoomx, float *zoomy)
{
int width, height;
@@ -557,6 +626,8 @@ void ED_space_clip_free_texture_buffer(SpaceClip *sc)
}
}
+/* ******** masking editing related functions ******** */
+
int ED_space_clip_show_trackedit(SpaceClip *sc)
{
if (sc) {
@@ -573,3 +644,23 @@ void ED_space_clip_update_dopesheet(SpaceClip *sc)
BKE_tracking_update_dopesheet(tracking);
}
+
+int ED_space_clip_show_maskedit(SpaceClip *sc)
+{
+ if (sc) {
+ return sc->mode == SC_MODE_MASKEDITING;
+ }
+
+ return FALSE;
+}
+
+void ED_space_clip_set_mask(bContext *C, SpaceClip *sc, Mask *mask)
+{
+ sc->mask = mask;
+
+ if(sc->mask && sc->mask->id.us==0)
+ sc->clip->id.us = 1;
+
+ if(C)
+ WM_event_add_notifier(C, NC_MASK|NA_SELECTED, mask);
+}
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index 80db32303cb..04b1c92cf8e 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -33,6 +33,7 @@
#include <stdio.h>
#include "DNA_scene_types.h"
+#include "DNA_mask_types.h"
#include "DNA_movieclip_types.h"
#include "MEM_guardedalloc.h"
@@ -49,6 +50,7 @@
#include "IMB_imbuf_types.h"
+#include "ED_mask.h"
#include "ED_screen.h"
#include "ED_clip.h"
#include "ED_transform.h"
@@ -357,6 +359,23 @@ static void clip_listener(ScrArea *sa, wmNotifier *wmn)
break;
}
break;
+ case NC_MASK:
+ switch(wmn->data) {
+ case ND_SELECT:
+ case ND_DATA:
+ ED_area_tag_redraw(sa);
+ break;
+ }
+ switch(wmn->action) {
+ case NA_SELECTED:
+ clip_scopes_tag_refresh(sa);
+ ED_area_tag_redraw(sa);
+ break;
+ case NA_EDITED:
+ ED_area_tag_redraw(sa);
+ break;
+ }
+ break;
case NC_GEOM:
switch (wmn->data) {
case ND_SELECT:
@@ -710,7 +729,7 @@ static void clip_keymap(struct wmKeyConfig *keyconf)
RNA_boolean_set(kmi->ptr, "extend", TRUE); /* toggle */
}
-const char *clip_context_dir[]= {"edit_movieclip", NULL};
+const char *clip_context_dir[]= {"edit_movieclip", "edit_mask", NULL};
static int clip_context(const bContext *C, const char *member, bContextDataResult *result)
{
@@ -724,7 +743,11 @@ static int clip_context(const bContext *C, const char *member, bContextDataResul
else if (CTX_data_equals(member, "edit_movieclip")) {
if (sc->clip)
CTX_data_id_pointer_set(result, &sc->clip->id);
-
+ return TRUE;
+ }
+ else if (CTX_data_equals(member, "edit_mask")) {
+ if (sc->mask)
+ CTX_data_id_pointer_set(result, &sc->mask->id);
return TRUE;
}
@@ -996,6 +1019,9 @@ static void clip_main_area_init(wmWindowManager *wm, ARegion *ar)
keymap = WM_keymap_find(wm->defaultconf, "Clip Editor", SPACE_CLIP, 0);
WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
+
+ keymap= WM_keymap_find(wm->defaultconf, "Mask Editor", 0, 0);
+ WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
}
static void clip_main_area_draw(const bContext *C, ARegion *ar)
@@ -1038,6 +1064,29 @@ static void clip_main_area_draw(const bContext *C, ARegion *ar)
/* Grease Pencil */
clip_draw_grease_pencil((bContext *)C, 1);
+ if(sc->mode == SC_MODE_MASKEDITING) {
+ int x, y;
+ int width, height;
+ float zoomx, zoomy, aspx, aspy;
+
+ /* find window pixel coordinates of origin */
+ UI_view2d_to_region_no_clip(&ar->v2d, 0.0f, 0.0f, &x, &y);
+
+ ED_space_clip_size(sc, &width, &height);
+ ED_space_clip_zoom(sc, ar, &zoomx, &zoomy);
+ ED_space_clip_aspect(sc, &aspx, &aspy);
+
+ /* apply transformation so mask editing tools will assume drawing from the origin in normalized space */
+ glPushMatrix();
+ glTranslatef(x, y, 0);
+ glScalef(width*zoomx, height*zoomy, 0);
+ glMultMatrixf(sc->stabmat);
+
+ ED_mask_draw((bContext *)C, width*aspx, height*aspy, zoomx, zoomy);
+
+ glPopMatrix();
+ }
+
/* reset view matrix */
UI_view2d_view_restore(C);
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index bd17c821567..883eb962c0c 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -1942,6 +1942,11 @@ static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, Po
uiItemR(layout, ptr, "distortion_type", 0, "", 0);
}
+static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *ptr)
+{
+ uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL);
+}
+
/* only once called */
static void node_composit_set_butfunc(bNodeType *ntype)
{
@@ -2108,6 +2113,9 @@ static void node_composit_set_butfunc(bNodeType *ntype)
case CMP_NODE_MOVIEDISTORTION:
ntype->uifunc= node_composit_buts_moviedistortion;
break;
+ case CMP_NODE_MASK:
+ ntype->uifunc= node_composit_buts_mask;
+ break;
default:
ntype->uifunc= NULL;
}
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index 3540c20e515..119b350052e 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -250,6 +250,13 @@ static void node_area_listener(ScrArea *sa, wmNotifier *wmn)
break;
}
break;
+ case NC_MASK:
+ if (wmn->action == NA_EDITED) {
+ if (type==NTREE_COMPOSIT) {
+ ED_area_tag_refresh(sa);
+ }
+ }
+ break;
case NC_IMAGE:
if (wmn->action == NA_EDITED) {
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 6d72ca99678..5fe8444fe65 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -49,6 +49,7 @@
#include "DNA_constraint_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_mask_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_scene_types.h" /* PET modes */
@@ -169,6 +170,13 @@ void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy)
r_vec[0] = (v2d->cur.xmax-v2d->cur.xmin)*(dx)/divx;
r_vec[1] = (v2d->cur.ymax-v2d->cur.ymin)*(dy)/divy;
r_vec[2] = 0.0f;
+
+ if (t->options & CTX_MASK) {
+ float aspx, aspy;
+ ED_space_clip_mask_aspect(t->sa->spacedata.first, &aspx, &aspy);
+ r_vec[0] *= aspx;
+ r_vec[1] *= aspy;
+ }
}
else {
printf("%s: called in an invalid context\n", __func__);
@@ -229,6 +237,19 @@ void projectIntView(TransInfo *t, const float vec[3], int adr[2])
copy_v2_v2(v, vec);
+ if (t->options & CTX_MASK) {
+ float aspx, aspy;
+ ED_space_clip_aspect(t->sa->spacedata.first, &aspx, &aspy);
+ v[0] /= aspx;
+ v[1] /= aspy;
+ }
+ else if (t->options & CTX_MASK) {
+ float aspx, aspy;
+ ED_space_clip_mask_aspect(t->sa->spacedata.first, &aspx, &aspy);
+ v[0] /= aspx;
+ v[1] /= aspy;
+ }
+
UI_view2d_to_region_no_clip(t->view, v[0], v[1], adr, adr+1);
}
}
@@ -279,13 +300,15 @@ void applyAspectRatio(TransInfo *t, float *vec)
vec[1] /= aspy;
}
else if ((t->spacetype==SPACE_CLIP) && (t->mode==TFM_TRANSLATION)) {
- if (t->options & CTX_MOVIECLIP) {
+ if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
SpaceClip *sc = t->sa->spacedata.first;
float aspx, aspy;
int width, height;
- ED_space_clip_size(sc, &width, &height);
- ED_space_clip_aspect(sc, &aspx, &aspy);
+ if (t->options & CTX_MOVIECLIP)
+ ED_space_clip_size(sc, &width, &height);
+ else if (t->options & CTX_MASK)
+ ED_space_clip_aspect(sc, &aspx, &aspy);
vec[0] *= width / aspx;
vec[1] *= height / aspy;
@@ -312,13 +335,15 @@ void removeAspectRatio(TransInfo *t, float *vec)
vec[1] *= aspy;
}
else if ((t->spacetype==SPACE_CLIP) && (t->mode==TFM_TRANSLATION)) {
- if (t->options & CTX_MOVIECLIP) {
+ if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
SpaceClip *sc = t->sa->spacedata.first;
float aspx, aspy;
int width, height;
- ED_space_clip_size(sc, &width, &height);
- ED_space_clip_aspect(sc, &aspx, &aspy);
+ if (t->options & CTX_MOVIECLIP)
+ ED_space_clip_size(sc, &width, &height);
+ else if (t->options & CTX_MASK)
+ ED_space_clip_aspect(sc, &aspx, &aspy);
vec[0] *= aspx / width;
vec[1] *= aspy / height;
@@ -367,12 +392,20 @@ static void viewRedrawForce(const bContext *C, TransInfo *t)
}
else if (t->spacetype==SPACE_CLIP) {
SpaceClip *sc = (SpaceClip*)t->sa->spacedata.first;
- MovieClip *clip = ED_space_clip(sc);
- /* objects could be parented to tracking data, so send this for viewport refresh */
- WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
+ if (ED_space_clip_show_trackedit(sc)) {
+ MovieClip *clip = ED_space_clip(sc);
+
+ /* objects could be parented to tracking data, so send this for viewport refresh */
+ WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
+
+ WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, clip);
+ }
+ else if (ED_space_clip_show_maskedit(sc)) {
+ Mask *mask = ED_space_clip_mask(sc);
- WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, clip);
+ WM_event_add_notifier(C, NC_MASK|NA_EDITED, mask);
+ }
}
}
@@ -654,7 +687,7 @@ int transformEvent(TransInfo *t, wmEvent *event)
t->redraw |= TREDRAW_HARD;
}
else if (t->mode == TFM_TRANSLATION) {
- if(t->options & CTX_MOVIECLIP) {
+ if(t->options & (CTX_MOVIECLIP | CTX_MASK)) {
restoreTransObjects(t);
t->flag ^= T_ALT_TRANSFORM;
@@ -1591,9 +1624,13 @@ int initTransform(bContext *C, TransInfo *t, wmOperator *op, wmEvent *event, int
t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
}
else if (t->spacetype == SPACE_CLIP) {
+ SpaceClip *sc = CTX_wm_space_clip(C);
unit_m3(t->spacemtx);
t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
- t->options |= CTX_MOVIECLIP;
+ if (ED_space_clip_show_trackedit(sc))
+ t->options |= CTX_MOVIECLIP;
+ else if (ED_space_clip_show_maskedit(sc))
+ t->options |= CTX_MASK;
}
else
unit_m3(t->spacemtx);
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 69af8cf2489..3fb86acf697 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -546,6 +546,7 @@ int clipUVTransform(TransInfo *t, float *vec, int resize);
void flushTransNodes(TransInfo *t);
void flushTransSeq(TransInfo *t);
void flushTransTracking(TransInfo *t);
+void flushTransMasking(TransInfo *t);
/*********************** exported from transform_manipulator.c ********** */
int gimbal_axis(struct Object *ob, float gmat[][3]); /* return 0 when no gimbal for selection */
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index ecd69f0d10c..85e970a0441 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -53,6 +53,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_movieclip_types.h"
+#include "DNA_mask_types.h"
#include "MEM_guardedalloc.h"
@@ -87,6 +88,7 @@
#include "BKE_sequencer.h"
#include "BKE_tessmesh.h"
#include "BKE_tracking.h"
+#include "BKE_mask.h"
#include "ED_anim_api.h"
@@ -102,6 +104,7 @@
#include "ED_types.h"
#include "ED_uvedit.h"
#include "ED_clip.h"
+#include "ED_mask.h"
#include "ED_util.h" /* for crazyspace correction */
#include "WM_api.h" /* for WM_event_add_notifier to deal with stabilization nodes */
@@ -4848,6 +4851,17 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL);
}
}
+ else if (t->options & CTX_MASK) {
+ SpaceClip *sc = t->sa->spacedata.first;
+ Mask *mask = ED_space_clip_mask(sc);
+
+ if (t->scene->nodetree) {
+ /* tracks can be used for stabilization nodes,
+ * flush update for such nodes */
+ nodeUpdateID(t->scene->nodetree, &mask->id);
+ WM_event_add_notifier(C, NC_SCENE|ND_NODES, NULL);
+ }
+ }
}
else if (t->spacetype == SPACE_ACTION) {
SpaceAction *saction= (SpaceAction *)t->sa->spacedata.first;
@@ -5773,6 +5787,197 @@ void flushTransTracking(TransInfo *t)
}
}
+/* * masking * */
+
+typedef struct TransDataMasking{
+ float is_handle;
+
+ float handle[2], orig_handle[2];
+ float vec[3][3];
+ MaskSplinePoint *point;
+} TransDataMasking;
+
+static void MaskPointToTransData(SpaceClip *sc, MaskSplinePoint *point, TransData *td, TransData2D *td2d, TransDataMasking *tdm)
+{
+ BezTriple *bezt = &point->bezt;
+ float aspx, aspy;
+
+ tdm->point = point;
+ copy_m3_m3(tdm->vec, bezt->vec);
+
+ ED_space_clip_mask_aspect(sc, &aspx, &aspy);
+
+ if (MASKPOINT_CV_ISSEL(point)) {
+ int i;
+ for (i = 0; i < 3; i++) {
+ /* CV coords are scaled by aspects. this is needed for rotations and
+ * proportional editing to be consistent with the stretched CV coords
+ * that are displayed. this also means that for display and numinput,
+ * and when the the CV coords are flushed, these are converted each time */
+ td2d->loc[0] = bezt->vec[i][0]*aspx;
+ td2d->loc[1] = bezt->vec[i][1]*aspy;
+ td2d->loc[2] = 0.0f;
+ td2d->loc2d = bezt->vec[i];
+
+ td->flag = 0;
+ td->loc = td2d->loc;
+ copy_v3_v3(td->center, td->loc);
+ copy_v3_v3(td->iloc, td->loc);
+
+ memset(td->axismtx, 0, sizeof(td->axismtx));
+ td->axismtx[2][2] = 1.0f;
+
+ td->ext= NULL;
+ td->val= NULL;
+
+ td->flag |= TD_SELECTED;
+ td->dist= 0.0;
+
+ unit_m3(td->mtx);
+ unit_m3(td->smtx);
+
+ td++;
+ td2d++;
+ }
+ }
+ else {
+ int width, height;
+
+ tdm->is_handle = TRUE;
+
+ ED_space_clip_mask_size(sc, &width, &height);
+ BKE_mask_point_handle(point, width, height, tdm->handle);
+
+ copy_v2_v2(tdm->orig_handle, tdm->handle);
+
+ td2d->loc[0] = tdm->handle[0]*aspx;
+ td2d->loc[1] = tdm->handle[1]*aspy;
+ td2d->loc[2] = 0.0f;
+ td2d->loc2d = tdm->handle;
+
+ td->flag = 0;
+ td->loc = td2d->loc;
+ copy_v3_v3(td->center, td->loc);
+ copy_v3_v3(td->iloc, td->loc);
+
+ memset(td->axismtx, 0, sizeof(td->axismtx));
+ td->axismtx[2][2] = 1.0f;
+
+ td->ext= NULL;
+ td->val= NULL;
+
+ td->flag |= TD_SELECTED;
+ td->dist= 0.0;
+
+ unit_m3(td->mtx);
+ unit_m3(td->smtx);
+
+ td++;
+ td2d++;
+ }
+}
+
+static void createTransMaskingData(bContext *C, TransInfo *t)
+{
+ SpaceClip *sc = CTX_wm_space_clip(C);
+ Mask *mask = CTX_data_edit_mask(C);
+ MaskShape *shape;
+ TransData *td = NULL;
+ TransData2D *td2d = NULL;
+ TransDataMasking *tdm = NULL;
+
+ /* count */
+ shape = mask->shapes.first;
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ int i;
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ if (MASKPOINT_ISSEL(point)) {
+ if (MASKPOINT_CV_ISSEL(point))
+ t->total += 3;
+ else
+ t->total += 1;
+ }
+ }
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+
+ if (t->total == 0)
+ return;
+
+ td = t->data = MEM_callocN(t->total*sizeof(TransData), "TransObData(Mask Editing)");
+ /* for each 2d uv coord a 3d vector is allocated, so that they can be
+ * treated just as if they were 3d verts */
+ td2d = t->data2d = MEM_callocN(t->total*sizeof(TransData2D), "TransObData2D(Mask Editing)");
+ tdm = t->customData = MEM_callocN(t->total*sizeof(TransDataMasking), "TransDataMasking(Mask Editing)");
+
+ t->flag |= T_FREE_CUSTOMDATA;
+
+ /* create data */
+ shape = mask->shapes.first;
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ int i;
+
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ if (MASKPOINT_ISSEL(point)) {
+ MaskPointToTransData(sc, point, td, td2d, tdm);
+
+ if (MASKPOINT_CV_ISSEL(point)) {
+ td += 3;
+ td2d += 3;
+ tdm += 3;
+ }
+ else {
+ td++;
+ td2d++;
+ tdm++;
+ }
+ }
+ }
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+}
+
+void flushTransMasking(TransInfo *t)
+{
+ SpaceClip *sc = t->sa->spacedata.first;
+ TransData2D *td;
+ TransDataMasking *tdm;
+ int a;
+ float aspx, aspy, invx, invy;
+
+ ED_space_clip_mask_aspect(sc, &aspx, &aspy);
+ invx = 1.0f/aspx;
+ invy = 1.0f/aspy;
+
+ /* flush to 2d vector from internally used 3d vector */
+ for(a=0, td = t->data2d, tdm = t->customData; a<t->total; a++, td++, tdm++) {
+ td->loc2d[0]= td->loc[0]*invx;
+ td->loc2d[1]= td->loc[1]*invy;
+
+ if (tdm->is_handle)
+ BKE_mask_point_set_handle(tdm->point, td->loc2d, t->flag & T_ALT_TRANSFORM, aspx, aspy, tdm->orig_handle, tdm->vec);
+ }
+}
+
void createTransData(bContext *C, TransInfo *t)
{
Scene *scene = t->scene;
@@ -5842,6 +6047,8 @@ void createTransData(bContext *C, TransInfo *t)
t->flag |= T_POINTS|T_2D_EDIT;
if (t->options & CTX_MOVIECLIP)
createTransTrackingData(C, t);
+ else if (t->options & CTX_MASK)
+ createTransMaskingData(C, t);
}
else if (t->obedit) {
t->ext = NULL;
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 0bf02d1a2bf..7f6cc9ca336 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -49,6 +49,7 @@
#include "DNA_view3d_types.h"
#include "DNA_modifier_types.h"
#include "DNA_movieclip_types.h"
+#include "DNA_mask_types.h"
#include "BLI_math.h"
#include "BLI_blenlib.h"
@@ -637,33 +638,42 @@ static void recalcData_spaceclip(TransInfo *t)
{
SpaceClip *sc = t->sa->spacedata.first;
- MovieClip *clip = ED_space_clip(sc);
- ListBase *tracksbase = BKE_tracking_get_tracks(&clip->tracking);
- MovieTrackingTrack *track;
-
- flushTransTracking(t);
-
- track = tracksbase->first;
- while (track) {
- if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED)==0) {
- if (t->mode == TFM_TRANSLATION) {
- if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT))
- BKE_tracking_clamp_track(track, CLAMP_PAT_POS);
- if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH))
- BKE_tracking_clamp_track(track, CLAMP_SEARCH_POS);
- }
- else if (t->mode == TFM_RESIZE) {
- if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT))
- BKE_tracking_clamp_track(track, CLAMP_PAT_DIM);
- if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH))
- BKE_tracking_clamp_track(track, CLAMP_SEARCH_DIM);
+ if (ED_space_clip_show_trackedit(sc)) {
+ MovieClip *clip = ED_space_clip(sc);
+ ListBase *tracksbase = BKE_tracking_get_tracks(&clip->tracking);
+ MovieTrackingTrack *track;
+
+ flushTransTracking(t);
+
+ track = tracksbase->first;
+ while (track) {
+ if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED)==0) {
+ if (t->mode == TFM_TRANSLATION) {
+ if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT))
+ BKE_tracking_clamp_track(track, CLAMP_PAT_POS);
+ if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH))
+ BKE_tracking_clamp_track(track, CLAMP_SEARCH_POS);
+ }
+ else if (t->mode == TFM_RESIZE) {
+ if (TRACK_AREA_SELECTED(track, TRACK_AREA_PAT))
+ BKE_tracking_clamp_track(track, CLAMP_PAT_DIM);
+ if (TRACK_AREA_SELECTED(track, TRACK_AREA_SEARCH))
+ BKE_tracking_clamp_track(track, CLAMP_SEARCH_DIM);
+ }
}
+
+ track = track->next;
}
- track = track->next;
+ DAG_id_tag_update(&clip->id, 0);
}
+ else if (ED_space_clip_show_maskedit(sc)) {
+ Mask *mask = ED_space_clip_mask(sc);
- DAG_id_tag_update(&clip->id, 0);
+ flushTransMasking(t);
+
+ DAG_id_tag_update(&mask->id, 0);
+ }
}
/* helper for recalcData() - for 3d-view transforms */
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index 83d4a5dfa6e..06355d623eb 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -967,6 +967,7 @@ void transform_keymap_for_space(wmKeyConfig *keyconf, wmKeyMap *keymap, int spac
WM_keymap_add_item(keymap, OP_TRANSLATION, GKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, OP_TRANSLATION, EVT_TWEAK_S, KM_ANY, 0, 0);
WM_keymap_add_item(keymap, OP_RESIZE, SKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, OP_ROTATION, RKEY, KM_PRESS, 0, 0);
break;
default:
break;
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 1737f3b79e6..2c664a7de21 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -207,6 +207,7 @@ typedef struct PreviewImage {
#define ID_GD MAKE_ID2('G', 'D') /* GreasePencil */
#define ID_WM MAKE_ID2('W', 'M') /* WindowManager */
#define ID_MC MAKE_ID2('M', 'C') /* MovieClip */
+#define ID_MSK MAKE_ID2('M', 'S') /* Mask */
/* NOTE! Fake IDs, needed for g.sipo->blocktype or outliner */
#define ID_SEQ MAKE_ID2('S', 'Q')
diff --git a/source/blender/makesdna/DNA_mask_types.h b/source/blender/makesdna/DNA_mask_types.h
new file mode 100644
index 00000000000..7597a21fd03
--- /dev/null
+++ b/source/blender/makesdna/DNA_mask_types.h
@@ -0,0 +1,108 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file DNA_mask_types.h
+ * \ingroup DNA
+ * \since march-2012
+ * \author Sergey Sharybin
+ */
+
+#ifndef __DNA_MASK_TYPES_H__
+#define __DNA_MASK_TYPES_H__
+
+#include "DNA_defs.h"
+#include "DNA_ID.h"
+#include "DNA_listBase.h"
+#include "DNA_curve_types.h"
+
+typedef struct Mask {
+ ID id;
+ struct AnimData *adt;
+ ListBase shapes; /* shapes which defines this mask */
+ int shapenr; /* index of active shape */
+ int tot_shape; /* total number of shapes */
+} Mask;
+
+typedef struct MaskParent {
+ int flag; /* parenting flags */
+ int id_type; /* type of parenting */
+ ID *id; /* ID block of entity to which mask/spline is parented to
+ * in case of parenting to movie tracking data set to MovieClip datablock */
+ char parent[64]; /* entity of parent to which parenting happened
+ * in case of parenting to movie tracking data contains name of object */
+ char sub_parent[64]; /* sub-entity of parent to which parenting happened
+ * in case of parenting to movie tracking data contains name of track */
+ float offset[2]; /* offset from parent position, so object/control point can be parented to a
+ * motion track and also be animated (see ZanQdo's request below) */
+} MaskParent;
+
+typedef struct MaskSplinePointUW {
+ float u, w; /* u coordinate along spline segment and weight of this point */
+ int flag; /* different flags of this point */
+} MaskSplinePointUW;
+
+typedef struct MaskSplinePoint {
+ BezTriple bezt; /* actual point coordinates and it's handles */
+ int pad;
+ int tot_uw; /* number of uv feather values */
+ MaskSplinePointUW *uw; /* feather UV values */
+ MaskParent parent; /* parenting information of particular spline point */
+} MaskSplinePoint;
+
+typedef struct MaskSpline {
+ struct MaskSpline *next, *prev;
+
+ int flag; /* defferent spline flag (closed, ...) */
+ int tot_point; /* total number of points */
+ MaskSplinePoint *points; /* points which defines spline itself */
+ MaskParent parent; /* parenting information of the whole spline */
+
+ int weight_interp, pad; /* weight interpolation */
+} MaskSpline;
+
+typedef struct MaskShape {
+ struct MaskShape *next, *prev;
+
+ char name[64]; /* name of the shape (64 = MAD_ID_NAME - 2) */
+
+ ListBase splines; /* list of splines which defines this shape */
+ struct MaskSpline *act_spline; /* active spline */
+ struct MaskSplinePoint *act_point; /* active point */
+} MaskShape;
+
+/* MaskParent->flag */
+#define MASK_PARENT_ACTIVE (1<<0)
+
+/* MaskSpline->flag */
+#define MASK_SPLINE_CYCLIC (1<<1)
+
+/* MaskSpline->weight_interp */
+#define MASK_SPLINE_INTERP_LINEAR 1
+#define MASK_SPLINE_INTERP_EASE 2
+
+#endif // __DNA_MASK_TYPES_H__
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index c2fbc611bc3..ad13f5047d5 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -67,6 +67,7 @@ struct wmOperator;
struct wmTimer;
struct MovieClip;
struct MovieClipScopes;
+struct Mask;
/**
* The base structure all the other spaces
@@ -523,6 +524,9 @@ typedef struct SpaceClip {
int postproc_flag, pad2;
void *draw_context;
+
+ /* **** mask editing **** */
+ struct Mask *mask;
} SpaceClip;
/* view3d Now in DNA_view3d_types.h */
@@ -910,6 +914,7 @@ enum {
#define SC_MODE_TRACKING 0
#define SC_MODE_RECONSTRUCTION 1
#define SC_MODE_DISTORTION 2
+#define SC_MODE_MASKEDITING 3
/* SpaceClip->view */
#define SC_VIEW_CLIP 0
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index adcdf5df106..d66682b8e0a 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -132,6 +132,7 @@ static const char *includefiles[] = {
"DNA_movieclip_types.h",
"DNA_tracking_types.h",
"DNA_dynamicpaint_types.h",
+ "DNA_mask_types.h",
// empty string to indicate end of includefiles
""
@@ -1239,4 +1240,5 @@ int main(int argc, char ** argv)
#include "DNA_movieclip_types.h"
#include "DNA_tracking_types.h"
#include "DNA_dynamicpaint_types.h"
+#include "DNA_mask_types.h"
/* end of list */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 316ea46d159..de2edbea845 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -145,6 +145,7 @@ extern StructRNA RNA_CompositorNodeLumaMatte;
extern StructRNA RNA_CompositorNodeMapUV;
extern StructRNA RNA_CompositorNodeMapValue;
extern StructRNA RNA_CompositorNodeMath;
+extern StructRNA RNA_CompositorNodeMask;
extern StructRNA RNA_CompositorNodeMixRGB;
extern StructRNA RNA_CompositorNodeNormal;
extern StructRNA RNA_CompositorNodeNormalize;
@@ -304,6 +305,7 @@ extern StructRNA RNA_MaterialStrand;
extern StructRNA RNA_MaterialSubsurfaceScattering;
extern StructRNA RNA_MaterialTextureSlot;
extern StructRNA RNA_MaterialVolume;
+extern StructRNA RNA_Mask;
extern StructRNA RNA_Menu;
extern StructRNA RNA_Mesh;
extern StructRNA RNA_MeshColor;
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 6f8e7656a83..71c03858d00 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -56,6 +56,7 @@ set(DEFSRC
rna_lamp.c
rna_lattice.c
rna_main.c
+ rna_mask.c
rna_material.c
rna_mesh.c
rna_meta.c
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index 62957ac5de8..1a4e8b3d5a7 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -2679,6 +2679,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_world.c", NULL, RNA_def_world},
{"rna_movieclip.c", NULL, RNA_def_movieclip},
{"rna_tracking.c", NULL, RNA_def_tracking},
+ {"rna_mask.c", NULL, RNA_def_mask},
{NULL, NULL}};
static void rna_generate(BlenderRNA *brna, FILE *f, const char *filename, const char *api_filename)
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 6992d992cca..0be95645a07 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -144,6 +144,7 @@ short RNA_type_to_ID_code(StructRNA *type)
if (RNA_struct_is_a(type, &RNA_World)) return ID_WO;
if (RNA_struct_is_a(type, &RNA_WindowManager)) return ID_WM;
if (RNA_struct_is_a(type, &RNA_MovieClip)) return ID_MC;
+ if (RNA_struct_is_a(type, &RNA_Mask)) return ID_MSK;
return 0;
}
@@ -179,6 +180,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_WO: return &RNA_World;
case ID_WM: return &RNA_WindowManager;
case ID_MC: return &RNA_MovieClip;
+ case ID_MSK: return &RNA_Mask;
default: return &RNA_ID;
}
}
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 155c9c67e4a..b52465fce57 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -179,6 +179,7 @@ void RNA_def_wm(struct BlenderRNA *brna);
void RNA_def_world(struct BlenderRNA *brna);
void RNA_def_movieclip(struct BlenderRNA *brna);
void RNA_def_tracking(struct BlenderRNA *brna);
+void RNA_def_mask(struct BlenderRNA *brna);
/* Common Define functions */
@@ -296,6 +297,7 @@ void RNA_def_main_actions(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_particles(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_movieclips(BlenderRNA *brna, PropertyRNA *cprop);
+void RNA_def_main_masks(BlenderRNA *brna, PropertyRNA *cprop);
/* ID Properties */
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index 1a039295924..a6d7f7d484e 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -253,6 +253,12 @@ static void rna_Main_movieclips_begin(CollectionPropertyIterator *iter, PointerR
rna_iterator_listbase_begin(iter, &bmain->movieclip, NULL);
}
+static void rna_Main_masks_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Main *bmain= (Main*)ptr->data;
+ rna_iterator_listbase_begin(iter, &bmain->mask, NULL);
+}
+
#ifdef UNIT_TEST
static PointerRNA rna_Test_test_get(PointerRNA *ptr)
@@ -322,6 +328,7 @@ void RNA_def_main(BlenderRNA *brna)
RNA_def_main_gpencil},
{"movieclips", "MovieClip", "rna_Main_movieclips_begin", "Movie Clips", "Movie Clip datablocks",
RNA_def_main_movieclips},
+ {"masks", "Mask", "rna_Main_masks_begin", "Masks", "Masks datablocks", RNA_def_main_masks},
{NULL, NULL, NULL, NULL, NULL, NULL}};
int i;
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 63006af7c72..8f4d1aa97b5 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -33,6 +33,8 @@
#include <stdio.h>
#include <errno.h>
+#include "DNA_ID.h"
+
#include "RNA_define.h"
#include "RNA_access.h"
#include "RNA_enum_types.h"
@@ -67,6 +69,7 @@
#include "BKE_depsgraph.h"
#include "BKE_speaker.h"
#include "BKE_movieclip.h"
+#include "BKE_mask.h"
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
@@ -87,6 +90,7 @@
#include "DNA_vfont_types.h"
#include "DNA_node_types.h"
#include "DNA_movieclip_types.h"
+#include "DNA_mask_types.h"
#include "ED_screen.h"
@@ -539,6 +543,22 @@ void rna_Main_movieclips_remove(Main *bmain, MovieClip *clip)
/* XXX python now has invalid pointer? */
}
+Mask *rna_Main_mask_new(Main *UNUSED(bmain), const char *name)
+{
+ Mask *mask;
+
+ mask = BKE_mask_new("Mask");
+
+ return mask;
+}
+
+void rna_Main_masks_remove(Main *bmain, Mask *mask)
+{
+ BKE_mask_unlink(bmain, mask);
+ free_libblock(&bmain->mask, mask);
+ /* XXX python now has invalid pointer? */
+}
+
/* tag functions, all the same */
void rna_Main_cameras_tag(Main *bmain, int value) { tag_main_lb(&bmain->camera, value); }
void rna_Main_scenes_tag(Main *bmain, int value) { tag_main_lb(&bmain->scene, value); }
@@ -569,6 +589,7 @@ void rna_Main_actions_tag(Main *bmain, int value) { tag_main_lb(&bmain->action,
void rna_Main_particles_tag(Main *bmain, int value) { tag_main_lb(&bmain->particle, value); }
void rna_Main_gpencil_tag(Main *bmain, int value) { tag_main_lb(&bmain->gpencil, value); }
void rna_Main_movieclips_tag(Main *bmain, int value) { tag_main_lb(&bmain->movieclip, value); }
+void rna_Main_masks_tag(Main *bmain, int value) { tag_main_lb(&bmain->mask, value); }
static int rna_Main_cameras_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_CA); }
static int rna_Main_scenes_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_SCE); }
@@ -1519,4 +1540,34 @@ void RNA_def_main_movieclips(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_return(func, parm);
}
+void RNA_def_main_masks(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "BlendDataMasks");
+ srna= RNA_def_struct(brna, "BlendDataMasks", NULL);
+ RNA_def_struct_sdna(srna, "Main");
+ RNA_def_struct_ui_text(srna, "Main Masks", "Collection of masks");
+
+ func= RNA_def_function(srna, "tag", "rna_Main_masks_tag");
+ parm= RNA_def_boolean(func, "value", 0, "Value", "");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ /* new func */
+ func = RNA_def_function(srna, "new", "rna_Main_mask_new");
+ RNA_def_function_ui_description(func, "Add a new mask with a given name to the main database");
+ parm = RNA_def_string_file_path(func, "name", "", MAX_ID_NAME - 2, "Mask", "Name of new mask datablock");
+ /* return type */
+ parm = RNA_def_pointer(func, "mask", "Mask", "", "New mask datablock");
+ RNA_def_function_return(func, parm);
+
+ /* remove func */
+ func= RNA_def_function(srna, "remove", "rna_Main_masks_remove");
+ RNA_def_function_ui_description(func, "Remove a masks from the current blendfile.");
+ parm= RNA_def_pointer(func, "mask", "Mask", "", "Mask to remove");
+ RNA_def_property_flag(parm, PROP_REQUIRED|PROP_NEVER_NULL);
+}
+
#endif
diff --git a/source/blender/makesrna/intern/rna_mask.c b/source/blender/makesrna/intern/rna_mask.c
new file mode 100644
index 00000000000..3d4a5a39dc2
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_mask.c
@@ -0,0 +1,600 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/makesrna/intern/rna_mask.c
+ * \ingroup RNA
+ */
+
+
+#include <stdlib.h>
+#include <limits.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+
+#include "RNA_define.h"
+
+#include "rna_internal.h"
+
+#include "DNA_mask_types.h"
+#include "DNA_object_types.h" /* SELECT */
+#include "DNA_scene_types.h"
+
+#include "WM_types.h"
+
+#include "IMB_imbuf_types.h"
+#include "IMB_imbuf.h"
+
+#ifdef RNA_RUNTIME
+
+#include "DNA_mask_types.h"
+
+#include "BKE_depsgraph.h"
+#include "BKE_mask.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+
+static void rna_Mask_update_data(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ Mask *mask = ptr->id.data;
+
+ WM_main_add_notifier(NC_MASK|ND_DATA, mask);
+ DAG_id_tag_update( &mask->id, 0);
+}
+
+/* note: this function exists only to avoid id refcounting */
+static void rna_MaskParent_id_set(PointerRNA *ptr, PointerRNA value)
+{
+ MaskParent *mpar = (MaskParent*) ptr->data;
+
+ mpar->id = value.data;
+}
+
+static StructRNA *rna_MaskParent_id_typef(PointerRNA *ptr)
+{
+ MaskParent *mpar = (MaskParent*) ptr->data;
+
+ return ID_code_to_RNA_type(mpar->id_type);
+}
+
+static void rna_MaskParent_id_type_set(PointerRNA *ptr, int value)
+{
+ MaskParent *mpar = (MaskParent*) ptr->data;
+
+ /* change ID-type to the new type */
+ mpar->id_type = value;
+
+ /* clear the id-block if the type is invalid */
+ if ((mpar->id) && (GS(mpar->id->name) != mpar->id_type))
+ mpar->id = NULL;
+}
+
+static void rna_Mask_shapes_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Mask *mask = (Mask *)ptr->id.data;
+
+ rna_iterator_listbase_begin(iter, &mask->shapes, NULL);
+}
+
+static int rna_Mask_active_shape_index_get(PointerRNA *ptr)
+{
+ Mask *mask = (Mask *)ptr->id.data;
+
+ return mask->shapenr;
+}
+
+static void rna_Mask_active_shape_index_set(PointerRNA *ptr, int value)
+{
+ Mask *mask = (Mask *)ptr->id.data;
+
+ mask->shapenr = value;
+}
+
+static void rna_Mask_active_shape_index_range(PointerRNA *ptr, int *min, int *max)
+{
+ Mask *mask = (Mask *)ptr->id.data;
+
+ *min = 0;
+ *max = mask->tot_shape - 1;
+ *max = MAX2(0, *max);
+}
+
+static PointerRNA rna_Mask_active_shape_get(PointerRNA *ptr)
+{
+ Mask *mask = (Mask *)ptr->id.data;
+ MaskShape *shape = BKE_mask_shape_active(mask);
+
+ return rna_pointer_inherit_refine(ptr, &RNA_MaskShape, shape);
+}
+
+static void rna_Mask_active_shape_set(PointerRNA *ptr, PointerRNA value)
+{
+ Mask *mask = (Mask *)ptr->id.data;
+ MaskShape *shape = (MaskShape *)value.data;
+
+ BKE_mask_shape_active_set(mask, shape);
+}
+
+static void rna_MaskShape_splines_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ MaskShape *shape = (MaskShape *)ptr->data;
+
+ rna_iterator_listbase_begin(iter, &shape->splines, NULL);
+}
+
+void rna_MaskShape_name_set(PointerRNA *ptr, const char *value)
+{
+ Mask *mask = (Mask *)ptr->id.data;
+ MaskShape *shape = (MaskShape *)ptr->data;
+
+ BLI_strncpy(shape->name, value, sizeof(shape->name));
+
+ BKE_mask_shape_unique_name(mask, shape);
+}
+
+static PointerRNA rna_MaskShape_active_spline_get(PointerRNA *ptr)
+{
+ MaskShape *shape = (MaskShape *)ptr->data;
+
+ return rna_pointer_inherit_refine(ptr, &RNA_MaskSpline, shape->act_spline);
+}
+
+static void rna_MaskShape_active_spline_set(PointerRNA *ptr, PointerRNA value)
+{
+ MaskShape *shape = (MaskShape *)ptr->data;
+ MaskSpline *spline = (MaskSpline *)value.data;
+ int index = BLI_findindex(&shape->splines, spline);
+
+ if (index >= 0)
+ shape->act_spline = spline;
+ else
+ shape->act_spline = NULL;
+}
+
+static PointerRNA rna_MaskShape_active_spline_point_get(PointerRNA *ptr)
+{
+ MaskShape *shape = (MaskShape *)ptr->data;
+
+ return rna_pointer_inherit_refine(ptr, &RNA_MaskSplinePoint, shape->act_point);
+}
+
+static void rna_MaskShape_active_spline_point_set(PointerRNA *ptr, PointerRNA value)
+{
+ MaskShape *shape = (MaskShape *)ptr->data;
+ MaskSpline *spline = shape->splines.first;
+ MaskSplinePoint *point = (MaskSplinePoint *)value.data;
+
+ shape->act_point = NULL;
+
+ while (spline) {
+ if (point >= spline->points && point < spline->points + spline->tot_point) {
+ shape->act_point = point;
+
+ break;
+ }
+
+ spline = spline->next;
+ }
+}
+
+static void rna_MaskSplinePoint_handle1_get(PointerRNA *ptr, float *values)
+{
+ MaskSplinePoint *point = (MaskSplinePoint*) ptr->data;
+ BezTriple *bezt = &point->bezt;
+
+ values[0] = bezt->vec[0][0];
+ values[1] = bezt->vec[0][1];
+ values[2] = bezt->vec[0][2];
+}
+
+static void rna_MaskSplinePoint_handle1_set(PointerRNA *ptr, const float *values)
+{
+ MaskSplinePoint *point = (MaskSplinePoint*) ptr->data;
+ BezTriple *bezt = &point->bezt;
+
+ bezt->vec[0][0] = values[0];
+ bezt->vec[0][1] = values[1];
+ bezt->vec[0][2] = values[2];
+}
+
+static void rna_MaskSplinePoint_handle2_get(PointerRNA *ptr, float *values)
+{
+ MaskSplinePoint *point = (MaskSplinePoint*) ptr->data;
+ BezTriple *bezt = &point->bezt;
+
+ values[0] = bezt->vec[2][0];
+ values[1] = bezt->vec[2][1];
+ values[2] = bezt->vec[2][2];
+}
+
+static void rna_MaskSplinePoint_handle2_set(PointerRNA *ptr, const float *values)
+{
+ MaskSplinePoint *point = (MaskSplinePoint*) ptr->data;
+ BezTriple *bezt = &point->bezt;
+
+ bezt->vec[2][0] = values[0];
+ bezt->vec[2][1] = values[1];
+ bezt->vec[2][2] = values[2];
+}
+
+static void rna_MaskSplinePoint_ctrlpoint_get(PointerRNA *ptr, float *values)
+{
+ MaskSplinePoint *point = (MaskSplinePoint*) ptr->data;
+ BezTriple *bezt = &point->bezt;
+
+ values[0] = bezt->vec[1][0];
+ values[1] = bezt->vec[1][1];
+ values[2] = bezt->vec[1][2];
+}
+
+static void rna_MaskSplinePoint_ctrlpoint_set(PointerRNA *ptr, const float *values)
+{
+ MaskSplinePoint *point = (MaskSplinePoint*) ptr->data;
+ BezTriple *bezt = &point->bezt;
+
+ bezt->vec[1][0] = values[0];
+ bezt->vec[1][1] = values[1];
+ bezt->vec[1][2] = values[2];
+}
+
+static int rna_MaskSplinePoint_handle_type_get(PointerRNA *ptr)
+{
+ MaskSplinePoint *point = (MaskSplinePoint*) ptr->data;
+ BezTriple *bezt = &point->bezt;
+
+ return bezt->h1;
+}
+
+static void rna_MaskSplinePoint_handle_type_set(PointerRNA *ptr, int value)
+{
+ MaskSplinePoint *point = (MaskSplinePoint*) ptr->data;
+ BezTriple *bezt = &point->bezt;
+
+ bezt->h1 = bezt->h2 = value;
+}
+
+/* ** API ** */
+
+static MaskShape *rna_Mask_shape_new(Mask *mask, const char *name)
+{
+ MaskShape *shape = BKE_mask_shape_new(mask, name);
+
+ WM_main_add_notifier(NC_MASK|NA_EDITED, mask);
+
+ return shape;
+}
+
+void rna_Mask_shape_remove(Mask *mask, MaskShape *shape)
+{
+ BKE_mask_shape_remove(mask, shape);
+
+ WM_main_add_notifier(NC_MASK|NA_EDITED, mask);
+}
+
+static void rna_MaskShape_spline_add(ID *id, MaskShape *shape, int number)
+{
+ Mask *mask = (Mask*) id;
+ int i;
+
+ for (i = 0; i < number; i++)
+ BKE_mask_spline_add(shape);
+
+ WM_main_add_notifier(NC_MASK|NA_EDITED, mask);
+}
+
+#else
+
+static void rna_def_maskParent(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem mask_id_type_items[] = {
+ {ID_MC, "MOVIECLIP", ICON_SEQUENCE, "Movie Clip", ""},
+ {0, NULL, 0, NULL, NULL}};
+
+ srna = RNA_def_struct(brna, "MaskParent", NULL);
+ RNA_def_struct_ui_text(srna, "Mask Parent", "Parenting settings for maskign element");
+
+ /* use_parent */
+ prop = RNA_def_property(srna, "use_parent", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", MASK_PARENT_ACTIVE);
+ RNA_def_property_ui_text(prop, "Use Parent", "Use parenting for this object");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ /* Target Properties - ID-block to Drive */
+ prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ID");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ // RNA_def_property_editable_func(prop, "rna_maskSpline_id_editable");
+ /* note: custom set function is ONLY to avoid rna setting a user for this. */
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_MaskParent_id_set", "rna_MaskParent_id_typef", NULL);
+ RNA_def_property_ui_text(prop, "ID", "ID-block to which masking element would be parented to or to it's property");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ prop = RNA_def_property(srna, "id_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "id_type");
+ RNA_def_property_enum_items(prop, mask_id_type_items);
+ RNA_def_property_enum_default(prop, ID_MC);
+ RNA_def_property_enum_funcs(prop, NULL, "rna_MaskParent_id_type_set", NULL);
+ //RNA_def_property_editable_func(prop, "rna_MaskParent_id_type_editable");
+ RNA_def_property_ui_text(prop, "ID Type", "Type of ID-block that can be used");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ /* parent */
+ prop = RNA_def_property(srna, "parent", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Parent", "Name of parent object in specified data block to which parenting happens");
+ RNA_def_property_string_maxlength(prop, MAX_ID_NAME - 2);
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ /* sub_parent */
+ prop = RNA_def_property(srna, "sub_parent", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Sub Parent", "Name of parent sub-object in specified data block to which parenting happens");
+ RNA_def_property_string_maxlength(prop, MAX_ID_NAME - 2);
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+}
+
+static void rna_def_maskSplinePointUW(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "MaskSplinePointUW", NULL);
+ RNA_def_struct_ui_text(srna, "Mask Spline UW Point", "Single point in spline segment defining feather");
+
+ /* u */
+ prop = RNA_def_property(srna, "u", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "u");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "U", "U coordinate of point along spline segment");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ /* weight */
+ prop = RNA_def_property(srna, "weight", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "w");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Weight", "Weight of feather point");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ /* select */
+ prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SELECT);
+ RNA_def_property_ui_text(prop, "Select", "Selection status");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+}
+
+static void rna_def_maskSplinePoint(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem handle_type_items[] = {
+ {HD_AUTO, "AUTO", 0, "Auto", ""},
+ {HD_VECT, "VECTOR", 0, "Vector", ""},
+ {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
+ {0, NULL, 0, NULL, NULL}};
+
+ rna_def_maskSplinePointUW(brna);
+
+ srna = RNA_def_struct(brna, "MaskSplinePoint", NULL);
+ RNA_def_struct_ui_text(srna, "Mask Spline Point", "Single point in spline used for defining mash shape");
+
+ /* Vector values */
+ prop = RNA_def_property(srna, "handle_left", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_float_funcs(prop, "rna_MaskSplinePoint_handle1_get", "rna_MaskSplinePoint_handle1_set", NULL);
+ RNA_def_property_ui_text(prop, "Handle 1", "Coordinates of the first handle");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ prop = RNA_def_property(srna, "co", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_float_funcs(prop, "rna_MaskSplinePoint_ctrlpoint_get", "rna_MaskSplinePoint_ctrlpoint_set", NULL);
+ RNA_def_property_ui_text(prop, "Control Point", "Coordinates of the control point");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ prop = RNA_def_property(srna, "handle_right", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_float_funcs(prop, "rna_MaskSplinePoint_handle2_get", "rna_MaskSplinePoint_handle2_set", NULL);
+ RNA_def_property_ui_text(prop, "Handle 2", "Coordinates of the second handle");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ /* handle_type */
+ prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_funcs(prop, "rna_MaskSplinePoint_handle_type_get", "rna_MaskSplinePoint_handle_type_set", NULL);
+ RNA_def_property_enum_items(prop, handle_type_items);
+ RNA_def_property_ui_text(prop, "Handle Type", "Handle type");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ /* select */
+ prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "bezt.f1", SELECT);
+ RNA_def_property_ui_text(prop, "Select", "Selection status");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ /* parent */
+ prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "MaskParent");
+
+ /* feather points */
+ prop = RNA_def_property(srna, "feather_points", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "MaskSplinePointUW");
+ RNA_def_property_collection_sdna(prop, NULL, "uw", "tot_uw");
+ RNA_def_property_ui_text(prop, "Feather Points", "Points defining feather");
+}
+
+static void rna_def_maskSplines(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "MaskSplines", NULL);
+ RNA_def_struct_sdna(srna, "MaskShape");
+ RNA_def_struct_ui_text(srna, "Mask Splines", "Collection of masking splines");
+
+ func = RNA_def_function(srna, "add", "rna_MaskShape_spline_add");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID);
+ RNA_def_function_ui_description(func, "Add a number of splines to mask shape");
+ RNA_def_int(func, "count", 1, 0, INT_MAX, "Number", "Number of splines to add to the shape", 0, INT_MAX);
+
+ /* active spline */
+ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "MaskSpline");
+ RNA_def_property_pointer_funcs(prop, "rna_MaskShape_active_spline_get", "rna_MaskShape_active_spline_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE|PROP_NEVER_UNLINK);
+ RNA_def_property_ui_text(prop, "Active Spline", "Active spline of masking shape");
+
+ /* active point */
+ prop = RNA_def_property(srna, "active_point", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "MaskSplinePoint");
+ RNA_def_property_pointer_funcs(prop, "rna_MaskShape_active_spline_point_get", "rna_MaskShape_active_spline_point_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE|PROP_NEVER_UNLINK);
+ RNA_def_property_ui_text(prop, "Active Spline", "Active spline of masking shape");
+}
+
+static void rna_def_maskSpline(BlenderRNA *brna)
+{
+ static EnumPropertyItem spline_interpolation_items[] = {
+ {MASK_SPLINE_INTERP_LINEAR, "LINEAR", 0, "Linear", ""},
+ {MASK_SPLINE_INTERP_EASE, "EASE", 0, "Ease", ""},
+ {0, NULL, 0, NULL, NULL}};
+
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ rna_def_maskSplinePoint(brna);
+
+ srna = RNA_def_struct(brna, "MaskSpline", NULL);
+ RNA_def_struct_ui_text(srna, "Mask spline", "Single spline used for defining mash shape");
+
+ /* weight interpolation */
+ prop = RNA_def_property(srna, "weight_interpolation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "weight_interp");
+ RNA_def_property_enum_items(prop, spline_interpolation_items);
+ RNA_def_property_ui_text(prop, "Weight Interpolation", "The type of weight interpolation for spline");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+
+ /* cyclic */
+ prop = RNA_def_property(srna, "use_cyclic", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", MASK_SPLINE_CYCLIC);
+ RNA_def_property_ui_text(prop, "Cyclic", "Make this spline a closed loop");
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+}
+
+static void rna_def_maskShape(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ rna_def_maskSpline(brna);
+ rna_def_maskSplines(brna);
+
+ srna = RNA_def_struct(brna, "MaskShape", NULL);
+ RNA_def_struct_ui_text(srna, "Mask shape", "Single shape used for masking stuff");
+
+ /* name */
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Name", "Unique name of shape");
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MaskShape_name_set");
+ RNA_def_property_string_maxlength(prop, MAX_ID_NAME - 2);
+ RNA_def_property_update(prop, 0, "rna_Mask_update_data");
+ RNA_def_struct_name_property(srna, prop);
+
+ /* splines */
+ prop = RNA_def_property(srna, "splines", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_funcs(prop, "rna_MaskShape_splines_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", 0, 0, 0, 0);
+ RNA_def_property_struct_type(prop, "MaskSpline");
+ RNA_def_property_ui_text(prop, "Splines", "Collection of splines which defines this shape");
+ RNA_def_property_srna(prop, "MaskSplines");
+}
+
+static void rna_def_maskShapes(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "MaskShapes");
+ srna = RNA_def_struct(brna, "MaskShapes", NULL);
+ RNA_def_struct_sdna(srna, "Mask");
+ RNA_def_struct_ui_text(srna, "Mask Shapes", "Collection of shapes used by mask");
+
+ func = RNA_def_function(srna, "new", "rna_Mask_shape_new");
+ RNA_def_function_ui_description(func, "Add shape to this mask");
+ RNA_def_string(func, "name", "", 0, "Name", "Name of new shape");
+ parm = RNA_def_pointer(func, "shape", "MaskShape", "", "New mask shape");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_Mask_shape_remove");
+ RNA_def_function_ui_description(func, "Remove shape from this mask");
+ RNA_def_pointer(func, "shape", "MaskShape", "", "Shape to be removed");
+
+ /* active object */
+ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "MaskShape");
+ RNA_def_property_pointer_funcs(prop, "rna_Mask_active_shape_get", "rna_Mask_active_shape_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE|PROP_NEVER_UNLINK);
+ RNA_def_property_ui_text(prop, "Active Shape", "Active shape in this mask");
+}
+
+static void rna_def_mask(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ rna_def_maskShape(brna);
+
+ srna = RNA_def_struct(brna, "Mask", "ID");
+ RNA_def_struct_ui_text(srna, "Mask", "Mask datablock defining mask for compositing");
+ RNA_def_struct_ui_icon(srna, ICON_MOD_MASK);
+
+ /* shapes */
+ prop = RNA_def_property(srna, "shapes", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_funcs(prop, "rna_Mask_shapes_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", 0, 0, 0, 0);
+ RNA_def_property_struct_type(prop, "MaskShape");
+ RNA_def_property_ui_text(prop, "Shapes", "Collection of shapes which defines this mask");
+ rna_def_maskShapes(brna, prop);
+
+ /* active shape index */
+ prop = RNA_def_property(srna, "active_shape_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "shapenr");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_int_funcs(prop, "rna_Mask_active_shape_index_get", "rna_Mask_active_shape_index_set", "rna_Mask_active_shape_index_range");
+ RNA_def_property_ui_text(prop, "Active Shape Index", "Index of active shape in list of all mask's shapes");
+}
+
+void RNA_def_mask(BlenderRNA *brna)
+{
+ rna_def_maskParent(brna);
+ rna_def_mask(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 58fd936819b..a6c3c664ce6 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -2875,6 +2875,18 @@ static void def_cmp_moviedistortion(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
}
+static void def_cmp_mask(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "mask", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "id");
+ RNA_def_property_struct_type(prop, "Mask");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Mask", "");
+ RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
+}
+
static void dev_cmd_transform(StructRNA *srna)
{
PropertyRNA *prop;
diff --git a/source/blender/makesrna/intern/rna_nodetree_types.h b/source/blender/makesrna/intern/rna_nodetree_types.h
index 5352bbd0e0f..e536314e145 100644
--- a/source/blender/makesrna/intern/rna_nodetree_types.h
+++ b/source/blender/makesrna/intern/rna_nodetree_types.h
@@ -158,6 +158,7 @@ DefNode( CompositorNode, CMP_NODE_MOVIECLIP, def_cmp_movieclip, "MOVIE
DefNode( CompositorNode, CMP_NODE_TRANSFORM, dev_cmd_transform, "TRANSFORM", Transform, "Transform", "" )
DefNode( CompositorNode, CMP_NODE_STABILIZE2D, def_cmp_stabilize2d, "STABILIZE2D", Stabilize, "Stabilize 2D", "" )
DefNode( CompositorNode, CMP_NODE_MOVIEDISTORTION,def_cmp_moviedistortion,"MOVIEDISTORTION",MovieDistortion, "Movie Distortion", "" )
+DefNode( CompositorNode, CMP_NODE_MASK, def_cmp_mask, "MASK", Mask, "Mask", "" )
DefNode( TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
DefNode( TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 47bad8f31e5..532690eff64 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -115,6 +115,7 @@ EnumPropertyItem viewport_shade_items[] = {
#ifdef RNA_RUNTIME
#include "DNA_anim_types.h"
+#include "DNA_mask_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -1029,6 +1030,13 @@ static void rna_SpaceClipEditor_clip_set(PointerRNA *ptr, PointerRNA value)
ED_space_clip_set(NULL, screen, sc, (MovieClip*)value.data);
}
+static void rna_SpaceClipEditor_mask_set(PointerRNA *ptr, PointerRNA value)
+{
+ SpaceClip *sc= (SpaceClip*)(ptr->data);
+
+ ED_space_clip_set_mask(NULL, sc, (Mask*)value.data);
+}
+
static void rna_SpaceClipEditor_clip_mode_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
SpaceClip *sc = (SpaceClip*)(ptr->data);
@@ -2926,6 +2934,7 @@ static void rna_def_space_clip(BlenderRNA *brna)
{SC_MODE_RECONSTRUCTION, "RECONSTRUCTION", ICON_SNAP_FACE, "Reconstruction",
"Show tracking/reconstruction tools"},
{SC_MODE_DISTORTION, "DISTORTION", ICON_GRID, "Distortion", "Show distortion tools"},
+ {SC_MODE_MASKEDITING, "MASKEDITING", ICON_MOD_MASK, "Mask editing", "Show mask editing tools"},
{0, NULL, 0, NULL, NULL}};
static EnumPropertyItem view_items[] = {
@@ -2954,6 +2963,13 @@ static void rna_def_space_clip(BlenderRNA *brna)
"Parameters defining which frame of the movie clip is displayed");
RNA_def_property_update(prop, NC_SPACE|ND_SPACE_CLIP, NULL);
+ /* mask */
+ prop= RNA_def_property(srna, "mask", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Mask", "Mask displayed and edited in this space");
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_SpaceClipEditor_mask_set", NULL, NULL);
+ RNA_def_property_update(prop, NC_SPACE|ND_SPACE_CLIP, NULL);
+
/* mode */
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "mode");
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 9bcbc91265c..5320d9f65cc 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -35,6 +35,7 @@ set(INC
../makesrna
../render/extern/include
../../../intern/guardedalloc
+ ../../../intern/raskter
)
set(INC_SYS
@@ -81,6 +82,7 @@ set(SRC
composite/nodes/node_composite_mapUV.c
composite/nodes/node_composite_mapValue.c
composite/nodes/node_composite_math.c
+ composite/nodes/node_composite_mask.c
composite/nodes/node_composite_mixrgb.c
composite/nodes/node_composite_movieclip.c
composite/nodes/node_composite_moviedistortion.c
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index 284b89bc095..4378c6a1d36 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -50,6 +50,7 @@ void register_node_type_cmp_value(struct bNodeTreeType *ttype);
void register_node_type_cmp_rgb(struct bNodeTreeType *ttype);
void register_node_type_cmp_curve_time(struct bNodeTreeType *ttype);
void register_node_type_cmp_movieclip(struct bNodeTreeType *ttype);
+void register_node_type_cmp_usermask(struct bNodeTreeType *ttype);
void register_node_type_cmp_composite(struct bNodeTreeType *ttype);
void register_node_type_cmp_viewer(struct bNodeTreeType *ttype);
@@ -115,6 +116,7 @@ void register_node_type_cmp_mapuv(struct bNodeTreeType *ttype);
void register_node_type_cmp_transform(struct bNodeTreeType *ttype);
void register_node_type_cmp_stabilize2d(struct bNodeTreeType *ttype);
void register_node_type_cmp_moviedistortion(struct bNodeTreeType *ttype);
+void register_node_type_cmp_mask(struct bNodeTreeType *ttype);
void register_node_type_cmp_glare(struct bNodeTreeType *ttype);
void register_node_type_cmp_tonemap(struct bNodeTreeType *ttype);
diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c
index 049b5dd8178..ca8922eaf64 100644
--- a/source/blender/nodes/composite/node_composite_tree.c
+++ b/source/blender/nodes/composite/node_composite_tree.c
@@ -872,6 +872,10 @@ int ntreeCompositTagAnimated(bNodeTree *ntree)
nodeUpdate(ntree, node);
tagged= 1;
}
+ else if (node->type==CMP_NODE_MASK) {
+ nodeUpdate(ntree, node);
+ tagged= 1;
+ }
}
return tagged;
diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.c b/source/blender/nodes/composite/nodes/node_composite_mask.c
new file mode 100644
index 00000000000..2c49be48f7d
--- /dev/null
+++ b/source/blender/nodes/composite/nodes/node_composite_mask.c
@@ -0,0 +1,124 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Blender Foundation,
+ * Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/composite/nodes/node_composite_mask.c
+ * \ingroup cmpnodes
+ */
+
+#include "BLF_translation.h"
+
+#include "DNA_mask_types.h"
+
+#include "BKE_mask.h"
+
+// XXX: ...
+#include "../../../../intern/raskter/raskter.h"
+#include "node_composite_util.h"
+
+/* **************** Translate ******************** */
+
+static bNodeSocketTemplate cmp_node_mask_in[] = {
+ { SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
+ { -1, 0, "" }
+};
+
+static bNodeSocketTemplate cmp_node_mask_out[] = {
+ { SOCK_RGBA, 0, "Image"},
+ { -1, 0, "" }
+};
+
+static void exec(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
+{
+ if (node->id) {
+ Mask *mask = (Mask *)node->id;
+ CompBuf *stackbuf;
+ RenderData *rd = data;
+ MaskShape *shape = mask->shapes.first;
+ float *res;
+ int sx, sy;
+
+ if (!out[0]->hasoutput) {
+ /* the node's output socket is not connected to anything...
+ * do not execute any further, just exit the node immediately
+ */
+ return;
+ }
+
+ if (in[0]->hasinput && in[0]->data) {
+ CompBuf *cbuf = typecheck_compbuf(in[0]->data, CB_RGBA);
+
+ sx = cbuf->x;
+ sy = cbuf->y;
+ }
+ else {
+ sx = (rd->size * rd->xsch) / 100;
+ sy = (rd->size * rd->ysch) / 100;
+ }
+
+ /* allocate the output buffer */
+ stackbuf = alloc_compbuf(sx, sy, CB_VAL, TRUE);
+ res = stackbuf->rect;
+
+ shape = mask->shapes.first;
+ while (shape) {
+ MaskSpline *spline = shape->splines.first;
+
+ while (spline) {
+ float *diff_points;
+ int tot_diff_point;
+
+ diff_points = BKE_mask_spline_differentiate(spline, &tot_diff_point);
+
+ if (tot_diff_point) {
+ PLX_raskterize(diff_points, tot_diff_point, res, sx, sy);
+
+ MEM_freeN(diff_points);
+ }
+
+ spline = spline->next;
+ }
+
+ shape = shape->next;
+ }
+
+ /* pass on output and free */
+ out[0]->data = stackbuf;
+ }
+}
+
+void register_node_type_cmp_mask(bNodeTreeType *ttype)
+{
+ static bNodeType ntype;
+
+ node_type_base(ttype, &ntype, CMP_NODE_MASK, "Mask", NODE_CLASS_INPUT, NODE_OPTIONS);
+ node_type_socket_templates(&ntype, cmp_node_mask_in, cmp_node_mask_out);
+ node_type_size(&ntype, 140, 100, 320);
+ node_type_exec(&ntype, exec);
+
+ nodeRegisterType(ttype, &ntype);
+}
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 7cbeab6a02e..25abc171057 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -167,6 +167,7 @@ typedef struct wmNotifier {
#define NC_ID (18<<24)
#define NC_LOGIC (19<<24)
#define NC_MOVIECLIP (20<<24)
+#define NC_MASK (21<<24)
/* data type, 256 entries is enough, it can overlap */
#define NOTE_DATA 0x00FF0000
diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt
index 7edf5314821..70354c0a2ec 100644
--- a/source/blenderplayer/CMakeLists.txt
+++ b/source/blenderplayer/CMakeLists.txt
@@ -154,6 +154,7 @@ endif()
bf_blenkernel # duplicate for linking
bf_intern_mikktspace
extern_recastnavigation
+ bf_intern_raskter
)
if(WITH_MOD_CLOTH_ELTOPO)
diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c
index a036e8a8212..ad9bd924e9f 100644
--- a/source/blenderplayer/bad_level_call_stubs/stubs.c
+++ b/source/blenderplayer/bad_level_call_stubs/stubs.c
@@ -236,6 +236,7 @@ void ED_space_image_uv_sculpt_update(struct wmWindowManager *wm, struct ToolSett
void ED_screen_set_scene(struct bContext *C, struct Scene *scene){}
void ED_space_clip_set(struct bContext *C, struct SpaceClip *sc, struct MovieClip *clip){}
+void ED_space_clip_set_mask(struct bContext *C, struct SpaceClip *sc, struct Mask *mask){}
void ED_area_tag_redraw_regiontype(struct ScrArea *sa, int regiontype){}
void ED_render_engine_changed(struct Main *bmain) {}
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index 0168c06b7da..f81d6320c23 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -807,6 +807,7 @@ endif()
bf_editor_sound
bf_editor_animation
bf_editor_datafiles
+ bf_editor_mask
bf_render
bf_intern_opennl
@@ -877,6 +878,7 @@ endif()
cycles_kernel
cycles_util
cycles_subd
+ bf_intern_raskter
)
if(WITH_LIBMV)