Cleanup: comment line length (editors)
[blender.git] / source / blender / compositor / nodes / COM_KeyingNode.cpp
1 /*
2  * Copyright 2012, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor:
19  *      Jeroen Bakker
20  *      Monique Dewanchand
21  *      Sergey Sharybin
22  */
23
24 #include "COM_KeyingNode.h"
25
26 #include "COM_ExecutionSystem.h"
27
28 #include "COM_KeyingOperation.h"
29 #include "COM_KeyingBlurOperation.h"
30 #include "COM_KeyingDespillOperation.h"
31 #include "COM_KeyingClipOperation.h"
32
33 #include "COM_MathBaseOperation.h"
34
35 #include "COM_ConvertOperation.h"
36 #include "COM_SetValueOperation.h"
37
38 #include "COM_DilateErodeOperation.h"
39
40 #include "COM_SetAlphaOperation.h"
41
42 #include "COM_GaussianAlphaXBlurOperation.h"
43 #include "COM_GaussianAlphaYBlurOperation.h"
44
45 KeyingNode::KeyingNode(bNode *editorNode) : Node(editorNode)
46 {
47         /* pass */
48 }
49
50 NodeOperationOutput *KeyingNode::setupPreBlur(NodeConverter &converter, NodeInput *inputImage, int size) const
51 {
52         ConvertRGBToYCCOperation *convertRGBToYCCOperation = new ConvertRGBToYCCOperation();
53         convertRGBToYCCOperation->setMode(BLI_YCC_ITU_BT709);
54         converter.addOperation(convertRGBToYCCOperation);
55
56         converter.mapInputSocket(inputImage, convertRGBToYCCOperation->getInputSocket(0));
57
58         CombineChannelsOperation *combineOperation = new CombineChannelsOperation();
59         converter.addOperation(combineOperation);
60
61         for (int channel = 0; channel < 4; channel++) {
62                 SeparateChannelOperation *separateOperation = new SeparateChannelOperation();
63                 separateOperation->setChannel(channel);
64                 converter.addOperation(separateOperation);
65
66                 converter.addLink(convertRGBToYCCOperation->getOutputSocket(0), separateOperation->getInputSocket(0));
67
68                 if (channel == 0 || channel == 3) {
69                         converter.addLink(separateOperation->getOutputSocket(0), combineOperation->getInputSocket(channel));
70                 }
71                 else {
72                         KeyingBlurOperation *blurXOperation = new KeyingBlurOperation();
73                         blurXOperation->setSize(size);
74                         blurXOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_X);
75                         converter.addOperation(blurXOperation);
76
77                         KeyingBlurOperation *blurYOperation = new KeyingBlurOperation();
78                         blurYOperation->setSize(size);
79                         blurYOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_Y);
80                         converter.addOperation(blurYOperation);
81
82                         converter.addLink(separateOperation->getOutputSocket(), blurXOperation->getInputSocket(0));
83                         converter.addLink(blurXOperation->getOutputSocket(), blurYOperation->getInputSocket(0));
84                         converter.addLink(blurYOperation->getOutputSocket(0), combineOperation->getInputSocket(channel));
85                 }
86         }
87
88         ConvertYCCToRGBOperation *convertYCCToRGBOperation = new ConvertYCCToRGBOperation();
89         convertYCCToRGBOperation->setMode(BLI_YCC_ITU_BT709);
90         converter.addOperation(convertYCCToRGBOperation);
91
92         converter.addLink(combineOperation->getOutputSocket(0), convertYCCToRGBOperation->getInputSocket(0));
93
94         return convertYCCToRGBOperation->getOutputSocket(0);
95 }
96
97 NodeOperationOutput *KeyingNode::setupPostBlur(NodeConverter &converter, NodeOperationOutput *postBlurInput, int size) const
98 {
99         KeyingBlurOperation *blurXOperation = new KeyingBlurOperation();
100         blurXOperation->setSize(size);
101         blurXOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_X);
102         converter.addOperation(blurXOperation);
103
104         KeyingBlurOperation *blurYOperation = new KeyingBlurOperation();
105         blurYOperation->setSize(size);
106         blurYOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_Y);
107         converter.addOperation(blurYOperation);
108
109         converter.addLink(postBlurInput, blurXOperation->getInputSocket(0));
110         converter.addLink(blurXOperation->getOutputSocket(), blurYOperation->getInputSocket(0));
111
112         return blurYOperation->getOutputSocket();
113 }
114
115 NodeOperationOutput *KeyingNode::setupDilateErode(NodeConverter &converter, NodeOperationOutput *dilateErodeInput, int distance) const
116 {
117         DilateDistanceOperation *dilateErodeOperation;
118         if (distance > 0) {
119                 dilateErodeOperation = new DilateDistanceOperation();
120                 dilateErodeOperation->setDistance(distance);
121         }
122         else {
123                 dilateErodeOperation = new ErodeDistanceOperation();
124                 dilateErodeOperation->setDistance(-distance);
125         }
126         converter.addOperation(dilateErodeOperation);
127
128         converter.addLink(dilateErodeInput, dilateErodeOperation->getInputSocket(0));
129
130         return dilateErodeOperation->getOutputSocket(0);
131 }
132
133 NodeOperationOutput *KeyingNode::setupFeather(NodeConverter &converter, const CompositorContext &context,
134                                    NodeOperationOutput *featherInput, int falloff, int distance) const
135 {
136         /* this uses a modified gaussian blur function otherwise its far too slow */
137         CompositorQuality quality = context.getQuality();
138
139         /* initialize node data */
140         NodeBlurData data;
141         memset(&data, 0, sizeof(NodeBlurData));
142         data.filtertype = R_FILTER_GAUSS;
143         if (distance > 0) {
144                 data.sizex = data.sizey = distance;
145         }
146         else {
147                 data.sizex = data.sizey = -distance;
148         }
149
150         GaussianAlphaXBlurOperation *operationx = new GaussianAlphaXBlurOperation();
151         operationx->setData(&data);
152         operationx->setQuality(quality);
153         operationx->setSize(1.0f);
154         operationx->setSubtract(distance < 0);
155         operationx->setFalloff(falloff);
156         converter.addOperation(operationx);
157
158         GaussianAlphaYBlurOperation *operationy = new GaussianAlphaYBlurOperation();
159         operationy->setData(&data);
160         operationy->setQuality(quality);
161         operationy->setSize(1.0f);
162         operationy->setSubtract(distance < 0);
163         operationy->setFalloff(falloff);
164         converter.addOperation(operationy);
165
166         converter.addLink(featherInput, operationx->getInputSocket(0));
167         converter.addLink(operationx->getOutputSocket(), operationy->getInputSocket(0));
168
169         return operationy->getOutputSocket();
170 }
171
172 NodeOperationOutput *KeyingNode::setupDespill(NodeConverter &converter, NodeOperationOutput *despillInput, NodeInput *inputScreen,
173                                    float factor, float colorBalance) const
174 {
175         KeyingDespillOperation *despillOperation = new KeyingDespillOperation();
176         despillOperation->setDespillFactor(factor);
177         despillOperation->setColorBalance(colorBalance);
178         converter.addOperation(despillOperation);
179
180         converter.addLink(despillInput, despillOperation->getInputSocket(0));
181         converter.mapInputSocket(inputScreen, despillOperation->getInputSocket(1));
182
183         return despillOperation->getOutputSocket(0);
184 }
185
186 NodeOperationOutput *KeyingNode::setupClip(NodeConverter &converter, NodeOperationOutput *clipInput, int kernelRadius, float kernelTolerance,
187                                 float clipBlack, float clipWhite, bool edgeMatte) const
188 {
189         KeyingClipOperation *clipOperation = new KeyingClipOperation();
190         clipOperation->setKernelRadius(kernelRadius);
191         clipOperation->setKernelTolerance(kernelTolerance);
192         clipOperation->setClipBlack(clipBlack);
193         clipOperation->setClipWhite(clipWhite);
194         clipOperation->setIsEdgeMatte(edgeMatte);
195         converter.addOperation(clipOperation);
196
197         converter.addLink(clipInput, clipOperation->getInputSocket(0));
198
199         return clipOperation->getOutputSocket(0);
200 }
201
202 void KeyingNode::convertToOperations(NodeConverter &converter, const CompositorContext &context) const
203 {
204         bNode *editorNode = this->getbNode();
205         NodeKeyingData *keying_data = (NodeKeyingData *) editorNode->storage;
206
207         NodeInput *inputImage = this->getInputSocket(0);
208         NodeInput *inputScreen = this->getInputSocket(1);
209         NodeInput *inputGarbageMatte = this->getInputSocket(2);
210         NodeInput *inputCoreMatte = this->getInputSocket(3);
211         NodeOutput *outputImage = this->getOutputSocket(0);
212         NodeOutput *outputMatte = this->getOutputSocket(1);
213         NodeOutput *outputEdges = this->getOutputSocket(2);
214         NodeOperationOutput *postprocessedMatte = NULL, *postprocessedImage = NULL, *edgesMatte = NULL;
215
216         /* keying operation */
217         KeyingOperation *keyingOperation = new KeyingOperation();
218         keyingOperation->setScreenBalance(keying_data->screen_balance);
219         converter.addOperation(keyingOperation);
220
221         converter.mapInputSocket(inputScreen, keyingOperation->getInputSocket(1));
222
223         if (keying_data->blur_pre) {
224                 /* chroma preblur operation for input of keying operation  */
225                 NodeOperationOutput *preBluredImage = setupPreBlur(converter, inputImage, keying_data->blur_pre);
226                 converter.addLink(preBluredImage, keyingOperation->getInputSocket(0));
227         }
228         else {
229                 converter.mapInputSocket(inputImage, keyingOperation->getInputSocket(0));
230         }
231
232         postprocessedMatte = keyingOperation->getOutputSocket();
233
234         /* black / white clipping */
235         if (keying_data->clip_black > 0.0f || keying_data->clip_white < 1.0f) {
236                 postprocessedMatte = setupClip(converter, postprocessedMatte,
237                                                keying_data->edge_kernel_radius, keying_data->edge_kernel_tolerance,
238                                                keying_data->clip_black, keying_data->clip_white, false);
239         }
240
241         /* output edge matte */
242         edgesMatte = setupClip(converter, postprocessedMatte,
243                                keying_data->edge_kernel_radius, keying_data->edge_kernel_tolerance,
244                                keying_data->clip_black, keying_data->clip_white, true);
245
246         /* apply garbage matte */
247         if (inputGarbageMatte->isLinked()) {
248                 SetValueOperation *valueOperation = new SetValueOperation();
249                 valueOperation->setValue(1.0f);
250                 converter.addOperation(valueOperation);
251
252                 MathSubtractOperation *subtractOperation = new MathSubtractOperation();
253                 converter.addOperation(subtractOperation);
254
255                 MathMinimumOperation *minOperation = new MathMinimumOperation();
256                 converter.addOperation(minOperation);
257
258                 converter.addLink(valueOperation->getOutputSocket(), subtractOperation->getInputSocket(0));
259                 converter.mapInputSocket(inputGarbageMatte, subtractOperation->getInputSocket(1));
260
261                 converter.addLink(subtractOperation->getOutputSocket(), minOperation->getInputSocket(0));
262                 converter.addLink(postprocessedMatte, minOperation->getInputSocket(1));
263
264                 postprocessedMatte = minOperation->getOutputSocket();
265         }
266
267         /* apply core matte */
268         if (inputCoreMatte->isLinked()) {
269                 MathMaximumOperation *maxOperation = new MathMaximumOperation();
270                 converter.addOperation(maxOperation);
271
272                 converter.mapInputSocket(inputCoreMatte, maxOperation->getInputSocket(0));
273                 converter.addLink(postprocessedMatte, maxOperation->getInputSocket(1));
274
275                 postprocessedMatte = maxOperation->getOutputSocket();
276         }
277
278         /* apply blur on matte if needed */
279         if (keying_data->blur_post)
280                 postprocessedMatte = setupPostBlur(converter, postprocessedMatte, keying_data->blur_post);
281
282         /* matte dilate/erode */
283         if (keying_data->dilate_distance != 0) {
284                 postprocessedMatte = setupDilateErode(converter, postprocessedMatte, keying_data->dilate_distance);
285         }
286
287         /* matte feather */
288         if (keying_data->feather_distance != 0) {
289                 postprocessedMatte = setupFeather(converter, context, postprocessedMatte, keying_data->feather_falloff,
290                                                   keying_data->feather_distance);
291         }
292
293         /* set alpha channel to output image */
294         SetAlphaOperation *alphaOperation = new SetAlphaOperation();
295         converter.addOperation(alphaOperation);
296
297         converter.mapInputSocket(inputImage, alphaOperation->getInputSocket(0));
298         converter.addLink(postprocessedMatte, alphaOperation->getInputSocket(1));
299
300         postprocessedImage = alphaOperation->getOutputSocket();
301
302         /* despill output image */
303         if (keying_data->despill_factor > 0.0f) {
304                 postprocessedImage = setupDespill(converter, postprocessedImage,
305                                                   inputScreen,
306                                                   keying_data->despill_factor,
307                                                   keying_data->despill_balance);
308         }
309
310         /* connect result to output sockets */
311         converter.mapOutputSocket(outputImage, postprocessedImage);
312         converter.mapOutputSocket(outputMatte, postprocessedMatte);
313
314         if (edgesMatte)
315                 converter.mapOutputSocket(outputEdges, edgesMatte);
316 }