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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--intern/CMakeLists.txt1
-rw-r--r--intern/SConscript3
-rw-r--r--intern/raskter/CMakeLists.txt40
-rw-r--r--intern/raskter/SConscript10
-rw-r--r--intern/raskter/raskter.c367
-rw-r--r--intern/raskter/raskter.h55
-rw-r--r--release/scripts/modules/bpy_extras/keyconfig_utils.py1
-rw-r--r--release/scripts/startup/bl_ui/space_clip.py121
-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
71 files changed, 5498 insertions, 42 deletions
diff --git a/intern/CMakeLists.txt b/intern/CMakeLists.txt
index c85f0fddc34..71d2ef5e410 100644
--- a/intern/CMakeLists.txt
+++ b/intern/CMakeLists.txt
@@ -31,6 +31,7 @@ add_subdirectory(memutil)
add_subdirectory(iksolver)
add_subdirectory(opennl)
add_subdirectory(mikktspace)
+add_subdirectory(raskter)
if(WITH_AUDASPACE)
add_subdirectory(audaspace)
diff --git a/intern/SConscript b/intern/SConscript
index b6305f7dbab..6f08c696317 100644
--- a/intern/SConscript
+++ b/intern/SConscript
@@ -14,7 +14,8 @@ SConscript(['audaspace/SConscript',
'boolop/SConscript',
'opennl/SConscript',
'mikktspace/SConscript',
- 'smoke/SConscript'])
+ 'smoke/SConscript',
+ 'raskter/SConscript'])
# NEW_CSG was intended for intern/csg, but
# getting it to compile is difficult
diff --git a/intern/raskter/CMakeLists.txt b/intern/raskter/CMakeLists.txt
new file mode 100644
index 00000000000..3e1368d8eb0
--- /dev/null
+++ b/intern/raskter/CMakeLists.txt
@@ -0,0 +1,40 @@
+# ***** 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): Peter Larabell
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+)
+
+set(INC_SYS
+
+)
+
+set(SRC
+ raskter.c
+
+ raskter.h
+)
+
+blender_add_lib(bf_intern_raskter "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/intern/raskter/SConscript b/intern/raskter/SConscript
new file mode 100644
index 00000000000..7ad505d70e4
--- /dev/null
+++ b/intern/raskter/SConscript
@@ -0,0 +1,10 @@
+#!/usr/bin/python
+
+Import ('env')
+
+sources = ['raskter.c']
+
+incs = ''
+defs = ''
+
+env.BlenderLib ('bf_intern_raskter', sources, Split(incs), Split(defs), libtype=['intern'], priority=[100] )
diff --git a/intern/raskter/raskter.c b/intern/raskter/raskter.c
new file mode 100644
index 00000000000..d5890f1741a
--- /dev/null
+++ b/intern/raskter/raskter.c
@@ -0,0 +1,367 @@
+/*
+ * ***** 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): Peter Larabell.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+/** \file raskter.c
+ * \ingroup RASKTER
+ */
+
+#include <malloc.h>
+#include "raskter.h"
+
+#define R_XCHG(a,b) t=a;a=b;b=t;
+
+struct e_status {
+ int x;
+ int ybeg;
+ int xshift;
+ int xdir;
+ int drift;
+ int drift_inc;
+ int drift_dec;
+ int num;
+ struct e_status *e_next;
+};
+
+struct r_buffer_stats {
+ float *buf;
+ int sizex;
+ int sizey;
+};
+
+static struct e_status *all_edges, *possible_edges;
+static struct r_buffer_stats rb;
+/*
+ * Sort all the edges of the input polygon by Y, then by X, of the "first" vertex encountered.
+ * This will ensure we can scan convert the entire poly in one pass.
+ *
+ * Really the poly should be clipped to the frame buffer's dimensions here for speed of drawing
+ * just the poly. Since the DEM code could end up being coupled with this, we'll keep it separate
+ * for now.
+ */
+static void preprocess_all_edges(struct poly_vert * verts, int num_verts, struct e_status * open_edge) {
+ int i;
+ int xbeg;
+ int ybeg;
+ int xend;
+ int yend;
+ int dx;
+ int dy;
+ int t;
+ int xdist;
+ struct e_status *e_new;
+ struct e_status *next_edge;
+ struct e_status **next_edge_ref;
+ struct poly_vert *v;
+ // set up pointers
+ v = verts;
+ all_edges = NULL;
+ // loop all verts
+ for(i = 0; i < num_verts; i++) {
+ xbeg = v[i].x;
+ ybeg = v[i].y;
+ // determine starts and ends of edges, linking last vertex to first vertex
+ if(i == 0) {
+ xend = v[num_verts-1].x;
+ yend = v[num_verts-1].y;
+ } else {
+ xend = v[i-1].x;
+ yend = v[i-1].y;
+ }
+ // make sure our edges are facing the correct direction
+ if(ybeg > yend) {
+ R_XCHG(xbeg, xend);
+ R_XCHG(ybeg, yend);
+ }
+ // create the edge and determine it's slope (for incremental line drawing)
+ if((dy = yend - ybeg) != 0) {
+ e_new = open_edge++;
+ e_new->xdir = ((dx = xend - xbeg) > 0) ? 1 : -1;
+ xdist = (dx > 0) ? dx : -dx;
+ e_new->x = xbeg;
+ e_new->ybeg = ybeg;
+ e_new->num = dy;
+ e_new->drift_dec = dy;
+ if(dx >= 0) {
+ e_new->drift = 0;
+ } else {
+ e_new->drift = -dy + 1;
+ }
+ // calculate deltas for drawing
+ if(dy >= xdist) {
+ e_new->drift_inc = xdist;
+ e_new->xshift = 0;
+ } else {
+ e_new->drift_inc = xdist % dy;
+ e_new->xshift = (xdist / dy) * e_new->xdir;
+ }
+ next_edge_ref = &all_edges;
+ // link in all the edges, in sorted order
+ for(;;) {
+ next_edge = *next_edge_ref;
+ if((next_edge == NULL) || (next_edge->ybeg > ybeg) || ((next_edge->ybeg == ybeg) && (next_edge->x >= xbeg))) {
+ e_new->e_next = next_edge;
+ *next_edge_ref = e_new;
+ break;
+ }
+ next_edge_ref = &next_edge->e_next;
+ }
+ }
+ }
+}
+
+/*
+ * This function clips drawing to the frame buffer. That clipping will likely be moved into the preprocessor
+ * for speed, but waiting on final design choices for curve-data before eliminating data the DEM code will need
+ * if it ends up being coupled with this function.
+ */
+int rast_scan_fill(struct poly_vert * verts, int num_verts) {
+ int x_curr; // current pixel position in X
+ int y_curr; // current scan line being drawn
+ int yp; // y-pixel's position in frame buffer
+ int swixd = 0; // whether or not edges switched position in X
+ float *cpxl; // pixel pointers...
+ float *mpxl;
+ float *spxl;
+ struct e_status *e_curr; // edge pointers...
+ struct e_status *e_temp;
+ struct e_status *edgbuf;
+ struct e_status **edgec;
+
+
+ /*
+ If the number of verts specified to render as a polygon is less than 3,
+ return immediately. Obviously we cant render a poly with sides < 3. The
+ return for this we set to 1, simply so it can be distinguished from the
+ next place we could return, which is a failure to allocate memory.
+ */
+ if(num_verts < 3) {
+ return(1);
+ }
+
+ /*
+ Try to allocate an edge buffer in memory. needs to be the size of the edge tracking data
+ multiplied by the number of edges, which is always equal to the number of verts in
+ a 2D polygon. Here we return 0 to indicate a memory allocation failure, as opposed to a 1 for
+ the preceeding error, which was a rasterization request on a 2D poly with less than
+ 3 sides.
+ */
+ if((edgbuf = (struct e_status *)(malloc(sizeof(struct e_status) * num_verts))) == NULL) {
+ return(0);
+ }
+
+ /*
+ Do some preprocessing on all edges. This constructs a table structure in memory of all
+ the edge properties and can "flip" some edges so sorting works correctly.
+ */
+ preprocess_all_edges(verts, num_verts, edgbuf);
+
+ /*
+ Set the pointer for tracking the edges currently in processing to NULL to make sure
+ we don't get some crazy value after initialization.
+ */
+ possible_edges = NULL;
+
+ /*
+ Loop through all scan lines to be drawn. Since we sorted by Y values during
+ preprocess_all_edges(), we can already exact values for the lowest and
+ highest Y values we could possibly need by induction. The preprocessing sorted
+ out edges by Y position, we can cycle the current edge being processed once
+ it runs out of Y pixels. When we have no more edges, meaning the current edge
+ is NULL after setting the "current" edge to be the previous current edge's
+ "next" edge in the Y sorted edge connection chain, we can stop looping Y values,
+ since we can't possibly have more scan lines if we ran out of edges. :)
+
+ TODO: This clips Y to the frame buffer, which should be done in the preprocessor, but for now is done here.
+ Will get changed once DEM code gets in.
+ */
+ for(y_curr = (all_edges->ybeg > 0 ? all_edges->ybeg : 0); ((all_edges != NULL) || (possible_edges != NULL)) && (y_curr < rb.sizey); y_curr++) {
+
+ /*
+ Link any edges that start on the current scan line into the list of
+ edges currently needed to draw at least this, if not several, scan lines.
+ */
+
+ /*
+ Set the current edge to the beginning of the list of edges to be rasterized
+ into this scan line.
+
+ We could have lots of edge here, so iterate over all the edges needed. The
+ preprocess_all_edges() function sorted edges by X within each chunk of Y sorting
+ so we safely cycle edges to thier own "next" edges in order.
+
+ At each iteration, make sure we still have a non-NULL edge.
+ */
+ for(edgec = &possible_edges; (all_edges != NULL) && (all_edges->ybeg == y_curr);) {
+ x_curr = all_edges->x; // Set current X position.
+ for(;;) { // Start looping edges. Will break when edges run out.
+ e_curr = *edgec; // Set up a current edge pointer.
+ if((e_curr == NULL) || (e_curr->x >= x_curr)) { // If we have an no edge, or we need to skip some X-span,
+ e_temp = all_edges->e_next; // set a temp "next" edge to test.
+ *edgec = all_edges; // Add this edge to the list to be scanned.
+ all_edges->e_next = e_curr; // Set up the next edge.
+ edgec = &all_edges->e_next; // Set our list to the next edge's location in memory.
+ all_edges = e_temp; // Skip the NULL or bad X edge, set pointer to next edge.
+ break; // Stop looping edges (since we ran out or hit empty X span.
+ } else {
+ edgec = &e_curr->e_next; // Set the pointer to the edge list the "next" edge.
+ }
+ }
+ }
+
+ /*
+ Determine the current scan line's offset in the pixel buffer based on its Y position.
+ Basically we just multiply the current scan line's Y value by the number of pixels in each line.
+ */
+ yp = y_curr * rb.sizex;
+ /*
+ Set a "scan line pointer" in memory. The location of the buffer plus the row offset.
+ */
+ spxl = rb.buf + (yp);
+ /*
+ Set up the current edge to the first (in X) edge. The edges which could possibly be in this
+ list were determined in the preceeding edge loop above. They were already sorted in X by the
+ initial processing function.
+
+ At each iteration, test for a NULL edge. Since we'll keep cycling edge's to their own "next" edge
+ we will eventually hit a NULL when the list runs out.
+ */
+ for(e_curr = possible_edges; e_curr != NULL; e_curr = e_curr->e_next) {
+ /*
+ Calculate a span of pixels to fill on the current scan line.
+
+ Set the current pixel pointer by adding the X offset to the scan line's start offset.
+ Cycle the current edge the next edge.
+ Set the max X value to draw to be one less than the next edge's first pixel. This way we are
+ sure not to ever get into a situation where we have overdraw. (drawing the same pixel more than
+ one time because it's on a vertex connecting two edges)
+
+ Then blast through all the pixels in the span, advancing the pointer and setting the color to white.
+
+ TODO: Here we clip to the scan line, this is not efficient, and should be done in the preprocessor,
+ but for now it is done here until the DEM code comes in.
+ */
+ cpxl = spxl + (e_curr->x > 0 ? e_curr->x : 0);
+ e_curr = e_curr->e_next;
+ mpxl = spxl + (e_curr->x < rb.sizex ? e_curr->x : rb.sizex) - 1;
+ for(; cpxl <= mpxl; *cpxl++ = 1.0f);
+ }
+
+ /*
+ Loop through all edges of polygon that could be hit by this scan line,
+ and figure out their x-intersections with the next scan line.
+
+ Either A.) we wont have any more edges to test, or B.) we just add on the
+ slope delta computed in preprocessing step. Since this draws non-antialiased
+ polygons, we dont have fractional positions, so we only move in x-direction
+ when needed to get all the way to the next pixel over...
+ */
+ for(edgec = &possible_edges; (e_curr = *edgec) != NULL;) {
+ if((--(e_curr->num)) == 0) {
+ *edgec = e_curr->e_next;
+ } else {
+ e_curr->x += e_curr->xshift;
+ if((e_curr->drift += e_curr->drift_inc) > 0) {
+ e_curr->x += e_curr->xdir;
+ e_curr->drift -= e_curr->drift_dec;
+ }
+ edgec = &e_curr->e_next;
+ }
+ }
+ /*
+ It's possible that some edges may have crossed during the last step, so we'll be sure
+ that we ALWAYS intersect scan lines in order by shuffling if needed to make all edges
+ sorted by x-intersection coordinate. We'll always scan through at least once to see if
+ edges crossed, and if so, we set the 'swixd' flag. If 'swixd' gets set on the initial
+ pass, then we know we need to sort by x, so then cycle through edges again and perform
+ the sort.-
+ */
+ if(possible_edges != NULL) {
+ for(edgec = &possible_edges; (e_curr = *edgec)->e_next != NULL;) {
+ if(e_curr->x > e_curr->e_next->x) {
+ e_temp = e_curr->e_next->e_next;
+ *edgec = e_curr->e_next;
+ e_curr->e_next->e_next = e_curr;
+ e_curr->e_next = e_temp;
+ swixd = 1;
+ }
+ edgec = &(*edgec)->e_next;
+ }
+ for(; swixd;) {
+ swixd = 0;
+ for(edgec = &possible_edges; (e_curr = *edgec)->e_next != NULL;) {
+ if(e_curr->x > e_curr->e_next->x) {
+ e_temp = e_curr->e_next->e_next;
+ *edgec = e_curr->e_next;
+ e_curr->e_next->e_next = e_curr;
+ e_curr->e_next = e_temp;
+ swixd = 1;
+ }
+ edgec = &(*edgec)->e_next;
+ }
+ }
+ }
+ }
+
+ free(edgbuf);
+ return 1;
+}
+
+int PLX_raskterize(float * verts, int num, float * buf, int buf_x, int buf_y) {
+ int i; // i: Loop counter.
+ struct poly_vert *ply; // ply: Pointer to a list of integer buffer-space vertex coordinates.
+
+ /*
+ * Allocate enough memory for our poly_vert list. It'll be the size of the poly_vert
+ * data structure multiplied by the number of verts.
+ *
+ * In the event of a failure to allocate the memory, return 0, so this error can
+ * be distinguished as a memory allocation error.
+ */
+ if((ply = (struct poly_vert *)(malloc(sizeof(struct poly_vert) * num))) == NULL) {
+ return(0);
+ }
+
+ /*
+ * Loop over all verts passed in to be rasterized. Each vertex's X and Y coordinates are
+ * then converted from normalized screen space (0.0 <= POS <= 1.0) to integer coordinates
+ * in the buffer-space coordinates passed in inside buf_x and buf_y.
+ *
+ * It's worth noting that this function ONLY outputs fully white pixels in a mask. Every pixel
+ * drawn will be 1.0f in value, there is no anti-aliasing.
+ */
+ for(i = 0; i < num; i++) { // Loop over all verts.
+ ply[i].x = (verts[i<<1] * buf_x) + 0.5f; // Range expand normalized X to integer buffer-space X.
+ ply[i].y = (verts[(i<<1)+1] * buf_y) + 0.5f; // Range expand normalized Y to integer buffer-space Y.
+ }
+
+ rb.buf = buf; // Set the output buffer pointer.
+ rb.sizex = buf_x; // Set the output buffer size in X. (width)
+ rb.sizey = buf_y; // Set the output buffer size in Y. (height)
+
+ i = rast_scan_fill(ply, num); // Call our rasterizer, passing in the integer coords for each vert.
+ free(ply); // Free the memory allocated for the integer coordinate table.
+ return(i); // Return the value returned by the rasterizer.
+}
diff --git a/intern/raskter/raskter.h b/intern/raskter/raskter.h
new file mode 100644
index 00000000000..0b3cf365ff4
--- /dev/null
+++ b/intern/raskter/raskter.h
@@ -0,0 +1,55 @@
+/*
+ * ***** 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): Peter Larabell.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+/** \file raskter.h
+ * \ingroup RASKTER
+ */
+
+struct poly_vert{
+ int x;
+ int y;
+};
+
+struct scan_line{
+ int xstart;
+ int xend;
+};
+
+struct scan_line_batch{
+ int num;
+ int ystart;
+ struct scan_line * slines;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int PLX_raskterize(float * verts, int num, float * buf, int buf_x, int buf_y);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/release/scripts/modules/bpy_extras/keyconfig_utils.py b/release/scripts/modules/bpy_extras/keyconfig_utils.py
index 20b0669e531..306371ea6f5 100644
--- a/release/scripts/modules/bpy_extras/keyconfig_utils.py
+++ b/release/scripts/modules/bpy_extras/keyconfig_utils.py
@@ -95,6 +95,7 @@ KM_HIERARCHY = [
('Clip', 'CLIP_EDITOR', 'WINDOW', [
('Clip Editor', 'CLIP_EDITOR', 'WINDOW', []),
('Clip Graph Editor', 'CLIP_EDITOR', 'WINDOW', []),
+ ('Mask Editor', 'EMPTY', 'WINDOW', []), # image (reverse order, UVEdit before Image
]),
('View3D Gesture Circle', 'EMPTY', 'WINDOW', []),
diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py
index e4f608e2660..e72778e3776 100644
--- a/release/scripts/startup/bl_ui/space_clip.py
+++ b/release/scripts/startup/bl_ui/space_clip.py
@@ -44,11 +44,12 @@ class CLIP_HT_header(Header):
sub.menu("CLIP_MT_clip")
- if clip:
+ if clip and sc.mode != 'MASKEDITING':
sub.menu("CLIP_MT_track")
sub.menu("CLIP_MT_reconstruction")
- layout.prop(sc, "view", text="", expand=True)
+ if sc.mode != 'MASKEDITING':
+ layout.prop(sc, "view", text="", expand=True)
if clip:
if sc.view == 'CLIP':
@@ -72,6 +73,10 @@ class CLIP_HT_header(Header):
row = layout.row()
row.template_ID(sc, "clip", open='clip.open')
+ if sc.mode == 'MASKEDITING':
+ row = layout.row()
+ row.template_ID(sc, "mask", new="mask.new")
+
if clip:
tracking = clip.tracking
active = tracking.objects.active
@@ -545,6 +550,112 @@ class CLIP_PT_tracking_camera(Panel):
col.prop(clip.tracking.camera, "k3")
+class CLIP_PT_shapes(Panel):
+ bl_space_type = 'CLIP_EDITOR'
+ bl_region_type = 'UI'
+ bl_label = "Shapes"
+
+ @classmethod
+ def poll(cls, context):
+ sc = context.space_data
+
+ return sc.mask and sc.mode == 'MASKEDITING'
+
+ def draw(self, context):
+ layout = self.layout
+
+ sc = context.space_data
+ mask = sc.mask
+
+ row = layout.row()
+ row.template_list(mask, "shapes",
+ mask, "active_shape_index", rows=3)
+
+ sub = row.column(align=True)
+
+ sub.operator("mask.shape_new", icon='ZOOMIN', text="")
+ sub.operator("mask.shape_remove", icon='ZOOMOUT', text="")
+
+ active = mask.shapes.active
+ if active:
+ layout.prop(active, "name")
+
+
+class CLIP_PT_active_mask_spline(Panel):
+ bl_space_type = 'CLIP_EDITOR'
+ bl_region_type = 'UI'
+ bl_label = "Active Spline"
+
+ @classmethod
+ def poll(cls, context):
+ sc = context.space_data
+ mask = sc.mask
+
+ if mask and sc.mode == 'MASKEDITING':
+ return mask.shapes.active and mask.shapes.active.splines.active
+
+ return False
+
+ def draw(self, context):
+ layout = self.layout
+
+ sc = context.space_data
+ mask = sc.mask
+ spline = mask.shapes.active.splines.active
+
+ col = layout.column()
+ col.prop(spline, "weight_interpolation")
+ col.prop(spline, "use_cyclic")
+
+
+class CLIP_PT_active_mask_point(Panel):
+ bl_space_type = 'CLIP_EDITOR'
+ bl_region_type = 'UI'
+ bl_label = "Active Point"
+
+ @classmethod
+ def poll(cls, context):
+ sc = context.space_data
+ mask = sc.mask
+
+ if mask and sc.mode == 'MASKEDITING':
+ return mask.shapes.active and mask.shapes.active.splines.active_point
+
+ return False
+
+ def draw(self, context):
+ layout = self.layout
+
+ sc = context.space_data
+ mask = sc.mask
+ point = mask.shapes.active.splines.active_point
+ parent = point.parent
+
+ col = layout.column()
+ col.prop(point, "handle_type")
+
+ col = layout.column()
+ col.prop(parent, "use_parent", text="Parent")
+ if parent.use_parent:
+ # Currently only parenting yo movie clip is allowed, so do not
+ # ver-oplicate things for now and use single template_ID
+ #col.template_any_ID(parent, "id", "id_type", text="")
+
+ col.template_ID(parent, "id")
+
+ if parent.id_type == 'MOVIECLIP' and parent.id:
+ clip = parent.id
+ tracking = clip.tracking
+
+ col.prop_search(parent, "parent", tracking, "objects", icon='OBJECT_DATA', text="Object:")
+
+ if parent.parent and parent.parent in tracking.objects:
+ object = clip.tracking.objects[parent.parent]
+ col.prop_search(parent, "sub_parent", object, "tracks", icon='ANIM_DATA', text="Track:")
+ else:
+ col.prop_search(parent, "sub_parent", clip.tracking, "tracks", icon='ANIM_DATA', text="Track:")
+
+
class CLIP_PT_display(CLIP_PT_clip_view_panel, Panel):
bl_space_type = 'CLIP_EDITOR'
bl_region_type = 'UI'
@@ -595,6 +706,12 @@ class CLIP_PT_marker_display(CLIP_PT_clip_view_panel, Panel):
bl_region_type = 'UI'
bl_label = "Marker Display"
+ @classmethod
+ def poll(cls, context):
+ sc = context.space_data
+
+ return sc.mode != 'MASKEDITING'
+
def draw(self, context):
layout = self.layout
sc = context.space_data
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)