+static int do_chromakey16_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
+{
+ AVFrame *frame = arg;
+
+ const int slice_start = (frame->height * jobnr) / nb_jobs;
+ const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs;
+
+ ChromakeyContext *ctx = avctx->priv;
+
+ int x, y, xo, yo;
+ uint16_t u[9], v[9];
+
+ for (int i = 0; i < 9; i++) {
+ u[i] = ctx->chromakey_uv[0];
+ v[i] = ctx->chromakey_uv[1];
+ }
+
+ for (y = slice_start; y < slice_end; ++y) {
+ for (x = 0; x < frame->width; ++x) {
+ uint16_t *dst = (uint16_t *)(frame->data[3] + frame->linesize[3] * y);
+
+ for (yo = 0; yo < 3; ++yo) {
+ for (xo = 0; xo < 3; ++xo) {
+ get_pixel16_uv(frame, ctx->hsub_log2, ctx->vsub_log2, x + xo - 1, y + yo - 1, &u[yo * 3 + xo], &v[yo * 3 + xo]);
+ }
+ }
+
+ dst[x] = do_chromakey_pixel16(ctx, u, v);
+ }
+ }
+
+ return 0;
+}
+
+static int do_chromahold_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
+{
+ ChromakeyContext *ctx = avctx->priv;
+ AVFrame *frame = arg;
+ const int slice_start = ((frame->height >> ctx->vsub_log2) * jobnr) / nb_jobs;
+ const int slice_end = ((frame->height >> ctx->vsub_log2) * (jobnr + 1)) / nb_jobs;
+
+ int x, y, alpha;
+
+ for (y = slice_start; y < slice_end; ++y) {
+ for (x = 0; x < frame->width >> ctx->hsub_log2; ++x) {
+ int u = frame->data[1][frame->linesize[1] * y + x];
+ int v = frame->data[2][frame->linesize[2] * y + x];
+ double diff;
+ int du, dv;
+
+ du = u - ctx->chromakey_uv[0];
+ dv = v - ctx->chromakey_uv[1];
+
+ diff = sqrt((du * du + dv * dv) / (255.0 * 255.0));
+
+ alpha = diff > ctx->similarity;
+ if (ctx->blend > 0.0001) {
+ double f = 1. - av_clipd((diff - ctx->similarity) / ctx->blend, 0.0, 1.0);
+
+ frame->data[1][frame->linesize[1] * y + x] = 128 + (u - 128) * f;
+ frame->data[2][frame->linesize[2] * y + x] = 128 + (v - 128) * f;
+ } else if (alpha) {
+ frame->data[1][frame->linesize[1] * y + x] = 128;
+ frame->data[2][frame->linesize[2] * y + x] = 128;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int do_chromahold16_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
+{
+ ChromakeyContext *ctx = avctx->priv;
+ AVFrame *frame = arg;
+ const int slice_start = ((frame->height >> ctx->vsub_log2) * jobnr) / nb_jobs;
+ const int slice_end = ((frame->height >> ctx->vsub_log2) * (jobnr + 1)) / nb_jobs;
+ const int mid = ctx->mid;
+ double max = ctx->max;
+
+ int x, y, alpha;
+
+ for (y = slice_start; y < slice_end; ++y) {
+ for (x = 0; x < frame->width >> ctx->hsub_log2; ++x) {
+ int u = AV_RN16(&frame->data[1][frame->linesize[1] * y + 2 * x]);
+ int v = AV_RN16(&frame->data[2][frame->linesize[2] * y + 2 * x]);
+ double diff;
+ int du, dv;
+
+ du = u - ctx->chromakey_uv[0];
+ dv = v - ctx->chromakey_uv[1];
+
+ diff = sqrt((du * du + dv * dv) / (max * max));
+
+ alpha = diff > ctx->similarity;
+ if (ctx->blend > 0.0001) {
+ double f = 1. - av_clipd((diff - ctx->similarity) / ctx->blend, 0.0, 1.0);
+
+ AV_WN16(&frame->data[1][frame->linesize[1] * y + 2 * x], mid + (u - mid) * f);
+ AV_WN16(&frame->data[2][frame->linesize[2] * y + 2 * x], mid + (v - mid) * f);
+ } else if (alpha) {
+ AV_WN16(&frame->data[1][frame->linesize[1] * y + 2 * x], mid);
+ AV_WN16(&frame->data[2][frame->linesize[2] * y + 2 * x], mid);
+ }
+ }
+ }
+
+ return 0;
+}
+