Blender V5.0
vse_effect_gaussian_blur.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_math_base.hh"
10#include "BLI_task.hh"
11
12#include "DNA_sequence_types.h"
13
14#include "IMB_imbuf.hh"
15
16#include "SEQ_render.hh"
17
18#include "effects.hh"
19
20namespace blender::seq {
21
23{
24 if (strip->effectdata) {
25 MEM_freeN(strip->effectdata);
26 }
27
29 strip->effectdata = data;
30 data->size_x = 9.0f;
31 data->size_y = 9.0f;
32}
33
35{
36 return 1;
37}
38
39static void free_gaussian_blur_effect(Strip *strip, const bool /*do_id_user*/)
40{
42}
43
44static void copy_gaussian_blur_effect(Strip *dst, const Strip *src, const int /*flag*/)
45{
47}
48
49static StripEarlyOut early_out_gaussian_blur(const Strip *strip, float /*fac*/)
50{
51 GaussianBlurVars *data = static_cast<GaussianBlurVars *>(strip->effectdata);
52 if (data->size_x == 0.0f && data->size_y == 0) {
54 }
56}
57
58template<typename T>
59static void gaussian_blur_x(const Span<float> gaussian,
60 int half_size,
61 int start_line,
62 int width,
63 int height,
64 int /*frame_height*/,
65 const T *rect,
66 T *dst)
67{
68 dst += int64_t(start_line) * width * 4;
69 for (int y = start_line; y < start_line + height; y++) {
70 for (int x = 0; x < width; x++) {
71 float4 accum(0.0f);
72 float accum_weight = 0.0f;
73
74 int xmin = math::max(x - half_size, 0);
75 int xmax = math::min(x + half_size, width - 1);
76 for (int nx = xmin, index = (xmin - x) + half_size; nx <= xmax; nx++, index++) {
77 float weight = gaussian[index];
78 int offset = (y * width + nx) * 4;
79 accum += float4(rect + offset) * weight;
80 accum_weight += weight;
81 }
82 accum *= (1.0f / accum_weight);
83 if constexpr (math::is_math_float_type<T>) {
84 dst[0] = accum[0];
85 dst[1] = accum[1];
86 dst[2] = accum[2];
87 dst[3] = accum[3];
88 }
89 else {
90 dst[0] = accum[0] + 0.5f;
91 dst[1] = accum[1] + 0.5f;
92 dst[2] = accum[2] + 0.5f;
93 dst[3] = accum[3] + 0.5f;
94 }
95 dst += 4;
96 }
97 }
98}
99
100template<typename T>
101static void gaussian_blur_y(const Span<float> gaussian,
102 int half_size,
103 int start_line,
104 int width,
105 int height,
106 int frame_height,
107 const T *rect,
108 T *dst)
109{
110 dst += int64_t(start_line) * width * 4;
111 for (int y = start_line; y < start_line + height; y++) {
112 for (int x = 0; x < width; x++) {
113 float4 accum(0.0f);
114 float accum_weight = 0.0f;
115 int ymin = math::max(y - half_size, 0);
116 int ymax = math::min(y + half_size, frame_height - 1);
117 for (int ny = ymin, index = (ymin - y) + half_size; ny <= ymax; ny++, index++) {
118 float weight = gaussian[index];
119 int offset = (ny * width + x) * 4;
120 accum += float4(rect + offset) * weight;
121 accum_weight += weight;
122 }
123 accum *= (1.0f / accum_weight);
124 if constexpr (math::is_math_float_type<T>) {
125 dst[0] = accum[0];
126 dst[1] = accum[1];
127 dst[2] = accum[2];
128 dst[3] = accum[3];
129 }
130 else {
131 dst[0] = accum[0] + 0.5f;
132 dst[1] = accum[1] + 0.5f;
133 dst[2] = accum[2] + 0.5f;
134 dst[3] = accum[3] + 0.5f;
135 }
136 dst += 4;
137 }
138 }
139}
140
142 SeqRenderState * /*state*/,
143 Strip *strip,
144 float /*timeline_frame*/,
145 float /*fac*/,
146 ImBuf *ibuf1,
147 ImBuf * /*ibuf2*/)
148{
149 using namespace blender;
150
151 /* Create blur kernel weights. */
152 const GaussianBlurVars *data = static_cast<const GaussianBlurVars *>(strip->effectdata);
153
154 const float size_scale = seq::get_render_scale_factor(*context);
155 const float size_x = data->size_x * size_scale;
156 const float size_y = data->size_y * size_scale;
157
158 const int half_size_x = int(size_x + 0.5f);
159 const int half_size_y = int(size_y + 0.5f);
160 Array<float> gaussian_x = make_gaussian_blur_kernel(size_x, half_size_x);
161 Array<float> gaussian_y = make_gaussian_blur_kernel(size_y, half_size_y);
162
163 const int width = context->rectx;
164 const int height = context->recty;
165 const bool is_float = ibuf1->float_buffer.data;
166
167 /* Horizontal blur: create output, blur ibuf1 into it. */
168 ImBuf *out = prepare_effect_imbufs(context, ibuf1, nullptr);
169 threading::parallel_for(IndexRange(context->recty), 32, [&](const IndexRange y_range) {
170 const int y_first = y_range.first();
171 const int y_size = y_range.size();
172 if (is_float) {
173 gaussian_blur_x(gaussian_x,
174 half_size_x,
175 y_first,
176 width,
177 y_size,
178 height,
179 ibuf1->float_buffer.data,
180 out->float_buffer.data);
181 }
182 else {
183 gaussian_blur_x(gaussian_x,
184 half_size_x,
185 y_first,
186 width,
187 y_size,
188 height,
189 ibuf1->byte_buffer.data,
190 out->byte_buffer.data);
191 }
192 });
193
194 /* Vertical blur: create output, blur previous output into it. */
195 ibuf1 = out;
196 out = prepare_effect_imbufs(context, ibuf1, nullptr);
197 threading::parallel_for(IndexRange(context->recty), 32, [&](const IndexRange y_range) {
198 const int y_first = y_range.first();
199 const int y_size = y_range.size();
200 if (is_float) {
201 gaussian_blur_y(gaussian_y,
202 half_size_y,
203 y_first,
204 width,
205 y_size,
206 height,
207 ibuf1->float_buffer.data,
208 out->float_buffer.data);
209 }
210 else {
211 gaussian_blur_y(gaussian_y,
212 half_size_y,
213 y_first,
214 width,
215 y_size,
216 height,
217 ibuf1->byte_buffer.data,
218 out->byte_buffer.data);
219 }
220 });
221
222 /* Free the first output. */
223 IMB_freeImBuf(ibuf1);
224
225 return out;
226}
227
237
238} // namespace blender::seq
void IMB_freeImBuf(ImBuf *ibuf)
#define MEM_SAFE_FREE(v)
BMesh const char void * data
long long int int64_t
#define out
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define T
constexpr bool is_math_float_type
T min(const T &a, const T &b)
T max(const T &a, const T &b)
static void init_gaussian_blur_effect(Strip *strip)
void gaussian_blur_effect_get_handle(EffectHandle &rval)
static void free_gaussian_blur_effect(Strip *strip, const bool)
float get_render_scale_factor(eSpaceSeq_Proxy_RenderSize render_size, short scene_render_scale)
Definition render.cc:2118
Array< float > make_gaussian_blur_kernel(float rad, int size)
Definition effects.cc:80
ImBuf * prepare_effect_imbufs(const RenderData *context, ImBuf *ibuf1, ImBuf *ibuf2, bool uninitialized_pixels)
Definition effects.cc:28
static void gaussian_blur_x(const Span< float > gaussian, int half_size, int start_line, int width, int height, int, const T *rect, T *dst)
static ImBuf * do_gaussian_blur_effect(const RenderData *context, SeqRenderState *, Strip *strip, float, float, ImBuf *ibuf1, ImBuf *)
static StripEarlyOut early_out_gaussian_blur(const Strip *strip, float)
static int num_inputs_gaussian_blur()
static void gaussian_blur_y(const Span< float > gaussian, int half_size, int start_line, int width, int height, int frame_height, const T *rect, T *dst)
static void copy_gaussian_blur_effect(Strip *dst, const Strip *src, const int)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
VecBase< float, 4 > float4
ImBufFloatBuffer float_buffer
void * effectdata
void(* copy)(Strip *dst, const Strip *src, int flag)
ImBuf *(* execute)(const RenderData *context, SeqRenderState *state, Strip *strip, float timeline_frame, float fac, ImBuf *ibuf1, ImBuf *ibuf2)
void(* free)(Strip *strip, bool do_id_user)
StripEarlyOut(* early_out)(const Strip *strip, float fac)
void(* init)(Strip *strip)