diff options
Diffstat (limited to 'source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc')
-rw-r--r-- | source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc new file mode 100644 index 00000000000..9bdc652b466 --- /dev/null +++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc @@ -0,0 +1,168 @@ +/* + * 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 2021, Blender Foundation. + */ + +#include "COM_GaussianAlphaBlurBaseOperation.h" + +namespace blender::compositor { + +GaussianAlphaBlurBaseOperation::GaussianAlphaBlurBaseOperation(eDimension dim) + : BlurBaseOperation(DataType::Value) +{ + this->m_gausstab = nullptr; + this->m_filtersize = 0; + this->m_falloff = -1; /* Intentionally invalid, so we can detect uninitialized values. */ + dimension_ = dim; +} + +void GaussianAlphaBlurBaseOperation::init_data() +{ + BlurBaseOperation::init_data(); + if (execution_model_ == eExecutionModel::FullFrame) { + rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f); + rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS); + m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS); + } +} + +void GaussianAlphaBlurBaseOperation::initExecution() +{ + BlurBaseOperation::initExecution(); + if (execution_model_ == eExecutionModel::FullFrame) { + m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize); + m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad_, m_filtersize, m_falloff); + } +} + +void GaussianAlphaBlurBaseOperation::deinitExecution() +{ + BlurBaseOperation::deinitExecution(); + + if (this->m_gausstab) { + MEM_freeN(this->m_gausstab); + this->m_gausstab = nullptr; + } + + if (this->m_distbuf_inv) { + MEM_freeN(this->m_distbuf_inv); + this->m_distbuf_inv = nullptr; + } +} + +void GaussianAlphaBlurBaseOperation::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 = output_area; + switch (dimension_) { + case eDimension::X: + r_input_area.xmin = output_area.xmin - m_filtersize - 1; + r_input_area.xmax = output_area.xmax + m_filtersize + 1; + break; + case eDimension::Y: + r_input_area.ymin = output_area.ymin - m_filtersize - 1; + r_input_area.ymax = output_area.ymax + m_filtersize + 1; + break; + } +} + +BLI_INLINE float finv_test(const float f, const bool test) +{ + return (LIKELY(test == false)) ? f : 1.0f - f; +} + +void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer *input = inputs[IMAGE_INPUT_INDEX]; + const rcti &input_rect = input->get_rect(); + BuffersIterator<float> it = output->iterate_with({input}, area); + + int min_input_coord = -1; + int max_input_coord = -1; + int elem_stride = -1; + std::function<int()> get_current_coord; + switch (dimension_) { + case eDimension::X: + min_input_coord = input_rect.xmin; + max_input_coord = input_rect.xmax; + get_current_coord = [&] { return it.x; }; + elem_stride = input->elem_stride; + break; + case eDimension::Y: + min_input_coord = input_rect.ymin; + max_input_coord = input_rect.ymax; + get_current_coord = [&] { return it.y; }; + elem_stride = input->row_stride; + break; + } + + for (; !it.is_end(); ++it) { + const int coord = get_current_coord(); + const int coord_min = max_ii(coord - m_filtersize, min_input_coord); + const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord); + + /* *** This is the main part which is different to #GaussianBlurBaseOperation. *** */ + /* Gauss. */ + float alpha_accum = 0.0f; + float multiplier_accum = 0.0f; + + /* Dilate. */ + const bool do_invert = m_do_subtract; + /* Init with the current color to avoid unneeded lookups. */ + float value_max = finv_test(*it.in(0), do_invert); + float distfacinv_max = 1.0f; /* 0 to 1 */ + + const int step = QualityStepHelper::getStep(); + const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride; + const int in_stride = elem_stride * step; + int index = (coord_min - coord) + m_filtersize; + const int index_end = index + (coord_max - coord_min); + for (; index < index_end; in += in_stride, index += step) { + float value = finv_test(*in, do_invert); + + /* Gauss. */ + float multiplier = m_gausstab[index]; + alpha_accum += value * multiplier; + multiplier_accum += multiplier; + + /* Dilate - find most extreme color. */ + if (value > value_max) { + multiplier = m_distbuf_inv[index]; + value *= multiplier; + if (value > value_max) { + value_max = value; + distfacinv_max = multiplier; + } + } + } + + /* Blend between the max value and gauss blue - gives nice feather. */ + const float value_blur = alpha_accum / multiplier_accum; + const float value_final = (value_max * distfacinv_max) + + (value_blur * (1.0f - distfacinv_max)); + *it.out = finv_test(value_final, do_invert); + } +} + +} // namespace blender::compositor |