Keying Screen node from tomato branch
authorSergey Sharybin <sergey.vfx@gmail.com>
Thu, 14 Jun 2012 12:18:42 +0000 (12:18 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Thu, 14 Jun 2012 12:18:42 +0000 (12:18 +0000)
Merge Keying Screen node developed in tomato branch into trunk.

This node is aimed to make dealing with non-even greenscreens better
by generating gradiented image which could be used a input for keyer
nodes.

Based on building voronoi diagram using motion tracking markers as
sites position and average pattern color as color for that site.

Pretty straignforward node, some documentation is there
http://wiki.blender.org/index.php/User:Nazg-gul/Keying#Screen_color

18 files changed:
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/node.c
source/blender/blenlib/BLI_voronoi.h [new file with mode: 0644]
source/blender/blenlib/CMakeLists.txt
source/blender/blenlib/intern/voronoi.c [new file with mode: 0644]
source/blender/compositor/CMakeLists.txt
source/blender/compositor/intern/COM_Converter.cpp
source/blender/compositor/nodes/COM_KeyingScreenNode.cpp [new file with mode: 0644]
source/blender/compositor/nodes/COM_KeyingScreenNode.h [new file with mode: 0644]
source/blender/compositor/operations/COM_KeyingScreenOperation.cpp [new file with mode: 0644]
source/blender/compositor/operations/COM_KeyingScreenOperation.h [new file with mode: 0644]
source/blender/editors/space_node/drawnode.c
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/makesrna/intern/rna_nodetree_types.h
source/blender/nodes/CMakeLists.txt
source/blender/nodes/NOD_composite.h
source/blender/nodes/composite/nodes/node_composite_keyingscreen.c [new file with mode: 0644]

index e8f863fbbfd11fd7aa7c785732f8cb7f472e168d..bf708fc550d3d864787869f0642b359a9702b9c9 100644 (file)
@@ -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
index c48c0231aa08cd83b6ae185b50f9e0f50e4756d2..20c9344a870b7c0f613945539f78faf3f4d8207e 100644 (file)
@@ -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 (file)
index 0000000..a67b01c
--- /dev/null
@@ -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__ */
index d535e1903145b6af4e20c6d5b2dbfeb4808a267f..ac7681e3be702f7ec22b2b3f16b8c2d9a7b91a73 100644 (file)
@@ -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 (file)
index 0000000..0088d24
--- /dev/null
@@ -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);
+}
index aa428c2b8d0863ea780446b3d198c79158d65b72..cfaf11c440058f58cdfd649f0f381b2c04b7c2c4 100644 (file)
@@ -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
index f4bb89969ff59bd5f8e3fe0eb144cae6ceeaf178..082e2fe5d0441f517ea0fc3dba7a3e3d16bd0238 100644 (file)
 #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 (file)
index 0000000..ad58ada
--- /dev/null
@@ -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 (file)
index 0000000..7c87219
--- /dev/null
@@ -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 (file)
index 0000000..757afe7
--- /dev/null
@@ -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 (file)
index 0000000..9d3f44f
--- /dev/null
@@ -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
index e6549034a855d5f688c9cc5d9d65a906ae7740ef..a42bf062b122bdee9614edd434b5482aed54cb5f 100644 (file)
@@ -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;
        }
index 5be7688d714a5f2778c8f5f95cf3c620528c32c2..a81d3f5b6cd918a65ddebbbd8d8c26d09150483c 100644 (file)
@@ -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 */
index 73e2b5da4f056f38ab0ea89bacdcbd850fd7c5fe..b43fca26748086f3f5908fd5008329e1cd23e7f5 100644 (file)
@@ -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)
 {
index 3981afe53490aee308ce63f57dbb41f2a20bd6d4..98170cc4d671da7063d2339acbced2553b45b29f 100644 (file)
@@ -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",           ""              )
index 8b2a5ebd263662ad5421e1e53b3bc039fb9134e8..7b0feab2bc1869c60bd60123fa5b8131f934bab1 100644 (file)
@@ -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
index f850ea91f12aace81486115dbd3e145a9a8ece5d..fd4918a32b592475bd543f9bb86c9f41786c00c7 100644 (file)
@@ -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 (file)
index 0000000..6149285
--- /dev/null
@@ -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);
+}