Blender V4.3
COM_VariableSizeBokehBlurOperation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_math_base.hh"
6#include "BLI_math_vector.hh"
7
9
10namespace blender::compositor {
11
13{
15 this->add_input_socket(DataType::Color, ResizeMode::Align); /* Do not resize the bokeh image. */
16 this->add_input_socket(DataType::Value); /* Radius. */
17 this->add_input_socket(DataType::Value); /* Bounding Box. */
18#ifdef COM_DEFOCUS_SEARCH
19 /* Inverse search radius optimization structure. */
21#endif
24
25 max_blur_ = 32.0f;
26 threshold_ = 1.0f;
27 do_size_scale_ = false;
28}
29
37
39 const rcti &output_area,
40 rcti &r_input_area)
41{
42 switch (input_idx) {
43 case IMAGE_INPUT_INDEX:
44 case BOUNDING_BOX_INPUT_INDEX:
45 case SIZE_INPUT_INDEX: {
46 const float max_dim = std::max(get_width(), get_height());
47 const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f;
48 const int max_blur_scalar = max_blur_ * scalar;
49 r_input_area.xmax = output_area.xmax + max_blur_scalar + 2;
50 r_input_area.xmin = output_area.xmin - max_blur_scalar - 2;
51 r_input_area.ymax = output_area.ymax + max_blur_scalar + 2;
52 r_input_area.ymin = output_area.ymin - max_blur_scalar - 2;
53 break;
54 }
55 case BOKEH_INPUT_INDEX: {
56 r_input_area = output_area;
57 r_input_area.xmax = r_input_area.xmin + COM_BLUR_BOKEH_PIXELS;
58 r_input_area.ymax = r_input_area.ymin + COM_BLUR_BOKEH_PIXELS;
59 break;
60 }
61#ifdef COM_DEFOCUS_SEARCH
62 case DEFOCUS_INPUT_INDEX: {
63 r_input_area.xmax = (output_area.xmax / InverseSearchRadiusOperation::DIVIDER) + 1;
64 r_input_area.xmin = (output_area.xmin / InverseSearchRadiusOperation::DIVIDER) - 1;
65 r_input_area.ymax = (output_area.ymax / InverseSearchRadiusOperation::DIVIDER) + 1;
66 r_input_area.ymin = (output_area.ymin / InverseSearchRadiusOperation::DIVIDER) - 1;
67 break;
68 }
69#endif
70 }
71}
72
74 const rcti &area,
76{
77 MemoryBuffer *input_buffer = inputs[0];
78 MemoryBuffer *bokeh_buffer = inputs[1];
79 MemoryBuffer *size_buffer = inputs[2];
80 MemoryBuffer *mask_buffer = inputs[3];
81
82 const float max_dim = std::max(get_width(), get_height());
83 const float base_size = do_size_scale_ ? (max_dim / 100.0f) : 1.0f;
84 const float maximum_size = size_buffer->get_max_value();
85 const int search_radius = math::clamp(int(maximum_size * base_size), 0, max_blur_);
86
87 BuffersIterator<float> it = output->iterate_with({}, area);
88 for (; !it.is_end(); ++it) {
89 if (*mask_buffer->get_elem(it.x, it.y) <= 0.0f) {
90 copy_v4_v4(it.out, input_buffer->get_elem(it.x, it.y));
91 continue;
92 }
93
94 const float center_size = math::max(0.0f, *size_buffer->get_elem(it.x, it.y) * base_size);
95
96 float4 accumulated_color = float4(input_buffer->get_elem(it.x, it.y));
97 float4 accumulated_weight = float4(1.0f);
98 if (center_size >= threshold_) {
99 for (int yi = -search_radius; yi <= search_radius; ++yi) {
100 for (int xi = -search_radius; xi <= search_radius; ++xi) {
101 if (xi == 0 && yi == 0) {
102 continue;
103 }
104 const float candidate_size = math::max(
105 0.0f, *size_buffer->get_elem_clamped(it.x + xi, it.y + yi) * base_size);
106 const float size = math::min(center_size, candidate_size);
107 if (size < threshold_ || math::max(math::abs(xi), math::abs(yi)) > size) {
108 continue;
109 }
110
111 const float2 normalized_texel = (float2(xi, yi) + size + 0.5f) / (size * 2.0f + 1.0f);
112 const float2 weight_texel = 1.0f - normalized_texel;
113 const float4 weight = bokeh_buffer->texture_bilinear_extend(weight_texel);
114 const float4 color = input_buffer->get_elem_clamped(it.x + xi, it.y + yi);
115 accumulated_color += color * weight;
116 accumulated_weight += weight;
117 }
118 }
119 }
120
121 const float4 final_color = math::safe_divide(accumulated_color, accumulated_weight);
122 copy_v4_v4(it.out, final_color);
123
124 /* blend in out values over the threshold, otherwise we get sharp, ugly transitions */
125 if ((center_size > threshold_) && (center_size < threshold_ * 2.0f)) {
126 /* factor from 0-1 */
127 float fac = (center_size - threshold_) / threshold_;
128 interp_v4_v4v4(it.out, input_buffer->get_elem(it.x, it.y), it.out, fac);
129 }
130 }
131}
132
133#ifdef COM_DEFOCUS_SEARCH
134/* #InverseSearchRadiusOperation. */
135InverseSearchRadiusOperation::InverseSearchRadiusOperation()
136{
137 this->add_input_socket(DataType::Value, ResizeMode::Align); /* Radius. */
138 this->add_output_socket(DataType::Color);
139}
140
141void InverseSearchRadiusOperation::determine_resolution(uint resolution[2],
142 uint preferred_resolution[2])
143{
144 NodeOperation::determine_resolution(resolution, preferred_resolution);
145 resolution[0] = resolution[0] / DIVIDER;
146 resolution[1] = resolution[1] / DIVIDER;
147}
148
149#endif
150
151} // namespace blender::compositor
MINLINE void copy_v4_v4(float r[4], const float a[4])
void interp_v4_v4v4(float r[4], const float a[4], const float b[4], float t)
Definition math_vector.c:45
unsigned int uint
a MemoryBuffer contains access to the data
const float * get_elem_clamped(int x, int y) const
float4 texture_bilinear_extend(float2 coordinates) const
void add_output_socket(DataType datatype)
void add_input_socket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override
Get input operation area being read by this operation on rendering given output area.
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
constexpr float COM_BLUR_BOKEH_PIXELS
Definition COM_defines.h:87
typename BuffersIteratorBuilder< T >::Iterator BuffersIterator
T clamp(const T &a, const T &min, const T &max)
T safe_divide(const T &a, const T &b)
T min(const T &a, const T &b)
T max(const T &a, const T &b)
T abs(const T &a)
VecBase< float, 4 > float4
VecBase< float, 2 > float2
int ymin
int ymax
int xmin
int xmax