You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1884 lines
122KB

  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. #include "libavutil/imgutils.h"
  21. #include "libavutil/eval.h"
  22. #include "libavutil/opt.h"
  23. #include "libavutil/pixfmt.h"
  24. #include "avfilter.h"
  25. #include "formats.h"
  26. #include "internal.h"
  27. #include "filters.h"
  28. #include "video.h"
  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. NB_TRANSITIONS,
  73. };
  74. typedef struct XFadeContext {
  75. const AVClass *class;
  76. int transition;
  77. int64_t duration;
  78. int64_t offset;
  79. char *custom_str;
  80. int nb_planes;
  81. int depth;
  82. int is_rgb;
  83. int64_t duration_pts;
  84. int64_t offset_pts;
  85. int64_t first_pts;
  86. int64_t last_pts;
  87. int64_t pts;
  88. int xfade_is_over;
  89. int need_second;
  90. int eof[2];
  91. AVFrame *xf[2];
  92. int max_value;
  93. uint16_t black[4];
  94. uint16_t white[4];
  95. void (*transitionf)(AVFilterContext *ctx, const AVFrame *a, const AVFrame *b, AVFrame *out, float progress,
  96. int slice_start, int slice_end, int jobnr);
  97. AVExpr *e;
  98. } XFadeContext;
  99. static const char *const var_names[] = { "X", "Y", "W", "H", "A", "B", "PLANE", "P", NULL };
  100. enum { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_A, VAR_B, VAR_PLANE, VAR_PROGRESS, VAR_VARS_NB };
  101. typedef struct ThreadData {
  102. const AVFrame *xf[2];
  103. AVFrame *out;
  104. float progress;
  105. } ThreadData;
  106. static int query_formats(AVFilterContext *ctx)
  107. {
  108. static const enum AVPixelFormat pix_fmts[] = {
  109. AV_PIX_FMT_YUVA444P,
  110. AV_PIX_FMT_YUVJ444P,
  111. AV_PIX_FMT_YUV444P,
  112. AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GRAY8,
  113. AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_GBRP9,
  114. AV_PIX_FMT_YUV444P10,
  115. AV_PIX_FMT_YUVA444P10,
  116. AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GRAY10,
  117. AV_PIX_FMT_YUV444P12,
  118. AV_PIX_FMT_YUVA444P12,
  119. AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GRAY12,
  120. AV_PIX_FMT_YUV444P14, AV_PIX_FMT_GBRP14,
  121. AV_PIX_FMT_YUV444P16,
  122. AV_PIX_FMT_YUVA444P16,
  123. AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GRAY16,
  124. AV_PIX_FMT_NONE
  125. };
  126. AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
  127. if (!fmts_list)
  128. return AVERROR(ENOMEM);
  129. return ff_set_common_formats(ctx, fmts_list);
  130. }
  131. static av_cold void uninit(AVFilterContext *ctx)
  132. {
  133. XFadeContext *s = ctx->priv;
  134. av_expr_free(s->e);
  135. }
  136. #define OFFSET(x) offsetof(XFadeContext, x)
  137. #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
  138. static const AVOption xfade_options[] = {
  139. { "transition", "set cross fade transition", OFFSET(transition), AV_OPT_TYPE_INT, {.i64=FADE}, -1, NB_TRANSITIONS-1, FLAGS, "transition" },
  140. { "custom", "custom transition", 0, AV_OPT_TYPE_CONST, {.i64=CUSTOM}, 0, 0, FLAGS, "transition" },
  141. { "fade", "fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADE}, 0, 0, FLAGS, "transition" },
  142. { "wipeleft", "wipe left transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPELEFT}, 0, 0, FLAGS, "transition" },
  143. { "wiperight", "wipe right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPERIGHT}, 0, 0, FLAGS, "transition" },
  144. { "wipeup", "wipe up transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEUP}, 0, 0, FLAGS, "transition" },
  145. { "wipedown", "wipe down transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEDOWN}, 0, 0, FLAGS, "transition" },
  146. { "slideleft", "slide left transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDELEFT}, 0, 0, FLAGS, "transition" },
  147. { "slideright", "slide right transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDERIGHT}, 0, 0, FLAGS, "transition" },
  148. { "slideup", "slide up transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEUP}, 0, 0, FLAGS, "transition" },
  149. { "slidedown", "slide down transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEDOWN}, 0, 0, FLAGS, "transition" },
  150. { "circlecrop", "circle crop transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECROP}, 0, 0, FLAGS, "transition" },
  151. { "rectcrop", "rect crop transition", 0, AV_OPT_TYPE_CONST, {.i64=RECTCROP}, 0, 0, FLAGS, "transition" },
  152. { "distance", "distance transition", 0, AV_OPT_TYPE_CONST, {.i64=DISTANCE}, 0, 0, FLAGS, "transition" },
  153. { "fadeblack", "fadeblack transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEBLACK}, 0, 0, FLAGS, "transition" },
  154. { "fadewhite", "fadewhite transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEWHITE}, 0, 0, FLAGS, "transition" },
  155. { "radial", "radial transition", 0, AV_OPT_TYPE_CONST, {.i64=RADIAL}, 0, 0, FLAGS, "transition" },
  156. { "smoothleft", "smoothleft transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHLEFT}, 0, 0, FLAGS, "transition" },
  157. { "smoothright","smoothright transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHRIGHT},0, 0, FLAGS, "transition" },
  158. { "smoothup", "smoothup transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHUP}, 0, 0, FLAGS, "transition" },
  159. { "smoothdown", "smoothdown transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHDOWN}, 0, 0, FLAGS, "transition" },
  160. { "circleopen", "circleopen transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLEOPEN}, 0, 0, FLAGS, "transition" },
  161. { "circleclose","circleclose transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECLOSE},0, 0, FLAGS, "transition" },
  162. { "vertopen", "vert open transition", 0, AV_OPT_TYPE_CONST, {.i64=VERTOPEN}, 0, 0, FLAGS, "transition" },
  163. { "vertclose", "vert close transition", 0, AV_OPT_TYPE_CONST, {.i64=VERTCLOSE}, 0, 0, FLAGS, "transition" },
  164. { "horzopen", "horz open transition", 0, AV_OPT_TYPE_CONST, {.i64=HORZOPEN}, 0, 0, FLAGS, "transition" },
  165. { "horzclose", "horz close transition", 0, AV_OPT_TYPE_CONST, {.i64=HORZCLOSE}, 0, 0, FLAGS, "transition" },
  166. { "dissolve", "dissolve transition", 0, AV_OPT_TYPE_CONST, {.i64=DISSOLVE}, 0, 0, FLAGS, "transition" },
  167. { "pixelize", "pixelize transition", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE}, 0, 0, FLAGS, "transition" },
  168. { "diagtl", "diag tl transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGTL}, 0, 0, FLAGS, "transition" },
  169. { "diagtr", "diag tr transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGTR}, 0, 0, FLAGS, "transition" },
  170. { "diagbl", "diag bl transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGBL}, 0, 0, FLAGS, "transition" },
  171. { "diagbr", "diag br transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGBR}, 0, 0, FLAGS, "transition" },
  172. { "hlslice", "hl slice transition", 0, AV_OPT_TYPE_CONST, {.i64=HLSLICE}, 0, 0, FLAGS, "transition" },
  173. { "hrslice", "hr slice transition", 0, AV_OPT_TYPE_CONST, {.i64=HRSLICE}, 0, 0, FLAGS, "transition" },
  174. { "vuslice", "vu slice transition", 0, AV_OPT_TYPE_CONST, {.i64=VUSLICE}, 0, 0, FLAGS, "transition" },
  175. { "vdslice", "vd slice transition", 0, AV_OPT_TYPE_CONST, {.i64=VDSLICE}, 0, 0, FLAGS, "transition" },
  176. { "hblur", "hblur transition", 0, AV_OPT_TYPE_CONST, {.i64=HBLUR}, 0, 0, FLAGS, "transition" },
  177. { "fadegrays", "fadegrays transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEGRAYS}, 0, 0, FLAGS, "transition" },
  178. { "wipetl", "wipe tl transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPETL}, 0, 0, FLAGS, "transition" },
  179. { "wipetr", "wipe tr transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPETR}, 0, 0, FLAGS, "transition" },
  180. { "wipebl", "wipe bl transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEBL}, 0, 0, FLAGS, "transition" },
  181. { "wipebr", "wipe br transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEBR}, 0, 0, FLAGS, "transition" },
  182. { "duration", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=1000000}, 0, 60000000, FLAGS },
  183. { "offset", "set cross fade start relative to first input stream", OFFSET(offset), AV_OPT_TYPE_DURATION, {.i64=0}, INT64_MIN, INT64_MAX, FLAGS },
  184. { "expr", "set expression for custom transition", OFFSET(custom_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
  185. { NULL }
  186. };
  187. AVFILTER_DEFINE_CLASS(xfade);
  188. #define CUSTOM_TRANSITION(name, type, div) \
  189. static void custom##name##_transition(AVFilterContext *ctx, \
  190. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  191. float progress, \
  192. int slice_start, int slice_end, int jobnr) \
  193. { \
  194. XFadeContext *s = ctx->priv; \
  195. const int height = slice_end - slice_start; \
  196. \
  197. double values[VAR_VARS_NB]; \
  198. values[VAR_W] = out->width; \
  199. values[VAR_H] = out->height; \
  200. values[VAR_PROGRESS] = progress; \
  201. \
  202. for (int p = 0; p < s->nb_planes; p++) { \
  203. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  204. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  205. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  206. \
  207. values[VAR_PLANE] = p; \
  208. \
  209. for (int y = 0; y < height; y++) { \
  210. values[VAR_Y] = slice_start + y; \
  211. for (int x = 0; x < out->width; x++) { \
  212. values[VAR_X] = x; \
  213. values[VAR_A] = xf0[x]; \
  214. values[VAR_B] = xf1[x]; \
  215. dst[x] = av_expr_eval(s->e, values, s); \
  216. } \
  217. \
  218. dst += out->linesize[p] / div; \
  219. xf0 += a->linesize[p] / div; \
  220. xf1 += b->linesize[p] / div; \
  221. } \
  222. } \
  223. }
  224. CUSTOM_TRANSITION(8, uint8_t, 1)
  225. CUSTOM_TRANSITION(16, uint16_t, 2)
  226. static inline float mix(float a, float b, float mix)
  227. {
  228. return a * mix + b * (1.f - mix);
  229. }
  230. static inline float fract(float a)
  231. {
  232. return a - floorf(a);
  233. }
  234. static inline float smoothstep(float edge0, float edge1, float x)
  235. {
  236. float t;
  237. t = av_clipf((x - edge0) / (edge1 - edge0), 0.f, 1.f);
  238. return t * t * (3.f - 2.f * t);
  239. }
  240. #define FADE_TRANSITION(name, type, div) \
  241. static void fade##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. \
  249. for (int p = 0; p < s->nb_planes; p++) { \
  250. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  251. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  252. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  253. \
  254. for (int y = 0; y < height; y++) { \
  255. for (int x = 0; x < out->width; x++) { \
  256. dst[x] = mix(xf0[x], xf1[x], progress); \
  257. } \
  258. \
  259. dst += out->linesize[p] / div; \
  260. xf0 += a->linesize[p] / div; \
  261. xf1 += b->linesize[p] / div; \
  262. } \
  263. } \
  264. }
  265. FADE_TRANSITION(8, uint8_t, 1)
  266. FADE_TRANSITION(16, uint16_t, 2)
  267. #define WIPELEFT_TRANSITION(name, type, div) \
  268. static void wipeleft##name##_transition(AVFilterContext *ctx, \
  269. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  270. float progress, \
  271. int slice_start, int slice_end, int jobnr) \
  272. { \
  273. XFadeContext *s = ctx->priv; \
  274. const int height = slice_end - slice_start; \
  275. const int z = out->width * progress; \
  276. \
  277. for (int p = 0; p < s->nb_planes; p++) { \
  278. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  279. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  280. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  281. \
  282. for (int y = 0; y < height; y++) { \
  283. for (int x = 0; x < out->width; x++) { \
  284. dst[x] = x > z ? xf1[x] : xf0[x]; \
  285. } \
  286. \
  287. dst += out->linesize[p] / div; \
  288. xf0 += a->linesize[p] / div; \
  289. xf1 += b->linesize[p] / div; \
  290. } \
  291. } \
  292. }
  293. WIPELEFT_TRANSITION(8, uint8_t, 1)
  294. WIPELEFT_TRANSITION(16, uint16_t, 2)
  295. #define WIPERIGHT_TRANSITION(name, type, div) \
  296. static void wiperight##name##_transition(AVFilterContext *ctx, \
  297. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  298. float progress, \
  299. int slice_start, int slice_end, int jobnr) \
  300. { \
  301. XFadeContext *s = ctx->priv; \
  302. const int height = slice_end - slice_start; \
  303. const int z = out->width * (1.f - progress); \
  304. \
  305. for (int p = 0; p < s->nb_planes; p++) { \
  306. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  307. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  308. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  309. \
  310. for (int y = 0; y < height; y++) { \
  311. for (int x = 0; x < out->width; x++) { \
  312. dst[x] = x > z ? xf0[x] : xf1[x]; \
  313. } \
  314. \
  315. dst += out->linesize[p] / div; \
  316. xf0 += a->linesize[p] / div; \
  317. xf1 += b->linesize[p] / div; \
  318. } \
  319. } \
  320. }
  321. WIPERIGHT_TRANSITION(8, uint8_t, 1)
  322. WIPERIGHT_TRANSITION(16, uint16_t, 2)
  323. #define WIPEUP_TRANSITION(name, type, div) \
  324. static void wipeup##name##_transition(AVFilterContext *ctx, \
  325. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  326. float progress, \
  327. int slice_start, int slice_end, int jobnr) \
  328. { \
  329. XFadeContext *s = ctx->priv; \
  330. const int height = slice_end - slice_start; \
  331. const int z = out->height * progress; \
  332. \
  333. for (int p = 0; p < s->nb_planes; p++) { \
  334. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  335. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  336. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  337. \
  338. for (int y = 0; y < height; y++) { \
  339. for (int x = 0; x < out->width; x++) { \
  340. dst[x] = slice_start + y > z ? xf1[x] : xf0[x]; \
  341. } \
  342. \
  343. dst += out->linesize[p] / div; \
  344. xf0 += a->linesize[p] / div; \
  345. xf1 += b->linesize[p] / div; \
  346. } \
  347. } \
  348. }
  349. WIPEUP_TRANSITION(8, uint8_t, 1)
  350. WIPEUP_TRANSITION(16, uint16_t, 2)
  351. #define WIPEDOWN_TRANSITION(name, type, div) \
  352. static void wipedown##name##_transition(AVFilterContext *ctx, \
  353. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  354. float progress, \
  355. int slice_start, int slice_end, int jobnr) \
  356. { \
  357. XFadeContext *s = ctx->priv; \
  358. const int height = slice_end - slice_start; \
  359. const int z = out->height * (1.f - progress); \
  360. \
  361. for (int p = 0; p < s->nb_planes; p++) { \
  362. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  363. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  364. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  365. \
  366. for (int y = 0; y < height; y++) { \
  367. for (int x = 0; x < out->width; x++) { \
  368. dst[x] = slice_start + y > z ? xf0[x] : xf1[x]; \
  369. } \
  370. \
  371. dst += out->linesize[p] / div; \
  372. xf0 += a->linesize[p] / div; \
  373. xf1 += b->linesize[p] / div; \
  374. } \
  375. } \
  376. }
  377. WIPEDOWN_TRANSITION(8, uint8_t, 1)
  378. WIPEDOWN_TRANSITION(16, uint16_t, 2)
  379. #define SLIDELEFT_TRANSITION(name, type, div) \
  380. static void slideleft##name##_transition(AVFilterContext *ctx, \
  381. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  382. float progress, \
  383. int slice_start, int slice_end, int jobnr) \
  384. { \
  385. XFadeContext *s = ctx->priv; \
  386. const int height = slice_end - slice_start; \
  387. const int width = out->width; \
  388. const int z = -progress * width; \
  389. \
  390. for (int p = 0; p < s->nb_planes; p++) { \
  391. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  392. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  393. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  394. \
  395. for (int y = 0; y < height; y++) { \
  396. for (int x = 0; x < width; x++) { \
  397. const int zx = z + x; \
  398. const int zz = zx % width + width * (zx < 0); \
  399. dst[x] = (zx > 0) && (zx < width) ? xf1[zz] : xf0[zz]; \
  400. } \
  401. \
  402. dst += out->linesize[p] / div; \
  403. xf0 += a->linesize[p] / div; \
  404. xf1 += b->linesize[p] / div; \
  405. } \
  406. } \
  407. }
  408. SLIDELEFT_TRANSITION(8, uint8_t, 1)
  409. SLIDELEFT_TRANSITION(16, uint16_t, 2)
  410. #define SLIDERIGHT_TRANSITION(name, type, div) \
  411. static void slideright##name##_transition(AVFilterContext *ctx, \
  412. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  413. float progress, \
  414. int slice_start, int slice_end, int jobnr) \
  415. { \
  416. XFadeContext *s = ctx->priv; \
  417. const int height = slice_end - slice_start; \
  418. const int width = out->width; \
  419. const int z = progress * width; \
  420. \
  421. for (int p = 0; p < s->nb_planes; p++) { \
  422. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  423. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  424. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  425. \
  426. for (int y = 0; y < height; y++) { \
  427. for (int x = 0; x < out->width; x++) { \
  428. const int zx = z + x; \
  429. const int zz = zx % width + width * (zx < 0); \
  430. dst[x] = (zx > 0) && (zx < width) ? xf1[zz] : xf0[zz]; \
  431. } \
  432. \
  433. dst += out->linesize[p] / div; \
  434. xf0 += a->linesize[p] / div; \
  435. xf1 += b->linesize[p] / div; \
  436. } \
  437. } \
  438. }
  439. SLIDERIGHT_TRANSITION(8, uint8_t, 1)
  440. SLIDERIGHT_TRANSITION(16, uint16_t, 2)
  441. #define SLIDEUP_TRANSITION(name, type, div) \
  442. static void slideup##name##_transition(AVFilterContext *ctx, \
  443. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  444. float progress, \
  445. int slice_start, int slice_end, int jobnr) \
  446. { \
  447. XFadeContext *s = ctx->priv; \
  448. const int height = out->height; \
  449. const int z = -progress * height; \
  450. \
  451. for (int p = 0; p < s->nb_planes; p++) { \
  452. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  453. \
  454. for (int y = slice_start; y < slice_end; y++) { \
  455. const int zy = z + y; \
  456. const int zz = zy % height + height * (zy < 0); \
  457. const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \
  458. const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \
  459. \
  460. for (int x = 0; x < out->width; x++) { \
  461. dst[x] = (zy > 0) && (zy < height) ? xf1[x] : xf0[x]; \
  462. } \
  463. \
  464. dst += out->linesize[p] / div; \
  465. } \
  466. } \
  467. }
  468. SLIDEUP_TRANSITION(8, uint8_t, 1)
  469. SLIDEUP_TRANSITION(16, uint16_t, 2)
  470. #define SLIDEDOWN_TRANSITION(name, type, div) \
  471. static void slidedown##name##_transition(AVFilterContext *ctx, \
  472. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  473. float progress, \
  474. int slice_start, int slice_end, int jobnr) \
  475. { \
  476. XFadeContext *s = ctx->priv; \
  477. const int height = out->height; \
  478. const int z = progress * height; \
  479. \
  480. for (int p = 0; p < s->nb_planes; p++) { \
  481. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  482. \
  483. for (int y = slice_start; y < slice_end; y++) { \
  484. const int zy = z + y; \
  485. const int zz = zy % height + height * (zy < 0); \
  486. const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \
  487. const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \
  488. \
  489. for (int x = 0; x < out->width; x++) { \
  490. dst[x] = (zy > 0) && (zy < height) ? xf1[x] : xf0[x]; \
  491. } \
  492. \
  493. dst += out->linesize[p] / div; \
  494. } \
  495. } \
  496. }
  497. SLIDEDOWN_TRANSITION(8, uint8_t, 1)
  498. SLIDEDOWN_TRANSITION(16, uint16_t, 2)
  499. #define CIRCLECROP_TRANSITION(name, type, div) \
  500. static void circlecrop##name##_transition(AVFilterContext *ctx, \
  501. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  502. float progress, \
  503. int slice_start, int slice_end, int jobnr) \
  504. { \
  505. XFadeContext *s = ctx->priv; \
  506. const int width = out->width; \
  507. const int height = out->height; \
  508. float z = powf(2.f * fabsf(progress - 0.5f), 3.f) * hypotf(width/2, height/2); \
  509. \
  510. for (int p = 0; p < s->nb_planes; p++) { \
  511. const int bg = s->black[p]; \
  512. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  513. \
  514. for (int y = slice_start; y < slice_end; y++) { \
  515. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  516. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  517. \
  518. for (int x = 0; x < width; x++) { \
  519. float dist = hypotf(x - width / 2, y - height / 2); \
  520. int val = progress < 0.5f ? xf1[x] : xf0[x]; \
  521. dst[x] = (z < dist) ? bg : val; \
  522. } \
  523. \
  524. dst += out->linesize[p] / div; \
  525. } \
  526. } \
  527. }
  528. CIRCLECROP_TRANSITION(8, uint8_t, 1)
  529. CIRCLECROP_TRANSITION(16, uint16_t, 2)
  530. #define RECTCROP_TRANSITION(name, type, div) \
  531. static void rectcrop##name##_transition(AVFilterContext *ctx, \
  532. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  533. float progress, \
  534. int slice_start, int slice_end, int jobnr) \
  535. { \
  536. XFadeContext *s = ctx->priv; \
  537. const int width = out->width; \
  538. const int height = out->height; \
  539. int zh = fabsf(progress - 0.5f) * height; \
  540. int zw = fabsf(progress - 0.5f) * width; \
  541. \
  542. for (int p = 0; p < s->nb_planes; p++) { \
  543. const int bg = s->black[p]; \
  544. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  545. \
  546. for (int y = slice_start; y < slice_end; y++) { \
  547. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  548. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  549. \
  550. for (int x = 0; x < width; x++) { \
  551. int dist = FFABS(x - width / 2) < zw && \
  552. FFABS(y - height / 2) < zh; \
  553. int val = progress < 0.5f ? xf1[x] : xf0[x]; \
  554. dst[x] = !dist ? bg : val; \
  555. } \
  556. \
  557. dst += out->linesize[p] / div; \
  558. } \
  559. } \
  560. }
  561. RECTCROP_TRANSITION(8, uint8_t, 1)
  562. RECTCROP_TRANSITION(16, uint16_t, 2)
  563. #define DISTANCE_TRANSITION(name, type, div) \
  564. static void distance##name##_transition(AVFilterContext *ctx, \
  565. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  566. float progress, \
  567. int slice_start, int slice_end, int jobnr) \
  568. { \
  569. XFadeContext *s = ctx->priv; \
  570. const int width = out->width; \
  571. const float max = s->max_value; \
  572. \
  573. for (int y = slice_start; y < slice_end; y++) { \
  574. for (int x = 0; x < width; x++) { \
  575. float dist = 0.f; \
  576. for (int p = 0; p < s->nb_planes; p++) { \
  577. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  578. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  579. \
  580. dist += (xf0[x] / max - xf1[x] / max) * \
  581. (xf0[x] / max - xf1[x] / max); \
  582. } \
  583. \
  584. dist = sqrtf(dist) <= progress; \
  585. for (int p = 0; p < s->nb_planes; p++) { \
  586. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  587. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  588. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  589. dst[x] = mix(mix(xf0[x], xf1[x], dist), xf1[x], progress); \
  590. } \
  591. } \
  592. } \
  593. }
  594. DISTANCE_TRANSITION(8, uint8_t, 1)
  595. DISTANCE_TRANSITION(16, uint16_t, 2)
  596. #define FADEBLACK_TRANSITION(name, type, div) \
  597. static void fadeblack##name##_transition(AVFilterContext *ctx, \
  598. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  599. float progress, \
  600. int slice_start, int slice_end, int jobnr) \
  601. { \
  602. XFadeContext *s = ctx->priv; \
  603. const int height = slice_end - slice_start; \
  604. const float phase = 0.2f; \
  605. \
  606. for (int p = 0; p < s->nb_planes; p++) { \
  607. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  608. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  609. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  610. const int bg = s->black[p]; \
  611. \
  612. for (int y = 0; y < height; y++) { \
  613. for (int x = 0; x < out->width; x++) { \
  614. dst[x] = mix(mix(xf0[x], bg, smoothstep(1.f-phase, 1.f, progress)), \
  615. mix(bg, xf1[x], smoothstep(phase, 1.f, progress)), \
  616. progress); \
  617. } \
  618. \
  619. dst += out->linesize[p] / div; \
  620. xf0 += a->linesize[p] / div; \
  621. xf1 += b->linesize[p] / div; \
  622. } \
  623. } \
  624. }
  625. FADEBLACK_TRANSITION(8, uint8_t, 1)
  626. FADEBLACK_TRANSITION(16, uint16_t, 2)
  627. #define FADEWHITE_TRANSITION(name, type, div) \
  628. static void fadewhite##name##_transition(AVFilterContext *ctx, \
  629. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  630. float progress, \
  631. int slice_start, int slice_end, int jobnr) \
  632. { \
  633. XFadeContext *s = ctx->priv; \
  634. const int height = slice_end - slice_start; \
  635. const float phase = 0.2f; \
  636. \
  637. for (int p = 0; p < s->nb_planes; p++) { \
  638. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  639. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  640. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  641. const int bg = s->white[p]; \
  642. \
  643. for (int y = 0; y < height; y++) { \
  644. for (int x = 0; x < out->width; x++) { \
  645. dst[x] = mix(mix(xf0[x], bg, smoothstep(1.f-phase, 1.f, progress)), \
  646. mix(bg, xf1[x], smoothstep(phase, 1.f, progress)), \
  647. progress); \
  648. } \
  649. \
  650. dst += out->linesize[p] / div; \
  651. xf0 += a->linesize[p] / div; \
  652. xf1 += b->linesize[p] / div; \
  653. } \
  654. } \
  655. }
  656. FADEWHITE_TRANSITION(8, uint8_t, 1)
  657. FADEWHITE_TRANSITION(16, uint16_t, 2)
  658. #define RADIAL_TRANSITION(name, type, div) \
  659. static void radial##name##_transition(AVFilterContext *ctx, \
  660. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  661. float progress, \
  662. int slice_start, int slice_end, int jobnr) \
  663. { \
  664. XFadeContext *s = ctx->priv; \
  665. const int width = out->width; \
  666. const int height = out->height; \
  667. \
  668. for (int y = slice_start; y < slice_end; y++) { \
  669. for (int x = 0; x < width; x++) { \
  670. const float smooth = atan2f(x - width / 2, y - height / 2) - \
  671. (progress - 0.5f) * (M_PI * 2.5f); \
  672. for (int p = 0; p < s->nb_planes; p++) { \
  673. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  674. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  675. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  676. \
  677. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  678. } \
  679. } \
  680. } \
  681. }
  682. RADIAL_TRANSITION(8, uint8_t, 1)
  683. RADIAL_TRANSITION(16, uint16_t, 2)
  684. #define SMOOTHLEFT_TRANSITION(name, type, div) \
  685. static void smoothleft##name##_transition(AVFilterContext *ctx, \
  686. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  687. float progress, \
  688. int slice_start, int slice_end, int jobnr) \
  689. { \
  690. XFadeContext *s = ctx->priv; \
  691. const int width = out->width; \
  692. const float w = width; \
  693. \
  694. for (int y = slice_start; y < slice_end; y++) { \
  695. for (int x = 0; x < width; x++) { \
  696. const float smooth = 1.f + x / w - progress * 2.f; \
  697. \
  698. for (int p = 0; p < s->nb_planes; p++) { \
  699. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  700. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  701. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  702. \
  703. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  704. } \
  705. } \
  706. } \
  707. }
  708. SMOOTHLEFT_TRANSITION(8, uint8_t, 1)
  709. SMOOTHLEFT_TRANSITION(16, uint16_t, 2)
  710. #define SMOOTHRIGHT_TRANSITION(name, type, div) \
  711. static void smoothright##name##_transition(AVFilterContext *ctx, \
  712. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  713. float progress, \
  714. int slice_start, int slice_end, int jobnr) \
  715. { \
  716. XFadeContext *s = ctx->priv; \
  717. const int width = out->width; \
  718. const float w = width; \
  719. \
  720. for (int y = slice_start; y < slice_end; y++) { \
  721. for (int x = 0; x < width; x++) { \
  722. const float smooth = 1.f + (w - 1 - x) / w - progress * 2.f; \
  723. \
  724. for (int p = 0; p < s->nb_planes; p++) { \
  725. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  726. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  727. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  728. \
  729. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  730. } \
  731. } \
  732. } \
  733. }
  734. SMOOTHRIGHT_TRANSITION(8, uint8_t, 1)
  735. SMOOTHRIGHT_TRANSITION(16, uint16_t, 2)
  736. #define SMOOTHUP_TRANSITION(name, type, div) \
  737. static void smoothup##name##_transition(AVFilterContext *ctx, \
  738. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  739. float progress, \
  740. int slice_start, int slice_end, int jobnr) \
  741. { \
  742. XFadeContext *s = ctx->priv; \
  743. const int width = out->width; \
  744. const float h = out->height; \
  745. \
  746. for (int y = slice_start; y < slice_end; y++) { \
  747. const float smooth = 1.f + y / h - progress * 2.f; \
  748. for (int x = 0; x < width; x++) { \
  749. for (int p = 0; p < s->nb_planes; p++) { \
  750. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  751. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  752. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  753. \
  754. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  755. } \
  756. } \
  757. } \
  758. }
  759. SMOOTHUP_TRANSITION(8, uint8_t, 1)
  760. SMOOTHUP_TRANSITION(16, uint16_t, 2)
  761. #define SMOOTHDOWN_TRANSITION(name, type, div) \
  762. static void smoothdown##name##_transition(AVFilterContext *ctx, \
  763. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  764. float progress, \
  765. int slice_start, int slice_end, int jobnr) \
  766. { \
  767. XFadeContext *s = ctx->priv; \
  768. const int width = out->width; \
  769. const float h = out->height; \
  770. \
  771. for (int y = slice_start; y < slice_end; y++) { \
  772. const float smooth = 1.f + (h - 1 - y) / h - progress * 2.f; \
  773. for (int x = 0; x < width; x++) { \
  774. for (int p = 0; p < s->nb_planes; p++) { \
  775. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  776. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  777. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  778. \
  779. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  780. } \
  781. } \
  782. } \
  783. }
  784. SMOOTHDOWN_TRANSITION(8, uint8_t, 1)
  785. SMOOTHDOWN_TRANSITION(16, uint16_t, 2)
  786. #define CIRCLEOPEN_TRANSITION(name, type, div) \
  787. static void circleopen##name##_transition(AVFilterContext *ctx, \
  788. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  789. float progress, \
  790. int slice_start, int slice_end, int jobnr) \
  791. { \
  792. XFadeContext *s = ctx->priv; \
  793. const int width = out->width; \
  794. const int height = out->height; \
  795. const float z = hypotf(width / 2, height / 2); \
  796. const float p = (progress - 0.5f) * 3.f; \
  797. \
  798. for (int y = slice_start; y < slice_end; y++) { \
  799. for (int x = 0; x < width; x++) { \
  800. const float smooth = hypotf(x - width / 2, y - height / 2) / z + p; \
  801. for (int p = 0; p < s->nb_planes; p++) { \
  802. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  803. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  804. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  805. \
  806. dst[x] = mix(xf0[x], xf1[x], smoothstep(0.f, 1.f, smooth)); \
  807. } \
  808. } \
  809. } \
  810. }
  811. CIRCLEOPEN_TRANSITION(8, uint8_t, 1)
  812. CIRCLEOPEN_TRANSITION(16, uint16_t, 2)
  813. #define CIRCLECLOSE_TRANSITION(name, type, div) \
  814. static void circleclose##name##_transition(AVFilterContext *ctx, \
  815. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  816. float progress, \
  817. int slice_start, int slice_end, int jobnr) \
  818. { \
  819. XFadeContext *s = ctx->priv; \
  820. const int width = out->width; \
  821. const int height = out->height; \
  822. const float z = hypotf(width / 2, height / 2); \
  823. const float p = (1.f - progress - 0.5f) * 3.f; \
  824. \
  825. for (int y = slice_start; y < slice_end; y++) { \
  826. for (int x = 0; x < width; x++) { \
  827. const float smooth = hypotf(x - width / 2, y - height / 2) / z + p; \
  828. for (int p = 0; p < s->nb_planes; p++) { \
  829. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  830. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  831. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  832. \
  833. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  834. } \
  835. } \
  836. } \
  837. }
  838. CIRCLECLOSE_TRANSITION(8, uint8_t, 1)
  839. CIRCLECLOSE_TRANSITION(16, uint16_t, 2)
  840. #define VERTOPEN_TRANSITION(name, type, div) \
  841. static void vertopen##name##_transition(AVFilterContext *ctx, \
  842. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  843. float progress, \
  844. int slice_start, int slice_end, int jobnr) \
  845. { \
  846. XFadeContext *s = ctx->priv; \
  847. const int width = out->width; \
  848. const float w2 = out->width / 2; \
  849. \
  850. for (int y = slice_start; y < slice_end; y++) { \
  851. for (int x = 0; x < width; x++) { \
  852. const float smooth = 2.f - fabsf((x - w2) / w2) - progress * 2.f; \
  853. for (int p = 0; p < s->nb_planes; p++) { \
  854. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  855. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  856. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  857. \
  858. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  859. } \
  860. } \
  861. } \
  862. }
  863. VERTOPEN_TRANSITION(8, uint8_t, 1)
  864. VERTOPEN_TRANSITION(16, uint16_t, 2)
  865. #define VERTCLOSE_TRANSITION(name, type, div) \
  866. static void vertclose##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 w2 = out->width / 2; \
  874. \
  875. for (int y = slice_start; y < slice_end; y++) { \
  876. for (int x = 0; x < width; x++) { \
  877. const float smooth = 1.f + fabsf((x - w2) / w2) - progress * 2.f; \
  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. VERTCLOSE_TRANSITION(8, uint8_t, 1)
  889. VERTCLOSE_TRANSITION(16, uint16_t, 2)
  890. #define HORZOPEN_TRANSITION(name, type, div) \
  891. static void horzopen##name##_transition(AVFilterContext *ctx, \
  892. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  893. float progress, \
  894. int slice_start, int slice_end, int jobnr) \
  895. { \
  896. XFadeContext *s = ctx->priv; \
  897. const int width = out->width; \
  898. const float h2 = out->height / 2; \
  899. \
  900. for (int y = slice_start; y < slice_end; y++) { \
  901. const float smooth = 2.f - fabsf((y - h2) / h2) - progress * 2.f; \
  902. for (int x = 0; x < width; x++) { \
  903. for (int p = 0; p < s->nb_planes; p++) { \
  904. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  905. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  906. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  907. \
  908. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  909. } \
  910. } \
  911. } \
  912. }
  913. HORZOPEN_TRANSITION(8, uint8_t, 1)
  914. HORZOPEN_TRANSITION(16, uint16_t, 2)
  915. #define HORZCLOSE_TRANSITION(name, type, div) \
  916. static void horzclose##name##_transition(AVFilterContext *ctx, \
  917. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  918. float progress, \
  919. int slice_start, int slice_end, int jobnr) \
  920. { \
  921. XFadeContext *s = ctx->priv; \
  922. const int width = out->width; \
  923. const float h2 = out->height / 2; \
  924. \
  925. for (int y = slice_start; y < slice_end; y++) { \
  926. const float smooth = 1.f + fabsf((y - h2) / h2) - progress * 2.f; \
  927. for (int x = 0; x < width; x++) { \
  928. for (int p = 0; p < s->nb_planes; p++) { \
  929. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  930. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  931. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  932. \
  933. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  934. } \
  935. } \
  936. } \
  937. }
  938. HORZCLOSE_TRANSITION(8, uint8_t, 1)
  939. HORZCLOSE_TRANSITION(16, uint16_t, 2)
  940. static float frand(int x, int y)
  941. {
  942. const float r = sinf(x * 12.9898f + y * 78.233f) * 43758.545f;
  943. return r - floorf(r);
  944. }
  945. #define DISSOLVE_TRANSITION(name, type, div) \
  946. static void dissolve##name##_transition(AVFilterContext *ctx, \
  947. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  948. float progress, \
  949. int slice_start, int slice_end, int jobnr) \
  950. { \
  951. XFadeContext *s = ctx->priv; \
  952. const int width = out->width; \
  953. \
  954. for (int y = slice_start; y < slice_end; y++) { \
  955. for (int x = 0; x < width; x++) { \
  956. const float smooth = frand(x, y) * 2.f + progress * 2.f - 1.5f; \
  957. for (int p = 0; p < s->nb_planes; p++) { \
  958. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  959. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  960. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  961. \
  962. dst[x] = smooth >= 0.5f ? xf0[x] : xf1[x]; \
  963. } \
  964. } \
  965. } \
  966. }
  967. DISSOLVE_TRANSITION(8, uint8_t, 1)
  968. DISSOLVE_TRANSITION(16, uint16_t, 2)
  969. #define PIXELIZE_TRANSITION(name, type, div) \
  970. static void pixelize##name##_transition(AVFilterContext *ctx, \
  971. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  972. float progress, \
  973. int slice_start, int slice_end, int jobnr) \
  974. { \
  975. XFadeContext *s = ctx->priv; \
  976. const int w = out->width; \
  977. const int h = out->height; \
  978. const float d = fminf(progress, 1.f - progress); \
  979. const float dist = ceilf(d * 50.f) / 50.f; \
  980. const float sqx = 2.f * dist * FFMIN(w, h) / 20.f; \
  981. const float sqy = 2.f * dist * FFMIN(w, h) / 20.f; \
  982. \
  983. for (int y = slice_start; y < slice_end; y++) { \
  984. for (int x = 0; x < w; x++) { \
  985. int sx = dist > 0.f ? FFMIN((floorf(x / sqx) + .5f) * sqx, w - 1) : x; \
  986. int sy = dist > 0.f ? FFMIN((floorf(y / sqy) + .5f) * sqy, h - 1) : y; \
  987. for (int p = 0; p < s->nb_planes; p++) { \
  988. const type *xf0 = (const type *)(a->data[p] + sy * a->linesize[p]); \
  989. const type *xf1 = (const type *)(b->data[p] + sy * b->linesize[p]); \
  990. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  991. \
  992. dst[x] = mix(xf0[sx], xf1[sx], progress); \
  993. } \
  994. } \
  995. } \
  996. }
  997. PIXELIZE_TRANSITION(8, uint8_t, 1)
  998. PIXELIZE_TRANSITION(16, uint16_t, 2)
  999. #define DIAGTL_TRANSITION(name, type, div) \
  1000. static void diagtl##name##_transition(AVFilterContext *ctx, \
  1001. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1002. float progress, \
  1003. int slice_start, int slice_end, int jobnr) \
  1004. { \
  1005. XFadeContext *s = ctx->priv; \
  1006. const int width = out->width; \
  1007. const float w = width; \
  1008. const float h = out->height; \
  1009. \
  1010. for (int y = slice_start; y < slice_end; y++) { \
  1011. for (int x = 0; x < width; x++) { \
  1012. const float smooth = 1.f + x / w * y / h - progress * 2.f; \
  1013. \
  1014. for (int p = 0; p < s->nb_planes; p++) { \
  1015. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  1016. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  1017. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  1018. \
  1019. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  1020. } \
  1021. } \
  1022. } \
  1023. }
  1024. DIAGTL_TRANSITION(8, uint8_t, 1)
  1025. DIAGTL_TRANSITION(16, uint16_t, 2)
  1026. #define DIAGTR_TRANSITION(name, type, div) \
  1027. static void diagtr##name##_transition(AVFilterContext *ctx, \
  1028. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1029. float progress, \
  1030. int slice_start, int slice_end, int jobnr) \
  1031. { \
  1032. XFadeContext *s = ctx->priv; \
  1033. const int width = out->width; \
  1034. const float w = width; \
  1035. const float h = out->height; \
  1036. \
  1037. for (int y = slice_start; y < slice_end; y++) { \
  1038. for (int x = 0; x < width; x++) { \
  1039. const float smooth = 1.f + (w - 1 - x) / w * y / h - progress * 2.f; \
  1040. \
  1041. for (int p = 0; p < s->nb_planes; p++) { \
  1042. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  1043. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  1044. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  1045. \
  1046. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  1047. } \
  1048. } \
  1049. } \
  1050. }
  1051. DIAGTR_TRANSITION(8, uint8_t, 1)
  1052. DIAGTR_TRANSITION(16, uint16_t, 2)
  1053. #define DIAGBL_TRANSITION(name, type, div) \
  1054. static void diagbl##name##_transition(AVFilterContext *ctx, \
  1055. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1056. float progress, \
  1057. int slice_start, int slice_end, int jobnr) \
  1058. { \
  1059. XFadeContext *s = ctx->priv; \
  1060. const int width = out->width; \
  1061. const float w = width; \
  1062. const float h = out->height; \
  1063. \
  1064. for (int y = slice_start; y < slice_end; y++) { \
  1065. for (int x = 0; x < width; x++) { \
  1066. const float smooth = 1.f + x / w * (h - 1 - y) / h - progress * 2.f; \
  1067. \
  1068. for (int p = 0; p < s->nb_planes; p++) { \
  1069. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  1070. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  1071. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  1072. \
  1073. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  1074. } \
  1075. } \
  1076. } \
  1077. }
  1078. DIAGBL_TRANSITION(8, uint8_t, 1)
  1079. DIAGBL_TRANSITION(16, uint16_t, 2)
  1080. #define DIAGBR_TRANSITION(name, type, div) \
  1081. static void diagbr##name##_transition(AVFilterContext *ctx, \
  1082. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1083. float progress, \
  1084. int slice_start, int slice_end, int jobnr) \
  1085. { \
  1086. XFadeContext *s = ctx->priv; \
  1087. const int width = out->width; \
  1088. const float w = width; \
  1089. const float h = out->height; \
  1090. \
  1091. for (int y = slice_start; y < slice_end; y++) { \
  1092. for (int x = 0; x < width; x++) { \
  1093. const float smooth = 1.f + (w - 1 - x) / w * (h - 1 - y) / h - \
  1094. progress * 2.f; \
  1095. \
  1096. for (int p = 0; p < s->nb_planes; p++) { \
  1097. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  1098. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  1099. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  1100. \
  1101. dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
  1102. } \
  1103. } \
  1104. } \
  1105. }
  1106. DIAGBR_TRANSITION(8, uint8_t, 1)
  1107. DIAGBR_TRANSITION(16, uint16_t, 2)
  1108. #define HLSLICE_TRANSITION(name, type, div) \
  1109. static void hlslice##name##_transition(AVFilterContext *ctx, \
  1110. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1111. float progress, \
  1112. int slice_start, int slice_end, int jobnr) \
  1113. { \
  1114. XFadeContext *s = ctx->priv; \
  1115. const int width = out->width; \
  1116. const float w = width; \
  1117. \
  1118. for (int y = slice_start; y < slice_end; y++) { \
  1119. for (int x = 0; x < width; x++) { \
  1120. const float smooth = smoothstep(-0.5f, 0.f, x / w - progress * 1.5f); \
  1121. const float ss = smooth <= fract(10.f * x / w) ? 0.f : 1.f; \
  1122. \
  1123. for (int p = 0; p < s->nb_planes; p++) { \
  1124. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  1125. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  1126. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  1127. \
  1128. dst[x] = mix(xf1[x], xf0[x], ss); \
  1129. } \
  1130. } \
  1131. } \
  1132. }
  1133. HLSLICE_TRANSITION(8, uint8_t, 1)
  1134. HLSLICE_TRANSITION(16, uint16_t, 2)
  1135. #define HRSLICE_TRANSITION(name, type, div) \
  1136. static void hrslice##name##_transition(AVFilterContext *ctx, \
  1137. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1138. float progress, \
  1139. int slice_start, int slice_end, int jobnr) \
  1140. { \
  1141. XFadeContext *s = ctx->priv; \
  1142. const int width = out->width; \
  1143. const float w = width; \
  1144. \
  1145. for (int y = slice_start; y < slice_end; y++) { \
  1146. for (int x = 0; x < width; x++) { \
  1147. const float xx = (w - 1 - x) / w; \
  1148. const float smooth = smoothstep(-0.5f, 0.f, xx - progress * 1.5f); \
  1149. const float ss = smooth <= fract(10.f * xx) ? 0.f : 1.f; \
  1150. \
  1151. for (int p = 0; p < s->nb_planes; p++) { \
  1152. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  1153. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  1154. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  1155. \
  1156. dst[x] = mix(xf1[x], xf0[x], ss); \
  1157. } \
  1158. } \
  1159. } \
  1160. }
  1161. HRSLICE_TRANSITION(8, uint8_t, 1)
  1162. HRSLICE_TRANSITION(16, uint16_t, 2)
  1163. #define VUSLICE_TRANSITION(name, type, div) \
  1164. static void vuslice##name##_transition(AVFilterContext *ctx, \
  1165. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1166. float progress, \
  1167. int slice_start, int slice_end, int jobnr) \
  1168. { \
  1169. XFadeContext *s = ctx->priv; \
  1170. const int width = out->width; \
  1171. const float h = out->height; \
  1172. \
  1173. for (int y = slice_start; y < slice_end; y++) { \
  1174. const float smooth = smoothstep(-0.5f, 0.f, y / h - progress * 1.5f); \
  1175. const float ss = smooth <= fract(10.f * y / h) ? 0.f : 1.f; \
  1176. \
  1177. for (int x = 0; x < width; x++) { \
  1178. for (int p = 0; p < s->nb_planes; p++) { \
  1179. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  1180. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  1181. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  1182. \
  1183. dst[x] = mix(xf1[x], xf0[x], ss); \
  1184. } \
  1185. } \
  1186. } \
  1187. }
  1188. VUSLICE_TRANSITION(8, uint8_t, 1)
  1189. VUSLICE_TRANSITION(16, uint16_t, 2)
  1190. #define VDSLICE_TRANSITION(name, type, div) \
  1191. static void vdslice##name##_transition(AVFilterContext *ctx, \
  1192. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1193. float progress, \
  1194. int slice_start, int slice_end, int jobnr) \
  1195. { \
  1196. XFadeContext *s = ctx->priv; \
  1197. const int width = out->width; \
  1198. const float h = out->height; \
  1199. \
  1200. for (int y = slice_start; y < slice_end; y++) { \
  1201. const float yy = (h - 1 - y) / h; \
  1202. const float smooth = smoothstep(-0.5f, 0.f, yy - progress * 1.5f); \
  1203. const float ss = smooth <= fract(10.f * yy) ? 0.f : 1.f; \
  1204. \
  1205. for (int x = 0; x < width; x++) { \
  1206. for (int p = 0; p < s->nb_planes; p++) { \
  1207. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  1208. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  1209. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  1210. \
  1211. dst[x] = mix(xf1[x], xf0[x], ss); \
  1212. } \
  1213. } \
  1214. } \
  1215. }
  1216. VDSLICE_TRANSITION(8, uint8_t, 1)
  1217. VDSLICE_TRANSITION(16, uint16_t, 2)
  1218. #define HBLUR_TRANSITION(name, type, div) \
  1219. static void hblur##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 width = out->width; \
  1226. const float prog = progress <= 0.5f ? progress * 2.f : (1.f - progress) * 2.f; \
  1227. const int size = 1 + (width / 2) * prog; \
  1228. \
  1229. for (int y = slice_start; y < slice_end; y++) { \
  1230. for (int p = 0; p < s->nb_planes; p++) { \
  1231. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  1232. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  1233. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  1234. float sum0 = 0.f; \
  1235. float sum1 = 0.f; \
  1236. float cnt = size; \
  1237. \
  1238. for (int x = 0; x < size; x++) { \
  1239. sum0 += xf0[x]; \
  1240. sum1 += xf1[x]; \
  1241. } \
  1242. \
  1243. for (int x = 0; x < width; x++) { \
  1244. dst[x] = mix(sum0 / cnt, sum1 / cnt, progress); \
  1245. \
  1246. if (x + size < width) { \
  1247. sum0 += xf0[x + size] - xf0[x]; \
  1248. sum1 += xf1[x + size] - xf1[x]; \
  1249. } else { \
  1250. sum0 -= xf0[x]; \
  1251. sum1 -= xf1[x]; \
  1252. cnt--; \
  1253. } \
  1254. } \
  1255. } \
  1256. } \
  1257. }
  1258. HBLUR_TRANSITION(8, uint8_t, 1)
  1259. HBLUR_TRANSITION(16, uint16_t, 2)
  1260. #define FADEGRAYS_TRANSITION(name, type, div) \
  1261. static void fadegrays##name##_transition(AVFilterContext *ctx, \
  1262. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1263. float progress, \
  1264. int slice_start, int slice_end, int jobnr) \
  1265. { \
  1266. XFadeContext *s = ctx->priv; \
  1267. const int width = out->width; \
  1268. const int is_rgb = s->is_rgb; \
  1269. const int mid = (s->max_value + 1) / 2; \
  1270. const float phase = 0.2f; \
  1271. \
  1272. for (int y = slice_start; y < slice_end; y++) { \
  1273. for (int x = 0; x < width; x++) { \
  1274. int bg[2][4]; \
  1275. if (is_rgb) { \
  1276. for (int p = 0; p < s->nb_planes; p++) { \
  1277. const type *xf0 = (const type *)(a->data[p] + \
  1278. y * a->linesize[p]); \
  1279. const type *xf1 = (const type *)(b->data[p] + \
  1280. y * b->linesize[p]); \
  1281. if (p == 3) { \
  1282. bg[0][3] = xf0[x]; \
  1283. bg[1][3] = xf1[x]; \
  1284. } else { \
  1285. bg[0][0] += xf0[x]; \
  1286. bg[1][0] += xf1[x]; \
  1287. } \
  1288. } \
  1289. bg[0][0] = bg[0][0] / 3; \
  1290. bg[1][0] = bg[1][0] / 3; \
  1291. bg[0][1] = bg[0][2] = bg[0][0]; \
  1292. bg[1][1] = bg[1][2] = bg[1][0]; \
  1293. } else { \
  1294. const type *yf0 = (const type *)(a->data[0] + \
  1295. y * a->linesize[0]); \
  1296. const type *yf1 = (const type *)(b->data[0] + \
  1297. y * a->linesize[0]); \
  1298. bg[0][0] = yf0[x]; \
  1299. bg[1][0] = yf1[x]; \
  1300. if (s->nb_planes == 4) { \
  1301. const type *af0 = (const type *)(a->data[3] + \
  1302. y * a->linesize[3]); \
  1303. const type *af1 = (const type *)(b->data[3] + \
  1304. y * a->linesize[3]); \
  1305. bg[0][3] = af0[x]; \
  1306. bg[1][3] = af1[x]; \
  1307. } \
  1308. bg[0][1] = bg[1][1] = mid; \
  1309. bg[0][2] = bg[1][2] = mid; \
  1310. } \
  1311. \
  1312. for (int p = 0; p < s->nb_planes; p++) { \
  1313. const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
  1314. const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
  1315. type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
  1316. \
  1317. dst[x] = mix(mix(xf0[x], bg[0][p], \
  1318. smoothstep(1.f-phase, 1.f, progress)), \
  1319. mix(bg[1][p], xf1[x], smoothstep(phase, 1.f, progress)), \
  1320. progress); \
  1321. } \
  1322. } \
  1323. } \
  1324. }
  1325. FADEGRAYS_TRANSITION(8, uint8_t, 1)
  1326. FADEGRAYS_TRANSITION(16, uint16_t, 2)
  1327. #define WIPETL_TRANSITION(name, type, div) \
  1328. static void wipetl##name##_transition(AVFilterContext *ctx, \
  1329. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1330. float progress, \
  1331. int slice_start, int slice_end, int jobnr) \
  1332. { \
  1333. XFadeContext *s = ctx->priv; \
  1334. const int height = slice_end - slice_start; \
  1335. const int zw = out->width * progress; \
  1336. const int zh = out->height * progress; \
  1337. \
  1338. for (int p = 0; p < s->nb_planes; p++) { \
  1339. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  1340. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  1341. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  1342. \
  1343. for (int y = 0; y < height; y++) { \
  1344. for (int x = 0; x < out->width; x++) { \
  1345. dst[x] = slice_start + y <= zh && \
  1346. x <= zw ? xf0[x] : xf1[x]; \
  1347. } \
  1348. \
  1349. dst += out->linesize[p] / div; \
  1350. xf0 += a->linesize[p] / div; \
  1351. xf1 += b->linesize[p] / div; \
  1352. } \
  1353. } \
  1354. }
  1355. WIPETL_TRANSITION(8, uint8_t, 1)
  1356. WIPETL_TRANSITION(16, uint16_t, 2)
  1357. #define WIPETR_TRANSITION(name, type, div) \
  1358. static void wipetr##name##_transition(AVFilterContext *ctx, \
  1359. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1360. float progress, \
  1361. int slice_start, int slice_end, int jobnr) \
  1362. { \
  1363. XFadeContext *s = ctx->priv; \
  1364. const int height = slice_end - slice_start; \
  1365. const int zw = out->width * (1.f - progress); \
  1366. const int zh = out->height * progress; \
  1367. \
  1368. for (int p = 0; p < s->nb_planes; p++) { \
  1369. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  1370. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  1371. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  1372. \
  1373. for (int y = 0; y < height; y++) { \
  1374. for (int x = 0; x < out->width; x++) { \
  1375. dst[x] = slice_start + y <= zh && \
  1376. x > zw ? xf0[x] : xf1[x]; \
  1377. } \
  1378. \
  1379. dst += out->linesize[p] / div; \
  1380. xf0 += a->linesize[p] / div; \
  1381. xf1 += b->linesize[p] / div; \
  1382. } \
  1383. } \
  1384. }
  1385. WIPETR_TRANSITION(8, uint8_t, 1)
  1386. WIPETR_TRANSITION(16, uint16_t, 2)
  1387. #define WIPEBL_TRANSITION(name, type, div) \
  1388. static void wipebl##name##_transition(AVFilterContext *ctx, \
  1389. const AVFrame *a, const AVFrame *b, AVFrame *out, \
  1390. float progress, \
  1391. int slice_start, int slice_end, int jobnr) \
  1392. { \
  1393. XFadeContext *s = ctx->priv; \
  1394. const int height = slice_end - slice_start; \
  1395. const int zw = out->width * progress; \
  1396. const int zh = out->height * (1.f - progress); \
  1397. \
  1398. for (int p = 0; p < s->nb_planes; p++) { \
  1399. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  1400. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  1401. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  1402. \
  1403. for (int y = 0; y < height; y++) { \
  1404. for (int x = 0; x < out->width; x++) { \
  1405. dst[x] = slice_start + y > zh && \
  1406. x <= zw ? xf0[x] : xf1[x]; \
  1407. } \
  1408. \
  1409. dst += out->linesize[p] / div; \
  1410. xf0 += a->linesize[p] / div; \
  1411. xf1 += b->linesize[p] / div; \
  1412. } \
  1413. } \
  1414. }
  1415. WIPEBL_TRANSITION(8, uint8_t, 1)
  1416. WIPEBL_TRANSITION(16, uint16_t, 2)
  1417. #define WIPEBR_TRANSITION(name, type, div) \
  1418. static void wipebr##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 height = slice_end - slice_start; \
  1425. const int zh = out->height * (1.f - progress); \
  1426. const int zw = out->width * (1.f - progress); \
  1427. \
  1428. for (int p = 0; p < s->nb_planes; p++) { \
  1429. const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
  1430. const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
  1431. type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
  1432. \
  1433. for (int y = 0; y < height; y++) { \
  1434. for (int x = 0; x < out->width; x++) { \
  1435. dst[x] = slice_start + y > zh && \
  1436. x > zw ? xf0[x] : xf1[x]; \
  1437. } \
  1438. \
  1439. dst += out->linesize[p] / div; \
  1440. xf0 += a->linesize[p] / div; \
  1441. xf1 += b->linesize[p] / div; \
  1442. } \
  1443. } \
  1444. }
  1445. WIPEBR_TRANSITION(8, uint8_t, 1)
  1446. WIPEBR_TRANSITION(16, uint16_t, 2)
  1447. static inline double getpix(void *priv, double x, double y, int plane, int nb)
  1448. {
  1449. XFadeContext *s = priv;
  1450. AVFrame *in = s->xf[nb];
  1451. const uint8_t *src = in->data[FFMIN(plane, s->nb_planes - 1)];
  1452. int linesize = in->linesize[FFMIN(plane, s->nb_planes - 1)];
  1453. const int w = in->width;
  1454. const int h = in->height;
  1455. int xi, yi;
  1456. xi = av_clipd(x, 0, w - 1);
  1457. yi = av_clipd(y, 0, h - 1);
  1458. if (s->depth > 8) {
  1459. const uint16_t *src16 = (const uint16_t*)src;
  1460. linesize /= 2;
  1461. return src16[xi + yi * linesize];
  1462. } else {
  1463. return src[xi + yi * linesize];
  1464. }
  1465. }
  1466. static double a0(void *priv, double x, double y) { return getpix(priv, x, y, 0, 0); }
  1467. static double a1(void *priv, double x, double y) { return getpix(priv, x, y, 1, 0); }
  1468. static double a2(void *priv, double x, double y) { return getpix(priv, x, y, 2, 0); }
  1469. static double a3(void *priv, double x, double y) { return getpix(priv, x, y, 3, 0); }
  1470. static double b0(void *priv, double x, double y) { return getpix(priv, x, y, 0, 1); }
  1471. static double b1(void *priv, double x, double y) { return getpix(priv, x, y, 1, 1); }
  1472. static double b2(void *priv, double x, double y) { return getpix(priv, x, y, 2, 1); }
  1473. static double b3(void *priv, double x, double y) { return getpix(priv, x, y, 3, 1); }
  1474. static int config_output(AVFilterLink *outlink)
  1475. {
  1476. AVFilterContext *ctx = outlink->src;
  1477. AVFilterLink *inlink0 = ctx->inputs[0];
  1478. AVFilterLink *inlink1 = ctx->inputs[1];
  1479. XFadeContext *s = ctx->priv;
  1480. const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink0->format);
  1481. if (inlink0->format != inlink1->format) {
  1482. av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n");
  1483. return AVERROR(EINVAL);
  1484. }
  1485. if (inlink0->w != inlink1->w || inlink0->h != inlink1->h) {
  1486. av_log(ctx, AV_LOG_ERROR, "First input link %s parameters "
  1487. "(size %dx%d) do not match the corresponding "
  1488. "second input link %s parameters (size %dx%d)\n",
  1489. ctx->input_pads[0].name, inlink0->w, inlink0->h,
  1490. ctx->input_pads[1].name, inlink1->w, inlink1->h);
  1491. return AVERROR(EINVAL);
  1492. }
  1493. if (inlink0->time_base.num != inlink1->time_base.num ||
  1494. inlink0->time_base.den != inlink1->time_base.den) {
  1495. av_log(ctx, AV_LOG_ERROR, "First input link %s timebase "
  1496. "(%d/%d) do not match the corresponding "
  1497. "second input link %s timebase (%d/%d)\n",
  1498. ctx->input_pads[0].name, inlink0->time_base.num, inlink0->time_base.den,
  1499. ctx->input_pads[1].name, inlink1->time_base.num, inlink1->time_base.den);
  1500. return AVERROR(EINVAL);
  1501. }
  1502. if (!inlink0->frame_rate.num || !inlink0->frame_rate.den) {
  1503. av_log(ctx, AV_LOG_ERROR, "The inputs needs to be a constant frame rate; "
  1504. "current rate of %d/%d is invalid\n", inlink0->frame_rate.num, inlink0->frame_rate.den);
  1505. return AVERROR(EINVAL);
  1506. }
  1507. if (inlink0->frame_rate.num != inlink1->frame_rate.num ||
  1508. inlink0->frame_rate.den != inlink1->frame_rate.den) {
  1509. av_log(ctx, AV_LOG_ERROR, "First input link %s frame rate "
  1510. "(%d/%d) do not match the corresponding "
  1511. "second input link %s frame rate (%d/%d)\n",
  1512. ctx->input_pads[0].name, inlink0->frame_rate.num, inlink0->frame_rate.den,
  1513. ctx->input_pads[1].name, inlink1->frame_rate.num, inlink1->frame_rate.den);
  1514. return AVERROR(EINVAL);
  1515. }
  1516. outlink->w = inlink0->w;
  1517. outlink->h = inlink0->h;
  1518. outlink->time_base = inlink0->time_base;
  1519. outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio;
  1520. outlink->frame_rate = inlink0->frame_rate;
  1521. s->depth = pix_desc->comp[0].depth;
  1522. s->is_rgb = !!(pix_desc->flags & AV_PIX_FMT_FLAG_RGB);
  1523. s->nb_planes = av_pix_fmt_count_planes(inlink0->format);
  1524. s->max_value = (1 << s->depth) - 1;
  1525. s->black[0] = 0;
  1526. s->black[1] = s->black[2] = s->is_rgb ? 0 : s->max_value / 2;
  1527. s->black[3] = s->max_value;
  1528. s->white[0] = s->white[3] = s->max_value;
  1529. s->white[1] = s->white[2] = s->is_rgb ? s->max_value : s->max_value / 2;
  1530. s->first_pts = s->last_pts = s->pts = AV_NOPTS_VALUE;
  1531. if (s->duration)
  1532. s->duration_pts = av_rescale_q(s->duration, AV_TIME_BASE_Q, outlink->time_base);
  1533. if (s->offset)
  1534. s->offset_pts = av_rescale_q(s->offset, AV_TIME_BASE_Q, outlink->time_base);
  1535. switch (s->transition) {
  1536. case CUSTOM: s->transitionf = s->depth <= 8 ? custom8_transition : custom16_transition; break;
  1537. case FADE: s->transitionf = s->depth <= 8 ? fade8_transition : fade16_transition; break;
  1538. case WIPELEFT: s->transitionf = s->depth <= 8 ? wipeleft8_transition : wipeleft16_transition; break;
  1539. case WIPERIGHT: s->transitionf = s->depth <= 8 ? wiperight8_transition : wiperight16_transition; break;
  1540. case WIPEUP: s->transitionf = s->depth <= 8 ? wipeup8_transition : wipeup16_transition; break;
  1541. case WIPEDOWN: s->transitionf = s->depth <= 8 ? wipedown8_transition : wipedown16_transition; break;
  1542. case SLIDELEFT: s->transitionf = s->depth <= 8 ? slideleft8_transition : slideleft16_transition; break;
  1543. case SLIDERIGHT: s->transitionf = s->depth <= 8 ? slideright8_transition : slideright16_transition; break;
  1544. case SLIDEUP: s->transitionf = s->depth <= 8 ? slideup8_transition : slideup16_transition; break;
  1545. case SLIDEDOWN: s->transitionf = s->depth <= 8 ? slidedown8_transition : slidedown16_transition; break;
  1546. case CIRCLECROP: s->transitionf = s->depth <= 8 ? circlecrop8_transition : circlecrop16_transition; break;
  1547. case RECTCROP: s->transitionf = s->depth <= 8 ? rectcrop8_transition : rectcrop16_transition; break;
  1548. case DISTANCE: s->transitionf = s->depth <= 8 ? distance8_transition : distance16_transition; break;
  1549. case FADEBLACK: s->transitionf = s->depth <= 8 ? fadeblack8_transition : fadeblack16_transition; break;
  1550. case FADEWHITE: s->transitionf = s->depth <= 8 ? fadewhite8_transition : fadewhite16_transition; break;
  1551. case RADIAL: s->transitionf = s->depth <= 8 ? radial8_transition : radial16_transition; break;
  1552. case SMOOTHLEFT: s->transitionf = s->depth <= 8 ? smoothleft8_transition : smoothleft16_transition; break;
  1553. case SMOOTHRIGHT:s->transitionf = s->depth <= 8 ? smoothright8_transition: smoothright16_transition;break;
  1554. case SMOOTHUP: s->transitionf = s->depth <= 8 ? smoothup8_transition : smoothup16_transition; break;
  1555. case SMOOTHDOWN: s->transitionf = s->depth <= 8 ? smoothdown8_transition : smoothdown16_transition; break;
  1556. case CIRCLEOPEN: s->transitionf = s->depth <= 8 ? circleopen8_transition : circleopen16_transition; break;
  1557. case CIRCLECLOSE:s->transitionf = s->depth <= 8 ? circleclose8_transition: circleclose16_transition;break;
  1558. case VERTOPEN: s->transitionf = s->depth <= 8 ? vertopen8_transition : vertopen16_transition; break;
  1559. case VERTCLOSE: s->transitionf = s->depth <= 8 ? vertclose8_transition : vertclose16_transition; break;
  1560. case HORZOPEN: s->transitionf = s->depth <= 8 ? horzopen8_transition : horzopen16_transition; break;
  1561. case HORZCLOSE: s->transitionf = s->depth <= 8 ? horzclose8_transition : horzclose16_transition; break;
  1562. case DISSOLVE: s->transitionf = s->depth <= 8 ? dissolve8_transition : dissolve16_transition; break;
  1563. case PIXELIZE: s->transitionf = s->depth <= 8 ? pixelize8_transition : pixelize16_transition; break;
  1564. case DIAGTL: s->transitionf = s->depth <= 8 ? diagtl8_transition : diagtl16_transition; break;
  1565. case DIAGTR: s->transitionf = s->depth <= 8 ? diagtr8_transition : diagtr16_transition; break;
  1566. case DIAGBL: s->transitionf = s->depth <= 8 ? diagbl8_transition : diagbl16_transition; break;
  1567. case DIAGBR: s->transitionf = s->depth <= 8 ? diagbr8_transition : diagbr16_transition; break;
  1568. case HLSLICE: s->transitionf = s->depth <= 8 ? hlslice8_transition : hlslice16_transition; break;
  1569. case HRSLICE: s->transitionf = s->depth <= 8 ? hrslice8_transition : hrslice16_transition; break;
  1570. case VUSLICE: s->transitionf = s->depth <= 8 ? vuslice8_transition : vuslice16_transition; break;
  1571. case VDSLICE: s->transitionf = s->depth <= 8 ? vdslice8_transition : vdslice16_transition; break;
  1572. case HBLUR: s->transitionf = s->depth <= 8 ? hblur8_transition : hblur16_transition; break;
  1573. case FADEGRAYS: s->transitionf = s->depth <= 8 ? fadegrays8_transition : fadegrays16_transition; break;
  1574. case WIPETL: s->transitionf = s->depth <= 8 ? wipetl8_transition : wipetl16_transition; break;
  1575. case WIPETR: s->transitionf = s->depth <= 8 ? wipetr8_transition : wipetr16_transition; break;
  1576. case WIPEBL: s->transitionf = s->depth <= 8 ? wipebl8_transition : wipebl16_transition; break;
  1577. case WIPEBR: s->transitionf = s->depth <= 8 ? wipebr8_transition : wipebr16_transition; break;
  1578. }
  1579. if (s->transition == CUSTOM) {
  1580. static const char *const func2_names[] = {
  1581. "a0", "a1", "a2", "a3",
  1582. "b0", "b1", "b2", "b3",
  1583. NULL
  1584. };
  1585. double (*func2[])(void *, double, double) = {
  1586. a0, a1, a2, a3,
  1587. b0, b1, b2, b3,
  1588. NULL };
  1589. int ret;
  1590. if (!s->custom_str)
  1591. return AVERROR(EINVAL);
  1592. ret = av_expr_parse(&s->e, s->custom_str, var_names,
  1593. NULL, NULL, func2_names, func2, 0, ctx);
  1594. if (ret < 0)
  1595. return ret;
  1596. }
  1597. return 0;
  1598. }
  1599. static int xfade_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
  1600. {
  1601. XFadeContext *s = ctx->priv;
  1602. AVFilterLink *outlink = ctx->outputs[0];
  1603. ThreadData *td = arg;
  1604. int slice_start = (outlink->h * jobnr ) / nb_jobs;
  1605. int slice_end = (outlink->h * (jobnr+1)) / nb_jobs;
  1606. s->transitionf(ctx, td->xf[0], td->xf[1], td->out, td->progress, slice_start, slice_end, jobnr);
  1607. return 0;
  1608. }
  1609. static int xfade_frame(AVFilterContext *ctx, AVFrame *a, AVFrame *b)
  1610. {
  1611. XFadeContext *s = ctx->priv;
  1612. AVFilterLink *outlink = ctx->outputs[0];
  1613. float progress = av_clipf(1.f - ((float)(s->pts - s->first_pts - s->offset_pts) / s->duration_pts), 0.f, 1.f);
  1614. ThreadData td;
  1615. AVFrame *out;
  1616. out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  1617. if (!out)
  1618. return AVERROR(ENOMEM);
  1619. td.xf[0] = a, td.xf[1] = b, td.out = out, td.progress = progress;
  1620. ctx->internal->execute(ctx, xfade_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
  1621. out->pts = s->pts;
  1622. return ff_filter_frame(outlink, out);
  1623. }
  1624. static int xfade_activate(AVFilterContext *ctx)
  1625. {
  1626. XFadeContext *s = ctx->priv;
  1627. AVFilterLink *outlink = ctx->outputs[0];
  1628. AVFrame *in = NULL;
  1629. int ret = 0, status;
  1630. int64_t pts;
  1631. FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx);
  1632. if (s->xfade_is_over) {
  1633. ret = ff_inlink_consume_frame(ctx->inputs[1], &in);
  1634. if (ret < 0) {
  1635. return ret;
  1636. } else if (ret > 0) {
  1637. in->pts = (in->pts - s->last_pts) + s->pts;
  1638. return ff_filter_frame(outlink, in);
  1639. } else if (ff_inlink_acknowledge_status(ctx->inputs[1], &status, &pts)) {
  1640. ff_outlink_set_status(outlink, status, s->pts);
  1641. return 0;
  1642. } else if (!ret) {
  1643. if (ff_outlink_frame_wanted(outlink))
  1644. ff_inlink_request_frame(ctx->inputs[1]);
  1645. return 0;
  1646. }
  1647. }
  1648. if (ff_inlink_queued_frames(ctx->inputs[0]) > 0) {
  1649. s->xf[0] = ff_inlink_peek_frame(ctx->inputs[0], 0);
  1650. if (s->xf[0]) {
  1651. if (s->first_pts == AV_NOPTS_VALUE) {
  1652. s->first_pts = s->xf[0]->pts;
  1653. }
  1654. s->pts = s->xf[0]->pts;
  1655. if (s->first_pts + s->offset_pts > s->xf[0]->pts) {
  1656. s->xf[0] = NULL;
  1657. s->need_second = 0;
  1658. ff_inlink_consume_frame(ctx->inputs[0], &in);
  1659. return ff_filter_frame(outlink, in);
  1660. }
  1661. s->need_second = 1;
  1662. }
  1663. }
  1664. if (s->xf[0] && ff_inlink_queued_frames(ctx->inputs[1]) > 0) {
  1665. ff_inlink_consume_frame(ctx->inputs[0], &s->xf[0]);
  1666. ff_inlink_consume_frame(ctx->inputs[1], &s->xf[1]);
  1667. s->last_pts = s->xf[1]->pts;
  1668. s->pts = s->xf[0]->pts;
  1669. if (s->xf[0]->pts - (s->first_pts + s->offset_pts) > s->duration_pts)
  1670. s->xfade_is_over = 1;
  1671. ret = xfade_frame(ctx, s->xf[0], s->xf[1]);
  1672. av_frame_free(&s->xf[0]);
  1673. av_frame_free(&s->xf[1]);
  1674. return ret;
  1675. }
  1676. if (ff_inlink_queued_frames(ctx->inputs[0]) > 0 &&
  1677. ff_inlink_queued_frames(ctx->inputs[1]) > 0) {
  1678. ff_filter_set_ready(ctx, 100);
  1679. return 0;
  1680. }
  1681. if (ff_outlink_frame_wanted(outlink)) {
  1682. if (!s->eof[0] && ff_outlink_get_status(ctx->inputs[0])) {
  1683. s->eof[0] = 1;
  1684. s->xfade_is_over = 1;
  1685. }
  1686. if (!s->eof[1] && ff_outlink_get_status(ctx->inputs[1])) {
  1687. s->eof[1] = 1;
  1688. }
  1689. if (!s->eof[0] && !s->xf[0] && ff_inlink_queued_frames(ctx->inputs[0]) == 0)
  1690. ff_inlink_request_frame(ctx->inputs[0]);
  1691. if (!s->eof[1] && (s->need_second || s->eof[0]) && ff_inlink_queued_frames(ctx->inputs[1]) == 0)
  1692. ff_inlink_request_frame(ctx->inputs[1]);
  1693. if (s->eof[0] && s->eof[1] && (
  1694. ff_inlink_queued_frames(ctx->inputs[0]) <= 0 &&
  1695. ff_inlink_queued_frames(ctx->inputs[1]) <= 0)) {
  1696. ff_outlink_set_status(outlink, AVERROR_EOF, AV_NOPTS_VALUE);
  1697. } else if (s->xfade_is_over) {
  1698. ff_filter_set_ready(ctx, 100);
  1699. }
  1700. return 0;
  1701. }
  1702. return FFERROR_NOT_READY;
  1703. }
  1704. static const AVFilterPad xfade_inputs[] = {
  1705. {
  1706. .name = "main",
  1707. .type = AVMEDIA_TYPE_VIDEO,
  1708. },
  1709. {
  1710. .name = "xfade",
  1711. .type = AVMEDIA_TYPE_VIDEO,
  1712. },
  1713. { NULL }
  1714. };
  1715. static const AVFilterPad xfade_outputs[] = {
  1716. {
  1717. .name = "default",
  1718. .type = AVMEDIA_TYPE_VIDEO,
  1719. .config_props = config_output,
  1720. },
  1721. { NULL }
  1722. };
  1723. AVFilter ff_vf_xfade = {
  1724. .name = "xfade",
  1725. .description = NULL_IF_CONFIG_SMALL("Cross fade one video with another video."),
  1726. .priv_size = sizeof(XFadeContext),
  1727. .priv_class = &xfade_class,
  1728. .query_formats = query_formats,
  1729. .activate = xfade_activate,
  1730. .uninit = uninit,
  1731. .inputs = xfade_inputs,
  1732. .outputs = xfade_outputs,
  1733. .flags = AVFILTER_FLAG_SLICE_THREADS,
  1734. };