Core matte input for keying node
[blender.git] / source / blender / compositor / operations / COM_KeyingOperation.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_KeyingOperation.h"
25
26 #include "MEM_guardedalloc.h"
27
28 #include "BLI_listbase.h"
29 #include "BLI_math.h"
30
31 static int get_pixel_primary_channel(float pixelColor[4])
32 {
33         float max_value = MAX3(pixelColor[0], pixelColor[1], pixelColor[2]);
34
35         if (max_value == pixelColor[0])
36                 return 0;
37         else if (max_value == pixelColor[1])
38                 return 1;
39
40         return 2;
41 }
42
43 static float get_pixel_saturation(float pixelColor[4], float screen_balance, int primary_channel)
44 {
45         int other_1 = (primary_channel + 1) % 3;
46         int other_2 = (primary_channel + 2) % 3;
47
48         float min = MIN2(pixelColor[other_1], pixelColor[other_2]);
49         float max = MAX2(pixelColor[other_1], pixelColor[other_2]);
50         float val = screen_balance * min + (1.0f - screen_balance) * max;
51
52         return (pixelColor[primary_channel] - val) * fabsf(1.0f - val);
53 }
54
55 KeyingOperation::KeyingOperation(): NodeOperation()
56 {
57         this->addInputSocket(COM_DT_COLOR);
58         this->addInputSocket(COM_DT_COLOR);
59         this->addInputSocket(COM_DT_VALUE);
60         this->addInputSocket(COM_DT_VALUE);
61         this->addOutputSocket(COM_DT_VALUE);
62
63         this->screenBalance = 0.5f;
64
65         this->pixelReader = NULL;
66         this->screenReader = NULL;
67         this->garbageReader = NULL;
68         this->coreReader = NULL;
69 }
70
71 void KeyingOperation::initExecution()
72 {
73         this->pixelReader = this->getInputSocketReader(0);
74         this->screenReader = this->getInputSocketReader(1);
75         this->garbageReader = this->getInputSocketReader(2);
76         this->coreReader = this->getInputSocketReader(3);
77 }
78
79 void KeyingOperation::deinitExecution()
80 {
81         this->pixelReader = NULL;
82         this->screenReader = NULL;
83         this->garbageReader = NULL;
84         this->coreReader = NULL;
85 }
86
87 void KeyingOperation::executePixel(float *color, float x, float y, PixelSampler sampler, MemoryBuffer *inputBuffers[])
88 {
89         float pixelColor[4];
90         float screenColor[4];
91         float garbageValue[4];
92         float coreValue[4];
93
94         this->pixelReader->read(pixelColor, x, y, sampler, inputBuffers);
95         this->screenReader->read(screenColor, x, y, sampler, inputBuffers);
96         this->garbageReader->read(garbageValue, x, y, sampler, inputBuffers);
97         this->coreReader->read(coreValue, x, y, sampler, inputBuffers);
98
99         int primary_channel = get_pixel_primary_channel(screenColor);
100
101         float saturation = get_pixel_saturation(pixelColor, this->screenBalance, primary_channel);
102         float screen_saturation = get_pixel_saturation(screenColor, this->screenBalance, primary_channel);
103
104         if (saturation < 0) {
105                 color[0] = 1.0f;
106         }
107         else if (saturation >= screen_saturation) {
108                 color[0] = 0.0f;
109         }
110         else {
111                 float distance = 1.0f - saturation / screen_saturation;
112
113                 color[0] = distance;
114         }
115
116         color[0] *= (1.0f - garbageValue[0]);
117
118         color[0] = MAX2(color[0], coreValue[0]);
119 }
120
121 bool KeyingOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
122 {
123         rcti newInput;
124
125         newInput.xmin = 0;
126         newInput.ymin = 0;
127         newInput.xmax = this->getWidth();
128         newInput.ymax = this->getHeight();
129
130         return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
131 }