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.h1
-rw-r--r--source/blender/blenkernel/intern/node.c1
-rw-r--r--source/blender/blenlib/BLI_voronoi.h70
-rw-r--r--source/blender/blenlib/CMakeLists.txt1
-rw-r--r--source/blender/blenlib/intern/voronoi.c833
-rw-r--r--source/blender/compositor/CMakeLists.txt7
-rw-r--r--source/blender/compositor/intern/COM_Converter.cpp4
-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_KeyingScreenOperation.cpp220
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.h79
-rw-r--r--source/blender/editors/space_node/drawnode.c21
-rw-r--r--source/blender/makesdna/DNA_node_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c18
-rw-r--r--source/blender/makesrna/intern/rna_nodetree_types.h1
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_composite.h1
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keyingscreen.c202
18 files changed, 1557 insertions, 0 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index e8f863fbbfd..bf708fc550d 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -658,6 +658,7 @@ 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_GLARE 301
#define CMP_NODE_TONEMAP 302
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index c48c0231aa0..20c9344a870 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -1900,6 +1900,7 @@ 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_translate(ttype);
register_node_type_cmp_rotate(ttype);
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/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 aa428c2b8d0..cfaf11c4400 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -323,6 +323,13 @@ set(SRC
operations/COM_DoubleEdgeMaskOperation.cpp
operations/COM_DoubleEdgeMaskOperation.h
+
+
+ nodes/COM_KeyingScreenNode.cpp
+ nodes/COM_KeyingScreenNode.h
+ operations/COM_KeyingScreenOperation.cpp
+ operations/COM_KeyingScreenOperation.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 f4bb89969ff..082e2fe5d04 100644
--- a/source/blender/compositor/intern/COM_Converter.cpp
+++ b/source/blender/compositor/intern/COM_Converter.cpp
@@ -112,6 +112,7 @@
#include "COM_DoubleEdgeMaskNode.h"
#include "COM_CropNode.h"
#include "COM_MaskNode.h"
+#include "COM_KeyingScreenNode.h"
Node *Converter::convert(bNode *bNode)
{
@@ -351,6 +352,9 @@ case CMP_NODE_OUTPUT_FILE:
case CMP_NODE_MASK:
node = new MaskNode(bNode);
break;
+ case CMP_NODE_KEYINGSCREEN:
+ node = new KeyingScreenNode(bNode);
+ break;
/* not inplemented yet */
default:
node = new MuteNode(bNode);
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_KeyingScreenOperation.cpp b/source/blender/compositor/operations/COM_KeyingScreenOperation.cpp
new file mode 100644
index 00000000000..757afe7b394
--- /dev/null
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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;
+
+ BKE_movieclip_user_set_frame(&user, framenumber);
+ ibuf = BKE_movieclip_get_ibuf(movieClip, &user);
+
+ if (!ibuf)
+ return NULL;
+
+ triangulation = (TriangulationData *) MEM_callocN(sizeof(TriangulationData), "keying screen triangulation data");
+
+ 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, TRUE, FALSE);
+ 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;
+
+ lockMutex();
+ if (this->cachedTriangulation == NULL) {
+ this->cachedTriangulation = buildVoronoiTriangulation();
+ }
+ unlockMutex();
+
+ 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 e6549034a85..a42bf062b12 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -2425,6 +2425,24 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p
}
+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);
+ }
+}
+
/* only once called */
static void node_composit_set_butfunc(bNodeType *ntype)
{
@@ -2617,6 +2635,9 @@ 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;
default:
ntype->uifunc = NULL;
}
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 5be7688d714..a81d3f5b6cd 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -628,6 +628,10 @@ typedef struct TexNodeOutput {
char name[64];
} TexNodeOutput;
+typedef struct NodeKeyingScreenData {
+ char tracking_object[64];
+} NodeKeyingScreenData;
+
/* 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/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 73e2b5da4f0..b43fca26748 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -3101,6 +3101,24 @@ static void def_cmp_mask(StructRNA *srna)
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 dev_cmd_transform(StructRNA *srna)
{
diff --git a/source/blender/makesrna/intern/rna_nodetree_types.h b/source/blender/makesrna/intern/rna_nodetree_types.h
index 3981afe5349..98170cc4d67 100644
--- a/source/blender/makesrna/intern/rna_nodetree_types.h
+++ b/source/blender/makesrna/intern/rna_nodetree_types.h
@@ -168,6 +168,7 @@ DefNode( CompositorNode, CMP_NODE_BOKEHBLUR, def_cmp_bokehblur, "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( 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 8b2a5ebd263..7b0feab2bc1 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -76,6 +76,7 @@ set(SRC
composite/nodes/node_composite_idMask.c
composite/nodes/node_composite_image.c
composite/nodes/node_composite_invert.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..fd4918a32b5 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -105,6 +105,7 @@ 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_translate(struct bNodeTreeType *ttype);
void register_node_type_cmp_rotate(struct bNodeTreeType *ttype);
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..6149285fdbc
--- /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, TRUE, FALSE);
+ 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);
+}