This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
Manuel Castilla 1c42d4930a Cleanup: convert camelCase naming to snake_case in Compositor
To convert old code to the current convention and
use a single code style.
2021-10-13 23:41:14 +02:00

497 lines
15 KiB
C++

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2011, Blender Foundation.
*/
#include "COM_GaussianBokehBlurOperation.h"
#include "RE_pipeline.h"
namespace blender::compositor {
GaussianBokehBlurOperation::GaussianBokehBlurOperation() : BlurBaseOperation(DataType::Color)
{
gausstab_ = nullptr;
}
void *GaussianBokehBlurOperation::initialize_tile_data(rcti * /*rect*/)
{
lock_mutex();
if (!sizeavailable_) {
update_gauss();
}
void *buffer = get_input_operation(0)->initialize_tile_data(nullptr);
unlock_mutex();
return buffer;
}
void GaussianBokehBlurOperation::init_data()
{
BlurBaseOperation::init_data();
const float width = this->get_width();
const float height = this->get_height();
if (!sizeavailable_) {
update_size();
}
radxf_ = size_ * (float)data_.sizex;
CLAMP(radxf_, 0.0f, width / 2.0f);
/* Vertical. */
radyf_ = size_ * (float)data_.sizey;
CLAMP(radyf_, 0.0f, height / 2.0f);
radx_ = ceil(radxf_);
rady_ = ceil(radyf_);
}
void GaussianBokehBlurOperation::init_execution()
{
BlurBaseOperation::init_execution();
init_mutex();
if (sizeavailable_) {
update_gauss();
}
}
void GaussianBokehBlurOperation::update_gauss()
{
if (gausstab_ == nullptr) {
int ddwidth = 2 * radx_ + 1;
int ddheight = 2 * rady_ + 1;
int n = ddwidth * ddheight;
/* create a full filter image */
float *ddgauss = (float *)MEM_mallocN(sizeof(float) * n, __func__);
float *dgauss = ddgauss;
float sum = 0.0f;
float facx = (radxf_ > 0.0f ? 1.0f / radxf_ : 0.0f);
float facy = (radyf_ > 0.0f ? 1.0f / radyf_ : 0.0f);
for (int j = -rady_; j <= rady_; j++) {
for (int i = -radx_; i <= radx_; i++, dgauss++) {
float fj = (float)j * facy;
float fi = (float)i * facx;
float dist = sqrt(fj * fj + fi * fi);
*dgauss = RE_filter_value(data_.filtertype, dist);
sum += *dgauss;
}
}
if (sum > 0.0f) {
/* normalize */
float norm = 1.0f / sum;
for (int j = n - 1; j >= 0; j--) {
ddgauss[j] *= norm;
}
}
else {
int center = rady_ * ddwidth + radx_;
ddgauss[center] = 1.0f;
}
gausstab_ = ddgauss;
}
}
void GaussianBokehBlurOperation::execute_pixel(float output[4], int x, int y, void *data)
{
float temp_color[4];
temp_color[0] = 0;
temp_color[1] = 0;
temp_color[2] = 0;
temp_color[3] = 0;
float multiplier_accum = 0;
MemoryBuffer *input_buffer = (MemoryBuffer *)data;
float *buffer = input_buffer->get_buffer();
int bufferwidth = input_buffer->get_width();
const rcti &input_rect = input_buffer->get_rect();
int bufferstartx = input_rect.xmin;
int bufferstarty = input_rect.ymin;
int ymin = max_ii(y - rady_, input_rect.ymin);
int ymax = min_ii(y + rady_ + 1, input_rect.ymax);
int xmin = max_ii(x - radx_, input_rect.xmin);
int xmax = min_ii(x + radx_ + 1, input_rect.xmax);
int index;
int step = QualityStepHelper::get_step();
int offsetadd = QualityStepHelper::get_offset_add();
const int add_const = (xmin - x + radx_);
const int mul_const = (radx_ * 2 + 1);
for (int ny = ymin; ny < ymax; ny += step) {
index = ((ny - y) + rady_) * mul_const + add_const;
int bufferindex = ((xmin - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth);
for (int nx = xmin; nx < xmax; nx += step) {
const float multiplier = gausstab_[index];
madd_v4_v4fl(temp_color, &buffer[bufferindex], multiplier);
multiplier_accum += multiplier;
index += step;
bufferindex += offsetadd;
}
}
mul_v4_v4fl(output, temp_color, 1.0f / multiplier_accum);
}
void GaussianBokehBlurOperation::deinit_execution()
{
BlurBaseOperation::deinit_execution();
if (gausstab_) {
MEM_freeN(gausstab_);
gausstab_ = nullptr;
}
deinit_mutex();
}
bool GaussianBokehBlurOperation::determine_depending_area_of_interest(
rcti *input, ReadBufferOperation *read_operation, rcti *output)
{
rcti new_input;
rcti size_input;
size_input.xmin = 0;
size_input.ymin = 0;
size_input.xmax = 5;
size_input.ymax = 5;
NodeOperation *operation = this->get_input_operation(1);
if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) {
return true;
}
if (sizeavailable_ && gausstab_ != nullptr) {
new_input.xmin = 0;
new_input.ymin = 0;
new_input.xmax = this->get_width();
new_input.ymax = this->get_height();
}
else {
int addx = radx_;
int addy = rady_;
new_input.xmax = input->xmax + addx;
new_input.xmin = input->xmin - addx;
new_input.ymax = input->ymax + addy;
new_input.ymin = input->ymin - addy;
}
return BlurBaseOperation::determine_depending_area_of_interest(
&new_input, read_operation, output);
}
void GaussianBokehBlurOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
if (input_idx != IMAGE_INPUT_INDEX) {
BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
return;
}
r_input_area.xmax = output_area.xmax + radx_;
r_input_area.xmin = output_area.xmin - radx_;
r_input_area.ymax = output_area.ymax + rady_;
r_input_area.ymin = output_area.ymin - rady_;
}
void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX];
BuffersIterator<float> it = output->iterate_with({}, area);
const rcti &input_rect = input->get_rect();
for (; !it.is_end(); ++it) {
const int x = it.x;
const int y = it.y;
const int ymin = max_ii(y - rady_, input_rect.ymin);
const int ymax = min_ii(y + rady_ + 1, input_rect.ymax);
const int xmin = max_ii(x - radx_, input_rect.xmin);
const int xmax = min_ii(x + radx_ + 1, input_rect.xmax);
float temp_color[4] = {0};
float multiplier_accum = 0;
const int step = QualityStepHelper::get_step();
const int elem_step = step * input->elem_stride;
const int add_const = (xmin - x + radx_);
const int mul_const = (radx_ * 2 + 1);
for (int ny = ymin; ny < ymax; ny += step) {
const float *color = input->get_elem(xmin, ny);
int gauss_index = ((ny - y) + rady_) * mul_const + add_const;
const int gauss_end = gauss_index + (xmax - xmin);
for (; gauss_index < gauss_end; gauss_index += step, color += elem_step) {
const float multiplier = gausstab_[gauss_index];
madd_v4_v4fl(temp_color, color, multiplier);
multiplier_accum += multiplier;
}
}
mul_v4_v4fl(it.out, temp_color, 1.0f / multiplier_accum);
}
}
// reference image
GaussianBlurReferenceOperation::GaussianBlurReferenceOperation()
: BlurBaseOperation(DataType::Color)
{
maintabs_ = nullptr;
use_variable_size_ = true;
}
void GaussianBlurReferenceOperation::init_data()
{
/* Setup variables for gausstab and area of interest. */
data_.image_in_width = this->get_width();
data_.image_in_height = this->get_height();
if (data_.relative) {
switch (data_.aspect) {
case CMP_NODE_BLUR_ASPECT_NONE:
data_.sizex = (int)(data_.percentx * 0.01f * data_.image_in_width);
data_.sizey = (int)(data_.percenty * 0.01f * data_.image_in_height);
break;
case CMP_NODE_BLUR_ASPECT_Y:
data_.sizex = (int)(data_.percentx * 0.01f * data_.image_in_width);
data_.sizey = (int)(data_.percenty * 0.01f * data_.image_in_width);
break;
case CMP_NODE_BLUR_ASPECT_X:
data_.sizex = (int)(data_.percentx * 0.01f * data_.image_in_height);
data_.sizey = (int)(data_.percenty * 0.01f * data_.image_in_height);
break;
}
}
/* Horizontal. */
filtersizex_ = (float)data_.sizex;
int imgx = get_width() / 2;
if (filtersizex_ > imgx) {
filtersizex_ = imgx;
}
else if (filtersizex_ < 1) {
filtersizex_ = 1;
}
radx_ = (float)filtersizex_;
/* Vertical. */
filtersizey_ = (float)data_.sizey;
int imgy = get_height() / 2;
if (filtersizey_ > imgy) {
filtersizey_ = imgy;
}
else if (filtersizey_ < 1) {
filtersizey_ = 1;
}
rady_ = (float)filtersizey_;
}
void *GaussianBlurReferenceOperation::initialize_tile_data(rcti * /*rect*/)
{
void *buffer = get_input_operation(0)->initialize_tile_data(nullptr);
return buffer;
}
void GaussianBlurReferenceOperation::init_execution()
{
BlurBaseOperation::init_execution();
update_gauss();
}
void GaussianBlurReferenceOperation::update_gauss()
{
int i;
int x = MAX2(filtersizex_, filtersizey_);
maintabs_ = (float **)MEM_mallocN(x * sizeof(float *), "gauss array");
for (i = 0; i < x; i++) {
maintabs_[i] = make_gausstab(i + 1, i + 1);
}
}
void GaussianBlurReferenceOperation::execute_pixel(float output[4], int x, int y, void *data)
{
MemoryBuffer *memorybuffer = (MemoryBuffer *)data;
float *buffer = memorybuffer->get_buffer();
float *gausstabx, *gausstabcenty;
float *gausstaby, *gausstabcentx;
int i, j;
float *src;
float sum, val;
float rval, gval, bval, aval;
int imgx = get_width();
int imgy = get_height();
float temp_size[4];
input_size_->read(temp_size, x, y, data);
float ref_size = temp_size[0];
int refradx = (int)(ref_size * radx_);
int refrady = (int)(ref_size * rady_);
if (refradx > filtersizex_) {
refradx = filtersizex_;
}
else if (refradx < 1) {
refradx = 1;
}
if (refrady > filtersizey_) {
refrady = filtersizey_;
}
else if (refrady < 1) {
refrady = 1;
}
if (refradx == 1 && refrady == 1) {
memorybuffer->read_no_check(output, x, y);
}
else {
int minxr = x - refradx < 0 ? -x : -refradx;
int maxxr = x + refradx > imgx ? imgx - x : refradx;
int minyr = y - refrady < 0 ? -y : -refrady;
int maxyr = y + refrady > imgy ? imgy - y : refrady;
float *srcd = buffer + COM_DATA_TYPE_COLOR_CHANNELS * ((y + minyr) * imgx + x + minxr);
gausstabx = maintabs_[refradx - 1];
gausstabcentx = gausstabx + refradx;
gausstaby = maintabs_[refrady - 1];
gausstabcenty = gausstaby + refrady;
sum = gval = rval = bval = aval = 0.0f;
for (i = minyr; i < maxyr; i++, srcd += COM_DATA_TYPE_COLOR_CHANNELS * imgx) {
src = srcd;
for (j = minxr; j < maxxr; j++, src += COM_DATA_TYPE_COLOR_CHANNELS) {
val = gausstabcenty[i] * gausstabcentx[j];
sum += val;
rval += val * src[0];
gval += val * src[1];
bval += val * src[2];
aval += val * src[3];
}
}
sum = 1.0f / sum;
output[0] = rval * sum;
output[1] = gval * sum;
output[2] = bval * sum;
output[3] = aval * sum;
}
}
void GaussianBlurReferenceOperation::deinit_execution()
{
int x, i;
x = MAX2(filtersizex_, filtersizey_);
for (i = 0; i < x; i++) {
MEM_freeN(maintabs_[i]);
}
MEM_freeN(maintabs_);
BlurBaseOperation::deinit_execution();
}
bool GaussianBlurReferenceOperation::determine_depending_area_of_interest(
rcti *input, ReadBufferOperation *read_operation, rcti *output)
{
rcti new_input;
NodeOperation *operation = this->get_input_operation(1);
if (operation->determine_depending_area_of_interest(input, read_operation, output)) {
return true;
}
int addx = data_.sizex + 2;
int addy = data_.sizey + 2;
new_input.xmax = input->xmax + addx;
new_input.xmin = input->xmin - addx;
new_input.ymax = input->ymax + addy;
new_input.ymin = input->ymin - addy;
return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output);
}
void GaussianBlurReferenceOperation::get_area_of_interest(const int input_idx,
const rcti &output_area,
rcti &r_input_area)
{
if (input_idx != IMAGE_INPUT_INDEX) {
BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
return;
}
const int add_x = data_.sizex + 2;
const int add_y = data_.sizey + 2;
r_input_area.xmax = output_area.xmax + add_x;
r_input_area.xmin = output_area.xmin - add_x;
r_input_area.ymax = output_area.ymax + add_y;
r_input_area.ymin = output_area.ymin - add_y;
}
void GaussianBlurReferenceOperation::update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs)
{
const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX];
MemoryBuffer *size_input = inputs[SIZE_INPUT_INDEX];
for (BuffersIterator<float> it = output->iterate_with({size_input}, area); !it.is_end(); ++it) {
const float ref_size = *it.in(0);
int ref_radx = (int)(ref_size * radx_);
int ref_rady = (int)(ref_size * rady_);
if (ref_radx > filtersizex_) {
ref_radx = filtersizex_;
}
else if (ref_radx < 1) {
ref_radx = 1;
}
if (ref_rady > filtersizey_) {
ref_rady = filtersizey_;
}
else if (ref_rady < 1) {
ref_rady = 1;
}
const int x = it.x;
const int y = it.y;
if (ref_radx == 1 && ref_rady == 1) {
image_input->read_elem(x, y, it.out);
continue;
}
const int w = get_width();
const int height = get_height();
const int minxr = x - ref_radx < 0 ? -x : -ref_radx;
const int maxxr = x + ref_radx > w ? w - x : ref_radx;
const int minyr = y - ref_rady < 0 ? -y : -ref_rady;
const int maxyr = y + ref_rady > height ? height - y : ref_rady;
const float *gausstabx = maintabs_[ref_radx - 1];
const float *gausstabcentx = gausstabx + ref_radx;
const float *gausstaby = maintabs_[ref_rady - 1];
const float *gausstabcenty = gausstaby + ref_rady;
float gauss_sum = 0.0f;
float color_sum[4] = {0};
const float *row_color = image_input->get_elem(x + minxr, y + minyr);
for (int i = minyr; i < maxyr; i++, row_color += image_input->row_stride) {
const float *color = row_color;
for (int j = minxr; j < maxxr; j++, color += image_input->elem_stride) {
const float val = gausstabcenty[i] * gausstabcentx[j];
gauss_sum += val;
madd_v4_v4fl(color_sum, color, val);
}
}
mul_v4_v4fl(it.out, color_sum, 1.0f / gauss_sum);
}
}
} // namespace blender::compositor