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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_node.h2
-rw-r--r--source/blender/blenkernel/intern/node.c2
-rw-r--r--source/blender/blenlib/BLI_math_base.h3
-rw-r--r--source/blender/blenlib/BLI_math_color.h4
-rw-r--r--source/blender/blenlib/BLI_math_geom.h3
-rw-r--r--source/blender/blenlib/BLI_voronoi.h70
-rw-r--r--source/blender/blenlib/CMakeLists.txt1
-rw-r--r--source/blender/blenlib/intern/math_color.c84
-rw-r--r--source/blender/blenlib/intern/math_geom.c39
-rw-r--r--source/blender/blenlib/intern/voronoi.c833
-rw-r--r--source/blender/compositor/CMakeLists.txt12
-rw-r--r--source/blender/compositor/intern/COM_Converter.cpp7
-rw-r--r--source/blender/compositor/nodes/COM_KeyingNode.cpp211
-rw-r--r--source/blender/compositor/nodes/COM_KeyingNode.h44
-rw-r--r--source/blender/compositor/nodes/COM_KeyingScreenNode.cpp57
-rw-r--r--source/blender/compositor/nodes/COM_KeyingScreenNode.h36
-rw-r--r--source/blender/compositor/operations/COM_KeyingDispillOperation.cpp89
-rw-r--r--source/blender/compositor/operations/COM_KeyingDispillOperation.h49
-rw-r--r--source/blender/compositor/operations/COM_KeyingOperation.cpp114
-rw-r--r--source/blender/compositor/operations/COM_KeyingOperation.h57
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.cpp217
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.h79
-rw-r--r--source/blender/editors/space_node/drawnode.c37
-rw-r--r--source/blender/makesdna/DNA_node_types.h11
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c61
-rw-r--r--source/blender/makesrna/intern/rna_nodetree_types.h2
-rw-r--r--source/blender/nodes/CMakeLists.txt2
-rw-r--r--source/blender/nodes/NOD_composite.h3
-rw-r--r--source/blender/nodes/composite/node_composite_util.c16
-rw-r--r--source/blender/nodes/composite/node_composite_util.h4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_blur.c22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_dilate.c8
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keying.c218
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keyingscreen.c202
35 files changed, 2587 insertions, 13 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 7e925e545dc..fde6d93849d 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -655,6 +655,8 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMaterial *mat);
#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_KEYINGSCREEN 269
+#define CMP_NODE_KEYING 270
#define CMP_NODE_GLARE 301
#define CMP_NODE_TONEMAP 302
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index 54edd7064a1..35e838d94fa 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -1926,6 +1926,8 @@ static void registerCompositNodes(bNodeTreeType *ttype)
register_node_type_cmp_color_spill(ttype);
register_node_type_cmp_luma_matte(ttype);
register_node_type_cmp_doubleedgemask(ttype);
+ register_node_type_cmp_keyingscreen(ttype);
+ register_node_type_cmp_keying(ttype);
register_node_type_cmp_translate(ttype);
register_node_type_cmp_rotate(ttype);
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index 22672db9edb..b0e0d3cbf19 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -53,6 +53,9 @@
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.70710678118654752440
#endif
+#ifndef M_SQRT3
+#define M_SQRT3 1.7320508075688772
+#endif
#ifndef M_1_PI
#define M_1_PI 0.318309886183790671538
#endif
diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h
index 746a2b958ea..44f54c41129 100644
--- a/source/blender/blenlib/BLI_math_color.h
+++ b/source/blender/blenlib/BLI_math_color.h
@@ -69,6 +69,8 @@ void rgb_to_hsv(float r, float g, float b, float *lh, float *ls, float *lv);
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3]);
void rgb_to_hsv_compat(float r, float g, float b, float *lh, float *ls, float *lv);
void rgb_to_hsv_compat_v(const float rgb[3], float r_hsv[3]);
+void rgb_to_lab(float r, float g, float b, float *ll, float *la, float *lb);
+void rgb_to_xyz(float r, float g, float b, float *x, float *y, float *z);
unsigned int rgb_to_cpack(float r, float g, float b);
unsigned int hsv_to_cpack(float h, float s, float v);
@@ -112,6 +114,8 @@ void rgba_uchar_to_float(float col_r[4], const unsigned char col_ub[4]);
void rgb_float_to_uchar(unsigned char col_r[3], const float col_f[3]);
void rgba_float_to_uchar(unsigned char col_r[4], const float col_f[4]);
+void xyz_to_lab(float x, float y, float z, float *l, float *a, float *b);
+
/***************** lift/gamma/gain / ASC-CDL conversion *****************/
void lift_gamma_gain_to_asc_cdl(float *lift, float *gamma, float *gain, float *offset, float *slope, float *power);
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 0560a3f6e64..93599dee63d 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -191,6 +191,9 @@ void barycentric_transform(float pt_tar[3], float const pt_src[3],
void barycentric_weights_v2(const float v1[2], const float v2[2], const float v3[2],
const float co[2], float w[3]);
+int barycentric_coords_v2(const float v1[2], const float v2[2], const float v3[2], const float co[2], float w[3]);
+int barycentric_inside_triangle_v2(const float w[3]);
+
void resolve_tri_uv(float r_uv[2], const float st[2], const float st0[2], const float st1[2], const float st2[2]);
void resolve_quad_uv(float uv[2], const float st[2], const float st0[2], const float st1[2], const float st2[2], const float st3[2]);
diff --git a/source/blender/blenlib/BLI_voronoi.h b/source/blender/blenlib/BLI_voronoi.h
new file mode 100644
index 00000000000..a67b01c5175
--- /dev/null
+++ b/source/blender/blenlib/BLI_voronoi.h
@@ -0,0 +1,70 @@
+/*
+ * ***** 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): Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BLI_VORONOI_H__
+#define __BLI_VORONOI_H__
+
+struct ListBase;
+
+/** \file BLI_voronoi.h
+ * \ingroup bli
+ */
+
+typedef struct VoronoiSite {
+ float co[2];
+ float color[3];
+} VoronoiSite;
+
+typedef struct VoronoiEdge {
+ struct VoronoiEdge *next, *prev;
+
+ float start[2], end[2]; /* start and end points */
+
+ /* this fields are used during diagram computation only */
+
+ float direction[2]; /* directional vector, from "start", points to "end", normal of |left, right| */
+
+ float left[2]; /* point on Voronoi place on the left side of edge */
+ float right[2]; /* point on Voronoi place on the right side of edge */
+
+ float f, g; /* directional coeffitients satisfying equation y = f*x + g (edge lies on this line) */
+
+ /* some edges consist of two parts, so we add the pointer to another part to connect them at the end of an algorithm */
+ struct VoronoiEdge *neighbour;
+} VoronoiEdge;
+
+typedef struct VoronoiTriangulationPoint {
+ float co[2];
+ float color[3];
+ int power;
+} VoronoiTriangulationPoint;
+
+void BLI_voronoi_compute(const VoronoiSite *sites, int sites_total, int width, int height, struct ListBase *edges);
+
+void BLI_voronoi_triangulate(const VoronoiSite *sites, int sites_total, struct ListBase *edges, int width, int height,
+ VoronoiTriangulationPoint **triangulated_points_r, int *triangulated_points_total_r,
+ int (**triangles_r)[3], int *triangles_total_r);
+
+#endif /* __BLI_VORONOI_H__ */
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index d535e190314..ac7681e3be7 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -89,6 +89,7 @@ set(SRC
intern/time.c
intern/uvproject.c
intern/voxel.c
+ intern/voronoi.c
intern/winstuff.c
BLI_args.h
diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c
index 152fc98945f..9433cfd31df 100644
--- a/source/blender/blenlib/intern/math_color.c
+++ b/source/blender/blenlib/intern/math_color.c
@@ -264,6 +264,35 @@ void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
rgb_to_hsv(rgb[0], rgb[1], rgb[2], &r_hsv[0], &r_hsv[1], &r_hsv[2]);
}
+void rgb_to_hsl(float r, float g, float b, float *lh, float *ls, float *ll)
+{
+ float cmax = MAX3(r, g, b);
+ float cmin = MIN3(r, g, b);
+ float h, s, l = (cmax + cmin) / 2.0f;
+
+ if (cmax == cmin) {
+ h = s = 0.0f; // achromatic
+ }
+ else {
+ float d = cmax - cmin;
+ s = l > 0.5f ? d / (2.0f - cmax - cmin) : d / (cmax + cmin);
+ if (cmax == r) {
+ h = (g - b) / d + (g < b ? 6.0f : 0.0f);
+ }
+ else if (cmax == g) {
+ h = (b - r) / d + 2.0f;
+ }
+ else {
+ h = (r - g) / d + 4.0f;
+ }
+ }
+ h /= 6.0f;
+
+ *lh = h;
+ *ls = s;
+ *ll = l;
+}
+
void rgb_to_hsv_compat(float r, float g, float b, float *lh, float *ls, float *lv)
{
float orig_h = *lh;
@@ -603,3 +632,58 @@ void BLI_init_srgb_conversion(void)
BLI_color_to_srgb_table[i] = b * 0x100;
}
}
+static float inverse_srgb_companding(float v)
+{
+ if (v > 0.04045f) {
+ return powf((v + 0.055f) / 1.055f, 2.4);
+ }
+ else {
+ return v / 12.92f;
+ }
+}
+
+void rgb_to_xyz(float r, float g, float b, float *x, float *y, float *z)
+{
+ r = inverse_srgb_companding(r) * 100.0f;
+ g = inverse_srgb_companding(g) * 100.0f;
+ b = inverse_srgb_companding(b) * 100.0f;
+
+ *x = r * 0.4124 + g * 0.3576 + b * 0.1805;
+ *y = r * 0.2126 + g * 0.7152 + b * 0.0722;
+ *z = r * 0.0193 + g * 0.1192 + b * 0.9505;
+}
+
+static float xyz_to_lab_component(float v)
+{
+ const float eps = 0.008856f;
+ const float k = 903.3f;
+
+ if (v > eps) {
+ return pow(v, 1.0f / 3.0f);
+ }
+ else {
+ return (k * v + 16.0f) / 116.0f;
+ }
+}
+
+void xyz_to_lab(float x, float y, float z, float *l, float *a, float *b)
+{
+ float xr = x / 95.047f;
+ float yr = y / 100.0f;
+ float zr = z / 108.883f;
+
+ float fx = xyz_to_lab_component(xr);
+ float fy = xyz_to_lab_component(yr);
+ float fz = xyz_to_lab_component(zr);
+
+ *l = 116.0f * fy - 16.0f;
+ *a = 500.0f * (fx - fy);
+ *b = 200.0f * (fy - fz);
+}
+
+void rgb_to_lab(float r, float g, float b, float *ll, float *la, float *lb)
+{
+ float x, y, z;
+ rgb_to_xyz(r, g, b, &x, &y, &z);
+ xyz_to_lab(x, y, z, ll, la, lb);
+}
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 3962f53862d..d35624e84d2 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -1825,6 +1825,45 @@ void interp_weights_face_v3(float w[4], const float v1[3], const float v2[3], co
}
}
+/* return 1 of point is inside triangle, 2 if it's on the edge, 0 if point is outside of triangle */
+int barycentric_inside_triangle_v2(const float w[3])
+{
+ if (IN_RANGE(w[0], 0.0f, 1.0f) &&
+ IN_RANGE(w[1], 0.0f, 1.0f) &&
+ IN_RANGE(w[2], 0.0f, 1.0f))
+ {
+ return 1;
+ }
+ else if (IN_RANGE_INCL(w[0], 0.0f, 1.0f) &&
+ IN_RANGE_INCL(w[1], 0.0f, 1.0f) &&
+ IN_RANGE_INCL(w[2], 0.0f, 1.0f))
+ {
+ return 2;
+ }
+
+ return 0;
+}
+
+/* returns 0 for degenerated triangles */
+int barycentric_coords_v2(const float v1[2], const float v2[2], const float v3[2], const float co[2], float w[3])
+{
+ float x = co[0], y = co[1];
+ float x1 = v1[0], y1 = v1[1];
+ float x2 = v2[0], y2 = v2[1];
+ float x3 = v3[0], y3 = v3[1];
+ float det = (y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3);
+
+ if (fabsf(det) > FLT_EPSILON) {
+ w[0] = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) / det;
+ w[1] = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) / det;
+ w[2] = 1.0f - w[0] - w[1];
+
+ return 1;
+ }
+
+ return 0;
+}
+
/* used by projection painting
* note: using area_tri_signed_v2 means locations outside the triangle are correctly weighted */
void barycentric_weights_v2(const float v1[2], const float v2[2], const float v3[2], const float co[2], float w[3])
diff --git a/source/blender/blenlib/intern/voronoi.c b/source/blender/blenlib/intern/voronoi.c
new file mode 100644
index 00000000000..0088d24d741
--- /dev/null
+++ b/source/blender/blenlib/intern/voronoi.c
@@ -0,0 +1,833 @@
+/*
+ * ***** 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): Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/*
+ * Fortune's algorithm implemented using explanation and some code snippets from
+ * http://blog.ivank.net/fortunes-algorithm-and-implementation.html
+ */
+
+/** \file blender/blenkernel/intern/tracking.c
+ * \ingroup bli
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_voronoi.h"
+#include "BLI_utildefines.h"
+
+#define VORONOI_EPS 1e-3
+
+enum {
+ voronoiEventType_Site = 0,
+ voronoiEventType_Circle = 1
+} voronoiEventType;
+
+typedef struct VoronoiEvent {
+ struct VoronoiEvent *next, *prev;
+
+ int type; /* type of event (site or circle) */
+ float site[2]; /* site for which event was generated */
+
+ struct VoronoiParabola *parabola; /* parabola for which event was generated */
+} VoronoiEvent;
+
+typedef struct VoronoiParabola {
+ struct VoronoiParabola *left, *right, *parent;
+ VoronoiEvent *event;
+ int is_leaf;
+ float site[2];
+ VoronoiEdge *edge;
+} VoronoiParabola;
+
+typedef struct VoronoiProcess {
+ ListBase queue, edges;
+ VoronoiParabola *root;
+ int width, height;
+ float current_y;
+} VoronoiProcess;
+
+/* event */
+
+static void voronoi_insertEvent(VoronoiProcess *process, VoronoiEvent *event)
+{
+ VoronoiEvent *current_event = process->queue.first;
+
+ while (current_event) {
+ if (current_event->site[1] < event->site[1]) {
+ break;
+ }
+ if (current_event->site[1] == event->site[1]) {
+ event->site[1] -= VORONOI_EPS;
+ }
+
+ current_event = current_event->next;
+ }
+
+ BLI_insertlinkbefore(&process->queue, current_event, event);
+}
+
+/* edge */
+static VoronoiEdge *voronoiEdge_new(float start[2], float left[2], float right[2])
+{
+ VoronoiEdge *edge = MEM_callocN(sizeof(VoronoiEdge), "voronoi edge");
+
+ copy_v2_v2(edge->start, start);
+ copy_v2_v2(edge->left, left);
+ copy_v2_v2(edge->right, right);
+
+ edge->neighbour = NULL;
+ edge->end[0] = 0;
+ edge->end[1] = 0;
+
+ edge->f = (right[0] - left[0]) / (left[1] - right[1]);
+ edge->g = start[1] - edge->f * start[0];
+
+ edge->direction[0] = right[1] - left[1];
+ edge->direction[1] = -(right[0] - left[0]);
+
+ return edge;
+}
+
+/* parabola */
+
+static VoronoiParabola *voronoiParabola_new(void)
+{
+ VoronoiParabola *parabola = MEM_callocN(sizeof(VoronoiParabola), "voronoi parabola");
+
+ parabola->is_leaf = FALSE;
+ parabola->event = NULL;
+ parabola->edge = NULL;
+ parabola->parent = 0;
+
+ return parabola;
+}
+
+static VoronoiParabola *voronoiParabola_newSite(float site[2])
+{
+ VoronoiParabola *parabola = MEM_callocN(sizeof(VoronoiParabola), "voronoi parabola site");
+
+ copy_v2_v2(parabola->site, site);
+ parabola->is_leaf = TRUE;
+ parabola->event = NULL;
+ parabola->edge = NULL;
+ parabola->parent = 0;
+
+ return parabola;
+}
+
+/* returns the closest leave which is on the left of current node */
+static VoronoiParabola *voronoiParabola_getLeftChild(VoronoiParabola *parabola)
+{
+ VoronoiParabola *current_parabola;
+
+ if (!parabola)
+ return NULL;
+
+ current_parabola = parabola->left;
+ while (!current_parabola->is_leaf) {
+ current_parabola = current_parabola->right;
+ }
+
+ return current_parabola;
+}
+
+/* returns the closest leave which is on the right of current node */
+static VoronoiParabola *voronoiParabola_getRightChild(VoronoiParabola *parabola)
+{
+ VoronoiParabola *current_parabola;
+
+ if (!parabola)
+ return NULL;
+
+ current_parabola = parabola->right;
+ while (!current_parabola->is_leaf) {
+ current_parabola = current_parabola->left;
+ }
+
+ return current_parabola;
+}
+
+/* returns the closest parent which is on the left */
+static VoronoiParabola *voronoiParabola_getLeftParent(VoronoiParabola *parabola)
+{
+ VoronoiParabola *current_par = parabola->parent;
+ VoronoiParabola *last_parabola = parabola;
+
+ while (current_par->left == last_parabola) {
+ if (!current_par->parent)
+ return NULL;
+
+ last_parabola = current_par;
+ current_par = current_par->parent;
+ }
+
+ return current_par;
+}
+
+/* returns the closest parent which is on the right */
+static VoronoiParabola *voronoiParabola_getRightParent(VoronoiParabola *parabola)
+{
+ VoronoiParabola *current_parabola = parabola->parent;
+ VoronoiParabola *last_parabola = parabola;
+
+ while (current_parabola->right == last_parabola) {
+ if (!current_parabola->parent)
+ return NULL;
+
+ last_parabola = current_parabola;
+ current_parabola = current_parabola->parent;
+ }
+
+ return current_parabola;
+}
+
+static void voronoiParabola_setLeft(VoronoiParabola *parabola, VoronoiParabola *left)
+{
+ parabola->left = left;
+ left->parent = parabola;
+}
+
+static void voronoiParabola_setRight(VoronoiParabola *parabola, VoronoiParabola *right)
+{
+ parabola->right = right;
+ right->parent = parabola;
+}
+
+static float voronoi_getY(VoronoiProcess *process, float p[2], float x)
+{
+ float ly = process->current_y;
+
+ float dp = 2 * (p[1] - ly);
+ float a1 = 1 / dp;
+ float b1 = -2 * p[0] / dp;
+ float c1 = ly + dp / 4 + p[0] * p[0] / dp;
+
+ return a1 * x * x + b1 * x + c1;
+}
+
+static float voronoi_getXOfEdge(VoronoiProcess *process, VoronoiParabola *par, float y)
+{
+ VoronoiParabola *left = voronoiParabola_getLeftChild(par);
+ VoronoiParabola *right = voronoiParabola_getRightChild(par);
+ float p[2], r[2];
+ float dp, a1, b1, c1, a2, b2, c2, a, b, c, disc, ry, x1, x2;
+ float ly = process->current_y;
+
+ copy_v2_v2(p, left->site);
+ copy_v2_v2(r, right->site);
+
+ dp = 2.0f * (p[1] - y);
+ a1 = 1.0f / dp;
+ b1 = -2.0f * p[0] / dp;
+ c1 = y + dp / 4 + p[0] * p[0] / dp;
+
+ dp = 2.0f * (r[1] - y);
+ a2 = 1.0f / dp;
+ b2 = -2.0f * r[0] / dp;
+ c2 = ly + dp / 4 + r[0] * r[0] / dp;
+
+ a = a1 - a2;
+ b = b1 - b2;
+ c = c1 - c2;
+
+ disc = b*b - 4 * a * c;
+ x1 = (-b + sqrtf(disc)) / (2*a);
+ x2 = (-b - sqrtf(disc)) / (2*a);
+
+ if (p[1] < r[1])
+ ry = MAX2(x1, x2);
+ else
+ ry = MIN2(x1, x2);
+
+ return ry;
+}
+
+static VoronoiParabola *voronoi_getParabolaByX(VoronoiProcess *process, float xx)
+{
+ VoronoiParabola * par = process->root;
+ float x = 0.0f;
+ float ly = process->current_y;
+
+ while (!par->is_leaf) {
+ x = voronoi_getXOfEdge(process, par, ly);
+
+ if (x > xx)
+ par = par->left;
+ else
+ par = par->right;
+ }
+
+ return par;
+}
+
+static int voronoi_getEdgeIntersection(VoronoiEdge *a, VoronoiEdge *b, float p[2])
+{
+ float x = (b->g - a->g) / (a->f - b->f);
+ float y = a->f * x + a->g;
+
+ if ((x - a->start[0]) / a->direction[0] < 0)
+ return 0;
+
+ if ((y - a->start[1]) / a->direction[1] < 0)
+ return 0;
+
+ if ((x - b->start[0]) / b->direction[0] < 0)
+ return 0;
+
+ if ((y - b->start[1]) / b->direction[1] < 0)
+ return 0;
+
+ p[0] = x;
+ p[1] = y;
+
+ return 1;
+}
+
+static void voronoi_checkCircle(VoronoiProcess *process, VoronoiParabola *b)
+{
+ VoronoiParabola *lp = voronoiParabola_getLeftParent(b);
+ VoronoiParabola *rp = voronoiParabola_getRightParent(b);
+
+ VoronoiParabola *a = voronoiParabola_getLeftChild(lp);
+ VoronoiParabola *c = voronoiParabola_getRightChild(rp);
+
+ VoronoiEvent *event;
+
+ float ly = process->current_y;
+ float s[2], dx, dy, d;
+
+ if (!a || !c || len_squared_v2v2(a->site, c->site) < VORONOI_EPS)
+ return;
+
+ if (!voronoi_getEdgeIntersection(lp->edge, rp->edge, s))
+ return;
+
+ dx = a->site[0] - s[0];
+ dy = a->site[1] - s[1];
+
+ d = sqrtf((dx * dx) + (dy * dy));
+
+ if (s[1] - d >= ly)
+ return;
+
+ event = MEM_callocN(sizeof(VoronoiEvent), "voronoi circle event");
+
+ event->type = voronoiEventType_Circle;
+
+ event->site[0] = s[0];
+ event->site[1] = s[1] - d;
+
+ b->event = event;
+ event->parabola = b;
+
+ voronoi_insertEvent(process, event);
+}
+
+static void voronoi_addParabola(VoronoiProcess *process, float site[2])
+{
+ VoronoiParabola *root = process->root;
+ VoronoiParabola *par, *p0, *p1, *p2;
+ VoronoiEdge *el, *er;
+ float start[2];
+
+ if (!process->root) {
+ process->root = voronoiParabola_newSite(site);
+
+ return;
+ }
+
+ if (root->is_leaf && root->site[1] - site[1] < 0) {
+ float *fp = root->site;
+ float s[2];
+
+ root->is_leaf = FALSE;
+ voronoiParabola_setLeft(root, voronoiParabola_newSite(fp));
+ voronoiParabola_setRight(root, voronoiParabola_newSite(site));
+
+ s[0] = (site[0] + fp[0]) / 2.0f;
+ s[1] = process->height;
+
+ if(site[0] > fp[0])
+ root->edge = voronoiEdge_new(s, fp, site);
+ else
+ root->edge = voronoiEdge_new(s, site, fp);
+
+ BLI_addtail(&process->edges, root->edge);
+
+ return;
+ }
+
+ par = voronoi_getParabolaByX(process, site[0]);
+
+ if (par->event) {
+ BLI_freelinkN(&process->queue, par->event);
+
+ par->event = NULL;
+ }
+
+ start[0] = site[0];
+ start[1] = voronoi_getY(process, par->site, site[0]);
+
+ el = voronoiEdge_new(start, par->site, site);
+ er = voronoiEdge_new(start, site, par->site);
+
+ el->neighbour = er;
+ BLI_addtail(&process->edges, el);
+
+ par->edge = er;
+ par->is_leaf = FALSE;
+
+ p0 = voronoiParabola_newSite(par->site);
+ p1 = voronoiParabola_newSite(site);
+ p2 = voronoiParabola_newSite(par->site);
+
+ voronoiParabola_setRight(par, p2);
+ voronoiParabola_setLeft(par, voronoiParabola_new());
+ par->left->edge = el;
+
+ voronoiParabola_setLeft(par->left, p0);
+ voronoiParabola_setRight(par->left, p1);
+
+ voronoi_checkCircle(process, p0);
+ voronoi_checkCircle(process, p2);
+}
+
+static void voronoi_removeParabola(VoronoiProcess *process, VoronoiEvent *event)
+{
+ VoronoiParabola *p1 = event->parabola;
+
+ VoronoiParabola *xl = voronoiParabola_getLeftParent(p1);
+ VoronoiParabola *xr = voronoiParabola_getRightParent(p1);
+
+ VoronoiParabola *p0 = voronoiParabola_getLeftChild(xl);
+ VoronoiParabola *p2 = voronoiParabola_getRightChild(xr);
+
+ VoronoiParabola *higher = NULL, *par, *gparent;
+
+ float p[2];
+
+ if (p0->event) {
+ BLI_freelinkN(&process->queue, p0->event);
+ p0->event = NULL;
+ }
+
+ if (p2->event) {
+ BLI_freelinkN(&process->queue, p2->event);
+ p2->event = NULL;
+ }
+
+ p[0] = event->site[0];
+ p[1] = voronoi_getY(process, p1->site, event->site[0]);
+
+ copy_v2_v2(xl->edge->end, p);
+ copy_v2_v2(xr->edge->end, p);
+
+ par = p1;
+ while (par != process->root) {
+ par = par->parent;
+
+ if (par == xl)
+ higher = xl;
+ if (par == xr)
+ higher = xr;
+ }
+
+ higher->edge = voronoiEdge_new(p, p0->site, p2->site);
+ BLI_addtail(&process->edges, higher->edge);
+
+ gparent = p1->parent->parent;
+ if (p1->parent->left == p1) {
+ if (gparent->left == p1->parent)
+ voronoiParabola_setLeft(gparent, p1->parent->right);
+ if (gparent->right == p1->parent)
+ voronoiParabola_setRight(gparent, p1->parent->right);
+ }
+ else {
+ if (gparent->left == p1->parent)
+ voronoiParabola_setLeft(gparent, p1->parent->left);
+ if (gparent->right == p1->parent)
+ voronoiParabola_setRight(gparent, p1->parent->left);
+ }
+
+ MEM_freeN(p1->parent);
+ MEM_freeN(p1);
+
+ voronoi_checkCircle(process, p0);
+ voronoi_checkCircle(process, p2);
+}
+
+void voronoi_finishEdge(VoronoiProcess *process, VoronoiParabola *parabola)
+{
+ float mx;
+
+ if (parabola->is_leaf) {
+ MEM_freeN(parabola);
+ return;
+ }
+
+ if (parabola->edge->direction[0] > 0.0f)
+ mx = MAX2(process->width, parabola->edge->start[0] + 10);
+ else
+ mx = MIN2(0.0, parabola->edge->start[0] - 10);
+
+ parabola->edge->end[0] = mx;
+ parabola->edge->end[1] = mx * parabola->edge->f + parabola->edge->g;
+
+ voronoi_finishEdge(process, parabola->left);
+ voronoi_finishEdge(process, parabola->right);
+
+ MEM_freeN(parabola);
+}
+
+void voronoi_clampEdgeVertex(int width, int height, float *coord, float *other_coord)
+{
+ const float corners[4][2] = {{0.0f, 0.0f},
+ {width - 1, 0.0f},
+ {width - 1, height - 1},
+ {0.0f, height - 1}};
+ int i;
+
+ if (IN_RANGE_INCL(coord[0], 0, width-1) && IN_RANGE_INCL(coord[1], 0, height-1)) {
+ return;
+ }
+
+ for (i = 0; i < 4; i++) {
+ float v1[2], v2[2];
+ float p[2];
+
+ copy_v2_v2(v1, corners[i]);
+
+ if (i == 3)
+ copy_v2_v2(v2, corners[0]);
+ else
+ copy_v2_v2(v2, corners[i + 1]);
+
+ if (isect_seg_seg_v2_point(v1, v2, coord, other_coord, p) == 1) {
+ if (i == 0 && coord[1] > p[1])
+ continue;
+ if (i == 1 && coord[0] < p[0])
+ continue;
+ if (i == 2 && coord[1] < p[1])
+ continue;
+ if (i == 3 && coord[0] > p[0])
+ continue;
+
+ copy_v2_v2(coord, p);
+ }
+ }
+}
+
+void voronoi_clampEdges(ListBase *edges, int width, int height, ListBase *clamped_edges)
+{
+ VoronoiEdge *edge;
+
+ edge = edges->first;
+ while (edge) {
+ VoronoiEdge *new_edge = MEM_callocN(sizeof(VoronoiEdge), "clamped edge");
+
+ *new_edge = *edge;
+ BLI_addtail(clamped_edges, new_edge);
+
+ voronoi_clampEdgeVertex(width, height, new_edge->start, new_edge->end);
+ voronoi_clampEdgeVertex(width, height, new_edge->end, new_edge->start);
+
+ edge = edge->next;
+ }
+}
+
+static int voronoi_getNextSideCoord(ListBase *edges, float coord[2], int dim, int dir, float next_coord[2])
+{
+ VoronoiEdge *edge = edges->first;
+ float distance = FLT_MAX;
+ int other_dim = dim ? 0 : 1;
+
+ while (edge) {
+ int ok = FALSE;
+ float co[2], cur_distance;
+
+ if (fabsf(edge->start[other_dim] - coord[other_dim]) < VORONOI_EPS &&
+ len_squared_v2v2(coord, edge->start) > VORONOI_EPS)
+ {
+ copy_v2_v2(co, edge->start);
+ ok = TRUE;
+ }
+
+ if (fabsf(edge->end[other_dim] - coord[other_dim]) < VORONOI_EPS &&
+ len_squared_v2v2(coord, edge->end) > VORONOI_EPS)
+ {
+ copy_v2_v2(co, edge->end);
+ ok = TRUE;
+ }
+
+ if (ok) {
+ if (dir > 0 && coord[dim] > co[dim]) {
+ ok = FALSE;
+ }
+ else if (dir < 0 && coord[dim] < co[dim]) {
+ ok = FALSE;
+ }
+ }
+
+ if (ok) {
+ cur_distance = len_squared_v2v2(coord, co);
+ if (cur_distance < distance) {
+ copy_v2_v2(next_coord, co);
+ distance = cur_distance;
+ }
+ }
+
+ edge = edge->next;
+ }
+
+ return distance < FLT_MAX;
+}
+
+static void voronoi_createBoundaryEdges(ListBase *edges, int width, int height)
+{
+ const float corners[4][2] = {{width - 1, 0.0f},
+ {width - 1, height - 1},
+ {0.0f, height - 1},
+ {0.0f, 0.0f}};
+ int i, dim = 0, dir = 1;
+
+ float coord[2] = {0.0f, 0.0f};
+ float next_coord[2] = {0.0f, 0.0f};
+
+ for (i = 0; i < 4; i++) {
+ while (voronoi_getNextSideCoord(edges, coord, dim, dir, next_coord)) {
+ VoronoiEdge *edge = MEM_callocN(sizeof(VoronoiEdge), "boundary edge");
+
+ copy_v2_v2(edge->start, coord);
+ copy_v2_v2(edge->end, next_coord);
+ BLI_addtail(edges, edge);
+
+ copy_v2_v2(coord, next_coord);
+ }
+
+ if (len_squared_v2v2(coord, corners[i]) > VORONOI_EPS) {
+ VoronoiEdge *edge = MEM_callocN(sizeof(VoronoiEdge), "boundary edge");
+
+ copy_v2_v2(edge->start, coord);
+ copy_v2_v2(edge->end, corners[i]);
+ BLI_addtail(edges, edge);
+ copy_v2_v2(coord, corners[i]);
+ }
+
+ dim = dim ? 0 : 1;
+ if (i == 1)
+ dir = -1;
+ }
+}
+
+void BLI_voronoi_compute(const VoronoiSite *sites, int sites_total, int width, int height, ListBase *edges)
+{
+ VoronoiProcess process;
+ VoronoiEdge *edge;
+ int i;
+
+ memset(&process, 0, sizeof(VoronoiProcess));
+
+ process.width = width;
+ process.height = height;
+
+ for (i = 0; i < sites_total; i++) {
+ VoronoiEvent *event = MEM_callocN(sizeof(VoronoiEvent), "voronoi site event");
+
+ event->type = voronoiEventType_Site;
+ copy_v2_v2(event->site, sites[i].co);
+
+ voronoi_insertEvent(&process, event);
+ }
+
+ while (process.queue.first) {
+ VoronoiEvent *event = process.queue.first;
+
+ process.current_y = event->site[1];
+
+ if (event->type == voronoiEventType_Site) {
+ voronoi_addParabola(&process, event->site);
+ }
+ else {
+ voronoi_removeParabola(&process, event);
+ }
+
+ BLI_freelinkN(&process.queue, event);
+ }
+
+ voronoi_finishEdge(&process, process.root);
+
+ edge = process.edges.first;
+ while (edge) {
+ if (edge->neighbour) {
+ copy_v2_v2(edge->start, edge->neighbour->end);
+ MEM_freeN(edge->neighbour);
+ }
+
+ edge = edge->next;
+ }
+
+ BLI_movelisttolist(edges, &process.edges);
+}
+
+static int testVoronoiEdge(const float site[2], const float point[2], const VoronoiEdge *edge)
+{
+ float p[2];
+
+ if (isect_seg_seg_v2_point(site, point, edge->start, edge->end, p) == 1) {
+ if (len_squared_v2v2(p, edge->start) > VORONOI_EPS &&
+ len_squared_v2v2(p, edge->end) > VORONOI_EPS)
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static int voronoi_addTriangulationPoint(const float coord[2], const float color[3],
+ VoronoiTriangulationPoint **triangulated_points,
+ int *triangulated_points_total)
+{
+ VoronoiTriangulationPoint *triangulation_point;
+ int i;
+
+ for (i = 0; i < *triangulated_points_total; i++) {
+ if (equals_v2v2(coord, (*triangulated_points)[i].co)) {
+ triangulation_point = &(*triangulated_points)[i];
+
+ add_v3_v3(triangulation_point->color, color);
+ triangulation_point->power++;
+
+ return i;
+ }
+ }
+
+ if (*triangulated_points) {
+ *triangulated_points = MEM_reallocN(*triangulated_points,
+ sizeof(VoronoiTriangulationPoint) * (*triangulated_points_total + 1));
+ }
+ else {
+ *triangulated_points = MEM_callocN(sizeof(VoronoiTriangulationPoint), "triangulation points");
+ }
+
+ triangulation_point = &(*triangulated_points)[(*triangulated_points_total)];
+ copy_v2_v2(triangulation_point->co, coord);
+ copy_v3_v3(triangulation_point->color, color);
+
+ triangulation_point->power = 1;
+
+ (*triangulated_points_total)++;
+
+ return (*triangulated_points_total) - 1;
+}
+
+static void voronoi_addTriangle(int v1, int v2, int v3, int (**triangles)[3], int *triangles_total)
+{
+ int *triangle;
+
+ if (*triangles) {
+ *triangles = MEM_reallocN(*triangles, sizeof(int[3]) * (*triangles_total + 1));
+ }
+ else {
+ *triangles = MEM_callocN(sizeof(int[3]), "trianglulation triangles");
+ }
+
+ triangle = (int*)&(*triangles)[(*triangles_total)];
+
+ triangle[0] = v1;
+ triangle[1] = v2;
+ triangle[2] = v3;
+
+ (*triangles_total)++;
+}
+
+void BLI_voronoi_triangulate(const VoronoiSite *sites, int sites_total, ListBase *edges, int width, int height,
+ VoronoiTriangulationPoint **triangulated_points_r, int *triangulated_points_total_r,
+ int (**triangles_r)[3], int *triangles_total_r)
+{
+ VoronoiTriangulationPoint *triangulated_points = NULL;
+ int (*triangles)[3] = NULL;
+ int triangulated_points_total = 0, triangles_total = 0;
+ int i;
+ ListBase boundary_edges = {NULL, NULL};
+
+ voronoi_clampEdges(edges, width, height, &boundary_edges);
+ voronoi_createBoundaryEdges(&boundary_edges, width, height);
+
+ for (i = 0; i < sites_total; i++) {
+ VoronoiEdge *edge;
+ int v1;
+
+ v1 = voronoi_addTriangulationPoint(sites[i].co, sites[i].color, &triangulated_points, &triangulated_points_total);
+
+ edge = boundary_edges.first;
+ while (edge) {
+ VoronoiEdge *test_edge = boundary_edges.first;
+ int ok_start = TRUE, ok_end = TRUE;
+
+ while (test_edge) {
+ float v1[2], v2[2];
+
+ sub_v2_v2v2(v1, edge->start, sites[i].co);
+ sub_v2_v2v2(v2, edge->end, sites[i].co);
+
+ if (ok_start && !testVoronoiEdge(sites[i].co, edge->start, test_edge))
+ ok_start = FALSE;
+
+ if (ok_end && !testVoronoiEdge(sites[i].co, edge->end, test_edge))
+ ok_end = FALSE;
+
+ test_edge = test_edge->next;
+ }
+
+ if (ok_start && ok_end) {
+ int v2, v3;
+
+ v2 = voronoi_addTriangulationPoint(edge->start, sites[i].color, &triangulated_points, &triangulated_points_total);
+ v3 = voronoi_addTriangulationPoint(edge->end, sites[i].color, &triangulated_points, &triangulated_points_total);
+
+ voronoi_addTriangle(v1, v2, v3, &triangles, &triangles_total);
+ }
+
+ edge = edge->next;
+ }
+ }
+
+ for (i = 0; i < triangulated_points_total; i++) {
+ VoronoiTriangulationPoint *triangulation_point = &triangulated_points[i];
+
+ mul_v3_fl(triangulation_point->color, 1.0f / triangulation_point->power);
+ }
+
+ *triangulated_points_r = triangulated_points;
+ *triangulated_points_total_r = triangulated_points_total;
+
+ *triangles_r = triangles;
+ *triangles_total_r = triangles_total;
+
+ BLI_freelistN(&boundary_edges);
+}
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 5e778d4d03b..d4e083f9ec1 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -331,6 +331,18 @@ set(SRC
operations/COM_DoubleEdgeMaskOperation.cpp
operations/COM_DoubleEdgeMaskOperation.h
+
+ nodes/COM_KeyingNode.cpp
+ nodes/COM_KeyingNode.h
+ nodes/COM_KeyingScreenNode.cpp
+ nodes/COM_KeyingScreenNode.h
+ operations/COM_KeyingOperation.cpp
+ operations/COM_KeyingOperation.h
+ operations/COM_KeyingScreenOperation.cpp
+ operations/COM_KeyingScreenOperation.h
+ operations/COM_KeyingDispillOperation.cpp
+ operations/COM_KeyingDispillOperation.h
+
operations/COM_ColorSpillOperation.cpp
operations/COM_ColorSpillOperation.h
operations/COM_RenderLayersBaseProg.cpp
diff --git a/source/blender/compositor/intern/COM_Converter.cpp b/source/blender/compositor/intern/COM_Converter.cpp
index dc6409e7b86..747d6495f5d 100644
--- a/source/blender/compositor/intern/COM_Converter.cpp
+++ b/source/blender/compositor/intern/COM_Converter.cpp
@@ -112,6 +112,8 @@
#include "COM_DoubleEdgeMaskNode.h"
#include "COM_CropNode.h"
#include "COM_MaskNode.h"
+#include "COM_KeyingScreenNode.h"
+#include "COM_KeyingNode.h"
Node *Converter::convert(bNode *bNode)
{
@@ -350,6 +352,11 @@ case CMP_NODE_OUTPUT_FILE:
break;
case CMP_NODE_MASK:
node = new MaskNode(bNode);
+ case CMP_NODE_KEYINGSCREEN:
+ node = new KeyingScreenNode(bNode);
+ break;
+ case CMP_NODE_KEYING:
+ node = new KeyingNode(bNode);
break;
/* not inplemented yet */
default:
diff --git a/source/blender/compositor/nodes/COM_KeyingNode.cpp b/source/blender/compositor/nodes/COM_KeyingNode.cpp
new file mode 100644
index 00000000000..725060371a1
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_KeyingNode.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2012, Blender Foundation.
+ *
+ * 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:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Sergey Sharybin
+ */
+
+#include "COM_KeyingNode.h"
+
+#include "COM_ExecutionSystem.h"
+
+#include "COM_KeyingOperation.h"
+#include "COM_KeyingDispillOperation.h"
+
+#include "COM_SeparateChannelOperation.h"
+#include "COM_CombineChannelsOperation.h"
+#include "COM_ConvertRGBToYCCOperation.h"
+#include "COM_ConvertYCCToRGBOperation.h"
+#include "COM_FastGaussianBlurOperation.h"
+#include "COM_SetValueOperation.h"
+
+#include "COM_DilateErodeOperation.h"
+
+#include "COM_SetAlphaOperation.h"
+
+KeyingNode::KeyingNode(bNode *editorNode): Node(editorNode)
+{
+}
+
+OutputSocket *KeyingNode::setupPreBlur(ExecutionSystem *graph, InputSocket *inputImage, int size, OutputSocket **originalImage)
+{
+ memset(&preBlurData, 0, sizeof(preBlurData));
+ preBlurData.sizex = size;
+ preBlurData.sizey = size;
+
+ ConvertRGBToYCCOperation *convertRGBToYCCOperation = new ConvertRGBToYCCOperation();
+ convertRGBToYCCOperation->setMode(0); /* ITU 601 */
+
+ inputImage->relinkConnections(convertRGBToYCCOperation->getInputSocket(0), 0, graph);
+ graph->addOperation(convertRGBToYCCOperation);
+
+ CombineChannelsOperation *combineOperation = new CombineChannelsOperation();
+ graph->addOperation(combineOperation);
+
+ for (int channel = 0; channel < 4; channel++) {
+ SeparateChannelOperation *separateOperation = new SeparateChannelOperation();
+ separateOperation->setChannel(channel);
+ addLink(graph, convertRGBToYCCOperation->getOutputSocket(0), separateOperation->getInputSocket(0));
+ graph->addOperation(separateOperation);
+
+ if (channel == 0 || channel == 3) {
+ addLink(graph, separateOperation->getOutputSocket(0), combineOperation->getInputSocket(channel));
+ }
+ else {
+ SetValueOperation *setValueOperation = new SetValueOperation();
+ setValueOperation->setValue(1.0f);
+ graph->addOperation(setValueOperation);
+
+ FastGaussianBlurOperation *blurOperation = new FastGaussianBlurOperation();
+ blurOperation->setData(&preBlurData);
+
+ addLink(graph, separateOperation->getOutputSocket(0), blurOperation->getInputSocket(0));
+ addLink(graph, setValueOperation->getOutputSocket(0), blurOperation->getInputSocket(1));
+ addLink(graph, blurOperation->getOutputSocket(0), combineOperation->getInputSocket(channel));
+ graph->addOperation(blurOperation);
+ }
+ }
+
+ ConvertYCCToRGBOperation *convertYCCToRGBOperation = new ConvertYCCToRGBOperation();
+ convertYCCToRGBOperation->setMode(0); /* ITU 601 */
+ addLink(graph, combineOperation->getOutputSocket(0), convertYCCToRGBOperation->getInputSocket(0));
+ graph->addOperation(convertYCCToRGBOperation);
+
+ *originalImage = convertRGBToYCCOperation->getInputSocket(0)->getConnection()->getFromSocket();
+
+ return convertYCCToRGBOperation->getOutputSocket(0);
+}
+
+OutputSocket *KeyingNode::setupPostBlur(ExecutionSystem *graph, OutputSocket *postBLurInput, int size)
+{
+ memset(&postBlurData, 0, sizeof(postBlurData));
+
+ postBlurData.sizex = size;
+ postBlurData.sizey = size;
+
+ SetValueOperation *setValueOperation = new SetValueOperation();
+
+ setValueOperation->setValue(1.0f);
+ graph->addOperation(setValueOperation);
+
+ FastGaussianBlurOperation *blurOperation = new FastGaussianBlurOperation();
+ blurOperation->setData(&postBlurData);
+
+ addLink(graph, postBLurInput, blurOperation->getInputSocket(0));
+ addLink(graph, setValueOperation->getOutputSocket(0), blurOperation->getInputSocket(1));
+
+ graph->addOperation(blurOperation);
+
+ return blurOperation->getOutputSocket();
+}
+
+OutputSocket *KeyingNode::setupDilateErode(ExecutionSystem *graph, OutputSocket *dilateErodeInput, int distance)
+{
+ DilateStepOperation *dilateErodeOperation;
+
+ if (distance > 0) {
+ dilateErodeOperation = new DilateStepOperation();
+ dilateErodeOperation->setIterations(distance);
+ }
+ else {
+ dilateErodeOperation = new ErodeStepOperation();
+ dilateErodeOperation->setIterations(-distance);
+ }
+
+ addLink(graph, dilateErodeInput, dilateErodeOperation->getInputSocket(0));
+
+ graph->addOperation(dilateErodeOperation);
+
+ return dilateErodeOperation->getOutputSocket(0);
+}
+
+OutputSocket *KeyingNode::setupDispill(ExecutionSystem *graph, OutputSocket *dispillInput, InputSocket *inputScreen, float factor)
+{
+ KeyingDispillOperation *dispillOperation = new KeyingDispillOperation();
+
+ dispillOperation->setDispillFactor(factor);
+
+ addLink(graph, dispillInput, dispillOperation->getInputSocket(0));
+ inputScreen->relinkConnections(dispillOperation->getInputSocket(1), 1, graph);
+
+ graph->addOperation(dispillOperation);
+
+ return dispillOperation->getOutputSocket(0);
+}
+
+void KeyingNode::convertToOperations(ExecutionSystem *graph, CompositorContext *context)
+{
+ InputSocket *inputImage = this->getInputSocket(0);
+ InputSocket *inputScreen = this->getInputSocket(1);
+ OutputSocket *outputImage = this->getOutputSocket(0);
+ OutputSocket *outputMatte = this->getOutputSocket(1);
+ OutputSocket *postprocessedMatte, *postprocessedImage, *originalImage;
+
+ bNode *editorNode = this->getbNode();
+ NodeKeyingData *keying_data = (NodeKeyingData *) editorNode->storage;
+
+ /* keying operation */
+ KeyingOperation *keyingOperation = new KeyingOperation();
+
+ keyingOperation->setClipBlack(keying_data->clip_black);
+ keyingOperation->setClipWhite(keying_data->clip_white);
+
+ inputScreen->relinkConnections(keyingOperation->getInputSocket(1), 1, graph);
+
+ if (keying_data->blur_pre) {
+ /* chroma preblur operation for input of keying operation */
+ OutputSocket *preBluredImage = setupPreBlur(graph, inputImage, keying_data->blur_pre, &originalImage);
+ addLink(graph, preBluredImage, keyingOperation->getInputSocket(0));
+ }
+ else {
+ inputImage->relinkConnections(keyingOperation->getInputSocket(0), 0, graph);
+ originalImage = keyingOperation->getInputSocket(0)->getConnection()->getFromSocket();
+ }
+
+ graph->addOperation(keyingOperation);
+
+ /* apply blur on matte if needed */
+ if (keying_data->blur_post)
+ postprocessedMatte = setupPostBlur(graph, keyingOperation->getOutputSocket(), keying_data->blur_post);
+ else
+ postprocessedMatte = keyingOperation->getOutputSocket();
+
+ /* matte dilate/erode */
+ if (keying_data->dilate_distance != 0) {
+ postprocessedMatte = setupDilateErode(graph, postprocessedMatte, keying_data->dilate_distance);
+ }
+
+ /* set alpha channel to output image */
+ SetAlphaOperation *alphaOperation = new SetAlphaOperation();
+ addLink(graph, originalImage, alphaOperation->getInputSocket(0));
+ addLink(graph, postprocessedMatte, alphaOperation->getInputSocket(1));
+
+ postprocessedImage = alphaOperation->getOutputSocket();
+
+ /* dispill output image */
+ if (keying_data->dispill_factor > 0.0f) {
+ postprocessedImage = setupDispill(graph, postprocessedImage, inputScreen, keying_data->dispill_factor);
+ }
+
+ /* connect result to output sockets */
+ outputImage->relinkConnections(postprocessedImage);
+ outputMatte->relinkConnections(postprocessedMatte);
+
+ graph->addOperation(alphaOperation);
+}
diff --git a/source/blender/compositor/nodes/COM_KeyingNode.h b/source/blender/compositor/nodes/COM_KeyingNode.h
new file mode 100644
index 00000000000..8c1ee8fdf72
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_KeyingNode.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012, Blender Foundation.
+ *
+ * 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:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Sergey Sharybin
+ */
+
+#include "COM_Node.h"
+#include "DNA_node_types.h"
+
+/**
+ * @brief KeyingNode
+ * @ingroup Node
+ */
+class KeyingNode : public Node {
+protected:
+ NodeBlurData preBlurData;
+ NodeBlurData postBlurData;
+
+ OutputSocket *setupPreBlur(ExecutionSystem *graph, InputSocket *inputImage, int size, OutputSocket **originalImage);
+ OutputSocket *setupPostBlur(ExecutionSystem *graph, OutputSocket *postBLurInput, int size);
+ OutputSocket *setupDilateErode(ExecutionSystem *graph, OutputSocket *dilateErodeInput, int distance);
+ OutputSocket *setupDispill(ExecutionSystem *graph, OutputSocket *dispillInput, InputSocket *inputSrceen, float factor);
+public:
+ KeyingNode(bNode *editorNode);
+ void convertToOperations(ExecutionSystem *graph, CompositorContext *context);
+
+};
diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.cpp b/source/blender/compositor/nodes/COM_KeyingScreenNode.cpp
new file mode 100644
index 00000000000..ad58adae48b
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012, Blender Foundation.
+ *
+ * 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:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Sergey Sharybin
+ */
+
+#include "COM_KeyingScreenNode.h"
+#include "COM_ExecutionSystem.h"
+#include "COM_KeyingScreenOperation.h"
+
+extern "C" {
+ #include "DNA_movieclip_types.h"
+}
+
+KeyingScreenNode::KeyingScreenNode(bNode *editorNode): Node(editorNode)
+{
+}
+
+void KeyingScreenNode::convertToOperations(ExecutionSystem *graph, CompositorContext * context)
+{
+ OutputSocket *outputScreen = this->getOutputSocket(0);
+
+ bNode *editorNode = this->getbNode();
+ MovieClip *clip = (MovieClip *) editorNode->id;
+
+ NodeKeyingScreenData *keyingscreen_data = (NodeKeyingScreenData *) editorNode->storage;
+
+ // always connect the output image
+ KeyingScreenOperation *operation = new KeyingScreenOperation();
+
+ if (outputScreen->isConnected()) {
+ outputScreen->relinkConnections(operation->getOutputSocket());
+ }
+
+ operation->setMovieClip(clip);
+ operation->setTrackingObject(keyingscreen_data->tracking_object);
+ operation->setFramenumber(context->getFramenumber());
+
+ graph->addOperation(operation);
+}
diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.h b/source/blender/compositor/nodes/COM_KeyingScreenNode.h
new file mode 100644
index 00000000000..7c87219ef6e
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012, Blender Foundation.
+ *
+ * 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:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Sergey Sharybin
+ */
+
+#include "COM_Node.h"
+#include "DNA_node_types.h"
+
+/**
+ * @brief KeyingScreenNode
+ * @ingroup Node
+ */
+class KeyingScreenNode : public Node {
+public:
+ KeyingScreenNode(bNode *editorNode);
+ void convertToOperations(ExecutionSystem *graph, CompositorContext *context);
+
+};
diff --git a/source/blender/compositor/operations/COM_KeyingDispillOperation.cpp b/source/blender/compositor/operations/COM_KeyingDispillOperation.cpp
new file mode 100644
index 00000000000..5f4eaf3148d
--- /dev/null
+++ b/source/blender/compositor/operations/COM_KeyingDispillOperation.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2012, Blender Foundation.
+ *
+ * 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:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Sergey Sharybin
+ */
+
+#include "COM_KeyingDispillOperation.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+static int get_pixel_primary_channel(float *pixel)
+{
+ float max_value = MAX3(pixel[0], pixel[1], pixel[2]);
+
+ if (max_value == pixel[0])
+ return 0;
+ else if (max_value == pixel[1])
+ return 1;
+
+ return 2;
+}
+
+KeyingDispillOperation::KeyingDispillOperation(): NodeOperation()
+{
+ this->addInputSocket(COM_DT_COLOR);
+ this->addInputSocket(COM_DT_COLOR);
+ this->addOutputSocket(COM_DT_COLOR);
+
+ this->dispillFactor = 0.5f;
+
+ this->pixelReader = NULL;
+ this->screenReader = NULL;
+}
+
+void KeyingDispillOperation::initExecution()
+{
+ this->pixelReader = this->getInputSocketReader(0);
+ this->screenReader = this->getInputSocketReader(1);
+}
+
+void KeyingDispillOperation::deinitExecution()
+{
+ this->pixelReader = NULL;
+ this->screenReader = NULL;
+}
+
+void KeyingDispillOperation::executePixel(float *color, float x, float y, PixelSampler sampler, MemoryBuffer *inputBuffers[])
+{
+ float pixelColor[4];
+ float screenColor[4];
+
+ this->pixelReader->read(pixelColor, x, y, sampler, inputBuffers);
+ this->screenReader->read(screenColor, x, y, sampler, inputBuffers);
+
+ int screen_primary_channel = get_pixel_primary_channel(screenColor);
+ float average_value, amount;
+
+ average_value = (pixelColor[0] + pixelColor[1] + pixelColor[2] - pixelColor[screen_primary_channel]) / 2.0f;
+ amount = pixelColor[screen_primary_channel] - average_value;
+
+ color[0] = pixelColor[0];
+ color[1] = pixelColor[1];
+ color[2] = pixelColor[2];
+ color[3] = pixelColor[3];
+
+ if (this->dispillFactor * amount > 0) {
+ color[screen_primary_channel] = pixelColor[screen_primary_channel] - this->dispillFactor * amount;
+ }
+}
diff --git a/source/blender/compositor/operations/COM_KeyingDispillOperation.h b/source/blender/compositor/operations/COM_KeyingDispillOperation.h
new file mode 100644
index 00000000000..a918a918381
--- /dev/null
+++ b/source/blender/compositor/operations/COM_KeyingDispillOperation.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2012, Blender Foundation.
+ *
+ * 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:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Sergey Sharybin
+ */
+
+#ifndef _COM_KeyingDispillOperation_h
+#define _COM_KeyingDispillOperation_h
+
+#include "COM_NodeOperation.h"
+
+/**
+ * Class with implementation of keying dispill node
+ */
+class KeyingDispillOperation : public NodeOperation {
+protected:
+ SocketReader *pixelReader;
+ SocketReader *screenReader;
+ float dispillFactor;
+
+public:
+ KeyingDispillOperation();
+
+ void initExecution();
+ void deinitExecution();
+
+ void setDispillFactor(float value) {this->dispillFactor = value;}
+
+ void executePixel(float *color, float x, float y, PixelSampler sampler, MemoryBuffer *inputBuffers[]);
+};
+
+#endif
diff --git a/source/blender/compositor/operations/COM_KeyingOperation.cpp b/source/blender/compositor/operations/COM_KeyingOperation.cpp
new file mode 100644
index 00000000000..43348568a5d
--- /dev/null
+++ b/source/blender/compositor/operations/COM_KeyingOperation.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2012, Blender Foundation.
+ *
+ * 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:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Sergey Sharybin
+ */
+
+#include "COM_KeyingOperation.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+static int get_pixel_primary_channel(float *pixel)
+{
+ float max_value = MAX3(pixel[0], pixel[1], pixel[2]);
+
+ if (max_value == pixel[0])
+ return 0;
+ else if (max_value == pixel[1])
+ return 1;
+
+ return 2;
+}
+
+static float get_pixel_saturation(float *pixel, float screen_balance)
+{
+ float min = MIN3(pixel[0], pixel[1], pixel[2]);
+ float max = MAX3(pixel[0], pixel[1], pixel[2]);
+ float mid = pixel[0] + pixel[1] + pixel[2] - min - max;
+ float val = (1.0f - screen_balance) * min + screen_balance * mid;
+
+ return max - val;
+}
+
+KeyingOperation::KeyingOperation(): NodeOperation()
+{
+ this->addInputSocket(COM_DT_COLOR);
+ this->addInputSocket(COM_DT_COLOR);
+ this->addOutputSocket(COM_DT_VALUE);
+
+ this->screenBalance = 0.5f;
+ this->clipBlack = 0.0f;
+ this->clipWhite = 1.0f;
+
+ this->pixelReader = NULL;
+ this->screenReader = NULL;
+}
+
+void KeyingOperation::initExecution()
+{
+ this->pixelReader = this->getInputSocketReader(0);
+ this->screenReader = this->getInputSocketReader(1);
+}
+
+void KeyingOperation::deinitExecution()
+{
+ this->pixelReader = NULL;
+ this->screenReader = NULL;
+}
+
+void KeyingOperation::executePixel(float *color, float x, float y, PixelSampler sampler, MemoryBuffer *inputBuffers[])
+{
+ float pixelColor[4];
+ float screenColor[4];
+
+ this->pixelReader->read(pixelColor, x, y, sampler, inputBuffers);
+ this->screenReader->read(screenColor, x, y, sampler, inputBuffers);
+
+ float saturation = get_pixel_saturation(pixelColor, this->screenBalance);
+ float screen_saturation = get_pixel_saturation(screenColor, this->screenBalance);
+ int primary_channel = get_pixel_primary_channel(pixelColor);
+ int screen_primary_channel = get_pixel_primary_channel(screenColor);
+
+ if (primary_channel != screen_primary_channel) {
+ /* different main channel means pixel is on foreground */
+ color[0] = 1.0f;
+ }
+ else if (saturation >= screen_saturation) {
+ /* saturation of main channel is more than screen, definitely a background */
+ color[0] = 0.0f;
+ }
+ else {
+ float distance;
+
+ distance = 1.0f - saturation / screen_saturation;
+
+ color[0] = distance;
+
+ if (color[0] < this->clipBlack)
+ color[0] = 0.0f;
+ else if (color[0] >= this->clipWhite)
+ color[0] = 1.0f;
+ else
+ color[0] = (color[0] - this->clipBlack) / (this->clipWhite - this->clipBlack);
+ }
+}
diff --git a/source/blender/compositor/operations/COM_KeyingOperation.h b/source/blender/compositor/operations/COM_KeyingOperation.h
new file mode 100644
index 00000000000..546ff355573
--- /dev/null
+++ b/source/blender/compositor/operations/COM_KeyingOperation.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012, Blender Foundation.
+ *
+ * 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:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Sergey Sharybin
+ */
+
+
+#ifndef _COM_KeyingOperation_h
+#define _COM_KeyingOperation_h
+
+#include <string.h>
+
+#include "COM_NodeOperation.h"
+
+#include "BLI_listbase.h"
+
+/**
+ * Class with implementation of keying node
+ */
+class KeyingOperation : public NodeOperation {
+protected:
+ SocketReader *pixelReader;
+ SocketReader *screenReader;
+ float screenBalance;
+ float clipBlack;
+ float clipWhite;
+
+public:
+ KeyingOperation();
+
+ void initExecution();
+ void deinitExecution();
+
+ void setClipBlack(float value) {this->clipBlack = value;}
+ void setClipWhite(float value) {this->clipWhite = value;}
+
+ void executePixel(float *color, float x, float y, PixelSampler sampler, MemoryBuffer *inputBuffers[]);
+};
+
+#endif
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cpp b/source/blender/compositor/operations/COM_KeyingScreenOperation.cpp
new file mode 100644
index 00000000000..456ab31db24
--- /dev/null
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2012, Blender Foundation.
+ *
+ * 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:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Sergey Sharybin
+ */
+
+#include "COM_KeyingScreenOperation.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_math_color.h"
+
+#include "DNA_scene_types.h"
+
+extern "C" {
+ #include "BKE_movieclip.h"
+ #include "BKE_tracking.h"
+
+ #include "IMB_imbuf.h"
+ #include "IMB_imbuf_types.h"
+}
+
+KeyingScreenOperation::KeyingScreenOperation(): NodeOperation()
+{
+ this->addOutputSocket(COM_DT_COLOR);
+ this->movieClip = NULL;
+ this->framenumber = 0;
+ this->trackingObject[0] = 0;
+ setComplex(true);
+}
+
+void KeyingScreenOperation::initExecution()
+{
+ initMutex();
+ this->cachedTriangulation = NULL;
+}
+
+void KeyingScreenOperation::deinitExecution()
+{
+ if (this->cachedTriangulation) {
+ TriangulationData *triangulation = cachedTriangulation;
+
+ if (triangulation->triangulated_points)
+ MEM_freeN(triangulation->triangulated_points);
+
+ if (triangulation->triangles)
+ MEM_freeN(triangulation->triangles);
+
+ MEM_freeN(this->cachedTriangulation);
+
+ this->cachedTriangulation = NULL;
+ }
+}
+
+KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTriangulation()
+{
+ MovieClipUser user = {0};
+ TriangulationData *triangulation;
+ MovieTracking *tracking = &movieClip->tracking;
+ MovieTrackingTrack *track;
+ VoronoiSite *sites;
+ ImBuf *ibuf;
+ ListBase *tracksbase;
+ ListBase edges = {NULL, NULL};
+ int sites_total;
+ int i;
+ int width = this->getWidth();
+ int height = this->getHeight();
+
+ if (this->trackingObject[0]) {
+ MovieTrackingObject *object = BKE_tracking_named_object(tracking, this->trackingObject);
+
+ if (!object)
+ return NULL;
+
+ tracksbase = BKE_tracking_object_tracks(tracking, object);
+ }
+ else
+ tracksbase = BKE_tracking_get_tracks(tracking);
+
+ sites_total = BLI_countlist(tracksbase);
+
+ if (!sites_total)
+ return NULL;
+
+ triangulation = (TriangulationData *) MEM_callocN(sizeof(TriangulationData), "keying screen triangulation data");
+
+ BKE_movieclip_user_set_frame(&user, framenumber);
+ ibuf = BKE_movieclip_get_ibuf(movieClip, &user);
+
+ sites = (VoronoiSite *) MEM_callocN(sizeof(VoronoiSite) * sites_total, "keyingscreen voronoi sites");
+ track = (MovieTrackingTrack *) tracksbase->first;
+ i = 0;
+ while (track) {
+ VoronoiSite *site = &sites[i];
+ MovieTrackingMarker *marker = BKE_tracking_get_marker(track, framenumber);
+ ImBuf *pattern_ibuf = BKE_tracking_get_pattern_imbuf(ibuf, track, marker, 0, TRUE, NULL, NULL);
+ int j;
+
+ zero_v3(site->color);
+ for (j = 0; j < pattern_ibuf->x * pattern_ibuf->y; j++) {
+ if (pattern_ibuf->rect_float) {
+ add_v3_v3(site->color, &pattern_ibuf->rect_float[4 * j]);
+ }
+ else {
+ unsigned char *rrgb = (unsigned char *)pattern_ibuf->rect;
+
+ site->color[0] += srgb_to_linearrgb((float)rrgb[4 * j + 0] / 255.0f);
+ site->color[1] += srgb_to_linearrgb((float)rrgb[4 * j + 1] / 255.0f);
+ site->color[2] += srgb_to_linearrgb((float)rrgb[4 * j + 2] / 255.0f);
+ }
+ }
+
+ mul_v3_fl(site->color, 1.0f / (pattern_ibuf->x * pattern_ibuf->y));
+ IMB_freeImBuf(pattern_ibuf);
+
+ site->co[0] = marker->pos[0] * width;
+ site->co[1] = marker->pos[1] * height;
+
+ track = track->next;
+ i++;
+ }
+
+ IMB_freeImBuf(ibuf);
+
+ BLI_voronoi_compute(sites, sites_total, width, height, &edges);
+
+ BLI_voronoi_triangulate(sites, sites_total, &edges, width, height,
+ &triangulation->triangulated_points, &triangulation->triangulated_points_total,
+ &triangulation->triangles, &triangulation->triangles_total);
+
+ MEM_freeN(sites);
+ BLI_freelistN(&edges);
+
+ return triangulation;
+}
+
+void *KeyingScreenOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
+{
+ if (this->movieClip == NULL)
+ return NULL;
+
+ if (this->cachedTriangulation)
+ return this->cachedTriangulation;
+
+ BLI_mutex_lock(getMutex());
+ if (this->cachedTriangulation == NULL) {
+ this->cachedTriangulation = buildVoronoiTriangulation();
+ }
+ BLI_mutex_unlock(getMutex());
+
+ return this->cachedTriangulation;
+}
+
+void KeyingScreenOperation::determineResolution(unsigned int resolution[], unsigned int preferredResolution[])
+{
+ resolution[0] = 0;
+ resolution[1] = 0;
+
+ if (this->movieClip) {
+ MovieClipUser user = {0};
+ int width, height;
+
+ BKE_movieclip_user_set_frame(&user, framenumber);
+ BKE_movieclip_get_size(this->movieClip, &user, &width, &height);
+
+ resolution[0] = width;
+ resolution[1] = height;
+ }
+}
+
+void KeyingScreenOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
+{
+ color[0] = 0.0f;
+ color[1] = 0.0f;
+ color[2] = 0.0f;
+ color[3] = 1.0f;
+
+ if (this->movieClip && data) {
+ TriangulationData *triangulation = (TriangulationData *) data;
+ int i;
+ for (i = 0; i < triangulation->triangles_total; i++) {
+ int *triangle = triangulation->triangles[i];
+ VoronoiTriangulationPoint *a = &triangulation->triangulated_points[triangle[0]],
+ *b = &triangulation->triangulated_points[triangle[1]],
+ *c = &triangulation->triangulated_points[triangle[2]];
+ float co[2] = {(float) x, (float) y}, w[3];
+
+ if (barycentric_coords_v2(a->co, b->co, c->co, co, w)) {
+ if (barycentric_inside_triangle_v2(w)) {
+ color[0] += a->color[0] * w[0] + b->color[0] * w[1] + c->color[0] * w[2];
+ color[1] += a->color[1] * w[0] + b->color[1] * w[1] + c->color[1] * w[2];
+ color[2] += a->color[2] * w[0] + b->color[2] * w[1] + c->color[2] * w[2];
+ }
+ }
+ }
+ }
+}
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
new file mode 100644
index 00000000000..9d3f44f6be2
--- /dev/null
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2012, Blender Foundation.
+ *
+ * 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:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Sergey Sharybin
+ */
+
+
+#ifndef _COM_KeyingScreenOperation_h
+#define _COM_KeyingScreenOperation_h
+
+#include <string.h>
+
+#include "COM_NodeOperation.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_movieclip_types.h"
+
+#include "BLI_listbase.h"
+
+extern "C" {
+ #include "BLI_voronoi.h"
+}
+
+/**
+ * Class with implementation of green screen gradient rasterization
+ */
+class KeyingScreenOperation : public NodeOperation {
+protected:
+ typedef struct TriangulationData {
+ VoronoiTriangulationPoint *triangulated_points;
+ int (*triangles)[3];
+ int triangulated_points_total, triangles_total;
+ } TriangulationData;
+
+ MovieClip *movieClip;
+ int framenumber;
+ TriangulationData *cachedTriangulation;
+ char trackingObject[64];
+
+ /**
+ * Determine the output resolution. The resolution is retrieved from the Renderer
+ */
+ void determineResolution(unsigned int resolution[], unsigned int preferredResolution[]);
+
+ TriangulationData *buildVoronoiTriangulation();
+
+ public:
+ KeyingScreenOperation();
+
+ void initExecution();
+ void deinitExecution();
+
+ void *initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers);
+
+ void setMovieClip(MovieClip *clip) {this->movieClip = clip;}
+ void setTrackingObject(char *object) {strncpy(this->trackingObject, object, sizeof(this->trackingObject));}
+ void setFramenumber(int framenumber) {this->framenumber = framenumber;}
+
+ void executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data);
+};
+
+#endif
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 3c070b4e637..341926f50a3 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -2394,6 +2394,36 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p
uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL);
}
+static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, PointerRNA *ptr)
+{
+ bNode *node= ptr->data;
+
+ uiTemplateID(layout, C, ptr, "clip", NULL, NULL, NULL);
+
+ if (node->id) {
+ MovieClip *clip = (MovieClip *) node->id;
+ uiLayout *col;
+ PointerRNA tracking_ptr;
+
+ RNA_pointer_create(&clip->id, &RNA_MovieTracking, &clip->tracking, &tracking_ptr);
+
+ col = uiLayoutColumn(layout, 1);
+ uiItemPointerR(col, ptr, "tracking_object", &tracking_ptr, "objects", "", ICON_OBJECT_DATA);
+ }
+}
+
+static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ bNode *node= ptr->data;
+
+ uiItemR(layout, ptr, "blur_pre", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "dispill_factor", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "clip_black", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "clip_white", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "dilate_distance", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "blur_post", 0, NULL, ICON_NONE);
+}
+
/* only once called */
static void node_composit_set_butfunc(bNodeType *ntype)
{
@@ -2586,7 +2616,12 @@ static void node_composit_set_butfunc(bNodeType *ntype)
case CMP_NODE_MASK:
ntype->uifunc= node_composit_buts_mask;
break;
-
+ case CMP_NODE_KEYINGSCREEN:
+ ntype->uifunc = node_composit_buts_keyingscreen;
+ break;
+ case CMP_NODE_KEYING:
+ ntype->uifunc = node_composit_buts_keying;
+ break;
default:
ntype->uifunc = NULL;
}
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index b2781675cbe..bad40e80265 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -617,6 +617,17 @@ typedef struct TexNodeOutput {
char name[64];
} TexNodeOutput;
+typedef struct NodeKeyingScreenData {
+ char tracking_object[64];
+} NodeKeyingScreenData;
+
+typedef struct NodeKeyingData {
+ float dispill_factor;
+ float clip_black, clip_white;
+ int dilate_distance;
+ int blur_pre, blur_post;
+} NodeKeyingData;
+
/* frame node flags */
#define NODE_FRAME_SHRINK 1 /* keep the bounding box minimal */
#define NODE_FRAME_RESIZEABLE 2 /* test flag, if frame can be resized by user */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 0a0efb66f7f..842ad4156d8 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -341,6 +341,7 @@ extern StructRNA RNA_MotionPathVert;
extern StructRNA RNA_MouseSensor;
extern StructRNA RNA_MovieSequence;
extern StructRNA RNA_MovieClipSequence;
+extern StructRNA RNA_MovieTracking;
extern StructRNA RNA_MovieTrackingTrack;
extern StructRNA RNA_MovieTrackingObject;
extern StructRNA RNA_MovieTrackingTrack;
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 59c4af4c376..7d2935ece6e 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -3046,6 +3046,67 @@ static void def_cmp_mask(StructRNA *srna)
RNA_def_property_struct_type(prop, "Mask");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Mask", "");
+}
+
+static void def_cmp_keyingscreen(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "clip", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "id");
+ RNA_def_property_struct_type(prop, "MovieClip");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Movie Clip", "");
+ RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
+
+ RNA_def_struct_sdna_from(srna, "NodeKeyingScreenData", "storage");
+
+ prop = RNA_def_property(srna, "tracking_object", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "tracking_object");
+ RNA_def_property_ui_text(prop, "Tracking Object", "");
+ RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
+}
+
+static void def_cmp_keying(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeKeyingData", "storage");
+
+ prop = RNA_def_property(srna, "dispill_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "dispill_factor");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Dispill", "");
+ RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "clip_black", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "clip_black");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Clip Black", "");
+ RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "clip_white", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "clip_white");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Clip White", "");
+ RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "blur_pre", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "blur_pre");
+ RNA_def_property_range(prop, 0, 2048);
+ RNA_def_property_ui_text(prop, "Pre Blur", "");
+ RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "blur_post", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "blur_post");
+ RNA_def_property_range(prop, 0, 2048);
+ RNA_def_property_ui_text(prop, "Post Blur", "");
+ RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "dilate_distance", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "dilate_distance");
+ RNA_def_property_range(prop, -100, 100);
+ RNA_def_property_ui_text(prop, "Dilate/Erode", "");
RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
}
diff --git a/source/blender/makesrna/intern/rna_nodetree_types.h b/source/blender/makesrna/intern/rna_nodetree_types.h
index d5b33f0d01b..d8c27a3677f 100644
--- a/source/blender/makesrna/intern/rna_nodetree_types.h
+++ b/source/blender/makesrna/intern/rna_nodetree_types.h
@@ -166,6 +166,8 @@ DefNode( CompositorNode, CMP_NODE_BOKEHIMAGE, def_cmp_bokehimage, "BOKEH
DefNode( CompositorNode, CMP_NODE_SWITCH, def_cmp_switch, "SWITCH" ,Switch, "Switch", "" )
DefNode( CompositorNode, CMP_NODE_COLORCORRECTION,def_cmp_colorcorrection,"COLORCORRECTION",ColorCorrection, "ColorCorrection", "" )
DefNode( CompositorNode, CMP_NODE_MASK, def_cmp_mask, "MASK", Mask, "Mask", "" )
+DefNode( CompositorNode, CMP_NODE_KEYINGSCREEN, def_cmp_keyingscreen, "KEYINGSCREEN", KeyingScreen, "KeyingScreen", "" )
+DefNode( CompositorNode, CMP_NODE_KEYING, def_cmp_keying, "KEYING", Keying, "Keying", "" )
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/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 5e36f90f217..d07d00a436d 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -76,6 +76,8 @@ set(SRC
composite/nodes/node_composite_idMask.c
composite/nodes/node_composite_image.c
composite/nodes/node_composite_invert.c
+ composite/nodes/node_composite_keying.c
+ composite/nodes/node_composite_keyingscreen.c
composite/nodes/node_composite_lensdist.c
composite/nodes/node_composite_levels.c
composite/nodes/node_composite_lummaMatte.c
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index f850ea91f12..efa107a2223 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -81,6 +81,7 @@ void register_node_type_cmp_bilateralblur(struct bNodeTreeType *ttype);
void register_node_type_cmp_vecblur(struct bNodeTreeType *ttype);
void register_node_type_cmp_dilateerode(struct bNodeTreeType *ttype);
void register_node_type_cmp_defocus(struct bNodeTreeType *ttype);
+void register_node_type_cmp_denoise(struct bNodeTreeType *ttype);
void register_node_type_cmp_valtorgb(struct bNodeTreeType *ttype);
void register_node_type_cmp_rgbtobw(struct bNodeTreeType *ttype);
@@ -105,6 +106,8 @@ void register_node_type_cmp_channel_matte(struct bNodeTreeType *ttype);
void register_node_type_cmp_color_spill(struct bNodeTreeType *ttype);
void register_node_type_cmp_luma_matte(struct bNodeTreeType *ttype);
void register_node_type_cmp_doubleedgemask(struct bNodeTreeType *ttype);
+void register_node_type_cmp_keyingscreen(struct bNodeTreeType *ttype);
+void register_node_type_cmp_keying(struct bNodeTreeType *ttype);
void register_node_type_cmp_translate(struct bNodeTreeType *ttype);
void register_node_type_cmp_rotate(struct bNodeTreeType *ttype);
diff --git a/source/blender/nodes/composite/node_composite_util.c b/source/blender/nodes/composite/node_composite_util.c
index 3806cf4543a..ff223ac83cf 100644
--- a/source/blender/nodes/composite/node_composite_util.c
+++ b/source/blender/nodes/composite/node_composite_util.c
@@ -568,6 +568,22 @@ CompBuf *valbuf_from_rgbabuf(CompBuf *cbuf, int channel)
return valbuf;
}
+void valbuf_to_rgbabuf(CompBuf *valbuf, CompBuf *cbuf, int channel)
+{
+ float *valf, *rectf;
+ int tot;
+
+ valf= valbuf->rect;
+
+ /* defaults to returning alpha channel */
+ if ((channel < CHAN_R) || (channel > CHAN_A)) channel = CHAN_A;
+
+ rectf = cbuf->rect + channel;
+
+ for (tot= cbuf->x*cbuf->y; tot>0; tot--, valf++, rectf+=4)
+ *rectf = *valf;
+}
+
static CompBuf *generate_procedural_preview(CompBuf *cbuf, int newx, int newy)
{
CompBuf *outbuf;
diff --git a/source/blender/nodes/composite/node_composite_util.h b/source/blender/nodes/composite/node_composite_util.h
index 51f047b94ff..18dac5dc5c1 100644
--- a/source/blender/nodes/composite/node_composite_util.h
+++ b/source/blender/nodes/composite/node_composite_util.h
@@ -155,6 +155,7 @@ void composit4_pixel_processor(bNode *node, CompBuf *out, CompBuf *src1_buf, flo
int src1_type, int fac1_type, int src2_type, int fac2_type);
CompBuf *valbuf_from_rgbabuf(CompBuf *cbuf, int channel);
+void valbuf_to_rgbabuf(CompBuf *valbuf, CompBuf *cbuf, int channel);
void generate_preview(void *data, bNode *node, CompBuf *stackbuf);
void do_copy_rgba(bNode *node, float *out, float *in);
@@ -220,6 +221,9 @@ void IIR_gauss(CompBuf* src, float sigma, int chan, int xy);
#define CMP_SCALE_MAX 12000
CompBuf* node_composit_transform(CompBuf *cbuf, float x, float y, float angle, float scale, int filter_type);
+void node_composit_blur_single_image(bNode *node, int filtertype, int sizex, int sizey, CompBuf *new, CompBuf *img, float scale);
+void node_composite_morpho_dilate(CompBuf *cbuf);
+void node_composite_morpho_erode(CompBuf *cbuf);
float *node_composit_get_float_buffer(RenderData *rd, ImBuf *ibuf, int *alloc);
#endif
diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.c b/source/blender/nodes/composite/nodes/node_composite_blur.c
index d19ea3f202f..9afdd6eadb7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_blur.c
+++ b/source/blender/nodes/composite/nodes/node_composite_blur.c
@@ -85,9 +85,8 @@ static float *make_bloomtab(int rad)
}
/* both input images of same type, either 4 or 1 channel */
-static void blur_single_image(bNode *node, CompBuf *new, CompBuf *img, float scale)
+void node_composit_blur_single_image(bNode *node, int filtertype, int sizex, int sizey, CompBuf *new, CompBuf *img, float scale)
{
- NodeBlurData *nbd= node->storage;
CompBuf *work;
register float sum, val;
float rval, gval, bval, aval;
@@ -101,17 +100,17 @@ static void blur_single_image(bNode *node, CompBuf *new, CompBuf *img, float sca
work= alloc_compbuf(imgx, imgy, img->type, 1); /* allocs */
/* horizontal */
- if (nbd->sizex == 0) {
+ if (sizex == 0) {
memcpy(work->rect, img->rect, sizeof(float) * img->type * imgx * imgy);
}
else {
- rad = scale*(float)nbd->sizex;
+ rad = scale*(float)sizex;
if (rad>imgx/2)
rad= imgx/2;
else if (rad<1)
rad= 1;
- gausstab= make_gausstab(nbd->filtertype, rad);
+ gausstab= make_gausstab(filtertype, rad);
gausstabcent= gausstab+rad;
for (y = 0; y < imgy; y++) {
@@ -152,17 +151,17 @@ static void blur_single_image(bNode *node, CompBuf *new, CompBuf *img, float sca
MEM_freeN(gausstab);
}
- if (nbd->sizey == 0) {
+ if (sizey == 0) {
memcpy(new->rect, work->rect, sizeof(float) * img->type * imgx * imgy);
}
else {
- rad = scale*(float)nbd->sizey;
+ rad = scale*(float)sizey;
if (rad>imgy/2)
rad= imgy/2;
else if (rad<1)
rad= 1;
- gausstab= make_gausstab(nbd->filtertype, rad);
+ gausstab= make_gausstab(filtertype, rad);
gausstabcent= gausstab+rad;
bigstep = pix*imgx;
@@ -207,6 +206,13 @@ static void blur_single_image(bNode *node, CompBuf *new, CompBuf *img, float sca
free_compbuf(work);
}
+static void blur_single_image(bNode *node, CompBuf *new, CompBuf *img, float scale)
+{
+ NodeBlurData *nbd = node->storage;
+
+ node_composit_blur_single_image(node, nbd->filtertype, nbd->sizex, nbd->sizey, new, img, scale);
+}
+
/* reference has to be mapped 0-1, and equal in size */
static void bloom_with_reference(CompBuf *new, CompBuf *img, CompBuf *UNUSED(ref), float UNUSED(fac), NodeBlurData *nbd)
{
diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.c b/source/blender/nodes/composite/nodes/node_composite_dilate.c
index ee857dd0007..1dc56fd4279 100644
--- a/source/blender/nodes/composite/nodes/node_composite_dilate.c
+++ b/source/blender/nodes/composite/nodes/node_composite_dilate.c
@@ -44,7 +44,7 @@ static bNodeSocketTemplate cmp_node_dilateerode_out[]= {
{ -1, 0, "" }
};
-static void morpho_dilate(CompBuf *cbuf)
+void node_composite_morpho_dilate(CompBuf *cbuf)
{
int x, y;
float *p, *rectf = cbuf->rect;
@@ -78,7 +78,7 @@ static void morpho_dilate(CompBuf *cbuf)
}
}
-static void morpho_erode(CompBuf *cbuf)
+void node_composite_morpho_erode(CompBuf *cbuf)
{
int x, y;
float *p, *rectf = cbuf->rect;
@@ -133,11 +133,11 @@ static void node_composit_exec_dilateerode(void *UNUSED(data), bNode *node, bNod
if (node->custom2 > 0) { // positive, dilate
for (i = 0; i < node->custom2; i++)
- morpho_dilate(stackbuf);
+ node_composite_morpho_dilate(stackbuf);
}
else if (node->custom2 < 0) { // negative, erode
for (i = 0; i > node->custom2; i--)
- morpho_erode(stackbuf);
+ node_composite_morpho_erode(stackbuf);
}
if (cbuf!=in[0]->data)
diff --git a/source/blender/nodes/composite/nodes/node_composite_keying.c b/source/blender/nodes/composite/nodes/node_composite_keying.c
new file mode 100644
index 00000000000..8e6fd86eae5
--- /dev/null
+++ b/source/blender/nodes/composite/nodes/node_composite_keying.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) 2011 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_keying.c
+ * \ingroup cmpnodes
+ */
+
+#include "BLF_translation.h"
+
+#include "DNA_movieclip_types.h"
+
+#include "BKE_movieclip.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math_base.h"
+#include "BLI_math_color.h"
+#include "BLI_voronoi.h"
+
+#include "node_composite_util.h"
+
+/* **************** Translate ******************** */
+
+static bNodeSocketTemplate cmp_node_keying_in[] = {
+ { SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
+ { SOCK_RGBA, 1, "Key Color", 1.0f, 1.0f, 1.0f, 1.0f},
+ { -1, 0, "" }
+};
+
+static bNodeSocketTemplate cmp_node_keying_out[] = {
+ { SOCK_RGBA, 0, "Image"},
+ { SOCK_FLOAT, 0, "Matte"},
+ { -1, 0, "" }
+};
+
+static int get_pixel_primary_channel(float *pixel)
+{
+ float max_value = MAX3(pixel[0], pixel[1], pixel[2]);
+
+ if (max_value == pixel[0])
+ return 0;
+ else if (max_value == pixel[1])
+ return 1;
+
+ return 2;
+}
+
+static float get_pixel_saturation(float *pixel, float screen_balance)
+{
+ float min = MIN3(pixel[0], pixel[1], pixel[2]);
+ float max = MAX3(pixel[0], pixel[1], pixel[2]);
+ float mid = pixel[0] + pixel[1] + pixel[2] - min - max;
+ float val = (1.0f - screen_balance) * min + screen_balance * mid;
+
+ return max - val;
+}
+
+static void despil_pixel(float *out, float *pixel, float *screen, float screen_gain)
+{
+ int screen_primary_channel = get_pixel_primary_channel(screen);
+ float average_value, amount;
+
+ average_value = (pixel[0] + pixel[1] + pixel[2] - pixel[screen_primary_channel]) / 2.0f;
+ amount = pixel[screen_primary_channel] - average_value;
+
+ if (screen_gain * amount > 0) {
+ out[screen_primary_channel] = pixel[screen_primary_channel] - screen_gain * amount;
+ }
+}
+
+static void do_key(bNode *node, float *out, float *pixel, float *screen)
+{
+ NodeKeyingData *data = node->storage;
+
+ float screen_balance = 0.5f;
+ float dispill_factor = data->dispill_factor;
+ float clip_black = data->clip_black;
+ float clip_white = data->clip_white;
+
+ float saturation = get_pixel_saturation(pixel, screen_balance);
+ float screen_saturation = get_pixel_saturation(screen, screen_balance);
+ int primary_channel = get_pixel_primary_channel(pixel);
+ int screen_primary_channel = get_pixel_primary_channel(screen);
+
+ if (primary_channel != screen_primary_channel) {
+ /* different main channel means pixel is on foreground,
+ * but screen color still need to be despilled from it */
+ despil_pixel(out, pixel, screen, dispill_factor);
+ out[3] = 1.0f;
+ }
+ else if (saturation >= screen_saturation) {
+ /* saturation of main channel is more than screen, definitely a background */
+ out[0] = 0.0f;
+ out[1] = 0.0f;
+ out[2] = 0.0f;
+ out[3] = 0.0f;
+ }
+ else {
+ float distance;
+
+ despil_pixel(out, pixel, screen, dispill_factor);
+
+ distance = 1.0f - saturation / screen_saturation;
+
+ out[3] = distance;
+
+ if (out[3] < clip_black)
+ out[3] = 0.0f;
+ else if (out[3] >= clip_white)
+ out[3] = 1.0f;
+ else
+ out[3] = (out[3] - clip_black) / (clip_white - clip_black);
+ }
+}
+
+static void exec(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
+{
+ if (in[0]->data) {
+ NodeKeyingData *keying_data = node->storage;
+ CompBuf *cbuf = typecheck_compbuf(in[0]->data, CB_RGBA);
+ CompBuf *keybuf, *mattebuf;
+
+ keybuf = dupalloc_compbuf(cbuf);
+
+ /* single color is used for screen detection */
+ composit2_pixel_processor(node, keybuf, cbuf, in[0]->vec, in[1]->data, in[1]->vec, do_key, CB_RGBA, CB_VAL);
+
+ /* create a matte from alpha channel */
+ mattebuf = valbuf_from_rgbabuf(keybuf, CHAN_A);
+
+ /* apply dilate/erode if needed */
+ if (keying_data->dilate_distance != 0) {
+ int i;
+
+ if (keying_data->dilate_distance > 0) {
+ for (i = 0; i < keying_data->dilate_distance; i++)
+ node_composite_morpho_dilate(mattebuf);
+ }
+ else {
+ for (i = 0; i < -keying_data->dilate_distance; i++)
+ node_composite_morpho_erode(mattebuf);
+ }
+ }
+
+ if (keying_data->blur_post > 0.0f) {
+ /* post-blur of matte */
+ CompBuf *newmatte = alloc_compbuf(mattebuf->x, mattebuf->y, mattebuf->type, TRUE);
+ int size = keying_data->blur_post;
+
+ node_composit_blur_single_image(node, R_FILTER_BOX, size, size, newmatte, mattebuf, 1.0f);
+
+ free_compbuf(mattebuf);
+ mattebuf = newmatte;
+
+ /* apply blurred matte on output buffer alpha */
+ valbuf_to_rgbabuf(mattebuf, keybuf, CHAN_A);
+ }
+
+ out[0]->data = keybuf;
+ out[1]->data = mattebuf;
+
+ generate_preview(data, node, keybuf);
+
+ if (cbuf!=in[0]->data)
+ free_compbuf(cbuf);
+ }
+}
+
+static void node_composit_init_keying(bNodeTree *UNUSED(ntree), bNode* node, bNodeTemplate *UNUSED(ntemp))
+{
+ NodeKeyingData *data;
+
+ data = MEM_callocN(sizeof(NodeKeyingData), "node keying data");
+
+ data->dispill_factor = 1.0f;
+ data->clip_black = 0.0f;
+ data->clip_white = 1.0f;
+
+ node->storage = data;
+}
+
+void register_node_type_cmp_keying(bNodeTreeType *ttype)
+{
+ static bNodeType ntype;
+
+ node_type_base(ttype, &ntype, CMP_NODE_KEYING, "Keying", NODE_CLASS_MATTE, NODE_OPTIONS);
+ node_type_socket_templates(&ntype, cmp_node_keying_in, cmp_node_keying_out);
+ node_type_size(&ntype, 140, 100, 320);
+ node_type_init(&ntype, node_composit_init_keying);
+ node_type_storage(&ntype, "NodeKeyingData", node_free_standard_storage, node_copy_standard_storage);
+ node_type_exec(&ntype, exec);
+
+ nodeRegisterType(ttype, &ntype);
+}
diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.c b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.c
new file mode 100644
index 00000000000..5cbaf4ae2ba
--- /dev/null
+++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.c
@@ -0,0 +1,202 @@
+/*
+ * ***** 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.
+ *
+ * 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_keyingscreen.c
+ * \ingroup cmpnodes
+ */
+
+#include "BLF_translation.h"
+
+#include "DNA_movieclip_types.h"
+
+#include "BKE_movieclip.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math_base.h"
+#include "BLI_math_color.h"
+#include "BLI_voronoi.h"
+
+#include "node_composite_util.h"
+
+/* **************** Translate ******************** */
+
+static bNodeSocketTemplate cmp_node_keyingscreen_out[] = {
+ { SOCK_RGBA, 0, "Screen"},
+ { -1, 0, "" }
+};
+
+
+static void compute_gradient_screen(RenderData *rd, NodeKeyingScreenData *keyingscreen_data, MovieClip *clip, CompBuf *screenbuf)
+{
+ MovieClipUser user = {0};
+ MovieTracking *tracking = &clip->tracking;
+ MovieTrackingTrack *track;
+ VoronoiTriangulationPoint *triangulated_points;
+ VoronoiSite *sites;
+ ImBuf *ibuf;
+ ListBase *tracksbase;
+ ListBase edges = {NULL, NULL};
+ int sites_total, triangulated_points_total, triangles_total;
+ int (*triangles)[3];
+ int i, x, y;
+ float *rect = screenbuf->rect;
+
+ if (keyingscreen_data->tracking_object[0]) {
+ MovieTrackingObject *object = BKE_tracking_named_object(tracking, keyingscreen_data->tracking_object);
+
+ if (!object)
+ return;
+
+ tracksbase = BKE_tracking_object_tracks(tracking, object);
+ }
+ else
+ tracksbase = BKE_tracking_get_tracks(tracking);
+
+ sites_total = BLI_countlist(tracksbase);
+
+ if (!sites_total)
+ return;
+
+ BKE_movieclip_user_set_frame(&user, rd->cfra);
+ ibuf = BKE_movieclip_get_ibuf(clip, &user);
+
+ sites = MEM_callocN(sizeof(VoronoiSite) * sites_total, "keyingscreen voronoi sites");
+ track = tracksbase->first;
+ i = 0;
+ while (track) {
+ VoronoiSite *site = &sites[i];
+ MovieTrackingMarker *marker = BKE_tracking_get_marker(track, rd->cfra);
+ ImBuf *pattern_ibuf = BKE_tracking_get_pattern_imbuf(ibuf, track, marker, 0, FALSE, NULL, NULL);
+ int j;
+
+ zero_v3(site->color);
+ for (j = 0; j < pattern_ibuf->x * pattern_ibuf->y; j++) {
+ if (pattern_ibuf->rect_float) {
+ add_v3_v3(site->color, &pattern_ibuf->rect_float[4 * j]);
+ }
+ else {
+ unsigned char *rrgb = (unsigned char *)pattern_ibuf->rect;
+
+ site->color[0] += srgb_to_linearrgb((float)rrgb[4 * j + 0] / 255.0f);
+ site->color[1] += srgb_to_linearrgb((float)rrgb[4 * j + 1] / 255.0f);
+ site->color[2] += srgb_to_linearrgb((float)rrgb[4 * j + 2] / 255.0f);
+ }
+ }
+
+ mul_v3_fl(site->color, 1.0f / (pattern_ibuf->x * pattern_ibuf->y));
+ IMB_freeImBuf(pattern_ibuf);
+
+ site->co[0] = marker->pos[0] * screenbuf->x;
+ site->co[1] = marker->pos[1] * screenbuf->y;
+
+ track = track->next;
+ i++;
+ }
+
+ IMB_freeImBuf(ibuf);
+
+ BLI_voronoi_compute(sites, sites_total, screenbuf->x, screenbuf->y, &edges);
+
+ BLI_voronoi_triangulate(sites, sites_total, &edges, screenbuf->x, screenbuf->y,
+ &triangulated_points, &triangulated_points_total,
+ &triangles, &triangles_total);
+
+ for (y = 0; y < screenbuf->y; y++) {
+ for (x = 0; x < screenbuf->x; x++) {
+ int index = 4 * (y * screenbuf->x + x);
+
+ rect[index + 0] = rect[index + 1] = rect[index + 2] = 0.0f;
+ rect[index + 3] = 1.0f;
+
+ for (i = 0; i < triangles_total; i++) {
+ int *triangle = triangles[i];
+ VoronoiTriangulationPoint *a = &triangulated_points[triangle[0]],
+ *b = &triangulated_points[triangle[1]],
+ *c = &triangulated_points[triangle[2]];
+ float co[2] = {x, y}, w[3];
+
+ if (barycentric_coords_v2(a->co, b->co, c->co, co, w)) {
+ if (barycentric_inside_triangle_v2(w)) {
+ rect[index + 0] += a->color[0] * w[0] + b->color[0] * w[1] + c->color[0] * w[2];
+ rect[index + 1] += a->color[1] * w[0] + b->color[1] * w[1] + c->color[1] * w[2];
+ rect[index + 2] += a->color[2] * w[0] + b->color[2] * w[1] + c->color[2] * w[2];
+ }
+ }
+ }
+ }
+ }
+
+ MEM_freeN(triangulated_points);
+ MEM_freeN(triangles);
+ MEM_freeN(sites);
+ BLI_freelistN(&edges);
+}
+
+static void exec(void *data, bNode *node, bNodeStack **UNUSED(in), bNodeStack **out)
+{
+ NodeKeyingScreenData *keyingscreen_data = node->storage;
+ RenderData *rd = data;
+ CompBuf *screenbuf = NULL;
+
+ if (node->id) {
+ MovieClip *clip = (MovieClip *) node->id;
+ MovieClipUser user = {0};
+ int width, height;
+
+ BKE_movieclip_user_set_frame(&user, rd->cfra);
+ BKE_movieclip_get_size(clip, &user, &width, &height);
+
+ screenbuf = alloc_compbuf(width, height, CB_RGBA, TRUE);
+ compute_gradient_screen(rd, keyingscreen_data, clip, screenbuf);
+ }
+
+ out[0]->data = screenbuf;
+}
+
+static void node_composit_init_keyingscreen(bNodeTree *UNUSED(ntree), bNode* node, bNodeTemplate *UNUSED(ntemp))
+{
+ NodeKeyingScreenData *data;
+
+ data = MEM_callocN(sizeof(NodeKeyingScreenData), "node keyingscreen data");
+
+ node->storage = data;
+}
+
+void register_node_type_cmp_keyingscreen(bNodeTreeType *ttype)
+{
+ static bNodeType ntype;
+
+ node_type_base(ttype, &ntype, CMP_NODE_KEYINGSCREEN, "Keying Screen", NODE_CLASS_MATTE, NODE_OPTIONS);
+ node_type_socket_templates(&ntype, NULL, cmp_node_keyingscreen_out);
+ node_type_size(&ntype, 140, 100, 320);
+ node_type_init(&ntype, node_composit_init_keyingscreen);
+ node_type_storage(&ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage);
+ node_type_exec(&ntype, exec);
+
+ nodeRegisterType(ttype, &ntype);
+}