Blender V4.5
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
28 strip->effectdata = MEM_callocN<GaussianBlurVars>("gaussianblurvars");
29}
30
32{
33 return 1;
34}
35
36static void free_gaussian_blur_effect(Strip *strip, const bool /*do_id_user*/)
37{
39}
40
41static void copy_gaussian_blur_effect(Strip *dst, const Strip *src, const int /*flag*/)
42{
44}
45
46static StripEarlyOut early_out_gaussian_blur(const Strip *strip, float /*fac*/)
47{
48 GaussianBlurVars *data = static_cast<GaussianBlurVars *>(strip->effectdata);
49 if (data->size_x == 0.0f && data->size_y == 0) {
51 }
53}
54
55template<typename T>
56static void gaussian_blur_x(const Span<float> gaussian,
57 int half_size,
58 int start_line,
59 int width,
60 int height,
61 int /*frame_height*/,
62 const T *rect,
63 T *dst)
64{
65 dst += int64_t(start_line) * width * 4;
66 for (int y = start_line; y < start_line + height; y++) {
67 for (int x = 0; x < width; x++) {
68 float4 accum(0.0f);
69 float accum_weight = 0.0f;
70
71 int xmin = math::max(x - half_size, 0);
72 int xmax = math::min(x + half_size, width - 1);
73 for (int nx = xmin, index = (xmin - x) + half_size; nx <= xmax; nx++, index++) {
74 float weight = gaussian[index];
75 int offset = (y * width + nx) * 4;
76 accum += float4(rect + offset) * weight;
77 accum_weight += weight;
78 }
79 accum *= (1.0f / accum_weight);
80 if constexpr (math::is_math_float_type<T>) {
81 dst[0] = accum[0];
82 dst[1] = accum[1];
83 dst[2] = accum[2];
84 dst[3] = accum[3];
85 }
86 else {
87 dst[0] = accum[0] + 0.5f;
88 dst[1] = accum[1] + 0.5f;
89 dst[2] = accum[2] + 0.5f;
90 dst[3] = accum[3] + 0.5f;
91 }
92 dst += 4;
93 }
94 }
95}
96
97template<typename T>
98static void gaussian_blur_y(const Span<float> gaussian,
99 int half_size,
100 int start_line,
101 int width,
102 int height,
103 int frame_height,
104 const T *rect,
105 T *dst)
106{
107 dst += int64_t(start_line) * width * 4;
108 for (int y = start_line; y < start_line + height; y++) {
109 for (int x = 0; x < width; x++) {
110 float4 accum(0.0f);
111 float accum_weight = 0.0f;
112 int ymin = math::max(y - half_size, 0);
113 int ymax = math::min(y + half_size, frame_height - 1);
114 for (int ny = ymin, index = (ymin - y) + half_size; ny <= ymax; ny++, index++) {
115 float weight = gaussian[index];
116 int offset = (ny * width + x) * 4;
117 accum += float4(rect + offset) * weight;
118 accum_weight += weight;
119 }
120 accum *= (1.0f / accum_weight);
121 if constexpr (math::is_math_float_type<T>) {
122 dst[0] = accum[0];
123 dst[1] = accum[1];
124 dst[2] = accum[2];
125 dst[3] = accum[3];
126 }
127 else {
128 dst[0] = accum[0] + 0.5f;
129 dst[1] = accum[1] + 0.5f;
130 dst[2] = accum[2] + 0.5f;
131 dst[3] = accum[3] + 0.5f;
132 }
133 dst += 4;
134 }
135 }
136}
137
139 Strip *strip,
140 float /*timeline_frame*/,
141 float /*fac*/,
142 ImBuf *ibuf1,
143 ImBuf * /*ibuf2*/)
144{
145 using namespace blender;
146
147 /* Create blur kernel weights. */
148 const GaussianBlurVars *data = static_cast<const GaussianBlurVars *>(strip->effectdata);
149 const int half_size_x = int(data->size_x + 0.5f);
150 const int half_size_y = int(data->size_y + 0.5f);
151 Array<float> gaussian_x = make_gaussian_blur_kernel(data->size_x, half_size_x);
152 Array<float> gaussian_y = make_gaussian_blur_kernel(data->size_y, half_size_y);
153
154 const int width = context->rectx;
155 const int height = context->recty;
156 const bool is_float = ibuf1->float_buffer.data;
157
158 /* Horizontal blur: create output, blur ibuf1 into it. */
159 ImBuf *out = prepare_effect_imbufs(context, ibuf1, nullptr);
160 threading::parallel_for(IndexRange(context->recty), 32, [&](const IndexRange y_range) {
161 const int y_first = y_range.first();
162 const int y_size = y_range.size();
163 if (is_float) {
164 gaussian_blur_x(gaussian_x,
165 half_size_x,
166 y_first,
167 width,
168 y_size,
169 height,
170 ibuf1->float_buffer.data,
171 out->float_buffer.data);
172 }
173 else {
174 gaussian_blur_x(gaussian_x,
175 half_size_x,
176 y_first,
177 width,
178 y_size,
179 height,
180 ibuf1->byte_buffer.data,
181 out->byte_buffer.data);
182 }
183 });
184
185 /* Vertical blur: create output, blur previous output into it. */
186 ibuf1 = out;
187 out = prepare_effect_imbufs(context, ibuf1, nullptr);
188 threading::parallel_for(IndexRange(context->recty), 32, [&](const IndexRange y_range) {
189 const int y_first = y_range.first();
190 const int y_size = y_range.size();
191 if (is_float) {
192 gaussian_blur_y(gaussian_y,
193 half_size_y,
194 y_first,
195 width,
196 y_size,
197 height,
198 ibuf1->float_buffer.data,
199 out->float_buffer.data);
200 }
201 else {
202 gaussian_blur_y(gaussian_y,
203 half_size_y,
204 y_first,
205 width,
206 y_size,
207 height,
208 ibuf1->byte_buffer.data,
209 out->byte_buffer.data);
210 }
211 });
212
213 /* Free the first output. */
214 IMB_freeImBuf(ibuf1);
215
216 return out;
217}
218
228
229} // namespace blender::seq
void IMB_freeImBuf(ImBuf *ibuf)
BMesh const char void * data
long long int int64_t
#define out
#define MEM_SAFE_FREE(v)
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)
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, 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, 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)