Kaying node from tomato branch
authorSergey Sharybin <sergey.vfx@gmail.com>
Thu, 14 Jun 2012 12:19:13 +0000 (12:19 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Thu, 14 Jun 2012 12:19:13 +0000 (12:19 +0000)
Merge keying node from tomato branch into trunk.

It was considered stable and helpful by Mango team and it'll help
studio pipeline, because nodes would stop disappearing when opening
files in current trunk.

Full information about keying nodes could be found there:
http://wiki.blender.org/index.php/User:Nazg-gul/Keying

18 files changed:
1  2 
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/node.c
source/blender/compositor/CMakeLists.txt
source/blender/compositor/intern/COM_Converter.cpp
source/blender/compositor/nodes/COM_KeyingNode.cpp
source/blender/compositor/nodes/COM_KeyingNode.h
source/blender/compositor/operations/COM_KeyingBlurOperation.cpp
source/blender/compositor/operations/COM_KeyingBlurOperation.h
source/blender/compositor/operations/COM_KeyingClipOperation.cpp
source/blender/compositor/operations/COM_KeyingClipOperation.h
source/blender/compositor/operations/COM_KeyingOperation.cpp
source/blender/compositor/operations/COM_KeyingOperation.h
source/blender/editors/space_clip/clip_dopesheet_ops.c
source/blender/editors/space_node/drawnode.c
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/nodes/CMakeLists.txt
source/blender/nodes/composite/nodes/node_composite_keying.c

@@@ -659,6 -660,7 +659,7 @@@ void                       ntreeGPUMaterialNodes(struct bNo
  #define CMP_NODE_OUTPUT_MULTI_FILE__DEPRECATED        267     /* DEPRECATED multi file node has been merged into regular CMP_NODE_OUTPUT_FILE */
  #define CMP_NODE_MASK         268
  #define CMP_NODE_KEYINGSCREEN         269
 -#define CMP_NODE_KEYING               270
++#define CMP_NODE_KEYING                       270
  
  #define CMP_NODE_GLARE                301
  #define CMP_NODE_TONEMAP      302
@@@ -325,11 -322,19 +325,22 @@@ set(SR
        operations/COM_DoubleEdgeMaskOperation.h
  
  
 -      nodes/COM_KeyingNode.cpp
 -      nodes/COM_KeyingNode.h
        nodes/COM_KeyingScreenNode.cpp
        nodes/COM_KeyingScreenNode.h
 -      operations/COM_KeyingOperation.cpp
 -      operations/COM_KeyingOperation.h
        operations/COM_KeyingScreenOperation.cpp
        operations/COM_KeyingScreenOperation.h
 +
++      nodes/COM_KeyingNode.cpp
++      nodes/COM_KeyingNode.h
++      operations/COM_KeyingOperation.cpp
++      operations/COM_KeyingOperation.h
++      operations/COM_KeyingBlurOperation.cpp
++      operations/COM_KeyingBlurOperation.h
+       operations/COM_KeyingDespillOperation.cpp
+       operations/COM_KeyingDespillOperation.h
+       operations/COM_KeyingClipOperation.cpp
+       operations/COM_KeyingClipOperation.h
        operations/COM_ColorSpillOperation.cpp
        operations/COM_ColorSpillOperation.h
        operations/COM_RenderLayersBaseProg.cpp
index 0000000,05d4738..74af25b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,230 +1,227 @@@
 -      memset(&preBlurData, 0, sizeof(preBlurData));
 -      preBlurData.sizex = size;
 -      preBlurData.sizey = size;
 -
+ /*
+  * Copyright 2012, Blender Foundation.
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  *
+  * Contributor:
+  *            Jeroen Bakker
+  *            Monique Dewanchand
+  *            Sergey Sharybin
+  */
+ #include "COM_KeyingNode.h"
+ #include "COM_ExecutionSystem.h"
+ #include "COM_KeyingOperation.h"
++#include "COM_KeyingBlurOperation.h"
+ #include "COM_KeyingDespillOperation.h"
+ #include "COM_KeyingClipOperation.h"
+ #include "COM_SeparateChannelOperation.h"
+ #include "COM_CombineChannelsOperation.h"
+ #include "COM_ConvertRGBToYCCOperation.h"
+ #include "COM_ConvertYCCToRGBOperation.h"
+ #include "COM_GaussianBokehBlurOperation.h"
+ #include "COM_SetValueOperation.h"
+ #include "COM_DilateErodeOperation.h"
+ #include "COM_SetAlphaOperation.h"
+ KeyingNode::KeyingNode(bNode *editorNode): Node(editorNode)
+ {
+ }
+ OutputSocket *KeyingNode::setupPreBlur(ExecutionSystem *graph, InputSocket *inputImage, int size, OutputSocket **originalImage)
+ {
 -                      SetValueOperation *setValueOperation = new SetValueOperation();
 -                      setValueOperation->setValue(1.0f);
 -                      graph->addOperation(setValueOperation);
+       ConvertRGBToYCCOperation *convertRGBToYCCOperation = new ConvertRGBToYCCOperation();
+       convertRGBToYCCOperation->setMode(0);  /* ITU 601 */
+       inputImage->relinkConnections(convertRGBToYCCOperation->getInputSocket(0), 0, graph);
+       graph->addOperation(convertRGBToYCCOperation);
+       CombineChannelsOperation *combineOperation = new CombineChannelsOperation();
+       graph->addOperation(combineOperation);
+       for (int channel = 0; channel < 4; channel++) {
+               SeparateChannelOperation *separateOperation = new SeparateChannelOperation();
+               separateOperation->setChannel(channel);
+               addLink(graph, convertRGBToYCCOperation->getOutputSocket(0), separateOperation->getInputSocket(0));
+               graph->addOperation(separateOperation);
+               if (channel == 0 || channel == 3) {
+                       addLink(graph, separateOperation->getOutputSocket(0), combineOperation->getInputSocket(channel));
+               }
+               else {
 -                      GaussianBokehBlurOperation *blurOperation = new GaussianBokehBlurOperation();
 -                      blurOperation->setData(&preBlurData);
 -                      blurOperation->setQuality(COM_QUALITY_HIGH);
++                      KeyingBlurOperation *blurOperation = new KeyingBlurOperation();
 -                      addLink(graph, setValueOperation->getOutputSocket(0), blurOperation->getInputSocket(1));
++                      blurOperation->setSize(size);
+                       addLink(graph, separateOperation->getOutputSocket(0), blurOperation->getInputSocket(0));
 -      memset(&postBlurData, 0, sizeof(postBlurData));
 -
 -      postBlurData.sizex = size;
 -      postBlurData.sizey = size;
 -
 -      SetValueOperation *setValueOperation = new SetValueOperation();
 -
 -      setValueOperation->setValue(1.0f);
 -      graph->addOperation(setValueOperation);
+                       addLink(graph, blurOperation->getOutputSocket(0), combineOperation->getInputSocket(channel));
+                       graph->addOperation(blurOperation);
+               }
+       }
+       ConvertYCCToRGBOperation *convertYCCToRGBOperation = new ConvertYCCToRGBOperation();
+       convertYCCToRGBOperation->setMode(0);  /* ITU 601 */
+       addLink(graph, combineOperation->getOutputSocket(0), convertYCCToRGBOperation->getInputSocket(0));
+       graph->addOperation(convertYCCToRGBOperation);
+       *originalImage = convertRGBToYCCOperation->getInputSocket(0)->getConnection()->getFromSocket();
+       return convertYCCToRGBOperation->getOutputSocket(0);
+ }
+ OutputSocket *KeyingNode::setupPostBlur(ExecutionSystem *graph, OutputSocket *postBLurInput, int size)
+ {
 -      GaussianBokehBlurOperation *blurOperation = new GaussianBokehBlurOperation();
 -      blurOperation->setData(&postBlurData);
 -      blurOperation->setQuality(COM_QUALITY_HIGH);
++      KeyingBlurOperation *blurOperation = new KeyingBlurOperation();
 -      addLink(graph, setValueOperation->getOutputSocket(0), blurOperation->getInputSocket(1));
++      blurOperation->setSize(size);
+       addLink(graph, postBLurInput, blurOperation->getInputSocket(0));
 -OutputSocket *KeyingNode::setupDespill(ExecutionSystem *graph, OutputSocket *despillInput, InputSocket *inputScreen, float factor)
+       graph->addOperation(blurOperation);
+       return blurOperation->getOutputSocket();
+ }
+ OutputSocket *KeyingNode::setupDilateErode(ExecutionSystem *graph, OutputSocket *dilateErodeInput, int distance)
+ {
+       DilateStepOperation *dilateErodeOperation;
+       if (distance > 0) {
+               dilateErodeOperation = new DilateStepOperation();
+               dilateErodeOperation->setIterations(distance);
+       }
+       else {
+               dilateErodeOperation = new ErodeStepOperation();
+               dilateErodeOperation->setIterations(-distance);
+       }
+       addLink(graph, dilateErodeInput, dilateErodeOperation->getInputSocket(0));
+       graph->addOperation(dilateErodeOperation);
+       return dilateErodeOperation->getOutputSocket(0);
+ }
 -      inputScreen->relinkConnections(despillOperation->getInputSocket(1), 1, graph);
++OutputSocket *KeyingNode::setupDespill(ExecutionSystem *graph, OutputSocket *despillInput, OutputSocket *inputScreen, float factor)
+ {
+       KeyingDespillOperation *despillOperation = new KeyingDespillOperation();
+       despillOperation->setDespillFactor(factor);
+       addLink(graph, despillInput, despillOperation->getInputSocket(0));
 -OutputSocket *KeyingNode::setupClip(ExecutionSystem *graph, OutputSocket *clipInput, float clipBlack, float clipWhite)
++      addLink(graph, inputScreen, despillOperation->getInputSocket(1));
+       graph->addOperation(despillOperation);
+       return despillOperation->getOutputSocket(0);
+ }
 -      OutputSocket *postprocessedMatte, *postprocessedImage, *originalImage;
++OutputSocket *KeyingNode::setupClip(ExecutionSystem *graph, OutputSocket *clipInput, int kernelRadius, float kernelTolerance,
++                                    float clipBlack, float clipWhite, bool edgeMatte)
+ {
+       KeyingClipOperation *clipOperation = new KeyingClipOperation();
++      clipOperation->setKernelRadius(kernelRadius);
++      clipOperation->setKernelTolerance(kernelTolerance);
++
+       clipOperation->setClipBlack(clipBlack);
+       clipOperation->setClipWhite(clipWhite);
++      clipOperation->setIsEdgeMatte(edgeMatte);
+       addLink(graph, clipInput, clipOperation->getInputSocket(0));
+       graph->addOperation(clipOperation);
+       return clipOperation->getOutputSocket(0);
+ }
+ void KeyingNode::convertToOperations(ExecutionSystem *graph, CompositorContext *context)
+ {
+       InputSocket *inputImage = this->getInputSocket(0);
+       InputSocket *inputScreen = this->getInputSocket(1);
+       OutputSocket *outputImage = this->getOutputSocket(0);
+       OutputSocket *outputMatte = this->getOutputSocket(1);
 -      if (keying_data->clip_black > 0.0f || keying_data->clip_white< 1.0f)
 -              postprocessedMatte = setupClip(graph, postprocessedMatte, keying_data->clip_black, keying_data->clip_white);
++      OutputSocket *outputEdges = this->getOutputSocket(2);
++      OutputSocket *postprocessedMatte, *postprocessedImage, *originalImage, *edgesMatte;
+       bNode *editorNode = this->getbNode();
+       NodeKeyingData *keying_data = (NodeKeyingData *) editorNode->storage;
+       /* keying operation */
+       KeyingOperation *keyingOperation = new KeyingOperation();
+       keyingOperation->setScreenBalance(keying_data->screen_balance);
+       inputScreen->relinkConnections(keyingOperation->getInputSocket(1), 1, graph);
+       if (keying_data->blur_pre) {
+               /* chroma preblur operation for input of keying operation  */
+               OutputSocket *preBluredImage = setupPreBlur(graph, inputImage, keying_data->blur_pre, &originalImage);
+               addLink(graph, preBluredImage, keyingOperation->getInputSocket(0));
+       }
+       else {
+               inputImage->relinkConnections(keyingOperation->getInputSocket(0), 0, graph);
+               originalImage = keyingOperation->getInputSocket(0)->getConnection()->getFromSocket();
+       }
+       graph->addOperation(keyingOperation);
+       postprocessedMatte = keyingOperation->getOutputSocket();
 -              postprocessedImage = setupDespill(graph, postprocessedImage, inputScreen, keying_data->despill_factor);
++      if (keying_data->clip_black > 0.0f || keying_data->clip_white< 1.0f) {
++              postprocessedMatte = setupClip(graph, postprocessedMatte,
++                                             keying_data->edge_kernel_radius, keying_data->edge_kernel_tolerance,
++                                             keying_data->clip_black, keying_data->clip_white, false);
++      }
++
++      edgesMatte = setupClip(graph, postprocessedMatte,
++                             keying_data->edge_kernel_radius, keying_data->edge_kernel_tolerance,
++                             keying_data->clip_black, keying_data->clip_white, true);
+       /* apply blur on matte if needed */
+       if (keying_data->blur_post)
+               postprocessedMatte = setupPostBlur(graph, postprocessedMatte, keying_data->blur_post);
+       /* matte dilate/erode */
+       if (keying_data->dilate_distance != 0) {
+               postprocessedMatte = setupDilateErode(graph, postprocessedMatte, keying_data->dilate_distance);
+       }
+       /* set alpha channel to output image */
+       SetAlphaOperation *alphaOperation = new SetAlphaOperation();
+       addLink(graph, originalImage, alphaOperation->getInputSocket(0));
+       addLink(graph, postprocessedMatte, alphaOperation->getInputSocket(1));
+       postprocessedImage = alphaOperation->getOutputSocket();
+       /* despill output image */
+       if (keying_data->despill_factor > 0.0f) {
++              postprocessedImage = setupDespill(graph, postprocessedImage,
++                                                keyingOperation->getInputSocket(1)->getConnection()->getFromSocket(),
++                                                keying_data->despill_factor);
+       }
+       /* connect result to output sockets */
+       outputImage->relinkConnections(postprocessedImage);
+       outputMatte->relinkConnections(postprocessedMatte);
++      outputEdges->relinkConnections(edgesMatte);
+       graph->addOperation(alphaOperation);
+ }
index 0000000,9d4067c..cc1d447
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,45 +1,42 @@@
 -#include "DNA_node_types.h"
+ /*
+  * 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"
 -      NodeBlurData preBlurData;
 -      NodeBlurData postBlurData;
 -
+ /**
+   * @brief KeyingNode
+   * @ingroup Node
+   */
+ class KeyingNode : public Node {
+ protected:
 -      OutputSocket *setupDespill(ExecutionSystem *graph, OutputSocket *despillInput, InputSocket *inputSrceen, float factor);
 -      OutputSocket *setupClip(ExecutionSystem *graph, OutputSocket *clipInput, float clipBlack, float clipWhite);
+       OutputSocket *setupPreBlur(ExecutionSystem *graph, InputSocket *inputImage, int size, OutputSocket **originalImage);
+       OutputSocket *setupPostBlur(ExecutionSystem *graph, OutputSocket *postBLurInput, int size);
+       OutputSocket *setupDilateErode(ExecutionSystem *graph, OutputSocket *dilateErodeInput, int distance);
++      OutputSocket *setupDespill(ExecutionSystem *graph, OutputSocket *despillInput, OutputSocket *inputSrceen, float factor);
++      OutputSocket *setupClip(ExecutionSystem *graph, OutputSocket *clipInput, int kernelRadius, float kernelTolerance,
++                              float clipBlack, float clipWhite, bool edgeMatte);
+ public:
+       KeyingNode(bNode *editorNode);
+       void convertToOperations(ExecutionSystem *graph, CompositorContext *context);
+ };
index 0000000,0000000..3ed3921
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,88 @@@
++/*
++ * 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_KeyingBlurOperation.h"
++
++#include "MEM_guardedalloc.h"
++
++#include "BLI_listbase.h"
++#include "BLI_math.h"
++
++KeyingBlurOperation::KeyingBlurOperation(): NodeOperation()
++{
++      this->addInputSocket(COM_DT_VALUE);
++      this->addOutputSocket(COM_DT_VALUE);
++
++      this->size = 0.0f;
++
++      this->setComplex(true);
++}
++
++void *KeyingBlurOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
++{
++      void *buffer = getInputOperation(0)->initializeTileData(rect, memoryBuffers);
++
++      return buffer;
++}
++
++void KeyingBlurOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
++{
++      MemoryBuffer *inputBuffer = (MemoryBuffer*)data;
++      float *buffer = inputBuffer->getBuffer();
++
++      int bufferWidth = inputBuffer->getWidth();
++      int bufferHeight = inputBuffer->getHeight();
++
++      int i, j, count = 0;
++
++      float average = 0.0f;
++
++      for (i = -this->size + 1; i < this->size; i++) {
++              for (j = -this->size + 1; j < this->size; j++) {
++                      int cx = x + j, cy = y + i;
++
++                      if (cx >= 0 && cx < bufferWidth && cy >= 0 && cy < bufferHeight) {
++                              int bufferIndex = (cy * bufferWidth + cx) * 4;
++
++                              average += buffer[bufferIndex];
++                              count++;
++                      }
++              }
++      }
++
++      average /= (float) count;
++
++      color[0] = average;
++}
++
++bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
++{
++      rcti newInput;
++
++      newInput.xmin = 0;
++      newInput.ymin = 0;
++      newInput.xmax = this->getWidth();
++      newInput.ymax = this->getHeight();
++
++      return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
++}
index 0000000,0000000..8d7834c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ * 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_KeyingBlurOperation_h
++#define _COM_KeyingBlurOperation_h
++
++#include "COM_NodeOperation.h"
++
++/**
++  * Class with implementation of bluring for keying node
++  */
++class KeyingBlurOperation : public NodeOperation {
++protected:
++      int size;
++
++public:
++      KeyingBlurOperation();
++
++      void setSize(float value) {this->size = value;}
++
++      void *initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers);
++
++      void executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data);
++
++      bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output);
++};
++
++#endif
index 0000000,38d67a7..09b5b7a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,92 +1,123 @@@
 -      const int delta = 3;
+ /*
+  * 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_KeyingClipOperation.h"
+ #include "MEM_guardedalloc.h"
+ #include "BLI_listbase.h"
+ #include "BLI_math.h"
+ KeyingClipOperation::KeyingClipOperation(): NodeOperation()
+ {
+       this->addInputSocket(COM_DT_VALUE);
+       this->addOutputSocket(COM_DT_VALUE);
++      this->kernelRadius = 3;
++      this->kernelTolerance = 0.1f;
++
+       this->clipBlack = 0.0f;
+       this->clipWhite = 1.0f;
++      this->isEdgeMatte = false;
++
+       this->setComplex(true);
+ }
+ void *KeyingClipOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
+ {
+       void *buffer = getInputOperation(0)->initializeTileData(rect, memoryBuffers);
+       return buffer;
+ }
+ void KeyingClipOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
+ {
 -      int count_black = 0, count_white = 0;
 -      int i, j;
++      const int delta = this->kernelRadius;
++      const float tolerance = this->kernelTolerance;
+       MemoryBuffer *inputBuffer = (MemoryBuffer*)data;
+       float *buffer = inputBuffer->getBuffer();
+       int bufferWidth = inputBuffer->getWidth();
+       int bufferHeight = inputBuffer->getHeight();
 -      int srcIndex = (y * bufferWidth + x) * 4;
++      int i, j, count = 0, totalCount = 0;
 -                              if (buffer[bufferIndex] < 0.4f)
 -                                      count_black++;
 -                              else if (buffer[bufferIndex] > 0.6f)
 -                                      count_white++;
++      float value = buffer[(y * bufferWidth + x) * 4];
++
++      bool ok = false;
+       for (i = -delta + 1; i < delta; i++) {
+               for (j = -delta + 1; j < delta; j++) {
+                       int cx = x + j, cy = y + i;
+                       if (i == 0 && j == 0)
+                               continue;
+                       if (cx >= 0 && cx < bufferWidth && cy >= 0 && cy < bufferHeight) {
+                               int bufferIndex = (cy * bufferWidth + cx) * 4;
++                              float currentValue = buffer[bufferIndex];
++
++                              if (fabsf(currentValue - value) < tolerance) {
++                                      count++;
++                              }
 -      color[0] = buffer[srcIndex];
++                              totalCount++;
+                       }
+               }
+       }
 -      if (count_black >= 22 || count_white >= 22) {
 -              if (color[0] < this->clipBlack)
++      ok = count >= (float) totalCount * 0.9f;
 -              else if (color[0] >= this->clipWhite)
 -                      color[0] = 1.0f;
++      if (this->isEdgeMatte) {
++              if (ok)
+                       color[0] = 0.0f;
 -                      color[0] = (color[0] - this->clipBlack) / (this->clipWhite - this->clipBlack);
+               else
++                      color[0] = 1.0f;
+       }
++      else {
++              color[0] = value;
++
++              if (ok) {
++                      if (color[0] < this->clipBlack)
++                              color[0] = 0.0f;
++                      else if (color[0] >= this->clipWhite)
++                              color[0] = 1.0f;
++                      else
++                              color[0] = (color[0] - this->clipBlack) / (this->clipWhite - this->clipBlack);
++              }
++      }
++}
++
++bool KeyingClipOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
++{
++      rcti newInput;
++
++      newInput.xmin = 0;
++      newInput.ymin = 0;
++      newInput.xmax = this->getWidth();
++      newInput.ymax = this->getHeight();
++
++      return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
+ }
index 0000000,4eab7d6..9c7b23b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,48 +1,59 @@@
+ /*
+  * 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_KeyingClipOperation_h
+ #define _COM_KeyingClipOperation_h
+ #include "COM_NodeOperation.h"
+ /**
+   * Class with implementation of black/white clipping for keying node
+   */
+ class KeyingClipOperation : public NodeOperation {
+ protected:
+       float clipBlack;
+       float clipWhite;
++      int kernelRadius;
++      float kernelTolerance;
++
++      bool isEdgeMatte;
+ public:
+       KeyingClipOperation();
+       void setClipBlack(float value) {this->clipBlack = value;}
+       void setClipWhite(float value) {this->clipWhite = value;}
++      void setKernelRadius(int value) {this->kernelRadius = value;}
++      void setKernelTolerance(float value) {this->kernelTolerance = value;}
++
++      void setIsEdgeMatte(bool value) {this->isEdgeMatte = value;}
++
+       void *initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers);
+       void executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data);
++
++      bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output);
+ };
+ #endif
index 0000000,a31b49c..fba4dc6
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,103 +1,115 @@@
+ /*
+  * Copyright 2012, Blender Foundation.
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  *
+  * Contributor:
+  *            Jeroen Bakker
+  *            Monique Dewanchand
+  *            Sergey Sharybin
+  */
+ #include "COM_KeyingOperation.h"
+ #include "MEM_guardedalloc.h"
+ #include "BLI_listbase.h"
+ #include "BLI_math.h"
+ static int get_pixel_primary_channel(float pixelColor[4])
+ {
+       float max_value = MAX3(pixelColor[0], pixelColor[1], pixelColor[2]);
+       if (max_value == pixelColor[0])
+               return 0;
+       else if (max_value == pixelColor[1])
+               return 1;
+       return 2;
+ }
+ static float get_pixel_saturation(float pixelColor[4], float screen_balance, int primary_channel)
+ {
+       int other_1 = (primary_channel + 1) % 3;
+       int other_2 = (primary_channel + 2) % 3;
+       float min = MIN2(pixelColor[other_1], pixelColor[other_2]);
+       float max = MAX2(pixelColor[other_1], pixelColor[other_2]);
+       float val = screen_balance * min + (1.0f - screen_balance) * max;
+       return (pixelColor[primary_channel] - val) * fabsf(1.0f - val);
+ }
+ KeyingOperation::KeyingOperation(): NodeOperation()
+ {
+       this->addInputSocket(COM_DT_COLOR);
+       this->addInputSocket(COM_DT_COLOR);
+       this->addOutputSocket(COM_DT_VALUE);
+       this->screenBalance = 0.5f;
+       this->pixelReader = NULL;
+       this->screenReader = NULL;
+ }
+ void KeyingOperation::initExecution()
+ {
+       this->pixelReader = this->getInputSocketReader(0);
+       this->screenReader = this->getInputSocketReader(1);
+ }
+ void KeyingOperation::deinitExecution()
+ {
+       this->pixelReader = NULL;
+       this->screenReader = NULL;
+ }
+ void KeyingOperation::executePixel(float *color, float x, float y, PixelSampler sampler, MemoryBuffer *inputBuffers[])
+ {
+       float pixelColor[4];
+       float screenColor[4];
+       this->pixelReader->read(pixelColor, x, y, sampler, inputBuffers);
+       this->screenReader->read(screenColor, x, y, sampler, inputBuffers);
+       int primary_channel = get_pixel_primary_channel(screenColor);
+       float saturation = get_pixel_saturation(pixelColor, this->screenBalance, primary_channel);
+       float screen_saturation = get_pixel_saturation(screenColor, this->screenBalance, primary_channel);
+       if (saturation < 0) {
+               color[0] = 1.0f;
+       }
+       else if (saturation >= screen_saturation) {
+               color[0] = 0.0f;
+       }
+       else {
+               float distance = 1.0f - saturation / screen_saturation;
+               color[0] = distance;
+       }
+ }
++
++bool KeyingOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
++{
++      rcti newInput;
++
++      newInput.xmin = 0;
++      newInput.ymin = 0;
++      newInput.xmax = this->getWidth();
++      newInput.ymax = this->getHeight();
++
++      return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
++}
index 0000000,1232d00..0fc1340
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,54 +1,56 @@@
+ /*
+  * Copyright 2012, Blender Foundation.
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  *
+  * Contributor:
+  *            Jeroen Bakker
+  *            Monique Dewanchand
+  *            Sergey Sharybin
+  */
+ #ifndef _COM_KeyingOperation_h
+ #define _COM_KeyingOperation_h
+ #include <string.h>
+ #include "COM_NodeOperation.h"
+ #include "BLI_listbase.h"
+ /**
+   * Class with implementation of keying node
+   */
+ class KeyingOperation : public NodeOperation {
+ protected:
+       SocketReader *pixelReader;
+       SocketReader *screenReader;
+       float screenBalance;
+ public:
+       KeyingOperation();
+       void initExecution();
+       void deinitExecution();
+       void setScreenBalance(float value) {this->screenBalance = value;}
+       void executePixel(float *color, float x, float y, PixelSampler sampler, MemoryBuffer *inputBuffers[]);
++
++      bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output);
+ };
+ #endif
@@@ -2443,6 -2441,19 +2443,21 @@@ static void node_composit_buts_keyingsc
        }
  }
  
+ static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+ {
+       /* bNode *node= ptr->data; */ /* UNUSED */
+       uiItemR(layout, ptr, "blur_pre", 0, NULL, ICON_NONE);
+       uiItemR(layout, ptr, "screen_balance", 0, NULL, ICON_NONE);
+       uiItemR(layout, ptr, "despill_factor", 0, NULL, ICON_NONE);
++      uiItemR(layout, ptr, "edge_kernel_radius", 0, NULL, ICON_NONE);
++      uiItemR(layout, ptr, "edge_kernel_tolerance", 0, NULL, ICON_NONE);
+       uiItemR(layout, ptr, "clip_black", 0, NULL, ICON_NONE);
+       uiItemR(layout, ptr, "clip_white", 0, NULL, ICON_NONE);
+       uiItemR(layout, ptr, "dilate_distance", 0, NULL, ICON_NONE);
+       uiItemR(layout, ptr, "blur_post", 0, NULL, ICON_NONE);
+ }
  /* only once called */
  static void node_composit_set_butfunc(bNodeType *ntype)
  {
@@@ -632,6 -632,14 +632,16 @@@ typedef struct NodeKeyingScreenData 
        char tracking_object[64];
  } NodeKeyingScreenData;
  
+ typedef struct NodeKeyingData {
+       float screen_balance;
+       float despill_factor;
++      int edge_kernel_radius;
++      float edge_kernel_tolerance;
+       float clip_black, clip_white;
+       int dilate_distance;
+       int blur_pre, blur_post;
+ } NodeKeyingData;
  /* frame node flags */
  #define NODE_FRAME_SHRINK             1       /* keep the bounding box minimal */
  #define NODE_FRAME_RESIZEABLE 2       /* test flag, if frame can be resized by user */
@@@ -3101,25 -3070,74 +3101,6 @@@ static void def_cmp_mask(StructRNA *srn
        RNA_def_property_ui_text(prop, "Mask", "");
  }
  
--static void def_cmp_keyingscreen(StructRNA *srna)
--{
--      PropertyRNA *prop;
--
--      prop = RNA_def_property(srna, "clip", PROP_POINTER, PROP_NONE);
--      RNA_def_property_pointer_sdna(prop, NULL, "id");
--      RNA_def_property_struct_type(prop, "MovieClip");
--      RNA_def_property_flag(prop, PROP_EDITABLE);
--      RNA_def_property_ui_text(prop, "Movie Clip", "");
--      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
--
--      RNA_def_struct_sdna_from(srna, "NodeKeyingScreenData", "storage");
--
--      prop = RNA_def_property(srna, "tracking_object", PROP_STRING, PROP_NONE);
--      RNA_def_property_string_sdna(prop, NULL, "tracking_object");
--      RNA_def_property_ui_text(prop, "Tracking Object", "");
 -      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
 -}
 -
 -static void def_cmp_keying(StructRNA *srna)
 -{
 -      PropertyRNA *prop;
 -
 -      RNA_def_struct_sdna_from(srna, "NodeKeyingData", "storage");
 -
 -      prop = RNA_def_property(srna, "screen_balance", PROP_FLOAT, PROP_FACTOR);
 -      RNA_def_property_float_sdna(prop, NULL, "screen_balance");
 -      RNA_def_property_range(prop, 0.0f, 1.0f);
 -      RNA_def_property_ui_text(prop, "Screen Balance", "");
 -      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
 -
 -      prop = RNA_def_property(srna, "despill_factor", PROP_FLOAT, PROP_FACTOR);
 -      RNA_def_property_float_sdna(prop, NULL, "despill_factor");
 -      RNA_def_property_range(prop, 0.0f, 1.0f);
 -      RNA_def_property_ui_text(prop, "Despill", "");
 -      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
 -
 -      prop = RNA_def_property(srna, "clip_black", PROP_FLOAT, PROP_FACTOR);
 -      RNA_def_property_float_sdna(prop, NULL, "clip_black");
 -      RNA_def_property_range(prop, 0.0f, 1.0f);
 -      RNA_def_property_ui_text(prop, "Clip Black", "");
 -      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
 -
 -      prop = RNA_def_property(srna, "clip_white", PROP_FLOAT, PROP_FACTOR);
 -      RNA_def_property_float_sdna(prop, NULL, "clip_white");
 -      RNA_def_property_range(prop, 0.0f, 1.0f);
 -      RNA_def_property_ui_text(prop, "Clip White", "");
 -      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
 -
 -      prop = RNA_def_property(srna, "blur_pre", PROP_INT, PROP_NONE);
 -      RNA_def_property_int_sdna(prop, NULL, "blur_pre");
 -      RNA_def_property_range(prop, 0, 2048);
 -      RNA_def_property_ui_text(prop, "Pre Blur", "");
 -      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
 -
 -      prop = RNA_def_property(srna, "blur_post", PROP_INT, PROP_NONE);
 -      RNA_def_property_int_sdna(prop, NULL, "blur_post");
 -      RNA_def_property_range(prop, 0, 2048);
 -      RNA_def_property_ui_text(prop, "Post Blur", "");
 -      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
 -
 -      prop = RNA_def_property(srna, "dilate_distance", PROP_INT, PROP_NONE);
 -      RNA_def_property_int_sdna(prop, NULL, "dilate_distance");
 -      RNA_def_property_range(prop, -100, 100);
 -      RNA_def_property_ui_text(prop, "Dilate/Erode", "");
--      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
--}
--
  static void dev_cmd_transform(StructRNA *srna)
  {
        PropertyRNA *prop;
@@@ -3526,6 -3544,6 +3507,85 @@@ static void def_cmp_viewer(StructRNA *s
        RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
  }
  
++static void def_cmp_keyingscreen(StructRNA *srna)
++{
++      PropertyRNA *prop;
++
++      prop = RNA_def_property(srna, "clip", PROP_POINTER, PROP_NONE);
++      RNA_def_property_pointer_sdna(prop, NULL, "id");
++      RNA_def_property_struct_type(prop, "MovieClip");
++      RNA_def_property_flag(prop, PROP_EDITABLE);
++      RNA_def_property_ui_text(prop, "Movie Clip", "");
++      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
++
++      RNA_def_struct_sdna_from(srna, "NodeKeyingScreenData", "storage");
++
++      prop = RNA_def_property(srna, "tracking_object", PROP_STRING, PROP_NONE);
++      RNA_def_property_string_sdna(prop, NULL, "tracking_object");
++      RNA_def_property_ui_text(prop, "Tracking Object", "");
++      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
++}
++
++static void def_cmp_keying(StructRNA *srna)
++{
++      PropertyRNA *prop;
++
++      RNA_def_struct_sdna_from(srna, "NodeKeyingData", "storage");
++
++      prop = RNA_def_property(srna, "screen_balance", PROP_FLOAT, PROP_FACTOR);
++      RNA_def_property_float_sdna(prop, NULL, "screen_balance");
++      RNA_def_property_range(prop, 0.0f, 1.0f);
++      RNA_def_property_ui_text(prop, "Screen Balance", "Balance between two non-primary channels primary channel is comparing against");
++      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
++
++      prop = RNA_def_property(srna, "despill_factor", PROP_FLOAT, PROP_FACTOR);
++      RNA_def_property_float_sdna(prop, NULL, "despill_factor");
++      RNA_def_property_range(prop, 0.0f, 1.0f);
++      RNA_def_property_ui_text(prop, "Despill", "Factor of despilling screen color from image");
++      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
++
++      prop = RNA_def_property(srna, "clip_black", PROP_FLOAT, PROP_FACTOR);
++      RNA_def_property_float_sdna(prop, NULL, "clip_black");
++      RNA_def_property_range(prop, 0.0f, 1.0f);
++      RNA_def_property_ui_text(prop, "Clip Black", "Value of on-scaled matte pixel which considers as fully background pixel");
++      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
++
++      prop = RNA_def_property(srna, "clip_white", PROP_FLOAT, PROP_FACTOR);
++      RNA_def_property_float_sdna(prop, NULL, "clip_white");
++      RNA_def_property_range(prop, 0.0f, 1.0f);
++      RNA_def_property_ui_text(prop, "Clip White", "Value of on-scaled matte pixel which considers as fully foreground pixel");
++      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
++
++      prop = RNA_def_property(srna, "blur_pre", PROP_INT, PROP_NONE);
++      RNA_def_property_int_sdna(prop, NULL, "blur_pre");
++      RNA_def_property_range(prop, 0, 2048);
++      RNA_def_property_ui_text(prop, "Pre Blur", "Chroma pre-blur size which applies before running keyer");
++      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
++
++      prop = RNA_def_property(srna, "blur_post", PROP_INT, PROP_NONE);
++      RNA_def_property_int_sdna(prop, NULL, "blur_post");
++      RNA_def_property_range(prop, 0, 2048);
++      RNA_def_property_ui_text(prop, "Post Blur", "Matte blur size which applies after clipping and dilate/eroding");
++      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
++
++      prop = RNA_def_property(srna, "dilate_distance", PROP_INT, PROP_NONE);
++      RNA_def_property_int_sdna(prop, NULL, "dilate_distance");
++      RNA_def_property_range(prop, -100, 100);
++      RNA_def_property_ui_text(prop, "Dilate/Erode", "Matte dilate/erode side");
++      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
++
++      prop = RNA_def_property(srna, "edge_kernel_radius", PROP_INT, PROP_NONE);
++      RNA_def_property_int_sdna(prop, NULL, "edge_kernel_radius");
++      RNA_def_property_range(prop, -100, 100);
++      RNA_def_property_ui_text(prop, "Edge Kernel Radius", "Radius of kernel used to detect whether pixel belongs to edge");
++      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
++
++      prop = RNA_def_property(srna, "edge_kernel_tolerance", PROP_FLOAT, PROP_FACTOR);
++      RNA_def_property_float_sdna(prop, NULL, "edge_kernel_tolerance");
++      RNA_def_property_range(prop, 0.0f, 1.0f);
++      RNA_def_property_ui_text(prop, "Edge Kernel Tolerance", "Tolerance to pixels inside kernel which are treating as belonging to the same plane");
++      RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
++}
  
  /* -- Texture Nodes --------------------------------------------------------- */
  
@@@ -76,7 -76,8 +76,8 @@@ set(SR
        composite/nodes/node_composite_idMask.c
        composite/nodes/node_composite_image.c
        composite/nodes/node_composite_invert.c
 -      composite/nodes/node_composite_keying.c
        composite/nodes/node_composite_keyingscreen.c
++      composite/nodes/node_composite_keying.c
        composite/nodes/node_composite_lensdist.c
        composite/nodes/node_composite_levels.c
        composite/nodes/node_composite_lummaMatte.c
index 0000000,16a9168..e5bf3b7
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,219 +1,94 @@@
 -static int get_pixel_primary_channel(float *pixel)
+ /*
+  * ***** BEGIN GPL LICENSE BLOCK *****
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  *
+  * The Original Code is Copyright (C) 2011 Blender Foundation.
+  * All rights reserved.
+  *
+  * The Original Code is: all of this file.
+  *
+  * Contributor(s): Blender Foundation,
+  *                 Sergey Sharybin
+  *
+  * ***** END GPL LICENSE BLOCK *****
+  */
+ /** \file blender/nodes/composite/nodes/node_composite_keying.c
+  *  \ingroup cmpnodes
+  */
+ #include "BLF_translation.h"
+ #include "DNA_movieclip_types.h"
+ #include "BKE_movieclip.h"
+ #include "BLI_listbase.h"
+ #include "BLI_math_base.h"
+ #include "BLI_math_color.h"
+ #include "BLI_voronoi.h"
+ #include "node_composite_util.h"
+ /* **************** Translate  ******************** */
+ static bNodeSocketTemplate cmp_node_keying_in[] = {
+       {       SOCK_RGBA, 1, "Image",                  0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
+       {       SOCK_RGBA, 1, "Key Color", 1.0f, 1.0f, 1.0f, 1.0f},
+       {       -1, 0, ""       }
+ };
+ static bNodeSocketTemplate cmp_node_keying_out[] = {
+       {       SOCK_RGBA,  0, "Image"},
+       {       SOCK_FLOAT, 0, "Matte"},
++      {       SOCK_FLOAT, 0, "Edges"},
+       {       -1, 0, ""       }
+ };
 -      float max_value = MAX3(pixel[0], pixel[1], pixel[2]);
 -
 -      if (max_value == pixel[0])
 -              return 0;
 -      else if (max_value == pixel[1])
 -              return 1;
 -
 -      return 2;
 -}
 -
 -static float get_pixel_saturation(float *pixel, float screen_balance)
 -{
 -      float min = MIN3(pixel[0], pixel[1], pixel[2]);
 -      float max = MAX3(pixel[0], pixel[1], pixel[2]);
 -      float mid = pixel[0] + pixel[1] + pixel[2] - min - max;
 -      float val = (1.0f - screen_balance) * min + screen_balance * mid;
 -
 -      return max - val;
 -}
 -
 -static void despil_pixel(float *out, float *pixel, float *screen, float screen_gain)
 -{
 -      int screen_primary_channel = get_pixel_primary_channel(screen);
 -      float average_value, amount;
 -
 -      average_value = (pixel[0] + pixel[1] + pixel[2] - pixel[screen_primary_channel]) / 2.0f;
 -      amount = pixel[screen_primary_channel] - average_value;
 -
 -      if (screen_gain * amount > 0) {
 -              out[screen_primary_channel] = pixel[screen_primary_channel] - screen_gain * amount;
 -      }
 -}
 -
 -static void do_key(bNode *node, float *out, float *pixel, float *screen)
 -{
 -      NodeKeyingData *data = node->storage;
 -
 -      float screen_balance = 0.5f;
 -      float despill_factor = data->despill_factor;
 -      float clip_black = data->clip_black;
 -      float clip_white = data->clip_white;
 -
 -      float saturation = get_pixel_saturation(pixel, screen_balance);
 -      float screen_saturation = get_pixel_saturation(screen, screen_balance);
 -      int primary_channel = get_pixel_primary_channel(pixel);
 -      int screen_primary_channel = get_pixel_primary_channel(screen);
 -
 -      if (primary_channel != screen_primary_channel) {
 -              /* different main channel means pixel is on foreground,
 -               * but screen color still need to be despilled from it */
 -              despil_pixel(out, pixel, screen, despill_factor);
 -              out[3] = 1.0f;
 -      }
 -      else if (saturation >= screen_saturation) {
 -              /* saturation of main channel is more than screen, definitely a background */
 -              out[0] = 0.0f;
 -              out[1] = 0.0f;
 -              out[2] = 0.0f;
 -              out[3] = 0.0f;
 -      }
 -      else {
 -              float distance;
 -
 -              despil_pixel(out, pixel, screen, despill_factor);
 -
 -              distance = 1.0f - saturation / screen_saturation;
 -
 -              out[3] = distance;
 -
 -              if (out[3] < clip_black)
 -                      out[3] = 0.0f;
 -              else if (out[3] >= clip_white)
 -                      out[3] = 1.0f;
 -              else
 -                      out[3] = (out[3] - clip_black) / (clip_white - clip_black);
 -      }
 -}
 -
 -static void exec(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
 -{
 -      if (in[0]->data) {
 -              NodeKeyingData *keying_data = node->storage;
 -              CompBuf *cbuf = typecheck_compbuf(in[0]->data, CB_RGBA);
 -              CompBuf *keybuf, *mattebuf;
 -
 -              keybuf = dupalloc_compbuf(cbuf);
 -
 -              /* single color is used for screen detection */
 -              composit2_pixel_processor(node, keybuf, cbuf, in[0]->vec, in[1]->data, in[1]->vec, do_key, CB_RGBA, CB_VAL);
 -
 -              /* create a matte from alpha channel */
 -              mattebuf = valbuf_from_rgbabuf(keybuf, CHAN_A);
 -
 -              /* apply dilate/erode if needed */
 -              if (keying_data->dilate_distance != 0) {
 -                      int i;
 -
 -                      if (keying_data->dilate_distance > 0) {
 -                              for (i = 0; i < keying_data->dilate_distance; i++)
 -                                      node_composite_morpho_dilate(mattebuf);
 -                      }
 -                      else {
 -                              for (i = 0; i < -keying_data->dilate_distance; i++)
 -                                      node_composite_morpho_erode(mattebuf);
 -                      }
 -              }
 -
 -              if (keying_data->blur_post > 0.0f) {
 -                      /* post-blur of matte */
 -                      CompBuf *newmatte = alloc_compbuf(mattebuf->x, mattebuf->y, mattebuf->type, TRUE);
 -                      int size = keying_data->blur_post;
 -
 -                      node_composit_blur_single_image(node, R_FILTER_BOX, size, size, newmatte, mattebuf, 1.0f);
 -
 -                      free_compbuf(mattebuf);
 -                      mattebuf = newmatte;
 -
 -                      /* apply blurred matte on output buffer alpha */
 -                      valbuf_to_rgbabuf(mattebuf, keybuf, CHAN_A);
 -              }
 -
 -              out[0]->data = keybuf;
 -              out[1]->data = mattebuf;
 -
 -              generate_preview(data, node, keybuf);
 -
 -              if (cbuf!=in[0]->data)
 -                      free_compbuf(cbuf);
 -      }
++static void exec(void *UNUSED(data), bNode *UNUSED(node), bNodeStack **UNUSED(in), bNodeStack **UNUSED(out))
+ {
+ }
+ static void node_composit_init_keying(bNodeTree *UNUSED(ntree), bNode* node, bNodeTemplate *UNUSED(ntemp))
+ {
+       NodeKeyingData *data;
+       data = MEM_callocN(sizeof(NodeKeyingData), "node keying data");
+       data->screen_balance = 0.5f;
+       data->despill_factor = 1.0f;
++      data->edge_kernel_radius = 3;
++      data->edge_kernel_tolerance = 0.1f;
++      data->clip_white = 1.0f;
+       data->clip_black = 0.0f;
+       data->clip_white = 1.0f;
+       node->storage = data;
+ }
+ void register_node_type_cmp_keying(bNodeTreeType *ttype)
+ {
+       static bNodeType ntype;
+       node_type_base(ttype, &ntype, CMP_NODE_KEYING, "Keying", NODE_CLASS_MATTE, NODE_OPTIONS);
+       node_type_socket_templates(&ntype, cmp_node_keying_in, cmp_node_keying_out);
+       node_type_size(&ntype, 140, 100, 320);
+       node_type_init(&ntype, node_composit_init_keying);
+       node_type_storage(&ntype, "NodeKeyingData", node_free_standard_storage, node_copy_standard_storage);
+       node_type_exec(&ntype, exec);
+       nodeRegisterType(ttype, &ntype);
+ }