Use a shorter/simpler license convention, stops the header taking so much space. Follow the SPDX license specification: https://spdx.org/licenses - C/C++/objc/objc++ - Python - Shell Scripts - CMake, GNUmakefile While most of the source tree has been included - `./extern/` was left out. - `./intern/cycles` & `./intern/atomic` are also excluded because they use different header conventions. doc/license/SPDX-license-identifiers.txt has been added to list SPDX all used identifiers. See P2788 for the script that automated these edits. Reviewed By: brecht, mont29, sergey Ref D14069
141 lines
4.5 KiB
C++
141 lines
4.5 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2012 Blender Foundation. */
|
|
|
|
#include "COM_KeyingOperation.h"
|
|
|
|
namespace blender::compositor {
|
|
|
|
static float get_pixel_saturation(const float pixel_color[4],
|
|
float screen_balance,
|
|
int primary_channel)
|
|
{
|
|
const int other_1 = (primary_channel + 1) % 3;
|
|
const int other_2 = (primary_channel + 2) % 3;
|
|
|
|
const int min_channel = MIN2(other_1, other_2);
|
|
const int max_channel = MAX2(other_1, other_2);
|
|
|
|
const float val = screen_balance * pixel_color[min_channel] +
|
|
(1.0f - screen_balance) * pixel_color[max_channel];
|
|
|
|
return (pixel_color[primary_channel] - val) * fabsf(1.0f - val);
|
|
}
|
|
|
|
KeyingOperation::KeyingOperation()
|
|
{
|
|
this->add_input_socket(DataType::Color);
|
|
this->add_input_socket(DataType::Color);
|
|
this->add_output_socket(DataType::Value);
|
|
|
|
screen_balance_ = 0.5f;
|
|
|
|
pixel_reader_ = nullptr;
|
|
screen_reader_ = nullptr;
|
|
}
|
|
|
|
void KeyingOperation::init_execution()
|
|
{
|
|
pixel_reader_ = this->get_input_socket_reader(0);
|
|
screen_reader_ = this->get_input_socket_reader(1);
|
|
}
|
|
|
|
void KeyingOperation::deinit_execution()
|
|
{
|
|
pixel_reader_ = nullptr;
|
|
screen_reader_ = nullptr;
|
|
}
|
|
|
|
void KeyingOperation::execute_pixel_sampled(float output[4],
|
|
float x,
|
|
float y,
|
|
PixelSampler sampler)
|
|
{
|
|
float pixel_color[4];
|
|
float screen_color[4];
|
|
|
|
pixel_reader_->read_sampled(pixel_color, x, y, sampler);
|
|
screen_reader_->read_sampled(screen_color, x, y, sampler);
|
|
|
|
const int primary_channel = max_axis_v3(screen_color);
|
|
const float min_pixel_color = min_fff(pixel_color[0], pixel_color[1], pixel_color[2]);
|
|
|
|
if (min_pixel_color > 1.0f) {
|
|
/* overexposure doesn't happen on screen itself and usually happens
|
|
* on light sources in the shot, this need to be checked separately
|
|
* because saturation and falloff calculation is based on the fact
|
|
* that pixels are not overexposed
|
|
*/
|
|
output[0] = 1.0f;
|
|
}
|
|
else {
|
|
float saturation = get_pixel_saturation(pixel_color, screen_balance_, primary_channel);
|
|
float screen_saturation = get_pixel_saturation(screen_color, screen_balance_, primary_channel);
|
|
|
|
if (saturation < 0) {
|
|
/* means main channel of pixel is different from screen,
|
|
* assume this is completely a foreground
|
|
*/
|
|
output[0] = 1.0f;
|
|
}
|
|
else if (saturation >= screen_saturation) {
|
|
/* matched main channels and higher saturation on pixel
|
|
* is treated as completely background
|
|
*/
|
|
output[0] = 0.0f;
|
|
}
|
|
else {
|
|
/* nice alpha falloff on edges */
|
|
float distance = 1.0f - saturation / screen_saturation;
|
|
|
|
output[0] = distance;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KeyingOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
|
const rcti &area,
|
|
Span<MemoryBuffer *> inputs)
|
|
{
|
|
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
|
const float *pixel_color = it.in(0);
|
|
const float *screen_color = it.in(1);
|
|
|
|
const int primary_channel = max_axis_v3(screen_color);
|
|
const float min_pixel_color = min_fff(pixel_color[0], pixel_color[1], pixel_color[2]);
|
|
|
|
if (min_pixel_color > 1.0f) {
|
|
/* Overexposure doesn't happen on screen itself and usually happens
|
|
* on light sources in the shot, this need to be checked separately
|
|
* because saturation and falloff calculation is based on the fact
|
|
* that pixels are not overexposed.
|
|
*/
|
|
it.out[0] = 1.0f;
|
|
}
|
|
else {
|
|
const float saturation = get_pixel_saturation(pixel_color, screen_balance_, primary_channel);
|
|
const float screen_saturation = get_pixel_saturation(
|
|
screen_color, screen_balance_, primary_channel);
|
|
|
|
if (saturation < 0) {
|
|
/* Means main channel of pixel is different from screen,
|
|
* assume this is completely a foreground.
|
|
*/
|
|
it.out[0] = 1.0f;
|
|
}
|
|
else if (saturation >= screen_saturation) {
|
|
/* Matched main channels and higher saturation on pixel
|
|
* is treated as completely background.
|
|
*/
|
|
it.out[0] = 0.0f;
|
|
}
|
|
else {
|
|
/* Nice alpha falloff on edges. */
|
|
const float distance = 1.0f - saturation / screen_saturation;
|
|
it.out[0] = distance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace blender::compositor
|