FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_xfade.c
Date: 2024-04-19 17:50:32
Exec Total Coverage
Lines: 0 361 0.0%
Functions: 0 139 0.0%
Branches: 0 1250 0.0%

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