tornavis/source/blender/compositor/nodes/COM_KeyingNode.cc

355 lines
14 KiB
C++

/*
* 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"
namespace blender::compositor {
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 */
eCompositorQuality 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);
}
}
} // namespace blender::compositor