Tomato: initial commit of mask editing tools into SVN
authorSergey Sharybin <sergey.vfx@gmail.com>
Mon, 30 Apr 2012 07:43:21 +0000 (07:43 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Mon, 30 Apr 2012 07:43:21 +0000 (07:43 +0000)
- Added new dtaablock called Mask which might be re-used in any area.
  Currently editing of masks happens in clip editor and they might be used in
  compositor nodes only.
- Added new mode to clip clip editor to interact with masks.
  Implemented basic tools to create shapes, splines and points. Also implemented
  idea of UW points for feather which means feather points are have got U
  coordinate along spline (which is measured from 0 to 1) and W is it's weight
  meaning distance from main spline.
- Spline points might be parented to movie tracks.
  Interface for this isn't best yet.
- Rasterisaztion of masks happens in compositor node (Input -> Mask)
  Input image of this ode is used as reference for mask resolution.
  Currently all splines of all shapes are rasterizing independently which
  means shapes with holes are not supported.
  Also feather rasterization is not implemented.

  Rasterized was implemented by Pete Larbell, thanks!

Do not consider this is something finished, there's still lots of things to
be done (especially from interface and usability points of view).

71 files changed:
intern/CMakeLists.txt
intern/SConscript
intern/raskter/CMakeLists.txt [new file with mode: 0644]
intern/raskter/SConscript [new file with mode: 0644]
intern/raskter/raskter.c [new file with mode: 0644]
intern/raskter/raskter.h [new file with mode: 0644]
release/scripts/modules/bpy_extras/keyconfig_utils.py
release/scripts/startup/bl_ui/space_clip.py
source/blender/CMakeLists.txt
source/blender/blenkernel/BKE_context.h
source/blender/blenkernel/BKE_library.h
source/blender/blenkernel/BKE_main.h
source/blender/blenkernel/BKE_mask.h [new file with mode: 0644]
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/CMakeLists.txt
source/blender/blenkernel/intern/context.c
source/blender/blenkernel/intern/depsgraph.c
source/blender/blenkernel/intern/idcode.c
source/blender/blenkernel/intern/library.c
source/blender/blenkernel/intern/mask.c [new file with mode: 0644]
source/blender/blenkernel/intern/node.c
source/blender/blenkernel/intern/scene.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/CMakeLists.txt
source/blender/editors/SConscript
source/blender/editors/include/ED_clip.h
source/blender/editors/include/ED_mask.h [new file with mode: 0644]
source/blender/editors/include/ED_screen.h
source/blender/editors/include/ED_transform.h
source/blender/editors/interface/interface_templates.c
source/blender/editors/mask/CMakeLists.txt [new file with mode: 0644]
source/blender/editors/mask/SConscript [new file with mode: 0644]
source/blender/editors/mask/mask_draw.c [new file with mode: 0644]
source/blender/editors/mask/mask_editor.c [new file with mode: 0644]
source/blender/editors/mask/mask_intern.h [new file with mode: 0644]
source/blender/editors/mask/mask_ops.c [new file with mode: 0644]
source/blender/editors/screen/screen_ops.c
source/blender/editors/space_api/spacetypes.c
source/blender/editors/space_clip/clip_editor.c
source/blender/editors/space_clip/space_clip.c
source/blender/editors/space_node/drawnode.c
source/blender/editors/space_node/space_node.c
source/blender/editors/transform/transform.c
source/blender/editors/transform/transform.h
source/blender/editors/transform/transform_conversions.c
source/blender/editors/transform/transform_generics.c
source/blender/editors/transform/transform_ops.c
source/blender/makesdna/DNA_ID.h
source/blender/makesdna/DNA_mask_types.h [new file with mode: 0644]
source/blender/makesdna/DNA_space_types.h
source/blender/makesdna/intern/makesdna.c
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/CMakeLists.txt
source/blender/makesrna/intern/makesrna.c
source/blender/makesrna/intern/rna_ID.c
source/blender/makesrna/intern/rna_internal.h
source/blender/makesrna/intern/rna_main.c
source/blender/makesrna/intern/rna_main_api.c
source/blender/makesrna/intern/rna_mask.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_nodetree.c
source/blender/makesrna/intern/rna_nodetree_types.h
source/blender/makesrna/intern/rna_space.c
source/blender/nodes/CMakeLists.txt
source/blender/nodes/NOD_composite.h
source/blender/nodes/composite/node_composite_tree.c
source/blender/nodes/composite/nodes/node_composite_mask.c [new file with mode: 0644]
source/blender/windowmanager/WM_types.h
source/blenderplayer/CMakeLists.txt
source/blenderplayer/bad_level_call_stubs/stubs.c
source/creator/CMakeLists.txt

index c85f0fd..71d2ef5 100644 (file)
@@ -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)
index b6305f7..6f08c69 100644 (file)
@@ -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 (file)
index 0000000..3e1368d
--- /dev/null
@@ -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 (file)
index 0000000..7ad505d
--- /dev/null
@@ -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 (file)
index 0000000..d5890f1
--- /dev/null
@@ -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 (file)
index 0000000..0b3cf36
--- /dev/null
@@ -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
index 20b0669..306371e 100644 (file)
@@ -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', []),
index e4f608e..e72778e 100644 (file)
@@ -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
index cb17a87..dc7e794 100644 (file)
@@ -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)
index b2bd840..c170cbb 100644 (file)
@@ -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);
 
index be21996..d97e035 100644 (file)
@@ -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);
index ffcbb6e..382ce7c 100644 (file)
@@ -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 (file)
index 0000000..268b7df
--- /dev/null
@@ -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
index a4eddd0..4f3a6d4 100644 (file)
@@ -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
index c2a83b5..6146104 100644 (file)
@@ -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
index 7a5b4ef..639af36 100644 (file)
@@ -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");
index d8cdd72..95ee3b3 100644 (file)
@@ -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) */
index cd24668..1f4d9d0 100644 (file)
@@ -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]);
 
index 2924ea4..bd147db 100644 (file)
@@ -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"
 #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 (file)
index 0000000..ad0baa9
--- /dev/null
@@ -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;
+}
index b2a85ad..19713f6 100644 (file)
@@ -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) 
index 8642608..2a311a8 100644 (file)
@@ -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
index 9934265..753cd93 100644 (file)
@@ -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 */
 
index 5580c9e..e11e027 100644 (file)
@@ -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);
index 088376b..67ed77b 100644 (file)
@@ -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)
index ed66a76..d08b496 100644 (file)
@@ -8,6 +8,7 @@ SConscript(['datafiles/SConscript',
             'interface/SConscript',
             'animation/SConscript',
             'armature/SConscript',
+            'mask/SConscript',
             'mesh/SConscript',
             'metaball/SConscript',
             'object/SConscript',
index db6d9bb..285f148 100644 (file)
@@ -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 (file)
index 0000000..5df91b4
--- /dev/null
@@ -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 */
index f62befd..44a66d4 100644 (file)
@@ -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 */
index f6dee35..2d6379a 100644 (file)
@@ -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)
index bafd85e..c6bc975 100644 (file)
@@ -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 (file)
index 0000000..fc1d2f4
--- /dev/null
@@ -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 (file)
index 0000000..4af000d
--- /dev/null
@@ -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 (file)
index 0000000..d898a1c
--- /dev/null
@@ -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 (file)
index 0000000..fcdf66e
--- /dev/null
@@ -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 (file)
index 0000000..073eba3
--- /dev/null
@@ -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 (file)
index 0000000..0fedc2b
--- /dev/null
@@ -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");
+}
index 49672b7..e6fa4d4 100644 (file)
@@ -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:  
index 3124318..6733d95 100644 (file)
@@ -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);
index e615802..2e7de31 100644 (file)
 #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);
+}
index 80db323..04b1c92 100644 (file)
@@ -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);
 
index bd17c82..883eb96 100644 (file)
@@ -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;
        }
index 3540c20..119b350 100644 (file)
@@ -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) {
index 6d72ca9..5fe8444 100644 (file)
@@ -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);
index 69af8cf..3fb86ac 100644 (file)
@@ -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 */
index ecd69f0..85e970a 100644 (file)
@@ -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"
 #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;
index 0bf02d1..7f6cc9c 100644 (file)
@@ -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 */
index 83d4a5d..06355d6 100644 (file)
@@ -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;
index 1737f3b..2c664a7 100644 (file)
@@ -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 (file)
index 0000000..7597a21
--- /dev/null
@@ -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__
index c2fbc61..ad13f50 100644 (file)
@@ -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
index adcdf5d..d66682b 100644 (file)
@@ -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 */
index 316ea46..de2edbe 100644 (file)
@@ -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;
index 6f8e765..71c0385 100644 (file)
@@ -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
index 62957ac..1a4e8b3 100644 (file)
@@ -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)
index 6992d99..0be9564 100644 (file)
@@ -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;
        }
 }
index 155c9c6..b52465f 100644 (file)
@@ -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 */
 
index 1a03929..a6d7f7d 100644 (file)
@@ -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;
index 63006af..8f4d1aa 100644 (file)
@@ -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 (file)
index 0000000..3d4a5a3
--- /dev/null
@@ -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
index 58fd936..a6c3c66 100644 (file)
@@ -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;
index 5352bbd..e536314 100644 (file)
@@ -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",           ""              )
index 47bad8f..532690e 100644 (file)
@@ -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");
index 9bcbc91..5320d9f 100644 (file)
@@ -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
index 284b89b..4378c6a 100644 (file)
@@ -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);
index 049b5dd..ca8922e 100644 (file)
@@ -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 (file)
index 0000000..2c49be4
--- /dev/null
@@ -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);
+}
index 7cbeab6..25abc17 100644 (file)
@@ -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
index 7edf531..70354c0 100644 (file)
@@ -154,6 +154,7 @@ endif()
                bf_blenkernel # duplicate for linking
                bf_intern_mikktspace
                extern_recastnavigation
+               bf_intern_raskter
        )
 
        if(WITH_MOD_CLOTH_ELTOPO)
index a036e8a..ad9bd92 100644 (file)
@@ -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) {}
index 0168c06..f81d632 100644 (file)
@@ -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)