FFmpeg coverage


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