Blender V5.0
vse_effect_wipe.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 <algorithm>
10
11#include "BLI_math_vector.hh"
12#include "BLI_task.hh"
13
14#include "DNA_sequence_types.h"
15
16#include "IMB_imbuf.hh"
17
18#include "SEQ_render.hh"
19
20#include "effects.hh"
21
22namespace blender::seq {
23
24struct WipeData {
25 WipeData(const WipeVars *wipe, int width, int height, float fac)
26 {
27 this->type = eEffectWipeType(wipe->wipetype);
28 this->forward = wipe->forward != 0;
29 this->size = float2(width, height);
30
31 if (this->type == SEQ_WIPE_SINGLE) {
32 /* Position that the wipe line goes through: moves along
33 * the image diagonal. The other diagonal when angle is negative. */
34 this->pos = this->size * (this->forward ? fac : (1.0f - fac));
35 if (wipe->angle < 0.0f) {
36 this->pos.x = this->size.x - this->pos.x;
37 }
38 }
39 if (this->type == SEQ_WIPE_DOUBLE) {
40 /* For double blend, position goes from center of screen
41 * along the diagonal. The other blend line position will be
42 * a mirror of it. */
43 float2 offset = this->size * (this->forward ? (1.0f - fac) : fac) * 0.5f;
44 if (wipe->angle < 0.0f) {
45 offset.x = -offset.x;
46 }
47 this->pos = this->size * 0.5f + offset;
48 }
49
50 /* Line direction: (cos(a), sin(a)). Perpendicular: (-sin(a), cos(a)).
51 * Angle is negative to match previous behavior. */
52 this->normal.x = -sinf(-wipe->angle);
53 this->normal.y = cosf(-wipe->angle);
54
55 /* Blend zone width. */
56 float blend_width = wipe->edgeWidth * ((width + height) / 2.0f);
58 blend_width *= 0.5f;
59 }
60 /* For single/double wipes, make sure the blend zone goes to zero at start & end
61 * of transition. */
63 blend_width = std::min(blend_width, fac * this->size.y);
64 blend_width = std::min(blend_width, this->size.y - fac * this->size.y);
65 }
66 this->blend_width_inv = math::safe_rcp(blend_width);
67
68 if (this->type == SEQ_WIPE_IRIS) {
69 /* Distance to Iris circle at current factor. */
70 float2 iris = this->size * 0.5f * (this->forward ? (1.0f - fac) : fac);
71 this->iris_dist = math::length(iris);
72 }
73
74 if (this->type == SEQ_WIPE_CLOCK) {
75 float angle_cur = 2.0f * float(M_PI) * (this->forward ? (1.0f - fac) : fac);
76 float angle_width = wipe->edgeWidth * float(M_PI);
77 float delta_neg = angle_width * (this->forward ? fac : (1.0f - fac));
78 float delta_pos = angle_width * (this->forward ? (1.0f - fac) : fac);
79 this->clock_angles.x = std::max(angle_cur - delta_neg, 0.0f);
80 this->clock_angles.y = std::min(angle_cur + delta_pos, 2.0f * float(M_PI));
81 this->clock_angle_inv_dif = math::safe_rcp(this->clock_angles.y - this->clock_angles.x);
82 }
83 }
84
85 float2 size; /* Image size. */
86 float2 pos; /* Position that wipe line goes through. */
87 float2 normal; /* Normal vector to single/double wipe line. */
88 float blend_width_inv = 0.0f;
89 float iris_dist = 0.0f;
90 float2 clock_angles; /* Min, max clock angles at current factor. */
91 float clock_angle_inv_dif = 0.0f;
93 bool forward = false;
94};
95
96static float calc_wipe_band(float dist, float inv_width)
97{
98 if (inv_width == 0.0f) {
99 return dist < 0.0f ? 0.0f : 1.0f;
100 }
101 return dist * inv_width + 0.5f;
102}
103
104static float calc_wipe_blend(const WipeData *data, int x, int y)
105{
106 float output = 0.0f;
107 switch (data->type) {
108 case SEQ_WIPE_SINGLE: {
109 /* Distance to line: dot(pixel_pos - line_pos, line_normal). */
110 float dist = math::dot(float2(x, y) - data->pos, data->normal);
111 output = calc_wipe_band(dist, data->blend_width_inv);
112 } break;
113
114 case SEQ_WIPE_DOUBLE: {
115 /* Distance to line: dot(pixel_pos - line_pos, line_normal).
116 * For double wipe, we have two lines to calculate the distance to. */
117 float2 pos1 = data->pos;
118 float2 pos2 = data->size - data->pos;
119 float dist1 = math::dot(float2(x, y) - pos1, -data->normal);
120 float dist2 = math::dot(float2(x, y) - pos2, data->normal);
121 float dist = std::min(dist1, dist2);
122 output = calc_wipe_band(dist, data->blend_width_inv);
123 } break;
124
125 case SEQ_WIPE_CLOCK: {
126 float2 offset = float2(x, y) - data->size * 0.5f;
127 if (math::length_squared(offset) < 1.0e-3f) {
128 output = 0.0f;
129 }
130 else {
131 float angle;
132 angle = atan2f(offset.y, offset.x);
133 if (angle < 0.0f) {
134 angle += 2.0f * float(M_PI);
135 }
136 if (angle < data->clock_angles.x) {
137 output = 1;
138 }
139 else if (angle > data->clock_angles.y) {
140 output = 0;
141 }
142 else {
143 output = (data->clock_angles.y - angle) * data->clock_angle_inv_dif;
144 }
145 }
146 } break;
147
148 case SEQ_WIPE_IRIS: {
149 float dist = math::distance(float2(x, y), data->size * 0.5f);
150 output = calc_wipe_band(data->iris_dist - dist, data->blend_width_inv);
151 } break;
152 }
153 if (!data->forward) {
154 output = 1.0f - output;
155 }
156 return output;
157}
158
159static void init_wipe_effect(Strip *strip)
160{
161 if (strip->effectdata) {
162 MEM_freeN(strip->effectdata);
163 }
164
165 strip->effectdata = MEM_callocN<WipeVars>("wipevars");
166}
167
168static int num_inputs_wipe()
169{
170 return 2;
171}
172
173static void free_wipe_effect(Strip *strip, const bool /*do_id_user*/)
174{
176}
177
178static void copy_wipe_effect(Strip *dst, const Strip *src, const int /*flag*/)
179{
181}
182
183template<typename T>
184static void do_wipe_effect(
185 const Strip *strip, float fac, int width, int height, const T *rect1, const T *rect2, T *out)
186{
187 using namespace blender;
188 const WipeVars *wipe = (const WipeVars *)strip->effectdata;
189
190 const WipeData data(wipe, width, height, fac);
191
192 threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
193 const T *cp1 = rect1 + y_range.first() * width * 4;
194 const T *cp2 = rect2 + y_range.first() * width * 4;
195 T *rt = out + y_range.first() * width * 4;
196 for (const int y : y_range) {
197 for (int x = 0; x < width; x++) {
198 float blend = calc_wipe_blend(&data, x, y);
199 if (blend <= 0.0f) {
200 memcpy(rt, cp2, sizeof(T) * 4);
201 }
202 else if (blend >= 1.0f) {
203 memcpy(rt, cp1, sizeof(T) * 4);
204 }
205 else {
206 float4 col1 = load_premul_pixel(cp1);
207 float4 col2 = load_premul_pixel(cp2);
208 float4 col = col1 * blend + col2 * (1.0f - blend);
210 }
211
212 rt += 4;
213 cp1 += 4;
214 cp2 += 4;
215 }
216 }
217 });
218}
219
220static ImBuf *do_wipe_effect(const RenderData *context,
221 SeqRenderState * /*state*/,
222 Strip *strip,
223 float /*timeline_frame*/,
224 float fac,
225 ImBuf *ibuf1,
226 ImBuf *ibuf2)
227{
228 ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2);
229
230 if (out->float_buffer.data) {
231 do_wipe_effect(strip,
232 fac,
233 context->rectx,
234 context->recty,
235 ibuf1->float_buffer.data,
236 ibuf2->float_buffer.data,
237 out->float_buffer.data);
238 }
239 else {
240 do_wipe_effect(strip,
241 fac,
242 context->rectx,
243 context->recty,
244 ibuf1->byte_buffer.data,
245 ibuf2->byte_buffer.data,
246 out->byte_buffer.data);
247 }
248
249 return out;
250}
251
262
263} // namespace blender::seq
#define M_PI
#define ELEM(...)
eEffectWipeType
@ SEQ_WIPE_DOUBLE
@ SEQ_WIPE_IRIS
@ SEQ_WIPE_CLOCK
@ SEQ_WIPE_SINGLE
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
#define MEM_SAFE_FREE(v)
BMesh const char void * data
constexpr int64_t first() const
nullptr float
uint col
#define out
#define output
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
T length_squared(const VecBase< T, Size > &a)
T safe_rcp(const T &a)
T distance(const T &a, const T &b)
T length(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
static void free_wipe_effect(Strip *strip, const bool)
void wipe_effect_get_handle(EffectHandle &rval)
static void copy_wipe_effect(Strip *dst, const Strip *src, const int)
static int num_inputs_wipe()
static void init_wipe_effect(Strip *strip)
void store_premul_pixel(const blender::float4 &pix, uchar *dst)
Definition effects.hh:60
ImBuf * prepare_effect_imbufs(const RenderData *context, ImBuf *ibuf1, ImBuf *ibuf2, bool uninitialized_pixels)
Definition effects.cc:28
void get_default_fac_fade(const Scene *scene, const Strip *strip, float timeline_frame, float *fac)
Definition effects.cc:152
blender::float4 load_premul_pixel(const uchar *ptr)
Definition effects.hh:48
static void do_wipe_effect(const Strip *strip, float fac, int width, int height, const T *rect1, const T *rect2, T *out)
static float calc_wipe_blend(const WipeData *data, int x, int y)
static float calc_wipe_band(float dist, float inv_width)
StripEarlyOut early_out_fade(const Strip *, float fac)
Definition effects.cc:117
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
VecBase< float, 2 > float2
#define sinf
#define cosf
#define atan2f
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_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(* get_default_fac)(const Scene *scene, const Strip *strip, float timeline_frame, float *fac)
void(* free)(Strip *strip, bool do_id_user)
StripEarlyOut(* early_out)(const Strip *strip, float fac)
void(* init)(Strip *strip)
WipeData(const WipeVars *wipe, int width, int height, float fac)
static int blend(const Tex *tex, const float texvec[3], TexResult *texres)