FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_xfade.c
Date: 2026-10-06 20:08:58
Exec Total Coverage
Lines: 139 374 37.2%
Functions: 15 140 10.7%
Branches: 135 1268 10.6%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2020 Paul B Mahol
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "libavutil/eval.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/pixdesc.h"
24 #include "libavutil/pixfmt.h"
25 #include "avfilter.h"
26 #include "filters.h"
27 #include "video.h"
28
29 enum XFadeTransitions {
30 CUSTOM = -1,
31 FADE,
32 WIPELEFT,
33 WIPERIGHT,
34 WIPEUP,
35 WIPEDOWN,
36 SLIDELEFT,
37 SLIDERIGHT,
38 SLIDEUP,
39 SLIDEDOWN,
40 CIRCLECROP,
41 RECTCROP,
42 DISTANCE,
43 FADEBLACK,
44 FADEWHITE,
45 RADIAL,
46 SMOOTHLEFT,
47 SMOOTHRIGHT,
48 SMOOTHUP,
49 SMOOTHDOWN,
50 CIRCLEOPEN,
51 CIRCLECLOSE,
52 VERTOPEN,
53 VERTCLOSE,
54 HORZOPEN,
55 HORZCLOSE,
56 DISSOLVE,
57 PIXELIZE,
58 DIAGTL,
59 DIAGTR,
60 DIAGBL,
61 DIAGBR,
62 HLSLICE,
63 HRSLICE,
64 VUSLICE,
65 VDSLICE,
66 HBLUR,
67 FADEGRAYS,
68 WIPETL,
69 WIPETR,
70 WIPEBL,
71 WIPEBR,
72 SQUEEZEH,
73 SQUEEZEV,
74 ZOOMIN,
75 FADEFAST,
76 FADESLOW,
77 HLWIND,
78 HRWIND,
79 VUWIND,
80 VDWIND,
81 COVERLEFT,
82 COVERRIGHT,
83 COVERUP,
84 COVERDOWN,
85 REVEALLEFT,
86 REVEALRIGHT,
87 REVEALUP,
88 REVEALDOWN,
89 NB_TRANSITIONS,
90 };
91
92 typedef struct XFadeContext {
93 const AVClass *class;
94
95 int transition;
96 int64_t duration;
97 int64_t offset;
98 char *custom_str;
99
100 int nb_planes;
101 int depth;
102 int is_rgb;
103
104 // PTS when the fade should start (in first inputs timebase)
105 int64_t start_pts;
106
107 // PTS offset between first and second input
108 int64_t inputs_offset_pts;
109
110 // Duration of the transition
111 int64_t duration_pts;
112
113 // Current PTS of the first input
114 int64_t pts;
115
116 // If frames are currently just passed through unmodified,
117 // like before and after the actual transition.
118 int passthrough;
119
120 int status[2];
121 AVFrame *xf[2];
122 int max_value;
123 uint16_t black[4];
124 uint16_t white[4];
125
126 void (*transitionf)(AVFilterContext *ctx, const AVFrame *a, const AVFrame *b, AVFrame *out, float progress,
127 int slice_start, int slice_end, int jobnr);
128
129 AVExpr *e;
130 } XFadeContext;
131
132 static const char *const var_names[] = { "X", "Y", "W", "H", "A", "B", "PLANE", "P", NULL };
133 enum { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_A, VAR_B, VAR_PLANE, VAR_PROGRESS, VAR_VARS_NB };
134
135 typedef struct ThreadData {
136 const AVFrame *xf[2];
137 AVFrame *out;
138 float progress;
139 } ThreadData;
140
141 static const enum AVPixelFormat pix_fmts[] = {
142 AV_PIX_FMT_YUVA444P,
143 AV_PIX_FMT_YUVJ444P,
144 AV_PIX_FMT_YUV444P,
145 AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GRAY8,
146 AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_GBRP9,
147 AV_PIX_FMT_YUV444P10,
148 AV_PIX_FMT_YUVA444P10,
149 AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GRAY10,
150 AV_PIX_FMT_YUV444P12,
151 AV_PIX_FMT_YUVA444P12,
152 AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GRAY12,
153 AV_PIX_FMT_YUV444P14, AV_PIX_FMT_GBRP14,
154 AV_PIX_FMT_YUV444P16,
155 AV_PIX_FMT_YUVA444P16,
156 AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GRAY16,
157 AV_PIX_FMT_NONE
158 };
159
160 16 static av_cold void uninit(AVFilterContext *ctx)
161 {
162 16 XFadeContext *s = ctx->priv;
163
164 16 av_expr_free(s->e);
165 16 }
166
167 #define OFFSET(x) offsetof(XFadeContext, x)
168 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
169
170 static const AVOption xfade_options[] = {
171 { "transition", "set cross fade transition", OFFSET(transition), AV_OPT_TYPE_INT, {.i64=FADE}, -1, NB_TRANSITIONS-1, FLAGS, .unit = "transition" },
172 { "custom", "custom transition", 0, AV_OPT_TYPE_CONST, {.i64=CUSTOM}, 0, 0, FLAGS, .unit = "transition" },
173 { "fade", "fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADE}, 0, 0, FLAGS, .unit = "transition" },
174 { "wipeleft", "wipe left transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPELEFT}, 0, 0, FLAGS, .unit = "transition" },
175 { "wiperight", "wipe right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPERIGHT}, 0, 0, FLAGS, .unit = "transition" },
176 { "wipeup", "wipe up transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEUP}, 0, 0, FLAGS, .unit = "transition" },
177 { "wipedown", "wipe down transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEDOWN}, 0, 0, FLAGS, .unit = "transition" },
178 { "slideleft", "slide left transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDELEFT}, 0, 0, FLAGS, .unit = "transition" },
179 { "slideright", "slide right transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDERIGHT}, 0, 0, FLAGS, .unit = "transition" },
180 { "slideup", "slide up transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEUP}, 0, 0, FLAGS, .unit = "transition" },
181 { "slidedown", "slide down transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEDOWN}, 0, 0, FLAGS, .unit = "transition" },
182 { "circlecrop", "circle crop transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECROP}, 0, 0, FLAGS, .unit = "transition" },
183 { "rectcrop", "rect crop transition", 0, AV_OPT_TYPE_CONST, {.i64=RECTCROP}, 0, 0, FLAGS, .unit = "transition" },
184 { "distance", "distance transition", 0, AV_OPT_TYPE_CONST, {.i64=DISTANCE}, 0, 0, FLAGS, .unit = "transition" },
185 { "fadeblack", "fadeblack transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEBLACK}, 0, 0, FLAGS, .unit = "transition" },
186 { "fadewhite", "fadewhite transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEWHITE}, 0, 0, FLAGS, .unit = "transition" },
187 { "radial", "radial transition", 0, AV_OPT_TYPE_CONST, {.i64=RADIAL}, 0, 0, FLAGS, .unit = "transition" },
188 { "smoothleft", "smoothleft transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHLEFT}, 0, 0, FLAGS, .unit = "transition" },
189 { "smoothright","smoothright transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHRIGHT},0, 0, FLAGS, .unit = "transition" },
190 { "smoothup", "smoothup transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHUP}, 0, 0, FLAGS, .unit = "transition" },
191 { "smoothdown", "smoothdown transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHDOWN}, 0, 0, FLAGS, .unit = "transition" },
192 { "circleopen", "circleopen transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLEOPEN}, 0, 0, FLAGS, .unit = "transition" },
193 { "circleclose","circleclose transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECLOSE},0, 0, FLAGS, .unit = "transition" },
194 { "vertopen", "vert open transition", 0, AV_OPT_TYPE_CONST, {.i64=VERTOPEN}, 0, 0, FLAGS, .unit = "transition" },
195 { "vertclose", "vert close transition", 0, AV_OPT_TYPE_CONST, {.i64=VERTCLOSE}, 0, 0, FLAGS, .unit = "transition" },
196 { "horzopen", "horz open transition", 0, AV_OPT_TYPE_CONST, {.i64=HORZOPEN}, 0, 0, FLAGS, .unit = "transition" },
197 { "horzclose", "horz close transition", 0, AV_OPT_TYPE_CONST, {.i64=HORZCLOSE}, 0, 0, FLAGS, .unit = "transition" },
198 { "dissolve", "dissolve transition", 0, AV_OPT_TYPE_CONST, {.i64=DISSOLVE}, 0, 0, FLAGS, .unit = "transition" },
199 { "pixelize", "pixelize transition", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE}, 0, 0, FLAGS, .unit = "transition" },
200 { "diagtl", "diag tl transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGTL}, 0, 0, FLAGS, .unit = "transition" },
201 { "diagtr", "diag tr transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGTR}, 0, 0, FLAGS, .unit = "transition" },
202 { "diagbl", "diag bl transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGBL}, 0, 0, FLAGS, .unit = "transition" },
203 { "diagbr", "diag br transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGBR}, 0, 0, FLAGS, .unit = "transition" },
204 { "hlslice", "hl slice transition", 0, AV_OPT_TYPE_CONST, {.i64=HLSLICE}, 0, 0, FLAGS, .unit = "transition" },
205 { "hrslice", "hr slice transition", 0, AV_OPT_TYPE_CONST, {.i64=HRSLICE}, 0, 0, FLAGS, .unit = "transition" },
206 { "vuslice", "vu slice transition", 0, AV_OPT_TYPE_CONST, {.i64=VUSLICE}, 0, 0, FLAGS, .unit = "transition" },
207 { "vdslice", "vd slice transition", 0, AV_OPT_TYPE_CONST, {.i64=VDSLICE}, 0, 0, FLAGS, .unit = "transition" },
208 { "hblur", "hblur transition", 0, AV_OPT_TYPE_CONST, {.i64=HBLUR}, 0, 0, FLAGS, .unit = "transition" },
209 { "fadegrays", "fadegrays transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEGRAYS}, 0, 0, FLAGS, .unit = "transition" },
210 { "wipetl", "wipe tl transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPETL}, 0, 0, FLAGS, .unit = "transition" },
211 { "wipetr", "wipe tr transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPETR}, 0, 0, FLAGS, .unit = "transition" },
212 { "wipebl", "wipe bl transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEBL}, 0, 0, FLAGS, .unit = "transition" },
213 { "wipebr", "wipe br transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEBR}, 0, 0, FLAGS, .unit = "transition" },
214 { "squeezeh", "squeeze h transition", 0, AV_OPT_TYPE_CONST, {.i64=SQUEEZEH}, 0, 0, FLAGS, .unit = "transition" },
215 { "squeezev", "squeeze v transition", 0, AV_OPT_TYPE_CONST, {.i64=SQUEEZEV}, 0, 0, FLAGS, .unit = "transition" },
216 { "zoomin", "zoom in transition", 0, AV_OPT_TYPE_CONST, {.i64=ZOOMIN}, 0, 0, FLAGS, .unit = "transition" },
217 { "fadefast", "fast fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEFAST}, 0, 0, FLAGS, .unit = "transition" },
218 { "fadeslow", "slow fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADESLOW}, 0, 0, FLAGS, .unit = "transition" },
219 { "hlwind", "hl wind transition", 0, AV_OPT_TYPE_CONST, {.i64=HLWIND}, 0, 0, FLAGS, .unit = "transition" },
220 { "hrwind", "hr wind transition", 0, AV_OPT_TYPE_CONST, {.i64=HRWIND}, 0, 0, FLAGS, .unit = "transition" },
221 { "vuwind", "vu wind transition", 0, AV_OPT_TYPE_CONST, {.i64=VUWIND}, 0, 0, FLAGS, .unit = "transition" },
222 { "vdwind", "vd wind transition", 0, AV_OPT_TYPE_CONST, {.i64=VDWIND}, 0, 0, FLAGS, .unit = "transition" },
223 { "coverleft", "cover left transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERLEFT}, 0, 0, FLAGS, .unit = "transition" },
224 { "coverright", "cover right transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERRIGHT}, 0, 0, FLAGS, .unit = "transition" },
225 { "coverup", "cover up transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERUP}, 0, 0, FLAGS, .unit = "transition" },
226 { "coverdown", "cover down transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERDOWN}, 0, 0, FLAGS, .unit = "transition" },
227 { "revealleft", "reveal left transition", 0, AV_OPT_TYPE_CONST, {.i64=REVEALLEFT}, 0, 0, FLAGS, .unit = "transition" },
228 { "revealright","reveal right transition",0, AV_OPT_TYPE_CONST, {.i64=REVEALRIGHT},0, 0, FLAGS, .unit = "transition" },
229 { "revealup", "reveal up transition", 0, AV_OPT_TYPE_CONST, {.i64=REVEALUP}, 0, 0, FLAGS, .unit = "transition" },
230 { "revealdown", "reveal down transition", 0, AV_OPT_TYPE_CONST, {.i64=REVEALDOWN}, 0, 0, FLAGS, .unit = "transition" },
231 { "duration", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=1000000}, 0, 60000000, FLAGS },
232 { "offset", "set cross fade start relative to first input stream", OFFSET(offset), AV_OPT_TYPE_DURATION, {.i64=0}, INT64_MIN, INT64_MAX, FLAGS },
233 { "expr", "set expression for custom transition", OFFSET(custom_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
234 { NULL }
235 };
236
237 AVFILTER_DEFINE_CLASS(xfade);
238
239 #define CUSTOM_TRANSITION(name, type, div) \
240 static void custom##name##_transition(AVFilterContext *ctx, \
241 const AVFrame *a, const AVFrame *b, AVFrame *out, \
242 float progress, \
243 int slice_start, int slice_end, int jobnr) \
244 { \
245 XFadeContext *s = ctx->priv; \
246 const int height = slice_end - slice_start; \
247 const int width = out->width; \
248 \
249 double values[VAR_VARS_NB]; \
250 values[VAR_W] = width; \
251 values[VAR_H] = out->height; \
252 values[VAR_PROGRESS] = progress; \
253 \
254 for (int p = 0; p < s->nb_planes; p++) { \
255 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
256 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
257 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
258 \
259 values[VAR_PLANE] = p; \
260 \
261 for (int y = 0; y < height; y++) { \
262 values[VAR_Y] = slice_start + y; \
263 for (int x = 0; x < width; x++) { \
264 values[VAR_X] = x; \
265 values[VAR_A] = xf0[x]; \
266 values[VAR_B] = xf1[x]; \
267 dst[x] = av_expr_eval(s->e, values, s); \
268 } \
269 \
270 dst += out->linesize[p] / div; \
271 xf0 += a->linesize[p] / div; \
272 xf1 += b->linesize[p] / div; \
273 } \
274 } \
275 }
276
277 ✗ CUSTOM_TRANSITION(8, uint8_t, 1)
278 ✗ CUSTOM_TRANSITION(16, uint16_t, 2)
279
280 ✗ static inline float mix(float a, float b, float mix)
281 {
282 ✗ return a * mix + b * (1.f - mix);
283 }
284
285 ✗ static inline float fract(float a)
286 {
287 ✗ return a - floorf(a);
288 }
289
290 ✗ static inline float smoothstep(float edge0, float edge1, float x)
291 {
292 float t;
293
294 ✗ t = av_clipf((x - edge0) / (edge1 - edge0), 0.f, 1.f);
295
296 ✗ return t * t * (3.f - 2.f * t);
297 }
298
299 3888 static inline int wrap_once(int index, int size)
300 {
301
3/4
✓ Branch 0 taken 1728 times.
✓ Branch 1 taken 2160 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2160 times.
3888 return index < 0 ? index + size : index >= size ? index - size : index;
302 }
303
304 #define FADE_TRANSITION(name, type, div) \
305 static void fade##name##_transition(AVFilterContext *ctx, \
306 const AVFrame *a, const AVFrame *b, AVFrame *out, \
307 float progress, \
308 int slice_start, int slice_end, int jobnr) \
309 { \
310 XFadeContext *s = ctx->priv; \
311 const int height = slice_end - slice_start; \
312 const int width = out->width; \
313 \
314 for (int p = 0; p < s->nb_planes; p++) { \
315 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
316 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
317 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
318 \
319 for (int y = 0; y < height; y++) { \
320 for (int x = 0; x < width; x++) { \
321 dst[x] = mix(xf0[x], xf1[x], progress); \
322 } \
323 \
324 dst += out->linesize[p] / div; \
325 xf0 += a->linesize[p] / div; \
326 xf1 += b->linesize[p] / div; \
327 } \
328 } \
329 }
330
331 ✗ FADE_TRANSITION(8, uint8_t, 1)
332 ✗ FADE_TRANSITION(16, uint16_t, 2)
333
334 #define WIPELEFT_TRANSITION(name, type, div) \
335 static void wipeleft##name##_transition(AVFilterContext *ctx, \
336 const AVFrame *a, const AVFrame *b, AVFrame *out, \
337 float progress, \
338 int slice_start, int slice_end, int jobnr) \
339 { \
340 XFadeContext *s = ctx->priv; \
341 const int height = slice_end - slice_start; \
342 const int width = out->width; \
343 const int z = width * progress; \
344 \
345 for (int p = 0; p < s->nb_planes; p++) { \
346 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
347 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
348 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
349 \
350 for (int y = 0; y < height; y++) { \
351 for (int x = 0; x < width; x++) { \
352 dst[x] = x > z ? xf1[x] : xf0[x]; \
353 } \
354 \
355 dst += out->linesize[p] / div; \
356 xf0 += a->linesize[p] / div; \
357 xf1 += b->linesize[p] / div; \
358 } \
359 } \
360 }
361
362 ✗ WIPELEFT_TRANSITION(8, uint8_t, 1)
363 ✗ WIPELEFT_TRANSITION(16, uint16_t, 2)
364
365 #define WIPERIGHT_TRANSITION(name, type, div) \
366 static void wiperight##name##_transition(AVFilterContext *ctx, \
367 const AVFrame *a, const AVFrame *b, AVFrame *out, \
368 float progress, \
369 int slice_start, int slice_end, int jobnr) \
370 { \
371 XFadeContext *s = ctx->priv; \
372 const int height = slice_end - slice_start; \
373 const int width = out->width; \
374 const int z = width * (1.f - progress); \
375 \
376 for (int p = 0; p < s->nb_planes; p++) { \
377 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
378 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
379 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
380 \
381 for (int y = 0; y < height; y++) { \
382 for (int x = 0; x < width; x++) { \
383 dst[x] = x > z ? xf0[x] : xf1[x]; \
384 } \
385 \
386 dst += out->linesize[p] / div; \
387 xf0 += a->linesize[p] / div; \
388 xf1 += b->linesize[p] / div; \
389 } \
390 } \
391 }
392
393 ✗ WIPERIGHT_TRANSITION(8, uint8_t, 1)
394 ✗ WIPERIGHT_TRANSITION(16, uint16_t, 2)
395
396 #define WIPEUP_TRANSITION(name, type, div) \
397 static void wipeup##name##_transition(AVFilterContext *ctx, \
398 const AVFrame *a, const AVFrame *b, AVFrame *out, \
399 float progress, \
400 int slice_start, int slice_end, int jobnr) \
401 { \
402 XFadeContext *s = ctx->priv; \
403 const int height = slice_end - slice_start; \
404 const int width = out->width; \
405 const int z = out->height * progress; \
406 \
407 for (int p = 0; p < s->nb_planes; p++) { \
408 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
409 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
410 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
411 \
412 for (int y = 0; y < height; y++) { \
413 for (int x = 0; x < width; x++) { \
414 dst[x] = slice_start + y > z ? xf1[x] : xf0[x]; \
415 } \
416 \
417 dst += out->linesize[p] / div; \
418 xf0 += a->linesize[p] / div; \
419 xf1 += b->linesize[p] / div; \
420 } \
421 } \
422 }
423
424 ✗ WIPEUP_TRANSITION(8, uint8_t, 1)
425 ✗ WIPEUP_TRANSITION(16, uint16_t, 2)
426
427 #define WIPEDOWN_TRANSITION(name, type, div) \
428 static void wipedown##name##_transition(AVFilterContext *ctx, \
429 const AVFrame *a, const AVFrame *b, AVFrame *out, \
430 float progress, \
431 int slice_start, int slice_end, int jobnr) \
432 { \
433 XFadeContext *s = ctx->priv; \
434 const int height = slice_end - slice_start; \
435 const int width = out->width; \
436 const int z = out->height * (1.f - progress); \
437 \
438 for (int p = 0; p < s->nb_planes; p++) { \
439 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
440 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
441 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
442 \
443 for (int y = 0; y < height; y++) { \
444 for (int x = 0; x < width; x++) { \
445 dst[x] = slice_start + y > z ? xf0[x] : xf1[x]; \
446 } \
447 \
448 dst += out->linesize[p] / div; \
449 xf0 += a->linesize[p] / div; \
450 xf1 += b->linesize[p] / div; \
451 } \
452 } \
453 }
454
455 ✗ WIPEDOWN_TRANSITION(8, uint8_t, 1)
456 ✗ WIPEDOWN_TRANSITION(16, uint16_t, 2)
457
458 #define SLIDELEFT_TRANSITION(name, type, div) \
459 static void slideleft##name##_transition(AVFilterContext *ctx, \
460 const AVFrame *a, const AVFrame *b, AVFrame *out, \
461 float progress, \
462 int slice_start, int slice_end, int jobnr) \
463 { \
464 XFadeContext *s = ctx->priv; \
465 const int height = slice_end - slice_start; \
466 const int width = out->width; \
467 const int z = -progress * width; \
468 \
469 for (int p = 0; p < s->nb_planes; p++) { \
470 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
471 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
472 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
473 \
474 for (int y = 0; y < height; y++) { \
475 for (int x = 0; x < width; x++) { \
476 const int zx = z + x; \
477 const int zz = zx % width + width * (zx % width < 0); \
478 dst[x] = (zx >= 0) && (zx < width) ? xf1[zz] : xf0[zz]; \
479 } \
480 \
481 dst += out->linesize[p] / div; \
482 xf0 += a->linesize[p] / div; \
483 xf1 += b->linesize[p] / div; \
484 } \
485 } \
486 }
487
488
9/10
✓ Branch 0 taken 1920 times.
✓ Branch 1 taken 1536 times.
✓ Branch 2 taken 1920 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3456 times.
✓ Branch 5 taken 432 times.
✓ Branch 6 taken 432 times.
✓ Branch 7 taken 432 times.
✓ Branch 8 taken 432 times.
✓ Branch 9 taken 144 times.
4464 SLIDELEFT_TRANSITION(8, uint8_t, 1)
489 ✗ SLIDELEFT_TRANSITION(16, uint16_t, 2)
490
491 #define SLIDERIGHT_TRANSITION(name, type, div) \
492 static void slideright##name##_transition(AVFilterContext *ctx, \
493 const AVFrame *a, const AVFrame *b, AVFrame *out, \
494 float progress, \
495 int slice_start, int slice_end, int jobnr) \
496 { \
497 XFadeContext *s = ctx->priv; \
498 const int height = slice_end - slice_start; \
499 const int width = out->width; \
500 const int z = progress * width; \
501 \
502 for (int p = 0; p < s->nb_planes; p++) { \
503 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
504 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
505 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
506 \
507 for (int y = 0; y < height; y++) { \
508 for (int x = 0; x < width; x++) { \
509 const int zx = z + x; \
510 const int zz = zx % width + width * (zx % width < 0); \
511 dst[x] = (zx >= 0) && (zx < width) ? xf1[zz] : xf0[zz]; \
512 } \
513 \
514 dst += out->linesize[p] / div; \
515 xf0 += a->linesize[p] / div; \
516 xf1 += b->linesize[p] / div; \
517 } \
518 } \
519 }
520
521 ✗ SLIDERIGHT_TRANSITION(8, uint8_t, 1)
522 ✗ SLIDERIGHT_TRANSITION(16, uint16_t, 2)
523
524 #define SLIDEUP_TRANSITION(name, type, div) \
525 static void slideup##name##_transition(AVFilterContext *ctx, \
526 const AVFrame *a, const AVFrame *b, AVFrame *out, \
527 float progress, \
528 int slice_start, int slice_end, int jobnr) \
529 { \
530 XFadeContext *s = ctx->priv; \
531 const int height = out->height; \
532 const int width = out->width; \
533 const int z = -progress * height; \
534 \
535 for (int p = 0; p < s->nb_planes; p++) { \
536 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
537 \
538 for (int y = slice_start; y < slice_end; y++) { \
539 const int zy = z + y; \
540 const int zz = zy % height + height * (zy % height < 0); \
541 const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \
542 const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \
543 \
544 for (int x = 0; x < width; x++) { \
545 dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \
546 } \
547 \
548 dst += out->linesize[p] / div; \
549 } \
550 } \
551 }
552
553
9/10
✓ Branch 0 taken 1920 times.
✓ Branch 1 taken 1536 times.
✓ Branch 2 taken 1920 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3456 times.
✓ Branch 5 taken 432 times.
✓ Branch 6 taken 432 times.
✓ Branch 7 taken 432 times.
✓ Branch 8 taken 432 times.
✓ Branch 9 taken 144 times.
4464 SLIDEUP_TRANSITION(8, uint8_t, 1)
554
9/10
✓ Branch 0 taken 1920 times.
✓ Branch 1 taken 1536 times.
✓ Branch 2 taken 1920 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3456 times.
✓ Branch 5 taken 432 times.
✓ Branch 6 taken 432 times.
✓ Branch 7 taken 432 times.
✓ Branch 8 taken 432 times.
✓ Branch 9 taken 144 times.
4464 SLIDEUP_TRANSITION(16, uint16_t, 2)
555
556 #define SLIDEDOWN_TRANSITION(name, type, div) \
557 static void slidedown##name##_transition(AVFilterContext *ctx, \
558 const AVFrame *a, const AVFrame *b, AVFrame *out, \
559 float progress, \
560 int slice_start, int slice_end, int jobnr) \
561 { \
562 XFadeContext *s = ctx->priv; \
563 const int height = out->height; \
564 const int width = out->width; \
565 const int z = progress * height; \
566 \
567 for (int p = 0; p < s->nb_planes; p++) { \
568 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
569 \
570 for (int y = slice_start; y < slice_end; y++) { \
571 const int zy = z + y; \
572 const int zz = zy % height + height * (zy % height < 0); \
573 const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \
574 const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \
575 \
576 for (int x = 0; x < width; x++) { \
577 dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \
578 } \
579 \
580 dst += out->linesize[p] / div; \
581 } \
582 } \
583 }
584
585 ✗ SLIDEDOWN_TRANSITION(8, uint8_t, 1)
586 ✗ SLIDEDOWN_TRANSITION(16, uint16_t, 2)
587
588 #define CIRCLECROP_TRANSITION(name, type, div) \
589 static void circlecrop##name##_transition(AVFilterContext *ctx, \
590 const AVFrame *a, const AVFrame *b, AVFrame *out, \
591 float progress, \
592 int slice_start, int slice_end, int jobnr) \
593 { \
594 XFadeContext *s = ctx->priv; \
595 const int width = out->width; \
596 const int height = out->height; \
597 float z = powf(2.f * fabsf(progress - 0.5f), 3.f) * hypotf(width/2, height/2); \
598 \
599 for (int p = 0; p < s->nb_planes; p++) { \
600 const int bg = s->black[p]; \
601 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
602 \
603 for (int y = slice_start; y < slice_end; y++) { \
604 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
605 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
606 \
607 for (int x = 0; x < width; x++) { \
608 float dist = hypotf(x - width / 2, y - height / 2); \
609 int val = progress < 0.5f ? xf1[x] : xf0[x]; \
610 dst[x] = (z < dist) ? bg : val; \
611 } \
612 \
613 dst += out->linesize[p] / div; \
614 } \
615 } \
616 }
617
618 ✗ CIRCLECROP_TRANSITION(8, uint8_t, 1)
619 ✗ CIRCLECROP_TRANSITION(16, uint16_t, 2)
620
621 #define RECTCROP_TRANSITION(name, type, div) \
622 static void rectcrop##name##_transition(AVFilterContext *ctx, \
623 const AVFrame *a, const AVFrame *b, AVFrame *out, \
624 float progress, \
625 int slice_start, int slice_end, int jobnr) \
626 { \
627 XFadeContext *s = ctx->priv; \
628 const int width = out->width; \
629 const int height = out->height; \
630 int zh = fabsf(progress - 0.5f) * height; \
631 int zw = fabsf(progress - 0.5f) * width; \
632 \
633 for (int p = 0; p < s->nb_planes; p++) { \
634 const int bg = s->black[p]; \
635 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
636 \
637 for (int y = slice_start; y < slice_end; y++) { \
638 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
639 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
640 \
641 for (int x = 0; x < width; x++) { \
642 int dist = FFABS(x - width / 2) < zw && \
643 FFABS(y - height / 2) < zh; \
644 int val = progress < 0.5f ? xf1[x] : xf0[x]; \
645 dst[x] = !dist ? bg : val; \
646 } \
647 \
648 dst += out->linesize[p] / div; \
649 } \
650 } \
651 }
652
653 ✗ RECTCROP_TRANSITION(8, uint8_t, 1)
654 ✗ RECTCROP_TRANSITION(16, uint16_t, 2)
655
656 #define DISTANCE_TRANSITION(name, type, div) \
657 static void distance##name##_transition(AVFilterContext *ctx, \
658 const AVFrame *a, const AVFrame *b, AVFrame *out, \
659 float progress, \
660 int slice_start, int slice_end, int jobnr) \
661 { \
662 XFadeContext *s = ctx->priv; \
663 const int width = out->width; \
664 const float max = s->max_value; \
665 \
666 for (int y = slice_start; y < slice_end; y++) { \
667 for (int x = 0; x < width; x++) { \
668 float dist = 0.f; \
669 for (int p = 0; p < s->nb_planes; p++) { \
670 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
671 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
672 \
673 dist += (xf0[x] / max - xf1[x] / max) * \
674 (xf0[x] / max - xf1[x] / max); \
675 } \
676 \
677 dist = sqrtf(dist) <= progress; \
678 for (int p = 0; p < s->nb_planes; p++) { \
679 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
680 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
681 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
682 dst[x] = mix(mix(xf0[x], xf1[x], dist), xf1[x], progress); \
683 } \
684 } \
685 } \
686 }
687
688 ✗ DISTANCE_TRANSITION(8, uint8_t, 1)
689 ✗ DISTANCE_TRANSITION(16, uint16_t, 2)
690
691 #define FADEBLACK_TRANSITION(name, type, div) \
692 static void fadeblack##name##_transition(AVFilterContext *ctx, \
693 const AVFrame *a, const AVFrame *b, AVFrame *out, \
694 float progress, \
695 int slice_start, int slice_end, int jobnr) \
696 { \
697 XFadeContext *s = ctx->priv; \
698 const int height = slice_end - slice_start; \
699 const int width = out->width; \
700 const float phase = 0.2f; \
701 \
702 for (int p = 0; p < s->nb_planes; p++) { \
703 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
704 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
705 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
706 const int bg = s->black[p]; \
707 \
708 for (int y = 0; y < height; y++) { \
709 for (int x = 0; x < width; x++) { \
710 dst[x] = mix(mix(xf0[x], bg, smoothstep(1.f-phase, 1.f, progress)), \
711 mix(bg, xf1[x], smoothstep(phase, 1.f, progress)), \
712 progress); \
713 } \
714 \
715 dst += out->linesize[p] / div; \
716 xf0 += a->linesize[p] / div; \
717 xf1 += b->linesize[p] / div; \
718 } \
719 } \
720 }
721
722 ✗ FADEBLACK_TRANSITION(8, uint8_t, 1)
723 ✗ FADEBLACK_TRANSITION(16, uint16_t, 2)
724
725 #define FADEWHITE_TRANSITION(name, type, div) \
726 static void fadewhite##name##_transition(AVFilterContext *ctx, \
727 const AVFrame *a, const AVFrame *b, AVFrame *out, \
728 float progress, \
729 int slice_start, int slice_end, int jobnr) \
730 { \
731 XFadeContext *s = ctx->priv; \
732 const int height = slice_end - slice_start; \
733 const int width = out->width; \
734 const float phase = 0.2f; \
735 \
736 for (int p = 0; p < s->nb_planes; p++) { \
737 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
738 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
739 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
740 const int bg = s->white[p]; \
741 \
742 for (int y = 0; y < height; y++) { \
743 for (int x = 0; x < width; x++) { \
744 dst[x] = mix(mix(xf0[x], bg, smoothstep(1.f-phase, 1.f, progress)), \
745 mix(bg, xf1[x], smoothstep(phase, 1.f, progress)), \
746 progress); \
747 } \
748 \
749 dst += out->linesize[p] / div; \
750 xf0 += a->linesize[p] / div; \
751 xf1 += b->linesize[p] / div; \
752 } \
753 } \
754 }
755
756 ✗ FADEWHITE_TRANSITION(8, uint8_t, 1)
757 ✗ FADEWHITE_TRANSITION(16, uint16_t, 2)
758
759 #define RADIAL_TRANSITION(name, type, div) \
760 static void radial##name##_transition(AVFilterContext *ctx, \
761 const AVFrame *a, const AVFrame *b, AVFrame *out, \
762 float progress, \
763 int slice_start, int slice_end, int jobnr) \
764 { \
765 XFadeContext *s = ctx->priv; \
766 const int width = out->width; \
767 const int height = out->height; \
768 \
769 for (int y = slice_start; y < slice_end; y++) { \
770 for (int x = 0; x < width; x++) { \
771 const float smooth = atan2f(x - width / 2, y - height / 2) - \
772 (progress - 0.5f) * (M_PI * 2.5f); \
773 for (int p = 0; p < s->nb_planes; p++) { \
774 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
775 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
776 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
777 \
778 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
779 } \
780 } \
781 } \
782 }
783
784 ✗ RADIAL_TRANSITION(8, uint8_t, 1)
785 ✗ RADIAL_TRANSITION(16, uint16_t, 2)
786
787 #define SMOOTHLEFT_TRANSITION(name, type, div) \
788 static void smoothleft##name##_transition(AVFilterContext *ctx, \
789 const AVFrame *a, const AVFrame *b, AVFrame *out, \
790 float progress, \
791 int slice_start, int slice_end, int jobnr) \
792 { \
793 XFadeContext *s = ctx->priv; \
794 const int width = out->width; \
795 const float w = width; \
796 \
797 for (int y = slice_start; y < slice_end; y++) { \
798 for (int x = 0; x < width; x++) { \
799 const float smooth = 1.f + x / w - progress * 2.f; \
800 \
801 for (int p = 0; p < s->nb_planes; p++) { \
802 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
803 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
804 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
805 \
806 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
807 } \
808 } \
809 } \
810 }
811
812 ✗ SMOOTHLEFT_TRANSITION(8, uint8_t, 1)
813 ✗ SMOOTHLEFT_TRANSITION(16, uint16_t, 2)
814
815 #define SMOOTHRIGHT_TRANSITION(name, type, div) \
816 static void smoothright##name##_transition(AVFilterContext *ctx, \
817 const AVFrame *a, const AVFrame *b, AVFrame *out, \
818 float progress, \
819 int slice_start, int slice_end, int jobnr) \
820 { \
821 XFadeContext *s = ctx->priv; \
822 const int width = out->width; \
823 const float w = width; \
824 \
825 for (int y = slice_start; y < slice_end; y++) { \
826 for (int x = 0; x < width; x++) { \
827 const float smooth = 1.f + (w - 1 - x) / w - progress * 2.f; \
828 \
829 for (int p = 0; p < s->nb_planes; p++) { \
830 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
831 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
832 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
833 \
834 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
835 } \
836 } \
837 } \
838 }
839
840 ✗ SMOOTHRIGHT_TRANSITION(8, uint8_t, 1)
841 ✗ SMOOTHRIGHT_TRANSITION(16, uint16_t, 2)
842
843 #define SMOOTHUP_TRANSITION(name, type, div) \
844 static void smoothup##name##_transition(AVFilterContext *ctx, \
845 const AVFrame *a, const AVFrame *b, AVFrame *out, \
846 float progress, \
847 int slice_start, int slice_end, int jobnr) \
848 { \
849 XFadeContext *s = ctx->priv; \
850 const int width = out->width; \
851 const float h = out->height; \
852 \
853 for (int y = slice_start; y < slice_end; y++) { \
854 const float smooth = 1.f + y / h - progress * 2.f; \
855 for (int x = 0; x < width; x++) { \
856 for (int p = 0; p < s->nb_planes; p++) { \
857 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
858 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
859 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
860 \
861 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
862 } \
863 } \
864 } \
865 }
866
867 ✗ SMOOTHUP_TRANSITION(8, uint8_t, 1)
868 ✗ SMOOTHUP_TRANSITION(16, uint16_t, 2)
869
870 #define SMOOTHDOWN_TRANSITION(name, type, div) \
871 static void smoothdown##name##_transition(AVFilterContext *ctx, \
872 const AVFrame *a, const AVFrame *b, AVFrame *out, \
873 float progress, \
874 int slice_start, int slice_end, int jobnr) \
875 { \
876 XFadeContext *s = ctx->priv; \
877 const int width = out->width; \
878 const float h = out->height; \
879 \
880 for (int y = slice_start; y < slice_end; y++) { \
881 const float smooth = 1.f + (h - 1 - y) / h - progress * 2.f; \
882 for (int x = 0; x < width; x++) { \
883 for (int p = 0; p < s->nb_planes; p++) { \
884 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
885 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
886 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
887 \
888 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
889 } \
890 } \
891 } \
892 }
893
894 ✗ SMOOTHDOWN_TRANSITION(8, uint8_t, 1)
895 ✗ SMOOTHDOWN_TRANSITION(16, uint16_t, 2)
896
897 #define CIRCLEOPEN_TRANSITION(name, type, div) \
898 static void circleopen##name##_transition(AVFilterContext *ctx, \
899 const AVFrame *a, const AVFrame *b, AVFrame *out, \
900 float progress, \
901 int slice_start, int slice_end, int jobnr) \
902 { \
903 XFadeContext *s = ctx->priv; \
904 const int width = out->width; \
905 const int height = out->height; \
906 const float z = hypotf(width / 2, height / 2); \
907 const float p = (progress - 0.5f) * 3.f; \
908 \
909 for (int y = slice_start; y < slice_end; y++) { \
910 for (int x = 0; x < width; x++) { \
911 const float smooth = hypotf(x - width / 2, y - height / 2) / z + p; \
912 for (int p = 0; p < s->nb_planes; p++) { \
913 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
914 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
915 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
916 \
917 dst[x] = mix(xf0[x], xf1[x], smoothstep(0.f, 1.f, smooth)); \
918 } \
919 } \
920 } \
921 }
922
923 ✗ CIRCLEOPEN_TRANSITION(8, uint8_t, 1)
924 ✗ CIRCLEOPEN_TRANSITION(16, uint16_t, 2)
925
926 #define CIRCLECLOSE_TRANSITION(name, type, div) \
927 static void circleclose##name##_transition(AVFilterContext *ctx, \
928 const AVFrame *a, const AVFrame *b, AVFrame *out, \
929 float progress, \
930 int slice_start, int slice_end, int jobnr) \
931 { \
932 XFadeContext *s = ctx->priv; \
933 const int width = out->width; \
934 const int height = out->height; \
935 const float z = hypotf(width / 2, height / 2); \
936 const float p = (1.f - progress - 0.5f) * 3.f; \
937 \
938 for (int y = slice_start; y < slice_end; y++) { \
939 for (int x = 0; x < width; x++) { \
940 const float smooth = hypotf(x - width / 2, y - height / 2) / z + p; \
941 for (int p = 0; p < s->nb_planes; p++) { \
942 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
943 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
944 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
945 \
946 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
947 } \
948 } \
949 } \
950 }
951
952 ✗ CIRCLECLOSE_TRANSITION(8, uint8_t, 1)
953 ✗ CIRCLECLOSE_TRANSITION(16, uint16_t, 2)
954
955 #define VERTOPEN_TRANSITION(name, type, div) \
956 static void vertopen##name##_transition(AVFilterContext *ctx, \
957 const AVFrame *a, const AVFrame *b, AVFrame *out, \
958 float progress, \
959 int slice_start, int slice_end, int jobnr) \
960 { \
961 XFadeContext *s = ctx->priv; \
962 const int width = out->width; \
963 const float w2 = out->width / 2.0; \
964 \
965 for (int y = slice_start; y < slice_end; y++) { \
966 for (int x = 0; x < width; x++) { \
967 const float smooth = 2.f - fabsf((x - w2) / w2) - progress * 2.f; \
968 for (int p = 0; p < s->nb_planes; p++) { \
969 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
970 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
971 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
972 \
973 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
974 } \
975 } \
976 } \
977 }
978
979 ✗ VERTOPEN_TRANSITION(8, uint8_t, 1)
980 ✗ VERTOPEN_TRANSITION(16, uint16_t, 2)
981
982 #define VERTCLOSE_TRANSITION(name, type, div) \
983 static void vertclose##name##_transition(AVFilterContext *ctx, \
984 const AVFrame *a, const AVFrame *b, AVFrame *out, \
985 float progress, \
986 int slice_start, int slice_end, int jobnr) \
987 { \
988 XFadeContext *s = ctx->priv; \
989 const int nb_planes = s->nb_planes; \
990 const int width = out->width; \
991 const float w2 = out->width / 2.0; \
992 \
993 for (int y = slice_start; y < slice_end; y++) { \
994 for (int x = 0; x < width; x++) { \
995 const float smooth = 1.f + fabsf((x - w2) / w2) - progress * 2.f; \
996 for (int p = 0; p < nb_planes; p++) { \
997 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
998 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
999 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1000 \
1001 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
1002 } \
1003 } \
1004 } \
1005 }
1006
1007 ✗ VERTCLOSE_TRANSITION(8, uint8_t, 1)
1008 ✗ VERTCLOSE_TRANSITION(16, uint16_t, 2)
1009
1010 #define HORZOPEN_TRANSITION(name, type, div) \
1011 static void horzopen##name##_transition(AVFilterContext *ctx, \
1012 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1013 float progress, \
1014 int slice_start, int slice_end, int jobnr) \
1015 { \
1016 XFadeContext *s = ctx->priv; \
1017 const int nb_planes = s->nb_planes; \
1018 const int width = out->width; \
1019 const float h2 = out->height / 2.0; \
1020 \
1021 for (int y = slice_start; y < slice_end; y++) { \
1022 const float smooth = 2.f - fabsf((y - h2) / h2) - progress * 2.f; \
1023 for (int x = 0; x < width; x++) { \
1024 for (int p = 0; p < nb_planes; p++) { \
1025 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1026 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1027 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1028 \
1029 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
1030 } \
1031 } \
1032 } \
1033 }
1034
1035 ✗ HORZOPEN_TRANSITION(8, uint8_t, 1)
1036 ✗ HORZOPEN_TRANSITION(16, uint16_t, 2)
1037
1038 #define HORZCLOSE_TRANSITION(name, type, div) \
1039 static void horzclose##name##_transition(AVFilterContext *ctx, \
1040 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1041 float progress, \
1042 int slice_start, int slice_end, int jobnr) \
1043 { \
1044 XFadeContext *s = ctx->priv; \
1045 const int nb_planes = s->nb_planes; \
1046 const int width = out->width; \
1047 const float h2 = out->height / 2.0; \
1048 \
1049 for (int y = slice_start; y < slice_end; y++) { \
1050 const float smooth = 1.f + fabsf((y - h2) / h2) - progress * 2.f; \
1051 for (int x = 0; x < width; x++) { \
1052 for (int p = 0; p < nb_planes; p++) { \
1053 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1054 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1055 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1056 \
1057 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
1058 } \
1059 } \
1060 } \
1061 }
1062
1063 ✗ HORZCLOSE_TRANSITION(8, uint8_t, 1)
1064 ✗ HORZCLOSE_TRANSITION(16, uint16_t, 2)
1065
1066 ✗ static float frand(int x, int y)
1067 {
1068 ✗ const float r = sinf(x * 12.9898f + y * 78.233f) * 43758.545f;
1069
1070 ✗ return r - floorf(r);
1071 }
1072
1073 #define DISSOLVE_TRANSITION(name, type, div) \
1074 static void dissolve##name##_transition(AVFilterContext *ctx, \
1075 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1076 float progress, \
1077 int slice_start, int slice_end, int jobnr) \
1078 { \
1079 XFadeContext *s = ctx->priv; \
1080 const int nb_planes = s->nb_planes; \
1081 const int width = out->width; \
1082 \
1083 for (int y = slice_start; y < slice_end; y++) { \
1084 for (int x = 0; x < width; x++) { \
1085 const float smooth = frand(x, y) * 2.f + progress * 2.f - 1.5f; \
1086 for (int p = 0; p < nb_planes; p++) { \
1087 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1088 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1089 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1090 \
1091 dst[x] = smooth >= 0.5f ? xf0[x] : xf1[x]; \
1092 } \
1093 } \
1094 } \
1095 }
1096
1097 ✗ DISSOLVE_TRANSITION(8, uint8_t, 1)
1098 ✗ DISSOLVE_TRANSITION(16, uint16_t, 2)
1099
1100 #define PIXELIZE_TRANSITION(name, type, div) \
1101 static void pixelize##name##_transition(AVFilterContext *ctx, \
1102 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1103 float progress, \
1104 int slice_start, int slice_end, int jobnr) \
1105 { \
1106 XFadeContext *s = ctx->priv; \
1107 const int nb_planes = s->nb_planes; \
1108 const int w = out->width; \
1109 const int h = out->height; \
1110 const float d = fminf(progress, 1.f - progress); \
1111 const float dist = ceilf(d * 50.f) / 50.f; \
1112 const float sqx = 2.f * dist * FFMIN(w, h) / 20.f; \
1113 const float sqy = 2.f * dist * FFMIN(w, h) / 20.f; \
1114 \
1115 for (int y = slice_start; y < slice_end; y++) { \
1116 for (int x = 0; x < w; x++) { \
1117 int sx = dist > 0.f ? FFMIN((floorf(x / sqx) + .5f) * sqx, w - 1) : x; \
1118 int sy = dist > 0.f ? FFMIN((floorf(y / sqy) + .5f) * sqy, h - 1) : y; \
1119 for (int p = 0; p < nb_planes; p++) { \
1120 const type *xf0 = (const type *)(a->data[p] + sy * a->linesize[p]); \
1121 const type *xf1 = (const type *)(b->data[p] + sy * b->linesize[p]); \
1122 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1123 \
1124 dst[x] = mix(xf0[sx], xf1[sx], progress); \
1125 } \
1126 } \
1127 } \
1128 }
1129
1130 ✗ PIXELIZE_TRANSITION(8, uint8_t, 1)
1131 ✗ PIXELIZE_TRANSITION(16, uint16_t, 2)
1132
1133 #define DIAGTL_TRANSITION(name, type, div) \
1134 static void diagtl##name##_transition(AVFilterContext *ctx, \
1135 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1136 float progress, \
1137 int slice_start, int slice_end, int jobnr) \
1138 { \
1139 XFadeContext *s = ctx->priv; \
1140 const int nb_planes = s->nb_planes; \
1141 const int width = out->width; \
1142 const float w = width; \
1143 const float h = out->height; \
1144 \
1145 for (int y = slice_start; y < slice_end; y++) { \
1146 for (int x = 0; x < width; x++) { \
1147 const float smooth = 1.f + x / w * y / h - progress * 2.f; \
1148 \
1149 for (int p = 0; p < nb_planes; p++) { \
1150 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1151 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1152 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1153 \
1154 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
1155 } \
1156 } \
1157 } \
1158 }
1159
1160 ✗ DIAGTL_TRANSITION(8, uint8_t, 1)
1161 ✗ DIAGTL_TRANSITION(16, uint16_t, 2)
1162
1163 #define DIAGTR_TRANSITION(name, type, div) \
1164 static void diagtr##name##_transition(AVFilterContext *ctx, \
1165 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1166 float progress, \
1167 int slice_start, int slice_end, int jobnr) \
1168 { \
1169 XFadeContext *s = ctx->priv; \
1170 const int nb_planes = s->nb_planes; \
1171 const int width = out->width; \
1172 const float w = width; \
1173 const float h = out->height; \
1174 \
1175 for (int y = slice_start; y < slice_end; y++) { \
1176 for (int x = 0; x < width; x++) { \
1177 const float smooth = 1.f + (w - 1 - x) / w * y / h - progress * 2.f; \
1178 \
1179 for (int p = 0; p < nb_planes; p++) { \
1180 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1181 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1182 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1183 \
1184 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
1185 } \
1186 } \
1187 } \
1188 }
1189
1190 ✗ DIAGTR_TRANSITION(8, uint8_t, 1)
1191 ✗ DIAGTR_TRANSITION(16, uint16_t, 2)
1192
1193 #define DIAGBL_TRANSITION(name, type, div) \
1194 static void diagbl##name##_transition(AVFilterContext *ctx, \
1195 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1196 float progress, \
1197 int slice_start, int slice_end, int jobnr) \
1198 { \
1199 XFadeContext *s = ctx->priv; \
1200 const int nb_planes = s->nb_planes; \
1201 const int width = out->width; \
1202 const float w = width; \
1203 const float h = out->height; \
1204 \
1205 for (int y = slice_start; y < slice_end; y++) { \
1206 for (int x = 0; x < width; x++) { \
1207 const float smooth = 1.f + x / w * (h - 1 - y) / h - progress * 2.f; \
1208 \
1209 for (int p = 0; p < nb_planes; p++) { \
1210 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1211 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1212 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1213 \
1214 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
1215 } \
1216 } \
1217 } \
1218 }
1219
1220 ✗ DIAGBL_TRANSITION(8, uint8_t, 1)
1221 ✗ DIAGBL_TRANSITION(16, uint16_t, 2)
1222
1223 #define DIAGBR_TRANSITION(name, type, div) \
1224 static void diagbr##name##_transition(AVFilterContext *ctx, \
1225 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1226 float progress, \
1227 int slice_start, int slice_end, int jobnr) \
1228 { \
1229 XFadeContext *s = ctx->priv; \
1230 const int nb_planes = s->nb_planes; \
1231 const int width = out->width; \
1232 const float w = width; \
1233 const float h = out->height; \
1234 \
1235 for (int y = slice_start; y < slice_end; y++) { \
1236 for (int x = 0; x < width; x++) { \
1237 const float smooth = 1.f + (w - 1 - x) / w * (h - 1 - y) / h - \
1238 progress * 2.f; \
1239 \
1240 for (int p = 0; p < nb_planes; p++) { \
1241 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1242 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1243 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1244 \
1245 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
1246 } \
1247 } \
1248 } \
1249 }
1250
1251 ✗ DIAGBR_TRANSITION(8, uint8_t, 1)
1252 ✗ DIAGBR_TRANSITION(16, uint16_t, 2)
1253
1254 #define HLSLICE_TRANSITION(name, type, div) \
1255 static void hlslice##name##_transition(AVFilterContext *ctx, \
1256 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1257 float progress, \
1258 int slice_start, int slice_end, int jobnr) \
1259 { \
1260 XFadeContext *s = ctx->priv; \
1261 const int nb_planes = s->nb_planes; \
1262 const int width = out->width; \
1263 const float w = width; \
1264 \
1265 for (int y = slice_start; y < slice_end; y++) { \
1266 for (int x = 0; x < width; x++) { \
1267 const float smooth = smoothstep(-0.5f, 0.f, x / w - progress * 1.5f); \
1268 const float ss = smooth <= fract(10.f * x / w) ? 0.f : 1.f; \
1269 \
1270 for (int p = 0; p < nb_planes; p++) { \
1271 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1272 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1273 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1274 \
1275 dst[x] = mix(xf1[x], xf0[x], ss); \
1276 } \
1277 } \
1278 } \
1279 }
1280
1281 ✗ HLSLICE_TRANSITION(8, uint8_t, 1)
1282 ✗ HLSLICE_TRANSITION(16, uint16_t, 2)
1283
1284 #define HRSLICE_TRANSITION(name, type, div) \
1285 static void hrslice##name##_transition(AVFilterContext *ctx, \
1286 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1287 float progress, \
1288 int slice_start, int slice_end, int jobnr) \
1289 { \
1290 XFadeContext *s = ctx->priv; \
1291 const int nb_planes = s->nb_planes; \
1292 const int width = out->width; \
1293 const float w = width; \
1294 \
1295 for (int y = slice_start; y < slice_end; y++) { \
1296 for (int x = 0; x < width; x++) { \
1297 const float xx = (w - 1 - x) / w; \
1298 const float smooth = smoothstep(-0.5f, 0.f, xx - progress * 1.5f); \
1299 const float ss = smooth <= fract(10.f * xx) ? 0.f : 1.f; \
1300 \
1301 for (int p = 0; p < nb_planes; p++) { \
1302 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1303 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1304 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1305 \
1306 dst[x] = mix(xf1[x], xf0[x], ss); \
1307 } \
1308 } \
1309 } \
1310 }
1311
1312 ✗ HRSLICE_TRANSITION(8, uint8_t, 1)
1313 ✗ HRSLICE_TRANSITION(16, uint16_t, 2)
1314
1315 #define VUSLICE_TRANSITION(name, type, div) \
1316 static void vuslice##name##_transition(AVFilterContext *ctx, \
1317 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1318 float progress, \
1319 int slice_start, int slice_end, int jobnr) \
1320 { \
1321 XFadeContext *s = ctx->priv; \
1322 const int nb_planes = s->nb_planes; \
1323 const int width = out->width; \
1324 const float h = out->height; \
1325 \
1326 for (int y = slice_start; y < slice_end; y++) { \
1327 const float smooth = smoothstep(-0.5f, 0.f, y / h - progress * 1.5f); \
1328 const float ss = smooth <= fract(10.f * y / h) ? 0.f : 1.f; \
1329 \
1330 for (int x = 0; x < width; x++) { \
1331 for (int p = 0; p < nb_planes; p++) { \
1332 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1333 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1334 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1335 \
1336 dst[x] = mix(xf1[x], xf0[x], ss); \
1337 } \
1338 } \
1339 } \
1340 }
1341
1342 ✗ VUSLICE_TRANSITION(8, uint8_t, 1)
1343 ✗ VUSLICE_TRANSITION(16, uint16_t, 2)
1344
1345 #define VDSLICE_TRANSITION(name, type, div) \
1346 static void vdslice##name##_transition(AVFilterContext *ctx, \
1347 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1348 float progress, \
1349 int slice_start, int slice_end, int jobnr) \
1350 { \
1351 XFadeContext *s = ctx->priv; \
1352 const int nb_planes = s->nb_planes; \
1353 const int width = out->width; \
1354 const float h = out->height; \
1355 \
1356 for (int y = slice_start; y < slice_end; y++) { \
1357 const float yy = (h - 1 - y) / h; \
1358 const float smooth = smoothstep(-0.5f, 0.f, yy - progress * 1.5f); \
1359 const float ss = smooth <= fract(10.f * yy) ? 0.f : 1.f; \
1360 \
1361 for (int x = 0; x < width; x++) { \
1362 for (int p = 0; p < nb_planes; p++) { \
1363 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1364 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1365 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1366 \
1367 dst[x] = mix(xf1[x], xf0[x], ss); \
1368 } \
1369 } \
1370 } \
1371 }
1372
1373 ✗ VDSLICE_TRANSITION(8, uint8_t, 1)
1374 ✗ VDSLICE_TRANSITION(16, uint16_t, 2)
1375
1376 #define HBLUR_TRANSITION(name, type, div) \
1377 static void hblur##name##_transition(AVFilterContext *ctx, \
1378 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1379 float progress, \
1380 int slice_start, int slice_end, int jobnr) \
1381 { \
1382 XFadeContext *s = ctx->priv; \
1383 const int nb_planes = s->nb_planes; \
1384 const int width = out->width; \
1385 const float prog = progress <= 0.5f ? progress * 2.f : (1.f - progress) * 2.f; \
1386 const int size = 1 + (width / 2) * prog; \
1387 \
1388 for (int y = slice_start; y < slice_end; y++) { \
1389 for (int p = 0; p < nb_planes; p++) { \
1390 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1391 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1392 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1393 float sum0 = 0.f; \
1394 float sum1 = 0.f; \
1395 float cnt = size; \
1396 \
1397 for (int x = 0; x < size; x++) { \
1398 sum0 += xf0[x]; \
1399 sum1 += xf1[x]; \
1400 } \
1401 \
1402 for (int x = 0; x < width; x++) { \
1403 dst[x] = mix(sum0 / cnt, sum1 / cnt, progress); \
1404 \
1405 if (x + size < width) { \
1406 sum0 += xf0[x + size] - xf0[x]; \
1407 sum1 += xf1[x + size] - xf1[x]; \
1408 } else { \
1409 sum0 -= xf0[x]; \
1410 sum1 -= xf1[x]; \
1411 cnt--; \
1412 } \
1413 } \
1414 } \
1415 } \
1416 }
1417
1418 ✗ HBLUR_TRANSITION(8, uint8_t, 1)
1419 ✗ HBLUR_TRANSITION(16, uint16_t, 2)
1420
1421 #define FADEGRAYS_TRANSITION(name, type, div) \
1422 static void fadegrays##name##_transition(AVFilterContext *ctx, \
1423 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1424 float progress, \
1425 int slice_start, int slice_end, int jobnr) \
1426 { \
1427 XFadeContext *s = ctx->priv; \
1428 const int width = out->width; \
1429 const int is_rgb = s->is_rgb; \
1430 const int mid = (s->max_value + 1) / 2; \
1431 const float phase = 0.2f; \
1432 \
1433 for (int y = slice_start; y < slice_end; y++) { \
1434 for (int x = 0; x < width; x++) { \
1435 int bg[2][4]; \
1436 if (is_rgb) { \
1437 bg[0][0] = bg[1][0] = 0; \
1438 for (int p = 0; p < s->nb_planes; p++) { \
1439 const type *xf0 = (const type *)(a->data[p] + \
1440 y * a->linesize[p]); \
1441 const type *xf1 = (const type *)(b->data[p] + \
1442 y * b->linesize[p]); \
1443 if (p == 3) { \
1444 bg[0][3] = xf0[x]; \
1445 bg[1][3] = xf1[x]; \
1446 } else { \
1447 bg[0][0] += xf0[x]; \
1448 bg[1][0] += xf1[x]; \
1449 } \
1450 } \
1451 bg[0][0] = bg[0][0] / 3; \
1452 bg[1][0] = bg[1][0] / 3; \
1453 bg[0][1] = bg[0][2] = bg[0][0]; \
1454 bg[1][1] = bg[1][2] = bg[1][0]; \
1455 } else { \
1456 const type *yf0 = (const type *)(a->data[0] + \
1457 y * a->linesize[0]); \
1458 const type *yf1 = (const type *)(b->data[0] + \
1459 y * b->linesize[0]); \
1460 bg[0][0] = yf0[x]; \
1461 bg[1][0] = yf1[x]; \
1462 if (s->nb_planes == 4) { \
1463 const type *af0 = (const type *)(a->data[3] + \
1464 y * a->linesize[3]); \
1465 const type *af1 = (const type *)(b->data[3] + \
1466 y * b->linesize[3]); \
1467 bg[0][3] = af0[x]; \
1468 bg[1][3] = af1[x]; \
1469 } \
1470 bg[0][1] = bg[1][1] = mid; \
1471 bg[0][2] = bg[1][2] = mid; \
1472 } \
1473 \
1474 for (int p = 0; p < s->nb_planes; p++) { \
1475 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1476 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1477 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1478 \
1479 dst[x] = mix(mix(xf0[x], bg[0][p], \
1480 smoothstep(1.f-phase, 1.f, progress)), \
1481 mix(bg[1][p], xf1[x], smoothstep(phase, 1.f, progress)), \
1482 progress); \
1483 } \
1484 } \
1485 } \
1486 }
1487
1488 ✗ FADEGRAYS_TRANSITION(8, uint8_t, 1)
1489 ✗ FADEGRAYS_TRANSITION(16, uint16_t, 2)
1490
1491 #define WIPETL_TRANSITION(name, type, div) \
1492 static void wipetl##name##_transition(AVFilterContext *ctx, \
1493 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1494 float progress, \
1495 int slice_start, int slice_end, int jobnr) \
1496 { \
1497 XFadeContext *s = ctx->priv; \
1498 const int height = slice_end - slice_start; \
1499 const int width = out->width; \
1500 const int zw = out->width * progress; \
1501 const int zh = out->height * progress; \
1502 \
1503 for (int p = 0; p < s->nb_planes; p++) { \
1504 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
1505 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
1506 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1507 \
1508 for (int y = 0; y < height; y++) { \
1509 for (int x = 0; x < width; x++) { \
1510 dst[x] = slice_start + y <= zh && \
1511 x <= zw ? xf0[x] : xf1[x]; \
1512 } \
1513 \
1514 dst += out->linesize[p] / div; \
1515 xf0 += a->linesize[p] / div; \
1516 xf1 += b->linesize[p] / div; \
1517 } \
1518 } \
1519 }
1520
1521 ✗ WIPETL_TRANSITION(8, uint8_t, 1)
1522 ✗ WIPETL_TRANSITION(16, uint16_t, 2)
1523
1524 #define WIPETR_TRANSITION(name, type, div) \
1525 static void wipetr##name##_transition(AVFilterContext *ctx, \
1526 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1527 float progress, \
1528 int slice_start, int slice_end, int jobnr) \
1529 { \
1530 XFadeContext *s = ctx->priv; \
1531 const int height = slice_end - slice_start; \
1532 const int width = out->width; \
1533 const int zw = width * (1.f - progress); \
1534 const int zh = out->height * progress; \
1535 \
1536 for (int p = 0; p < s->nb_planes; p++) { \
1537 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
1538 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
1539 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1540 \
1541 for (int y = 0; y < height; y++) { \
1542 for (int x = 0; x < width; x++) { \
1543 dst[x] = slice_start + y <= zh && \
1544 x > zw ? xf0[x] : xf1[x]; \
1545 } \
1546 \
1547 dst += out->linesize[p] / div; \
1548 xf0 += a->linesize[p] / div; \
1549 xf1 += b->linesize[p] / div; \
1550 } \
1551 } \
1552 }
1553
1554 ✗ WIPETR_TRANSITION(8, uint8_t, 1)
1555 ✗ WIPETR_TRANSITION(16, uint16_t, 2)
1556
1557 #define WIPEBL_TRANSITION(name, type, div) \
1558 static void wipebl##name##_transition(AVFilterContext *ctx, \
1559 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1560 float progress, \
1561 int slice_start, int slice_end, int jobnr) \
1562 { \
1563 XFadeContext *s = ctx->priv; \
1564 const int height = slice_end - slice_start; \
1565 const int width = out->width; \
1566 const int zw = width * progress; \
1567 const int zh = out->height * (1.f - progress); \
1568 \
1569 for (int p = 0; p < s->nb_planes; p++) { \
1570 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
1571 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
1572 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1573 \
1574 for (int y = 0; y < height; y++) { \
1575 for (int x = 0; x < width; x++) { \
1576 dst[x] = slice_start + y > zh && \
1577 x <= zw ? xf0[x] : xf1[x]; \
1578 } \
1579 \
1580 dst += out->linesize[p] / div; \
1581 xf0 += a->linesize[p] / div; \
1582 xf1 += b->linesize[p] / div; \
1583 } \
1584 } \
1585 }
1586
1587 ✗ WIPEBL_TRANSITION(8, uint8_t, 1)
1588 ✗ WIPEBL_TRANSITION(16, uint16_t, 2)
1589
1590 #define WIPEBR_TRANSITION(name, type, div) \
1591 static void wipebr##name##_transition(AVFilterContext *ctx, \
1592 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1593 float progress, \
1594 int slice_start, int slice_end, int jobnr) \
1595 { \
1596 XFadeContext *s = ctx->priv; \
1597 const int height = slice_end - slice_start; \
1598 const int zh = out->height * (1.f - progress); \
1599 const int width = out->width; \
1600 const int zw = width * (1.f - progress); \
1601 \
1602 for (int p = 0; p < s->nb_planes; p++) { \
1603 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
1604 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
1605 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1606 \
1607 for (int y = 0; y < height; y++) { \
1608 for (int x = 0; x < width; x++) { \
1609 dst[x] = slice_start + y > zh && \
1610 x > zw ? xf0[x] : xf1[x]; \
1611 } \
1612 \
1613 dst += out->linesize[p] / div; \
1614 xf0 += a->linesize[p] / div; \
1615 xf1 += b->linesize[p] / div; \
1616 } \
1617 } \
1618 }
1619
1620 ✗ WIPEBR_TRANSITION(8, uint8_t, 1)
1621 ✗ WIPEBR_TRANSITION(16, uint16_t, 2)
1622
1623 #define SQUEEZEH_TRANSITION(name, type, div) \
1624 static void squeezeh##name##_transition(AVFilterContext *ctx, \
1625 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1626 float progress, \
1627 int slice_start, int slice_end, int jobnr) \
1628 { \
1629 XFadeContext *s = ctx->priv; \
1630 const float h = out->height; \
1631 const int height = slice_end - slice_start; \
1632 const int width = out->width; \
1633 \
1634 for (int p = 0; p < s->nb_planes; p++) { \
1635 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
1636 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1637 \
1638 for (int y = 0; y < height; y++) { \
1639 const float z = progress > 0.f ? \
1640 .5f + ((slice_start + y) / h - .5f) / progress : -1.f; \
1641 \
1642 if (z < 0.f || z > 1.f) { \
1643 for (int x = 0; x < width; x++) \
1644 dst[x] = xf1[x]; \
1645 } else { \
1646 const int yy = lrintf(z * (h - 1.f)); \
1647 const type *xf0 = (const type *)(a->data[p] + yy * a->linesize[p]); \
1648 \
1649 for (int x = 0; x < width; x++) \
1650 dst[x] = xf0[x]; \
1651 } \
1652 \
1653 dst += out->linesize[p] / div; \
1654 xf1 += b->linesize[p] / div; \
1655 } \
1656 } \
1657 }
1658
1659
14/14
✓ Branch 0 taken 408 times.
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 300 times.
✓ Branch 3 taken 204 times.
✓ Branch 4 taken 63 times.
✓ Branch 5 taken 237 times.
✓ Branch 6 taken 2136 times.
✓ Branch 7 taken 267 times.
✓ Branch 8 taken 1896 times.
✓ Branch 9 taken 237 times.
✓ Branch 10 taken 504 times.
✓ Branch 11 taken 504 times.
✓ Branch 12 taken 504 times.
✓ Branch 13 taken 168 times.
5208 SQUEEZEH_TRANSITION(8, uint8_t, 1)
1660 ✗ SQUEEZEH_TRANSITION(16, uint16_t, 2)
1661
1662 #define SQUEEZEV_TRANSITION(name, type, div) \
1663 static void squeezev##name##_transition(AVFilterContext *ctx, \
1664 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1665 float progress, \
1666 int slice_start, int slice_end, int jobnr) \
1667 { \
1668 XFadeContext *s = ctx->priv; \
1669 const int width = out->width; \
1670 const float w = width; \
1671 const int height = slice_end - slice_start; \
1672 \
1673 for (int p = 0; p < s->nb_planes; p++) { \
1674 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
1675 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
1676 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1677 \
1678 for (int y = 0; y < height; y++) { \
1679 for (int x = 0; x < width; x++) { \
1680 const float z = progress > 0.f ? \
1681 .5f + (x / w - .5f) / progress : -1.f; \
1682 \
1683 if (z < 0.f || z > 1.f) { \
1684 dst[x] = xf1[x]; \
1685 } else { \
1686 const int xx = lrintf(z * (w - 1.f)); \
1687 \
1688 dst[x] = xf0[xx]; \
1689 } \
1690 } \
1691 \
1692 dst += out->linesize[p] / div; \
1693 xf0 += a->linesize[p] / div; \
1694 xf1 += b->linesize[p] / div; \
1695 } \
1696 } \
1697 }
1698
1699
12/12
✓ Branch 0 taken 3072 times.
✓ Branch 1 taken 384 times.
✓ Branch 2 taken 2208 times.
✓ Branch 3 taken 1248 times.
✓ Branch 4 taken 504 times.
✓ Branch 5 taken 1704 times.
✓ Branch 6 taken 3456 times.
✓ Branch 7 taken 432 times.
✓ Branch 8 taken 432 times.
✓ Branch 9 taken 432 times.
✓ Branch 10 taken 432 times.
✓ Branch 11 taken 144 times.
4464 SQUEEZEV_TRANSITION(8, uint8_t, 1)
1700 ✗ SQUEEZEV_TRANSITION(16, uint16_t, 2)
1701
1702 ✗ static void zoom(float *u, float *v, float amount)
1703 {
1704 ✗ *u = 0.5f + ((*u - 0.5f) * amount);
1705 ✗ *v = 0.5f + ((*v - 0.5f) * amount);
1706 ✗ }
1707
1708 #define ZOOMIN_TRANSITION(name, type, div) \
1709 static void zoomin##name##_transition(AVFilterContext *ctx, \
1710 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1711 float progress, \
1712 int slice_start, int slice_end, int jobnr) \
1713 { \
1714 XFadeContext *s = ctx->priv; \
1715 const int width = out->width; \
1716 const float w = width; \
1717 const float h = out->height; \
1718 const float zf = smoothstep(0.5f, 1.f, progress); \
1719 \
1720 for (int p = 0; p < s->nb_planes; p++) { \
1721 const type *xf0 = (const type *)(a->data[p]); \
1722 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
1723 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1724 \
1725 for (int y = slice_start; y < slice_end; y++) { \
1726 for (int x = 0; x < width; x++) { \
1727 float zv, u, v; \
1728 int iu, iv; \
1729 \
1730 u = x / w; \
1731 v = y / h; \
1732 zoom(&u, &v, zf); \
1733 iu = ceilf(u * (w - 1)); \
1734 iv = ceilf(v * (h - 1)); \
1735 zv = xf0[iu + iv * a->linesize[p] / div]; \
1736 dst[x] = mix(zv, xf1[x], smoothstep(0.f, 0.5f, progress)); \
1737 } \
1738 dst += out->linesize[p] / div; \
1739 xf1 += b->linesize[p] / div; \
1740 } \
1741 } \
1742 }
1743
1744 ✗ ZOOMIN_TRANSITION(8, uint8_t, 1)
1745 ✗ ZOOMIN_TRANSITION(16, uint16_t, 2)
1746
1747 #define FADEFAST_TRANSITION(name, type, div) \
1748 static void fadefast##name##_transition(AVFilterContext *ctx, \
1749 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1750 float progress, \
1751 int slice_start, int slice_end, int jobnr) \
1752 { \
1753 XFadeContext *s = ctx->priv; \
1754 const int height = slice_end - slice_start; \
1755 const int width = out->width; \
1756 const float imax = 1.f / s->max_value; \
1757 \
1758 for (int p = 0; p < s->nb_planes; p++) { \
1759 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
1760 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
1761 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1762 \
1763 for (int y = 0; y < height; y++) { \
1764 for (int x = 0; x < width; x++) { \
1765 dst[x] = mix(xf0[x], xf1[x], powf(progress, 1.f + \
1766 logf(1.f+FFABS(xf0[x]-xf1[x])*imax)\
1767 )); \
1768 } \
1769 \
1770 dst += out->linesize[p] / div; \
1771 xf0 += a->linesize[p] / div; \
1772 xf1 += b->linesize[p] / div; \
1773 } \
1774 } \
1775 }
1776
1777 ✗ FADEFAST_TRANSITION(8, uint8_t, 1)
1778 ✗ FADEFAST_TRANSITION(16, uint16_t, 2)
1779
1780 #define FADESLOW_TRANSITION(name, type, div) \
1781 static void fadeslow##name##_transition(AVFilterContext *ctx, \
1782 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1783 float progress, \
1784 int slice_start, int slice_end, int jobnr) \
1785 { \
1786 XFadeContext *s = ctx->priv; \
1787 const int height = slice_end - slice_start; \
1788 const int width = out->width; \
1789 const float imax = 1.f / s->max_value; \
1790 \
1791 for (int p = 0; p < s->nb_planes; p++) { \
1792 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
1793 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
1794 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1795 \
1796 for (int y = 0; y < height; y++) { \
1797 for (int x = 0; x < width; x++) { \
1798 dst[x] = mix(xf0[x], xf1[x], powf(progress, 1.f + \
1799 logf(2.f-FFABS(xf0[x]-xf1[x])*imax)\
1800 )); \
1801 } \
1802 \
1803 dst += out->linesize[p] / div; \
1804 xf0 += a->linesize[p] / div; \
1805 xf1 += b->linesize[p] / div; \
1806 } \
1807 } \
1808 }
1809
1810 ✗ FADESLOW_TRANSITION(8, uint8_t, 1)
1811 ✗ FADESLOW_TRANSITION(16, uint16_t, 2)
1812
1813 #define HWIND_TRANSITION(name, z, type, div, expr) \
1814 static void h##z##wind##name##_transition(AVFilterContext *ctx, \
1815 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1816 float progress, \
1817 int slice_start, int slice_end, int jobnr) \
1818 { \
1819 XFadeContext *s = ctx->priv; \
1820 const int width = out->width; \
1821 \
1822 for (int y = slice_start; y < slice_end; y++) { \
1823 const float r = frand(0, y); \
1824 for (int x = 0; x < width; x++) { \
1825 const float fx = expr x / (float)width; \
1826 for (int p = 0; p < s->nb_planes; p++) { \
1827 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1828 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1829 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1830 \
1831 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f,-0.2f, fx * (1.f - 0.2f)\
1832 + 0.2f * r - (1.f - progress)\
1833 * (1.f + 0.2f))); \
1834 } \
1835 } \
1836 } \
1837 }
1838
1839 ✗ HWIND_TRANSITION(8, l, uint8_t, 1, 1.f - )
1840 ✗ HWIND_TRANSITION(16, l, uint16_t, 2, 1.f - )
1841 ✗ HWIND_TRANSITION(8, r, uint8_t, 1, )
1842 ✗ HWIND_TRANSITION(16, r, uint16_t, 2, )
1843
1844 #define VWIND_TRANSITION(name, z, type, div, expr) \
1845 static void v##z##wind##name##_transition(AVFilterContext *ctx, \
1846 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1847 float progress, \
1848 int slice_start, int slice_end, int jobnr) \
1849 { \
1850 XFadeContext *s = ctx->priv; \
1851 const int width = out->width; \
1852 \
1853 for (int y = slice_start; y < slice_end; y++) { \
1854 const float fy = expr y / (float)out->height; \
1855 for (int x = 0; x < width; x++) { \
1856 const float r = frand(x, 0); \
1857 for (int p = 0; p < s->nb_planes; p++) { \
1858 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1859 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1860 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1861 \
1862 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f,-0.2f, fy * (1.f - 0.2f) \
1863 + 0.2f * r - (1.f - progress)\
1864 * (1.f + 0.2f))); \
1865 } \
1866 } \
1867 } \
1868 }
1869
1870 ✗ VWIND_TRANSITION(8, u, uint8_t, 1, 1.f - )
1871 ✗ VWIND_TRANSITION(16, u, uint16_t, 2, 1.f - )
1872 ✗ VWIND_TRANSITION(8, d, uint8_t, 1, )
1873 ✗ VWIND_TRANSITION(16, d, uint16_t, 2, )
1874
1875 #define COVERH_TRANSITION(dir, name, type, div, expr) \
1876 static void cover##dir##name##_transition(AVFilterContext *ctx, \
1877 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1878 float progress, \
1879 int slice_start, int slice_end, int jobnr) \
1880 { \
1881 XFadeContext *s = ctx->priv; \
1882 const int height = slice_end - slice_start; \
1883 const int width = out->width; \
1884 const int z = (expr progress) * width; \
1885 \
1886 for (int p = 0; p < s->nb_planes; p++) { \
1887 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
1888 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
1889 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1890 \
1891 for (int y = 0; y < height; y++) { \
1892 for (int x = 0; x < width; x++) { \
1893 const int zx = z + x; \
1894 const int zz = wrap_once(zx, width); \
1895 dst[x] = (zx >= 0) && (zx < width) ? xf1[zz] : xf0[x]; \
1896 } \
1897 \
1898 dst += out->linesize[p] / div; \
1899 xf0 += a->linesize[p] / div; \
1900 xf1 += b->linesize[p] / div; \
1901 } \
1902 } \
1903 }
1904
1905 ✗ COVERH_TRANSITION(left, 8, uint8_t, 1, -)
1906 ✗ COVERH_TRANSITION(left, 16, uint16_t, 2, -)
1907 ✗ COVERH_TRANSITION(right, 8, uint8_t, 1, )
1908 ✗ COVERH_TRANSITION(right, 16, uint16_t, 2, )
1909
1910 #define COVERV_TRANSITION(dir, name, type, div, expr) \
1911 static void cover##dir##name##_transition(AVFilterContext *ctx, \
1912 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1913 float progress, \
1914 int slice_start, int slice_end, int jobnr) \
1915 { \
1916 XFadeContext *s = ctx->priv; \
1917 const int height = out->height; \
1918 const int width = out->width; \
1919 const int z = (expr progress) * height; \
1920 \
1921 for (int p = 0; p < s->nb_planes; p++) { \
1922 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1923 \
1924 for (int y = slice_start; y < slice_end; y++) { \
1925 const int zy = z + y; \
1926 const int zz = wrap_once(zy, height); \
1927 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1928 const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \
1929 \
1930 for (int x = 0; x < width; x++) \
1931 dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \
1932 \
1933 dst += out->linesize[p] / div; \
1934 } \
1935 } \
1936 }
1937
1938 ✗ COVERV_TRANSITION(up, 8, uint8_t, 1, -)
1939 ✗ COVERV_TRANSITION(up, 16, uint16_t, 2, -)
1940 ✗ COVERV_TRANSITION(down, 8, uint8_t, 1, )
1941 ✗ COVERV_TRANSITION(down, 16, uint16_t, 2, )
1942
1943 #define REVEALH_TRANSITION(dir, name, type, div, expr) \
1944 static void reveal##dir##name##_transition(AVFilterContext *ctx, \
1945 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1946 float progress, \
1947 int slice_start, int slice_end, int jobnr) \
1948 { \
1949 XFadeContext *s = ctx->priv; \
1950 const int height = slice_end - slice_start; \
1951 const int width = out->width; \
1952 const int z = (expr progress) * width; \
1953 \
1954 for (int p = 0; p < s->nb_planes; p++) { \
1955 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
1956 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
1957 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1958 \
1959 for (int y = 0; y < height; y++) { \
1960 for (int x = 0; x < width; x++) { \
1961 const int zx = z + x; \
1962 const int zz = wrap_once(zx, width); \
1963 dst[x] = (zx >= 0) && (zx < width) ? xf1[x] : xf0[zz]; \
1964 } \
1965 \
1966 dst += out->linesize[p] / div; \
1967 xf0 += a->linesize[p] / div; \
1968 xf1 += b->linesize[p] / div; \
1969 } \
1970 } \
1971 }
1972
1973
9/10
✓ Branch 1 taken 1920 times.
✓ Branch 2 taken 1536 times.
✓ Branch 3 taken 1920 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3456 times.
✓ Branch 6 taken 432 times.
✓ Branch 7 taken 432 times.
✓ Branch 8 taken 432 times.
✓ Branch 9 taken 432 times.
✓ Branch 10 taken 144 times.
4464 REVEALH_TRANSITION(left, 8, uint8_t, 1, -)
1974 ✗ REVEALH_TRANSITION(left, 16, uint16_t, 2, -)
1975 ✗ REVEALH_TRANSITION(right, 8, uint8_t, 1, )
1976 ✗ REVEALH_TRANSITION(right, 16, uint16_t, 2, )
1977
1978 #define REVEALV_TRANSITION(dir, name, type, div, expr) \
1979 static void reveal##dir##name##_transition(AVFilterContext *ctx, \
1980 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1981 float progress, \
1982 int slice_start, int slice_end, int jobnr) \
1983 { \
1984 XFadeContext *s = ctx->priv; \
1985 const int height = out->height; \
1986 const int width = out->width; \
1987 const int z = (expr progress) * height; \
1988 \
1989 for (int p = 0; p < s->nb_planes; p++) { \
1990 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
1991 \
1992 for (int y = slice_start; y < slice_end; y++) { \
1993 const int zy = z + y; \
1994 const int zz = wrap_once(zy, height); \
1995 const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \
1996 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1997 \
1998 for (int x = 0; x < width; x++) \
1999 dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \
2000 \
2001 dst += out->linesize[p] / div; \
2002 } \
2003 } \
2004 }
2005
2006
9/10
✓ Branch 1 taken 1920 times.
✓ Branch 2 taken 1536 times.
✓ Branch 3 taken 1920 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3456 times.
✓ Branch 6 taken 432 times.
✓ Branch 7 taken 432 times.
✓ Branch 8 taken 432 times.
✓ Branch 9 taken 432 times.
✓ Branch 10 taken 144 times.
4464 REVEALV_TRANSITION(up, 8, uint8_t, 1, -)
2007 ✗ REVEALV_TRANSITION(up, 16, uint16_t, 2, -)
2008 ✗ REVEALV_TRANSITION(down, 8, uint8_t, 1, )
2009 ✗ REVEALV_TRANSITION(down, 16, uint16_t, 2, )
2010
2011 ✗ static inline double getpix(void *priv, double x, double y, int plane, int nb)
2012 {
2013 ✗ XFadeContext *s = priv;
2014 ✗ AVFrame *in = s->xf[nb];
2015 ✗ const uint8_t *src = in->data[FFMIN(plane, s->nb_planes - 1)];
2016 ✗ int linesize = in->linesize[FFMIN(plane, s->nb_planes - 1)];
2017 ✗ const int w = in->width;
2018 ✗ const int h = in->height;
2019
2020 int xi, yi;
2021
2022 ✗ xi = av_clipd(x, 0, w - 1);
2023 ✗ yi = av_clipd(y, 0, h - 1);
2024
2025 ✗ if (s->depth > 8) {
2026 ✗ const uint16_t *src16 = (const uint16_t*)src;
2027
2028 ✗ linesize /= 2;
2029 ✗ return src16[xi + yi * linesize];
2030 } else {
2031 ✗ return src[xi + yi * linesize];
2032 }
2033 }
2034
2035 ✗ static double a0(void *priv, double x, double y) { return getpix(priv, x, y, 0, 0); }
2036 ✗ static double a1(void *priv, double x, double y) { return getpix(priv, x, y, 1, 0); }
2037 ✗ static double a2(void *priv, double x, double y) { return getpix(priv, x, y, 2, 0); }
2038 ✗ static double a3(void *priv, double x, double y) { return getpix(priv, x, y, 3, 0); }
2039
2040 ✗ static double b0(void *priv, double x, double y) { return getpix(priv, x, y, 0, 1); }
2041 ✗ static double b1(void *priv, double x, double y) { return getpix(priv, x, y, 1, 1); }
2042 ✗ static double b2(void *priv, double x, double y) { return getpix(priv, x, y, 2, 1); }
2043 ✗ static double b3(void *priv, double x, double y) { return getpix(priv, x, y, 3, 1); }
2044
2045 8 static int config_output(AVFilterLink *outlink)
2046 {
2047 8 AVFilterContext *ctx = outlink->src;
2048 8 AVFilterLink *inlink0 = ctx->inputs[0];
2049 8 AVFilterLink *inlink1 = ctx->inputs[1];
2050 8 FilterLink *inl0 = ff_filter_link(inlink0);
2051 8 FilterLink *inl1 = ff_filter_link(inlink1);
2052 8 FilterLink *ol = ff_filter_link(outlink);
2053 8 XFadeContext *s = ctx->priv;
2054 8 const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink0->format);
2055
2056
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (inlink0->w != inlink1->w || inlink0->h != inlink1->h) {
2057 ✗ av_log(ctx, AV_LOG_ERROR, "First input link %s parameters "
2058 "(size %dx%d) do not match the corresponding "
2059 "second input link %s parameters (size %dx%d)\n",
2060 ✗ ctx->input_pads[0].name, inlink0->w, inlink0->h,
2061 ✗ ctx->input_pads[1].name, inlink1->w, inlink1->h);
2062 ✗ return AVERROR(EINVAL);
2063 }
2064
2065
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (inlink0->time_base.num != inlink1->time_base.num ||
2066
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 inlink0->time_base.den != inlink1->time_base.den) {
2067 ✗ av_log(ctx, AV_LOG_ERROR, "First input link %s timebase "
2068 "(%d/%d) do not match the corresponding "
2069 "second input link %s timebase (%d/%d)\n",
2070 ✗ ctx->input_pads[0].name, inlink0->time_base.num, inlink0->time_base.den,
2071 ✗ ctx->input_pads[1].name, inlink1->time_base.num, inlink1->time_base.den);
2072 ✗ return AVERROR(EINVAL);
2073 }
2074
2075
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (!inl0->frame_rate.num || !inl0->frame_rate.den) {
2076 ✗ av_log(ctx, AV_LOG_ERROR, "The inputs needs to be a constant frame rate; "
2077 "current rate of %d/%d is invalid\n", inl0->frame_rate.num, inl0->frame_rate.den);
2078 ✗ return AVERROR(EINVAL);
2079 }
2080
2081
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (inl0->frame_rate.num != inl1->frame_rate.num ||
2082
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 inl0->frame_rate.den != inl1->frame_rate.den) {
2083 ✗ av_log(ctx, AV_LOG_ERROR, "First input link %s frame rate "
2084 "(%d/%d) do not match the corresponding "
2085 "second input link %s frame rate (%d/%d)\n",
2086 ✗ ctx->input_pads[0].name, inl0->frame_rate.num, inl0->frame_rate.den,
2087 ✗ ctx->input_pads[1].name, inl1->frame_rate.num, inl1->frame_rate.den);
2088 ✗ return AVERROR(EINVAL);
2089 }
2090
2091 8 outlink->w = inlink0->w;
2092 8 outlink->h = inlink0->h;
2093 8 outlink->time_base = inlink0->time_base;
2094 8 outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio;
2095 8 ol->frame_rate = inl0->frame_rate;
2096
2097 8 s->depth = pix_desc->comp[0].depth;
2098 8 s->is_rgb = !!(pix_desc->flags & AV_PIX_FMT_FLAG_RGB);
2099 8 s->nb_planes = av_pix_fmt_count_planes(inlink0->format);
2100 8 s->max_value = (1 << s->depth) - 1;
2101 8 s->black[0] = 0;
2102
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 s->black[1] = s->black[2] = s->is_rgb ? 0 : s->max_value / 2;
2103 8 s->black[3] = s->max_value;
2104 8 s->white[0] = s->white[3] = s->max_value;
2105
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 s->white[1] = s->white[2] = s->is_rgb ? s->max_value : s->max_value / 2;
2106
2107 8 s->start_pts = s->inputs_offset_pts = AV_NOPTS_VALUE;
2108
2109 8 s->duration_pts = av_rescale_q(s->duration, AV_TIME_BASE_Q, outlink->time_base);
2110
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (!s->duration_pts)
2111 1 s->duration_pts = 1;
2112
2113
6/60
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✓ Branch 42 taken 2 times.
✓ Branch 43 taken 1 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✓ Branch 55 taken 1 times.
✗ Branch 56 not taken.
✓ Branch 57 taken 1 times.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
8 switch (s->transition) {
2114 ✗ case CUSTOM: s->transitionf = s->depth <= 8 ? custom8_transition : custom16_transition; break;
2115 ✗ case FADE: s->transitionf = s->depth <= 8 ? fade8_transition : fade16_transition; break;
2116 ✗ case WIPELEFT: s->transitionf = s->depth <= 8 ? wipeleft8_transition : wipeleft16_transition; break;
2117 ✗ case WIPERIGHT: s->transitionf = s->depth <= 8 ? wiperight8_transition : wiperight16_transition; break;
2118 ✗ case WIPEUP: s->transitionf = s->depth <= 8 ? wipeup8_transition : wipeup16_transition; break;
2119 ✗ case WIPEDOWN: s->transitionf = s->depth <= 8 ? wipedown8_transition : wipedown16_transition; break;
2120
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 case SLIDELEFT: s->transitionf = s->depth <= 8 ? slideleft8_transition : slideleft16_transition; break;
2121 ✗ case SLIDERIGHT: s->transitionf = s->depth <= 8 ? slideright8_transition : slideright16_transition; break;
2122
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 case SLIDEUP: s->transitionf = s->depth <= 8 ? slideup8_transition : slideup16_transition; break;
2123 ✗ case SLIDEDOWN: s->transitionf = s->depth <= 8 ? slidedown8_transition : slidedown16_transition; break;
2124 ✗ case CIRCLECROP: s->transitionf = s->depth <= 8 ? circlecrop8_transition : circlecrop16_transition; break;
2125 ✗ case RECTCROP: s->transitionf = s->depth <= 8 ? rectcrop8_transition : rectcrop16_transition; break;
2126 ✗ case DISTANCE: s->transitionf = s->depth <= 8 ? distance8_transition : distance16_transition; break;
2127 ✗ case FADEBLACK: s->transitionf = s->depth <= 8 ? fadeblack8_transition : fadeblack16_transition; break;
2128 ✗ case FADEWHITE: s->transitionf = s->depth <= 8 ? fadewhite8_transition : fadewhite16_transition; break;
2129 ✗ case RADIAL: s->transitionf = s->depth <= 8 ? radial8_transition : radial16_transition; break;
2130 ✗ case SMOOTHLEFT: s->transitionf = s->depth <= 8 ? smoothleft8_transition : smoothleft16_transition; break;
2131 ✗ case SMOOTHRIGHT:s->transitionf = s->depth <= 8 ? smoothright8_transition: smoothright16_transition;break;
2132 ✗ case SMOOTHUP: s->transitionf = s->depth <= 8 ? smoothup8_transition : smoothup16_transition; break;
2133 ✗ case SMOOTHDOWN: s->transitionf = s->depth <= 8 ? smoothdown8_transition : smoothdown16_transition; break;
2134 ✗ case CIRCLEOPEN: s->transitionf = s->depth <= 8 ? circleopen8_transition : circleopen16_transition; break;
2135 ✗ case CIRCLECLOSE:s->transitionf = s->depth <= 8 ? circleclose8_transition: circleclose16_transition;break;
2136 ✗ case VERTOPEN: s->transitionf = s->depth <= 8 ? vertopen8_transition : vertopen16_transition; break;
2137 ✗ case VERTCLOSE: s->transitionf = s->depth <= 8 ? vertclose8_transition : vertclose16_transition; break;
2138 ✗ case HORZOPEN: s->transitionf = s->depth <= 8 ? horzopen8_transition : horzopen16_transition; break;
2139 ✗ case HORZCLOSE: s->transitionf = s->depth <= 8 ? horzclose8_transition : horzclose16_transition; break;
2140 ✗ case DISSOLVE: s->transitionf = s->depth <= 8 ? dissolve8_transition : dissolve16_transition; break;
2141 ✗ case PIXELIZE: s->transitionf = s->depth <= 8 ? pixelize8_transition : pixelize16_transition; break;
2142 ✗ case DIAGTL: s->transitionf = s->depth <= 8 ? diagtl8_transition : diagtl16_transition; break;
2143 ✗ case DIAGTR: s->transitionf = s->depth <= 8 ? diagtr8_transition : diagtr16_transition; break;
2144 ✗ case DIAGBL: s->transitionf = s->depth <= 8 ? diagbl8_transition : diagbl16_transition; break;
2145 ✗ case DIAGBR: s->transitionf = s->depth <= 8 ? diagbr8_transition : diagbr16_transition; break;
2146 ✗ case HLSLICE: s->transitionf = s->depth <= 8 ? hlslice8_transition : hlslice16_transition; break;
2147 ✗ case HRSLICE: s->transitionf = s->depth <= 8 ? hrslice8_transition : hrslice16_transition; break;
2148 ✗ case VUSLICE: s->transitionf = s->depth <= 8 ? vuslice8_transition : vuslice16_transition; break;
2149 ✗ case VDSLICE: s->transitionf = s->depth <= 8 ? vdslice8_transition : vdslice16_transition; break;
2150 ✗ case HBLUR: s->transitionf = s->depth <= 8 ? hblur8_transition : hblur16_transition; break;
2151 ✗ case FADEGRAYS: s->transitionf = s->depth <= 8 ? fadegrays8_transition : fadegrays16_transition; break;
2152 ✗ case WIPETL: s->transitionf = s->depth <= 8 ? wipetl8_transition : wipetl16_transition; break;
2153 ✗ case WIPETR: s->transitionf = s->depth <= 8 ? wipetr8_transition : wipetr16_transition; break;
2154 ✗ case WIPEBL: s->transitionf = s->depth <= 8 ? wipebl8_transition : wipebl16_transition; break;
2155 ✗ case WIPEBR: s->transitionf = s->depth <= 8 ? wipebr8_transition : wipebr16_transition; break;
2156
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 case SQUEEZEH: s->transitionf = s->depth <= 8 ? squeezeh8_transition : squeezeh16_transition; break;
2157
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 case SQUEEZEV: s->transitionf = s->depth <= 8 ? squeezev8_transition : squeezev16_transition; break;
2158 ✗ case ZOOMIN: s->transitionf = s->depth <= 8 ? zoomin8_transition : zoomin16_transition; break;
2159 ✗ case FADEFAST: s->transitionf = s->depth <= 8 ? fadefast8_transition : fadefast16_transition; break;
2160 ✗ case FADESLOW: s->transitionf = s->depth <= 8 ? fadeslow8_transition : fadeslow16_transition; break;
2161 ✗ case HLWIND: s->transitionf = s->depth <= 8 ? hlwind8_transition : hlwind16_transition; break;
2162 ✗ case HRWIND: s->transitionf = s->depth <= 8 ? hrwind8_transition : hrwind16_transition; break;
2163 ✗ case VUWIND: s->transitionf = s->depth <= 8 ? vuwind8_transition : vuwind16_transition; break;
2164 ✗ case VDWIND: s->transitionf = s->depth <= 8 ? vdwind8_transition : vdwind16_transition; break;
2165 ✗ case COVERLEFT: s->transitionf = s->depth <= 8 ? coverleft8_transition : coverleft16_transition; break;
2166 ✗ case COVERRIGHT: s->transitionf = s->depth <= 8 ? coverright8_transition : coverright16_transition; break;
2167 ✗ case COVERUP: s->transitionf = s->depth <= 8 ? coverup8_transition : coverup16_transition; break;
2168 ✗ case COVERDOWN: s->transitionf = s->depth <= 8 ? coverdown8_transition : coverdown16_transition; break;
2169
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 case REVEALLEFT: s->transitionf = s->depth <= 8 ? revealleft8_transition : revealleft16_transition; break;
2170 ✗ case REVEALRIGHT:s->transitionf = s->depth <= 8 ? revealright8_transition: revealright16_transition;break;
2171
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 case REVEALUP: s->transitionf = s->depth <= 8 ? revealup8_transition : revealup16_transition; break;
2172 ✗ case REVEALDOWN: s->transitionf = s->depth <= 8 ? revealdown8_transition : revealdown16_transition; break;
2173 ✗ default: return AVERROR_BUG;
2174 }
2175
2176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (s->transition == CUSTOM) {
2177 static const char *const func2_names[] = {
2178 "a0", "a1", "a2", "a3",
2179 "b0", "b1", "b2", "b3",
2180 NULL
2181 };
2182 ✗ double (*func2[])(void *, double, double) = {
2183 a0, a1, a2, a3,
2184 b0, b1, b2, b3,
2185 NULL };
2186 int ret;
2187
2188 ✗ if (!s->custom_str)
2189 ✗ return AVERROR(EINVAL);
2190 ✗ ret = av_expr_parse(&s->e, s->custom_str, var_names,
2191 NULL, NULL, func2_names, func2, 0, ctx);
2192 ✗ if (ret < 0)
2193 ✗ return ret;
2194 }
2195
2196 8 return 0;
2197 }
2198
2199 1032 static int xfade_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
2200 {
2201 1032 XFadeContext *s = ctx->priv;
2202 1032 AVFilterLink *outlink = ctx->outputs[0];
2203 1032 ThreadData *td = arg;
2204 1032 int slice_start = ff_slice_pos(outlink->h, jobnr, nb_jobs);
2205 1032 int slice_end = ff_slice_pos(outlink->h, jobnr + 1, nb_jobs);
2206
2207 1032 s->transitionf(ctx, td->xf[0], td->xf[1], td->out, td->progress, slice_start, slice_end, jobnr);
2208
2209 1032 return 0;
2210 }
2211
2212 129 static int xfade_frame(AVFilterContext *ctx, AVFrame *a, AVFrame *b)
2213 {
2214 129 XFadeContext *s = ctx->priv;
2215 129 AVFilterLink *outlink = ctx->outputs[0];
2216 129 float progress = av_clipf(1.f - ((float)(s->pts - s->start_pts) / s->duration_pts), 0.f, 1.f);
2217 ThreadData td;
2218 AVFrame *out;
2219
2220 129 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
2221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 129 times.
129 if (!out)
2222 ✗ return AVERROR(ENOMEM);
2223 129 av_frame_copy_props(out, a);
2224
2225 129 td.xf[0] = a, td.xf[1] = b, td.out = out, td.progress = progress;
2226 129 ff_filter_execute(ctx, xfade_slice, &td, NULL,
2227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 129 times.
129 FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
2228
2229 129 out->pts = s->pts;
2230
2231 129 return ff_filter_frame(outlink, out);
2232 }
2233
2234 270 static int forward_frame(XFadeContext *s,
2235 AVFilterLink *inlink, AVFilterLink *outlink)
2236 {
2237 int64_t status_pts;
2238 270 int ret = 0, status;
2239 270 AVFrame *frame = NULL;
2240
2241 270 ret = ff_inlink_consume_frame(inlink, &frame);
2242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 270 times.
270 if (ret < 0)
2243 ✗ return ret;
2244
2245
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 143 times.
270 if (ret > 0) {
2246 // If we do not have an offset yet, it's because we
2247 // never got a first input. Just offset to 0
2248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
127 if (s->inputs_offset_pts == AV_NOPTS_VALUE)
2249 ✗ s->inputs_offset_pts = -frame->pts;
2250
2251 // We got a frame, nothing to do other than adjusting the timestamp
2252 127 frame->pts += s->inputs_offset_pts;
2253 127 return ff_filter_frame(outlink, frame);
2254 }
2255
2256 // Forward status with our timestamp
2257
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 135 times.
143 if (ff_inlink_acknowledge_status(inlink, &status, &status_pts)) {
2258
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (s->inputs_offset_pts == AV_NOPTS_VALUE)
2259 ✗ s->inputs_offset_pts = -status_pts;
2260
2261 8 ff_outlink_set_status(outlink, status, status_pts + s->inputs_offset_pts);
2262 8 return 0;
2263 }
2264
2265 // No frame available, request one if needed
2266
1/2
✓ Branch 1 taken 135 times.
✗ Branch 2 not taken.
135 if (ff_outlink_frame_wanted(outlink))
2267 135 ff_inlink_request_frame(inlink);
2268
2269 135 return 0;
2270 }
2271
2272 785 static int xfade_activate(AVFilterContext *avctx)
2273 {
2274 785 XFadeContext *s = avctx->priv;
2275 785 AVFilterLink *in_a = avctx->inputs[0];
2276 785 AVFilterLink *in_b = avctx->inputs[1];
2277 785 AVFilterLink *outlink = avctx->outputs[0];
2278 int64_t status_pts;
2279
2280
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 785 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
785 FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, avctx);
2281
2282 // Check if we already transitioned or first input ended prematurely,
2283 // in which case just forward the frames from second input with adjusted
2284 // timestamps until EOF.
2285
3/4
✓ Branch 0 taken 270 times.
✓ Branch 1 taken 515 times.
✓ Branch 2 taken 270 times.
✗ Branch 3 not taken.
785 if (s->status[0] && !s->status[1])
2286 270 return forward_frame(s, in_b, outlink);
2287
2288 // We did not finish transitioning yet and the first stream
2289 // did not end either, so check if there are more frames to consume.
2290
2/2
✓ Branch 1 taken 322 times.
✓ Branch 2 taken 193 times.
515 if (ff_inlink_check_available_frame(in_a)) {
2291 322 AVFrame *peeked_frame = ff_inlink_peek_frame(in_a, 0);
2292 322 s->pts = peeked_frame->pts;
2293
2294
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 314 times.
322 if (s->start_pts == AV_NOPTS_VALUE)
2295 8 s->start_pts =
2296 8 s->pts + av_rescale_q(s->offset, AV_TIME_BASE_Q, in_a->time_base);
2297
2298 // Check if we are not yet transitioning, in which case
2299 // just request and forward the input frame.
2300
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 258 times.
322 if (s->start_pts > s->pts) {
2301 int ret;
2302 64 s->passthrough = 1;
2303 64 ret = ff_inlink_consume_frame(in_a, &s->xf[0]);
2304
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64 times.
64 if (ret < 0)
2305 ✗ return ret;
2306 64 return ff_filter_frame(outlink, s->xf[0]);
2307 }
2308 258 s->passthrough = 0;
2309
2310 // We are transitioning, so we need a frame from second input
2311
2/2
✓ Branch 1 taken 129 times.
✓ Branch 2 taken 129 times.
258 if (ff_inlink_check_available_frame(in_b)) {
2312 int ret;
2313 129 ret = ff_inlink_consume_frame(avctx->inputs[0], &s->xf[0]);
2314
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 129 times.
129 if (ret < 0)
2315 ✗ return ret;
2316 129 ret = ff_inlink_consume_frame(avctx->inputs[1], &s->xf[1]);
2317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 129 times.
129 if (ret < 0) {
2318 ✗ av_frame_free(&s->xf[0]);
2319 ✗ return ret;
2320 }
2321
2322 // Calculate PTS offset to first input
2323
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 121 times.
129 if (s->inputs_offset_pts == AV_NOPTS_VALUE)
2324 8 s->inputs_offset_pts = s->pts - s->xf[1]->pts;
2325
2326 // Check if we finished transitioning, in which case we
2327 // report back EOF to first input as it is no longer needed.
2328
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 121 times.
129 if (s->pts - s->start_pts > s->duration_pts) {
2329 8 s->status[0] = AVERROR_EOF;
2330 8 ff_inlink_set_status(in_a, AVERROR_EOF);
2331 8 s->passthrough = 1;
2332 }
2333 129 ret = xfade_frame(avctx, s->xf[0], s->xf[1]);
2334 129 av_frame_free(&s->xf[0]);
2335 129 av_frame_free(&s->xf[1]);
2336 129 return ret;
2337 }
2338
2339 // We did not get a frame from second input, check its status.
2340
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 129 times.
129 if (ff_inlink_acknowledge_status(in_b, &s->status[1], &status_pts)) {
2341 // We should transition, but second input is EOF so just report EOF output now.
2342 ✗ ff_outlink_set_status(outlink, s->status[1], s->pts);
2343 ✗ return 0;
2344 }
2345
2346 // We did not get a frame for second input but no EOF either, so just request more.
2347
1/2
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
129 if (ff_outlink_frame_wanted(outlink)) {
2348 129 ff_inlink_request_frame(in_b);
2349 129 return 0;
2350 }
2351 }
2352
2353 // We did not get a frame from first input, check its status.
2354
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 193 times.
193 if (ff_inlink_acknowledge_status(in_a, &s->status[0], &status_pts)) {
2355 // No more frames from first input, do not report EOF though, we will just
2356 // forward the second input frames in the next activate calls.
2357 ✗ s->passthrough = 1;
2358 ✗ ff_filter_set_ready(avctx, 100);
2359 ✗ return 0;
2360 }
2361
2362 // We have no frames yet from first input and no EOF, so request some.
2363
1/2
✓ Branch 1 taken 193 times.
✗ Branch 2 not taken.
193 if (ff_outlink_frame_wanted(outlink)) {
2364 193 ff_inlink_request_frame(in_a);
2365 193 return 0;
2366 }
2367
2368 ✗ return FFERROR_NOT_READY;
2369 }
2370
2371 72 static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h)
2372 {
2373 72 XFadeContext *s = inlink->dst->priv;
2374
2375 72 return s->passthrough ?
2376
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 50 times.
122 ff_null_get_video_buffer (inlink, w, h) :
2377 50 ff_default_get_video_buffer(inlink, w, h);
2378 }
2379
2380 static const AVFilterPad xfade_inputs[] = {
2381 {
2382 .name = "main",
2383 .type = AVMEDIA_TYPE_VIDEO,
2384 .get_buffer.video = get_video_buffer,
2385 },
2386 {
2387 .name = "xfade",
2388 .type = AVMEDIA_TYPE_VIDEO,
2389 .get_buffer.video = get_video_buffer,
2390 },
2391 };
2392
2393 static const AVFilterPad xfade_outputs[] = {
2394 {
2395 .name = "default",
2396 .type = AVMEDIA_TYPE_VIDEO,
2397 .config_props = config_output,
2398 },
2399 };
2400
2401 const FFFilter ff_vf_xfade = {
2402 .p.name = "xfade",
2403 .p.description = NULL_IF_CONFIG_SMALL("Cross fade one video with another video."),
2404 .p.priv_class = &xfade_class,
2405 .p.flags = AVFILTER_FLAG_SLICE_THREADS,
2406 .priv_size = sizeof(XFadeContext),
2407 .activate = xfade_activate,
2408 .uninit = uninit,
2409 FILTER_INPUTS(xfade_inputs),
2410 FILTER_OUTPUTS(xfade_outputs),
2411 FILTER_PIXFMTS_ARRAY(pix_fmts),
2412 };
2413