diff options
Diffstat (limited to 'source/blender/compositor/nodes/COM_KeyingNode.cc')
-rw-r--r-- | source/blender/compositor/nodes/COM_KeyingNode.cc | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/source/blender/compositor/nodes/COM_KeyingNode.cc b/source/blender/compositor/nodes/COM_KeyingNode.cc new file mode 100644 index 00000000000..9b493d3f332 --- /dev/null +++ b/source/blender/compositor/nodes/COM_KeyingNode.cc @@ -0,0 +1,350 @@ +/* + * 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. + * + * Copyright 2012, Blender Foundation. + */ + +#include "COM_KeyingNode.h" + +#include "COM_ExecutionSystem.h" + +#include "COM_KeyingBlurOperation.h" +#include "COM_KeyingClipOperation.h" +#include "COM_KeyingDespillOperation.h" +#include "COM_KeyingOperation.h" + +#include "COM_MathBaseOperation.h" + +#include "COM_ConvertOperation.h" +#include "COM_SetValueOperation.h" + +#include "COM_DilateErodeOperation.h" + +#include "COM_SetAlphaMultiplyOperation.h" + +#include "COM_GaussianAlphaXBlurOperation.h" +#include "COM_GaussianAlphaYBlurOperation.h" + +KeyingNode::KeyingNode(bNode *editorNode) : Node(editorNode) +{ + /* pass */ +} + +NodeOperationOutput *KeyingNode::setupPreBlur(NodeConverter &converter, + NodeInput *inputImage, + int size) const +{ + ConvertRGBToYCCOperation *convertRGBToYCCOperation = new ConvertRGBToYCCOperation(); + convertRGBToYCCOperation->setMode(BLI_YCC_ITU_BT709); + converter.addOperation(convertRGBToYCCOperation); + + converter.mapInputSocket(inputImage, convertRGBToYCCOperation->getInputSocket(0)); + + CombineChannelsOperation *combineOperation = new CombineChannelsOperation(); + converter.addOperation(combineOperation); + + for (int channel = 0; channel < 4; channel++) { + SeparateChannelOperation *separateOperation = new SeparateChannelOperation(); + separateOperation->setChannel(channel); + converter.addOperation(separateOperation); + + converter.addLink(convertRGBToYCCOperation->getOutputSocket(0), + separateOperation->getInputSocket(0)); + + if (ELEM(channel, 0, 3)) { + converter.addLink(separateOperation->getOutputSocket(0), + combineOperation->getInputSocket(channel)); + } + else { + KeyingBlurOperation *blurXOperation = new KeyingBlurOperation(); + blurXOperation->setSize(size); + blurXOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_X); + converter.addOperation(blurXOperation); + + KeyingBlurOperation *blurYOperation = new KeyingBlurOperation(); + blurYOperation->setSize(size); + blurYOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_Y); + converter.addOperation(blurYOperation); + + converter.addLink(separateOperation->getOutputSocket(), blurXOperation->getInputSocket(0)); + converter.addLink(blurXOperation->getOutputSocket(), blurYOperation->getInputSocket(0)); + converter.addLink(blurYOperation->getOutputSocket(0), + combineOperation->getInputSocket(channel)); + } + } + + ConvertYCCToRGBOperation *convertYCCToRGBOperation = new ConvertYCCToRGBOperation(); + convertYCCToRGBOperation->setMode(BLI_YCC_ITU_BT709); + converter.addOperation(convertYCCToRGBOperation); + + converter.addLink(combineOperation->getOutputSocket(0), + convertYCCToRGBOperation->getInputSocket(0)); + + return convertYCCToRGBOperation->getOutputSocket(0); +} + +NodeOperationOutput *KeyingNode::setupPostBlur(NodeConverter &converter, + NodeOperationOutput *postBlurInput, + int size) const +{ + KeyingBlurOperation *blurXOperation = new KeyingBlurOperation(); + blurXOperation->setSize(size); + blurXOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_X); + converter.addOperation(blurXOperation); + + KeyingBlurOperation *blurYOperation = new KeyingBlurOperation(); + blurYOperation->setSize(size); + blurYOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_Y); + converter.addOperation(blurYOperation); + + converter.addLink(postBlurInput, blurXOperation->getInputSocket(0)); + converter.addLink(blurXOperation->getOutputSocket(), blurYOperation->getInputSocket(0)); + + return blurYOperation->getOutputSocket(); +} + +NodeOperationOutput *KeyingNode::setupDilateErode(NodeConverter &converter, + NodeOperationOutput *dilateErodeInput, + int distance) const +{ + DilateDistanceOperation *dilateErodeOperation; + if (distance > 0) { + dilateErodeOperation = new DilateDistanceOperation(); + dilateErodeOperation->setDistance(distance); + } + else { + dilateErodeOperation = new ErodeDistanceOperation(); + dilateErodeOperation->setDistance(-distance); + } + converter.addOperation(dilateErodeOperation); + + converter.addLink(dilateErodeInput, dilateErodeOperation->getInputSocket(0)); + + return dilateErodeOperation->getOutputSocket(0); +} + +NodeOperationOutput *KeyingNode::setupFeather(NodeConverter &converter, + const CompositorContext &context, + NodeOperationOutput *featherInput, + int falloff, + int distance) const +{ + /* this uses a modified gaussian blur function otherwise its far too slow */ + CompositorQuality quality = context.getQuality(); + + /* initialize node data */ + NodeBlurData data; + memset(&data, 0, sizeof(NodeBlurData)); + data.filtertype = R_FILTER_GAUSS; + if (distance > 0) { + data.sizex = data.sizey = distance; + } + else { + data.sizex = data.sizey = -distance; + } + + GaussianAlphaXBlurOperation *operationx = new GaussianAlphaXBlurOperation(); + operationx->setData(&data); + operationx->setQuality(quality); + operationx->setSize(1.0f); + operationx->setSubtract(distance < 0); + operationx->setFalloff(falloff); + converter.addOperation(operationx); + + GaussianAlphaYBlurOperation *operationy = new GaussianAlphaYBlurOperation(); + operationy->setData(&data); + operationy->setQuality(quality); + operationy->setSize(1.0f); + operationy->setSubtract(distance < 0); + operationy->setFalloff(falloff); + converter.addOperation(operationy); + + converter.addLink(featherInput, operationx->getInputSocket(0)); + converter.addLink(operationx->getOutputSocket(), operationy->getInputSocket(0)); + + return operationy->getOutputSocket(); +} + +NodeOperationOutput *KeyingNode::setupDespill(NodeConverter &converter, + NodeOperationOutput *despillInput, + NodeInput *inputScreen, + float factor, + float colorBalance) const +{ + KeyingDespillOperation *despillOperation = new KeyingDespillOperation(); + despillOperation->setDespillFactor(factor); + despillOperation->setColorBalance(colorBalance); + converter.addOperation(despillOperation); + + converter.addLink(despillInput, despillOperation->getInputSocket(0)); + converter.mapInputSocket(inputScreen, despillOperation->getInputSocket(1)); + + return despillOperation->getOutputSocket(0); +} + +NodeOperationOutput *KeyingNode::setupClip(NodeConverter &converter, + NodeOperationOutput *clipInput, + int kernelRadius, + float kernelTolerance, + float clipBlack, + float clipWhite, + bool edgeMatte) const +{ + KeyingClipOperation *clipOperation = new KeyingClipOperation(); + clipOperation->setKernelRadius(kernelRadius); + clipOperation->setKernelTolerance(kernelTolerance); + clipOperation->setClipBlack(clipBlack); + clipOperation->setClipWhite(clipWhite); + clipOperation->setIsEdgeMatte(edgeMatte); + converter.addOperation(clipOperation); + + converter.addLink(clipInput, clipOperation->getInputSocket(0)); + + return clipOperation->getOutputSocket(0); +} + +void KeyingNode::convertToOperations(NodeConverter &converter, + const CompositorContext &context) const +{ + bNode *editorNode = this->getbNode(); + NodeKeyingData *keying_data = (NodeKeyingData *)editorNode->storage; + + NodeInput *inputImage = this->getInputSocket(0); + NodeInput *inputScreen = this->getInputSocket(1); + NodeInput *inputGarbageMatte = this->getInputSocket(2); + NodeInput *inputCoreMatte = this->getInputSocket(3); + NodeOutput *outputImage = this->getOutputSocket(0); + NodeOutput *outputMatte = this->getOutputSocket(1); + NodeOutput *outputEdges = this->getOutputSocket(2); + NodeOperationOutput *postprocessedMatte = nullptr, *postprocessedImage = nullptr, + *edgesMatte = nullptr; + + /* keying operation */ + KeyingOperation *keyingOperation = new KeyingOperation(); + keyingOperation->setScreenBalance(keying_data->screen_balance); + converter.addOperation(keyingOperation); + + converter.mapInputSocket(inputScreen, keyingOperation->getInputSocket(1)); + + if (keying_data->blur_pre) { + /* Chroma pre-blur operation for input of keying operation. */ + NodeOperationOutput *preBlurredImage = setupPreBlur( + converter, inputImage, keying_data->blur_pre); + converter.addLink(preBlurredImage, keyingOperation->getInputSocket(0)); + } + else { + converter.mapInputSocket(inputImage, keyingOperation->getInputSocket(0)); + } + + postprocessedMatte = keyingOperation->getOutputSocket(); + + /* black / white clipping */ + if (keying_data->clip_black > 0.0f || keying_data->clip_white < 1.0f) { + postprocessedMatte = setupClip(converter, + postprocessedMatte, + keying_data->edge_kernel_radius, + keying_data->edge_kernel_tolerance, + keying_data->clip_black, + keying_data->clip_white, + false); + } + + /* output edge matte */ + edgesMatte = setupClip(converter, + postprocessedMatte, + keying_data->edge_kernel_radius, + keying_data->edge_kernel_tolerance, + keying_data->clip_black, + keying_data->clip_white, + true); + + /* apply garbage matte */ + if (inputGarbageMatte->isLinked()) { + SetValueOperation *valueOperation = new SetValueOperation(); + valueOperation->setValue(1.0f); + converter.addOperation(valueOperation); + + MathSubtractOperation *subtractOperation = new MathSubtractOperation(); + converter.addOperation(subtractOperation); + + MathMinimumOperation *minOperation = new MathMinimumOperation(); + converter.addOperation(minOperation); + + converter.addLink(valueOperation->getOutputSocket(), subtractOperation->getInputSocket(0)); + converter.mapInputSocket(inputGarbageMatte, subtractOperation->getInputSocket(1)); + + converter.addLink(subtractOperation->getOutputSocket(), minOperation->getInputSocket(0)); + converter.addLink(postprocessedMatte, minOperation->getInputSocket(1)); + + postprocessedMatte = minOperation->getOutputSocket(); + } + + /* apply core matte */ + if (inputCoreMatte->isLinked()) { + MathMaximumOperation *maxOperation = new MathMaximumOperation(); + converter.addOperation(maxOperation); + + converter.mapInputSocket(inputCoreMatte, maxOperation->getInputSocket(0)); + converter.addLink(postprocessedMatte, maxOperation->getInputSocket(1)); + + postprocessedMatte = maxOperation->getOutputSocket(); + } + + /* apply blur on matte if needed */ + if (keying_data->blur_post) { + postprocessedMatte = setupPostBlur(converter, postprocessedMatte, keying_data->blur_post); + } + + /* matte dilate/erode */ + if (keying_data->dilate_distance != 0) { + postprocessedMatte = setupDilateErode( + converter, postprocessedMatte, keying_data->dilate_distance); + } + + /* matte feather */ + if (keying_data->feather_distance != 0) { + postprocessedMatte = setupFeather(converter, + context, + postprocessedMatte, + keying_data->feather_falloff, + keying_data->feather_distance); + } + + /* set alpha channel to output image */ + SetAlphaMultiplyOperation *alphaOperation = new SetAlphaMultiplyOperation(); + converter.addOperation(alphaOperation); + + converter.mapInputSocket(inputImage, alphaOperation->getInputSocket(0)); + converter.addLink(postprocessedMatte, alphaOperation->getInputSocket(1)); + + postprocessedImage = alphaOperation->getOutputSocket(); + + /* despill output image */ + if (keying_data->despill_factor > 0.0f) { + postprocessedImage = setupDespill(converter, + postprocessedImage, + inputScreen, + keying_data->despill_factor, + keying_data->despill_balance); + } + + /* connect result to output sockets */ + converter.mapOutputSocket(outputImage, postprocessedImage); + converter.mapOutputSocket(outputMatte, postprocessedMatte); + + if (edgesMatte) { + converter.mapOutputSocket(outputEdges, edgesMatte); + } +} |