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