1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * Copyright 2012, Blender Foundation.
17 */
18
19 #include "COM_KeyingOperation.h"
20
21 #include "MEM_guardedalloc.h"
22
23 #include "BLI_listbase.h"
24 #include "BLI_math.h"
25
get_pixel_saturation(const float pixelColor[4],float screen_balance,int primary_channel)26 static float get_pixel_saturation(const float pixelColor[4],
27 float screen_balance,
28 int primary_channel)
29 {
30 const int other_1 = (primary_channel + 1) % 3;
31 const int other_2 = (primary_channel + 2) % 3;
32
33 const int min_channel = min(other_1, other_2);
34 const int max_channel = max(other_1, other_2);
35
36 const float val = screen_balance * pixelColor[min_channel] +
37 (1.0f - screen_balance) * pixelColor[max_channel];
38
39 return (pixelColor[primary_channel] - val) * fabsf(1.0f - val);
40 }
41
KeyingOperation()42 KeyingOperation::KeyingOperation() : NodeOperation()
43 {
44 this->addInputSocket(COM_DT_COLOR);
45 this->addInputSocket(COM_DT_COLOR);
46 this->addOutputSocket(COM_DT_VALUE);
47
48 this->m_screenBalance = 0.5f;
49
50 this->m_pixelReader = NULL;
51 this->m_screenReader = NULL;
52 }
53
initExecution()54 void KeyingOperation::initExecution()
55 {
56 this->m_pixelReader = this->getInputSocketReader(0);
57 this->m_screenReader = this->getInputSocketReader(1);
58 }
59
deinitExecution()60 void KeyingOperation::deinitExecution()
61 {
62 this->m_pixelReader = NULL;
63 this->m_screenReader = NULL;
64 }
65
executePixelSampled(float output[4],float x,float y,PixelSampler sampler)66 void KeyingOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
67 {
68 float pixel_color[4];
69 float screen_color[4];
70
71 this->m_pixelReader->readSampled(pixel_color, x, y, sampler);
72 this->m_screenReader->readSampled(screen_color, x, y, sampler);
73
74 const int primary_channel = max_axis_v3(screen_color);
75 const float min_pixel_color = min_fff(pixel_color[0], pixel_color[1], pixel_color[2]);
76
77 if (min_pixel_color > 1.0f) {
78 /* overexposure doesn't happen on screen itself and usually happens
79 * on light sources in the shot, this need to be checked separately
80 * because saturation and falloff calculation is based on the fact
81 * that pixels are not overexposed
82 */
83 output[0] = 1.0f;
84 }
85 else {
86 float saturation = get_pixel_saturation(pixel_color, this->m_screenBalance, primary_channel);
87 float screen_saturation = get_pixel_saturation(
88 screen_color, this->m_screenBalance, primary_channel);
89
90 if (saturation < 0) {
91 /* means main channel of pixel is different from screen,
92 * assume this is completely a foreground
93 */
94 output[0] = 1.0f;
95 }
96 else if (saturation >= screen_saturation) {
97 /* matched main channels and higher saturation on pixel
98 * is treated as completely background
99 */
100 output[0] = 0.0f;
101 }
102 else {
103 /* nice alpha falloff on edges */
104 float distance = 1.0f - saturation / screen_saturation;
105
106 output[0] = distance;
107 }
108 }
109 }
110