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

index bf708fc550d3d864787869f0642b359a9702b9c9,73a0d2d375b210c8a847184244d194804c231fc0..c856d407f054fdd2b47193458df523ef084c4364
@@@ -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
index cfaf11c440058f58cdfd649f0f381b2c04b7c2c4,b9e5c7d385cf70490bf5c1ab32f0e08ed778eb4c..fbe391a554d0b1f5eabba5e4b5e502f1fb970556
@@@ -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 0000000000000000000000000000000000000000,05d4738de94a742ee28597c22a1772b58ad2ab05..74af25b8e5ffb37e063597876725e68c1db9d00d
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 0000000000000000000000000000000000000000,9d4067ce599f1072a0080094584fbcacdcce4143..cc1d447c66e89b1677f644f99b03d94e007db19b
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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3ed3921e0c1e7333219e5b668e59f360f1a416f7
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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d7834c7265959280100d258e765178966fb3910
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 0000000000000000000000000000000000000000,38d67a76c72ced5e5ba454ce1b21e2cfe8a87359..09b5b7a523c588e0060764d1a7c0b06bd01f9255
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 0000000000000000000000000000000000000000,4eab7d6b0e3865692a7ce05b4b782c92eb0e2647..9c7b23b01603ca7460b38e408db979096473b1bc
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 0000000000000000000000000000000000000000,a31b49c4a5cf00f22538670717f4d4ca0e1c9a64..fba4dc65fafc5ac627ed8f87559ba2fc6acc37ca
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 0000000000000000000000000000000000000000,1232d008ac8836b7729c0f78ed41198af217490e..0fc13407d1483d69df7efde219b1292439c72394
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
index a42bf062b122bdee9614edd434b5482aed54cb5f,afaa7ac8a2844325ec98b116b33e867e3036d770..033bfd165c23c332e326f6feed8d0c23d6ddce55
@@@ -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)
  {
index a81d3f5b6cd918a65ddebbbd8d8c26d09150483c,cfc8ba526fa42dab4060710d7adc93efe3987786..a10d610c6d4aed04fd27e60d39246555ff4a9d71
@@@ -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 */
index b43fca26748086f3f5908fd5008329e1cd23e7f5,96258eff9c599339977cbed56c06951282036167..509e20949c2638b05487e32659cf1df59ffe023b
@@@ -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 --------------------------------------------------------- */
  
index 7b0feab2bc1869c60bd60123fa5b8131f934bab1,6d88f64b5d567b2905fadfcebec6b8c5ecc937e0..2415c7842a766e7ee8ef6c33a0a07caa16aa4fbe
@@@ -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 0000000000000000000000000000000000000000,16a9168e4063320634244ea2ee1df8f669df7907..e5bf3b7ae62f47c40f21e4c53c4619742529b5ec
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);
+ }