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