FFmpeg
uops.c
Go to the documentation of this file.
1 /**
2  * Copyright (C) 2026 Niklas Haas
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 <stdbool.h>
22 
23 #include "libavutil/avassert.h"
24 #include "libavutil/mem.h"
25 #include "libavutil/refstruct.h"
26 
27 #include "ops.h"
28 #include "uops.h"
29 #include "uops_list.h"
30 
31 int ff_sws_uop_cmp(const SwsUOp *a, const SwsUOp *b)
32 {
33  if (a->type != b->type)
34  return (int) a->type - b->type;
35  if (a->uop != b->uop)
36  return (int) a->uop - b->uop;
37  if (a->mask != b->mask)
38  return (int) a->mask - b->mask;
39  return memcmp(&a->par, &b->par, sizeof(a->par));
40 }
41 
42 static const struct {
43  char abbr[32];
45 #define UOP_NAME(OP, ABBR) [OP] = { ABBR },
47 #undef UOP_NAME
48 };
49 
51 {
52  av_assert1(val.den != 0);
53  switch (type) {
54  case SWS_PIXEL_U8: return (SwsPixel) { .u8 = val.num / val.den };
55  case SWS_PIXEL_U16: return (SwsPixel) { .u16 = val.num / val.den };
56  case SWS_PIXEL_U32: return (SwsPixel) { .u32 = val.num / val.den };
57  case SWS_PIXEL_F32: return (SwsPixel) { .f32 = (float) val.num / val.den };
58  case SWS_PIXEL_NONE:
59  case SWS_PIXEL_TYPE_NB: break;
60  }
61 
62  av_unreachable("Invalid pixel type!");
63  return (SwsPixel) {0};
64 }
65 
66 #define Q2PIXEL(val) pixel_from_q64(op->type, val)
67 
69 {
70  switch (ff_sws_pixel_type_size(type)) {
71  case 1: return val.u8 == UINT8_MAX;
72  case 2: return val.u16 == UINT16_MAX;
73  case 4: return val.u32 == UINT32_MAX;
74  default: break;
75  }
76 
77  av_unreachable("Invalid pixel type!");
78  return false;
79 }
80 
81 void ff_sws_uop_name(const SwsUOp *op, char buf[SWS_UOP_NAME_MAX])
82 {
83  AVBPrint bp;
85 
86  if (op->type != SWS_PIXEL_NONE)
87  av_bprintf(&bp, "%s_", ff_sws_pixel_type_name(op->type));
88  av_bprintf(&bp, "%s", uop_names[op->uop].abbr);
89 
90  if (op->mask)
91  av_bprintf(&bp, "_%s", ff_sws_comp_mask_str(op->mask));
92 
93  const SwsUOpParams *par = &op->par;
94  switch (op->uop) {
98  av_bprintf(&bp, "_%s", ff_sws_pixel_type_name(par->filter.type));
99  break;
100  case SWS_UOP_RW_SHUFFLE:
101  av_bprintf(&bp, "_%x_%u_%u", par->shuffle.clear_value,
102  par->shuffle.read_size, par->shuffle.write_size);
103  break;
104  case SWS_UOP_LSHIFT:
105  case SWS_UOP_RSHIFT:
106  av_bprintf(&bp, "_%u", par->shift.amount);
107  break;
108  case SWS_UOP_PERMUTE:
109  case SWS_UOP_COPY:
110  av_bprint_chars(&bp, '_', 1);
111  for (int i = 0; i < par->move.num_moves; i++)
112  av_bprint_chars(&bp, "txyzw"[par->move.dst[i] + 1], 1);
113  av_bprint_chars(&bp, '_', 1);
114  for (int i = 0; i < par->move.num_moves; i++)
115  av_bprint_chars(&bp, "txyzw"[par->move.src[i] + 1], 1);
116  break;
117  case SWS_UOP_PACK:
118  case SWS_UOP_UNPACK:
119  av_bprint_chars(&bp, '_', 1);
120  for (int i = 0; i < 4 && par->pack.pattern[i]; i++)
121  av_bprintf(&bp, "%x", par->pack.pattern[i]);
122  break;
123  case SWS_UOP_CLEAR:
124  av_bprint_chars(&bp, '_', 1);
125  for (int i = 0; i < 4; i++) {
126  if (!SWS_COMP_TEST(op->mask, i))
127  continue;
128  else if (SWS_COMP_TEST(par->clear.one, i))
129  av_bprint_chars(&bp, '1', 1);
130  else if (SWS_COMP_TEST(par->clear.zero, i))
131  av_bprint_chars(&bp, '0', 1);
132  else
133  av_bprint_chars(&bp, 'x', 1);
134  }
135  break;
136  case SWS_UOP_LINEAR:
137  case SWS_UOP_LINEAR_FMA:
138  for (int i = 0; i < 4; i++) {
139  if (!SWS_COMP_TEST(op->mask, i))
140  continue;
141  av_bprint_chars(&bp, '_', 1);
142  for (int j = 0; j < 5; j++) {
143  if (par->lin.one & SWS_MASK(i, j))
144  av_bprint_chars(&bp, '1', 1);
145  else if (par->lin.zero & SWS_MASK(i, j))
146  av_bprint_chars(&bp, '0', 1);
147  else if (par->lin.exact & SWS_MASK(i, j))
148  av_bprint_chars(&bp, 'X', 1);
149  else
150  av_bprint_chars(&bp, 'x', 1);
151  }
152  }
153  break;
154  case SWS_UOP_DITHER:
155  for (int i = 0; i < 4; i++) {
156  if (SWS_COMP_TEST(op->mask, i))
157  av_bprintf(&bp, "_%d", par->dither.y_offset[i]);
158  }
159  const unsigned size = 1u << par->dither.size_log2;
160  av_bprintf(&bp, "_%ux%u", size, size);
161  break;
162  }
163 
165 }
166 
167 static void uop_uninit(SwsUOp *uop)
168 {
169  switch (uop->uop) {
170  case SWS_UOP_DITHER:
171  av_refstruct_unref(&uop->data.ptr);
172  break;
177  break;
178  }
179 
180  *uop = (SwsUOp) {0};
181 }
182 
184 {
185  SwsUOpList *ops = *p_ops;
186  if (!ops)
187  return;
188 
189  for (int i = 0; i < ops->num_ops; i++)
190  uop_uninit(&ops->ops[i]);
191 
192  av_freep(&ops->ops);
193  av_free(ops);
194  *p_ops = NULL;
195 }
196 
198 {
199  return av_mallocz(sizeof(SwsUOpList));
200 }
201 
203 {
204  if (!av_dynarray2_add((void **) &uops->ops, &uops->num_ops,
205  sizeof(*uop), (uint8_t *) uop))
206  {
207  uop_uninit(uop);
208  return AVERROR(ENOMEM);
209  }
210 
211  *uop = (SwsUOp) {0};
212  return 0;
213 }
214 
215 void ff_sws_uop_list_remove_at(SwsUOpList *uops, int index, int count)
216 {
217  const int end = uops->num_ops - count;
218  av_assert2(index >= 0 && count >= 0 && index + count <= uops->num_ops);
219  for (int i = 0; i < count; i++)
220  uop_uninit(&uops->ops[index + i]);
221  for (int i = index; i < end; i++)
222  uops->ops[i] = uops->ops[i + count];
223  uops->num_ops = end;
224 }
225 
227 {
228  int max_offset = 0;
229  for (int i = 0; i < 4; i++)
230  max_offset = FFMAX(max_offset, dither->y_offset[i]);
231  return (1 << dither->size_log2) + max_offset;
232 }
233 
235 {
236  switch (ff_sws_pixel_type_size(type)) {
237  case 1: return SWS_PIXEL_U8;
238  case 2: return SWS_PIXEL_U16;
239  case 4: return SWS_PIXEL_U32;
240  default: break;
241  }
242 
243  av_unreachable("Invalid pixel type!");
244  return SWS_PIXEL_NONE;
245 }
246 
247 static bool exact_product_f32(float a, float b)
248 {
249  volatile float prod = a * b;
250  volatile float result = b ? prod / b : 0.0f;
251  return !b || result == a;
252 }
253 
255  const SwsComps *comps, int idx)
256 {
257  const AVRational64 minq = comps->min[idx];
258  const AVRational64 maxq = comps->max[idx];
260  return true;
261  else if (!minq.den || !maxq.den)
262  return false; /* unknown bounds */
263 
264  const SwsPixel min = pixel_from_q64(type, minq);
265  const SwsPixel max = pixel_from_q64(type, maxq);
266  switch (type) {
267  case SWS_PIXEL_F32:
268  return exact_product_f32(coef.f32, min.f32) &&
269  exact_product_f32(coef.f32, max.f32);
270  }
271 
272  av_unreachable("Invalid pixel type!");
273  return false;
274 }
275 
277 {
278  if (!(flags & SWS_UOP_FLAG_FMA))
279  return false;
280  if (!(ctx->flags & SWS_BITEXACT))
281  return true;
282  if (!ff_sws_pixel_type_is_int(op->type))
283  return false;
284 
285  const int bits = ff_sws_pixel_type_size(op->type) * 8;
286  const uint64_t max_val = UINT64_MAX >> (64 - bits);
287 
288  /* Maximum value representable losslessly as float. Note that this is
289  * currently true only for U8, but that may change if we ever update the
290  * value of SWS_FILTER_SCALE. */
291  return max_val * SWS_FILTER_SCALE <= (1 << 22);
292 }
293 
295  const SwsOp *op)
296 {
297  SwsUOp uop = {
298  .type = op->type,
299  .mask = SWS_COMP_MASK(op->rw.elems > 0, op->rw.elems > 1,
300  op->rw.elems > 2, op->rw.elems > 3),
301  };
302 
303  /* Non-filtered reads don't care about the exact pixel contents */
304  if (!op->rw.filter.op)
305  uop.type = pixel_type_to_int(op->type);
306 
307  const bool is_read = op->op == SWS_OP_READ;
308  if (op->rw.filter.op) {
309  if (op->op == SWS_OP_WRITE || op->rw.frac || op->rw.mode != SWS_RW_PLANAR)
310  return AVERROR(ENOTSUP);
311  uop.par.filter.type = op->rw.filter.type;
312  uop.data.kernel = av_refstruct_ref(op->rw.filter.kernel);
313  if (op->rw.filter.op == SWS_OP_FILTER_H) {
315  } else if (check_filter_fma(ctx, flags, op)) {
317  } else {
319  }
320  } else if (op->rw.mode == SWS_RW_PACKED && op->rw.elems > 1) {
321  if (op->rw.frac)
322  return AVERROR(ENOTSUP);
323  uop.uop = is_read ? SWS_UOP_READ_PACKED : SWS_UOP_WRITE_PACKED;
324  } else if (op->rw.mode == SWS_RW_PALETTE) {
325  if (op->rw.frac || !is_read)
326  return AVERROR(ENOTSUP);
328  } else if (op->rw.frac == 3) {
329  uop.uop = is_read ? SWS_UOP_READ_BIT : SWS_UOP_WRITE_BIT;
330  } else if (op->rw.frac == 1) {
331  uop.uop = is_read ? SWS_UOP_READ_NIBBLE : SWS_UOP_WRITE_NIBBLE;
332  } else {
333  av_assert0(!op->rw.frac);
334  uop.uop = is_read ? SWS_UOP_READ_PLANAR : SWS_UOP_WRITE_PLANAR;
335  }
336 
337  const int planes = ff_sws_rw_op_planes(op);
338  if (op->op == SWS_OP_READ) {
340  } else {
342  }
343 
344  return ff_sws_uop_list_append(ops, &uop);
345 }
346 
347 static int count_idx(const int *arr, size_t size, int val)
348 {
349  int num = 0;
350  for (size_t i = 0; i < size; i++) {
351  if (arr[i] == val)
352  num++;
353  }
354 
355  return num;
356 }
357 
358 static int translate_swizzle(SwsUOpList *ops, const SwsOp *op)
359 {
360  SwsUOp uop = {
361  .uop = SWS_UOP_PERMUTE,
362  .type = pixel_type_to_int(op->type),
363  .mask = ff_sws_comp_mask_needed(op),
364  };
365  SwsMoveUOp *par = &uop.par.move;
366 
367  /* Mask of components that are not yet satisfied */
368  SwsCompMask todo = uop.mask;
369  for (int i = 0; i < 4; i++) {
370  if (op->swizzle.in[i] == i)
371  todo &= ~SWS_COMP(i);
372  }
373 
374  /* Mask of components whose value is required for the final output */
375  SwsCompMask needed = 0;
376  for (int i = 0; i < 4; i++) {
377  if (SWS_OP_NEEDED(op, i))
378  needed |= SWS_COMP(op->swizzle.in[i]);
379  }
380 
381  /* Current mapping of registers to components */
382  int idx[4 + 1] = { 0, 1, 2, 3, -1 }; /* +1 for tmp */
383 
384  /* Decompose the swizzle mask into a series of register-register moves */
385  while (todo) {
386  int dst = -1, src = -1;
387 
388  /* Find next unsatisfied dst <- src move that doesn't clobber a value */
389  for (dst = 0; dst < 4; dst++) {
390  if (!SWS_COMP_TEST(todo, dst))
391  continue; /* already satisfied */
392  const int cur = idx[dst];
393  if (count_idx(idx, FF_ARRAY_ELEMS(idx), cur) == 1 && SWS_COMP_TEST(needed, cur))
394  continue; /* clobbers last remaining, still-needed value */
395  for (src = 0; src < FF_ARRAY_ELEMS(idx); src++) {
396  if (idx[src] == op->swizzle.in[dst]) {
397  /* Prevent read-after-write dependency. */
398  if (par->num_moves > 0 && src == par->dst[par->num_moves - 1])
399  src = par->src[par->num_moves - 1];
400  break;
401  }
402  }
403  av_assert1(src < FF_ARRAY_ELEMS(idx));
404  todo &= ~SWS_COMP(dst);
405  break;
406  }
407 
408  if (dst == 4) {
409  /* Stuck in a cycle, break it by saving to the scratch register */
410  dst = 4;
411  for (src = 0; src < 4; src++) {
412  if (SWS_COMP_TEST(todo, src)) {
413  needed &= ~SWS_COMP(idx[src]);
414  break;
415  }
416  }
417  av_assert1(src < 4);
418  }
419 
421  par->dst[par->num_moves] = dst > 3 ? -1 : dst;
422  par->src[par->num_moves] = src > 3 ? -1 : src;
423  par->num_moves++;
424  idx[dst] = idx[src];
425  }
426 
427  /* Check for duplicates in the final register map */
428  SwsCompMask seen = 0;
429  for (int i = 0; i < 4; i++) {
430  if (!SWS_COMP_TEST(uop.mask, i))
431  continue;
432  av_assert2(idx[i] >= 0); /* should be no tmp register */
433  const SwsCompMask bit = SWS_COMP(idx[i]);
434  if (seen & bit) {
435  uop.uop = SWS_UOP_COPY;
436  break;
437  }
438  seen |= bit;
439  }
440 
441  /* Add any extra unused components to the mask, to prevent generating
442  * duplicate uops like permute_xyz_txy_xyt and permute_xyzw_txy_xyt */
443  for (int i = 0; i < 4; i++) {
444  const SwsCompMask bit = SWS_COMP(i);
445  if (!(seen & bit) && idx[i] == i)
446  uop.mask |= bit;
447  }
448 
449  return ff_sws_uop_list_append(ops, &uop);
450 }
451 
452 static int translate_dither_op(SwsUOpList *ops, const SwsOp *op)
453 {
454  SwsUOp uop = {
455  .type = op->type,
456  .uop = SWS_UOP_DITHER,
457  .par.dither.size_log2 = op->dither.size_log2,
458  };
459 
460  if (op->dither.size_log2 == 0) {
461  /* Constant offset */
462  const SwsPixel val = Q2PIXEL(op->dither.matrix[0]);
463  uop.uop = SWS_UOP_ADD;
464  for (int i = 0; i < 4; i++) {
465  if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0)
466  continue;
467  uop.mask |= SWS_COMP(i);
468  uop.data.vec4[i] = val;
469  }
470 
471  return ff_sws_uop_list_append(ops, &uop);
472  }
473 
474  const int size = 1 << op->dither.size_log2;
475  for (int i = 0; i < 4; i++) {
476  if (!SWS_OP_NEEDED(op, i) || op->dither.y_offset[i] < 0)
477  continue;
478  const uint8_t off = op->dither.y_offset[i] & (size - 1);
479  uop.mask |= SWS_COMP(i);
480  uop.par.dither.y_offset[i] = off;
481  }
482 
483  /* Allocate extra rows to allow over-reading for row offsets. Note that
484  * y_offset is currently never larger than 5, so the extra space needed
485  * for this over-allocation is bounded by 5 * size * sizeof(float),
486  * typically 320 bytes for a 16x16 dither matrix. */
487  const int stride = size * sizeof(SwsPixel);
488  const int num_rows = ff_sws_dither_height(&uop.par.dither);
489  SwsPixel *matrix = uop.data.ptr = av_refstruct_allocz(num_rows * stride);
490  if (!matrix)
491  return AVERROR(ENOMEM);
492 
493  for (int i = 0; i < size * size; i++)
494  matrix[i] = Q2PIXEL(op->dither.matrix[i]);
495  memcpy(&matrix[size * size], matrix, (num_rows - size) * stride);
496 
497  return ff_sws_uop_list_append(ops, &uop);
498 }
499 
501  SwsUOpFlags flags, const SwsOp *op,
502  const SwsComps *input)
503 {
504  SwsUOp uop = {
505  .type = op->type,
506  .uop = SWS_UOP_LINEAR,
507  };
508 
509  const bool bitexact = ctx->flags & SWS_BITEXACT;
510  uint32_t exact = 0;
511 
512  for (int i = 0; i < 4; i++) {
513  if (!SWS_OP_NEEDED(op, i) || !(op->lin.mask & SWS_MASK_ROW(i))) {
514  uop.par.lin.zero |= SWS_MASK_ROW(i);
515  continue;
516  }
517  uop.mask |= SWS_COMP(i);
518  bool nonzero = (op->lin.m[i][4].num != 0);
519  for (int j = 0; j < 5; j++) {
520  const AVRational64 k = op->lin.m[i][j];
521  const SwsPixel px = Q2PIXEL(k);
522  uop.data.mat4[i][j] = px;
523  if (k.num == 0)
524  uop.par.lin.zero |= SWS_MASK(i, j);
525  else if (j < 4 && k.num == k.den)
526  uop.par.lin.one |= SWS_MASK(i, j);
527  else if (j < 4 && nonzero && (!bitexact || exact_prod(uop.type, px, input, j)))
528  exact |= SWS_MASK(i, j);
529  if (k.num != 0)
530  nonzero = true;
531  }
532  }
533 
534  if (flags & SWS_UOP_FLAG_FMA) {
535  /* multiplication by 1 and 0 are always exact by definition */
536  uop.uop = SWS_UOP_LINEAR_FMA;
537  uop.par.lin.exact = exact | uop.par.lin.zero | uop.par.lin.one;
538  }
539 
540  return ff_sws_uop_list_append(ops, &uop);
541 }
542 
544 {
545  if (factor.den != 1)
546  return false;
547 
548  switch (type) {
549  case SWS_PIXEL_U8: return factor.num == UINT8_MAX;
550  case SWS_PIXEL_U16: return factor.num == UINT16_MAX;
551  case SWS_PIXEL_U32: return factor.num == UINT32_MAX;
552  case SWS_PIXEL_F32: return false;
553  case SWS_PIXEL_NONE:
554  case SWS_PIXEL_TYPE_NB: break;
555  }
556 
557  av_unreachable("Invalid pixel type!");
558  return false;
559 }
560 
562  const SwsOp *op, const SwsComps *input)
563 {
564  switch (op->op) {
565  case SWS_OP_FILTER_H:
566  case SWS_OP_FILTER_V:
567  return AVERROR(ENOTSUP); /* always handled by subpass splitting */
568  case SWS_OP_READ:
569  case SWS_OP_WRITE:
570  return translate_rw_op(ctx, uops, flags, op);
571  case SWS_OP_SWIZZLE:
572  return translate_swizzle(uops, op);
573  case SWS_OP_DITHER:
574  return translate_dither_op(uops, op);
575  case SWS_OP_LINEAR:
576  return translate_linear_op(ctx, uops, flags, op, input);
577  default:
578  break;
579  }
580 
581  /* Default handling for "simple" ops */
582  SwsUOp uop = {
583  .type = op->type,
584  .uop = SWS_UOP_INVALID,
585  .mask = ff_sws_comp_mask_needed(op),
586  };
587 
588  switch (op->op) {
589  case SWS_OP_CONVERT:
590  if (op->convert.expand) {
591  av_assert0(op->type == SWS_PIXEL_U8);
592  switch (op->convert.to) {
593  case SWS_PIXEL_U16: uop.uop = SWS_UOP_EXPAND_PAIR; break;
594  case SWS_PIXEL_U32: uop.uop = SWS_UOP_EXPAND_QUAD; break;
595  }
596  } else {
597  switch (op->convert.to) {
598  case SWS_PIXEL_U8: uop.uop = SWS_UOP_TO_U8; break;
599  case SWS_PIXEL_U16: uop.uop = SWS_UOP_TO_U16; break;
600  case SWS_PIXEL_U32: uop.uop = SWS_UOP_TO_U32; break;
601  case SWS_PIXEL_F32: uop.uop = SWS_UOP_TO_F32; break;
602  }
603  }
604  break;
605  case SWS_OP_UNPACK:
606  case SWS_OP_PACK:
607  uop.uop = op->op == SWS_OP_PACK ? SWS_UOP_PACK : SWS_UOP_UNPACK;
608  uop.mask = 0;
609  for (int i = 0; i < 4 && op->pack.pattern[i]; i++) {
610  uop.par.pack.pattern[i] = op->pack.pattern[i];
611  if (op->op == SWS_OP_PACK || SWS_OP_NEEDED(op, i))
612  uop.mask |= SWS_COMP(i);
613  }
614  break;
615  case SWS_OP_LSHIFT:
616  case SWS_OP_RSHIFT:
618  uop.par.shift.amount = op->shift.amount;
619  break;
620  case SWS_OP_CLEAR:
621  uop.uop = SWS_UOP_CLEAR;
622  uop.type = pixel_type_to_int(op->type);
623  uop.mask &= op->clear.mask;
624  for (int i = 0; i < 4; i++) {
625  if (!SWS_COMP_TEST(op->clear.mask, i))
626  continue;
627  const AVRational64 v = op->clear.value[i];
628  const SwsPixel px = Q2PIXEL(op->clear.value[i]);
629  uop.data.vec4[i] = px;
630  if (v.num == 0)
631  uop.par.clear.zero |= SWS_COMP(i);
632  else if (pixel_is_1s(op->type, px))
633  uop.par.clear.one |= SWS_COMP(i);
634  }
635  break;
636  case SWS_OP_SCALE:
637  if (is_expand_bit(op->type, op->scale.factor)) {
638  uop.uop = SWS_UOP_EXPAND_BIT;
639  } else {
640  uop.uop = SWS_UOP_SCALE;
641  uop.data.scalar = Q2PIXEL(op->scale.factor);
642  }
643  break;
644  case SWS_OP_MIN:
645  case SWS_OP_MAX:
646  uop.uop = op->op == SWS_OP_MIN ? SWS_UOP_MIN : SWS_UOP_MAX;
647  uop.mask &= ff_sws_comp_mask_q4(op->clamp.limit);
648  for (int i = 0; i < 4; i++) {
649  if (SWS_COMP_TEST(uop.mask, i))
650  uop.data.vec4[i] = Q2PIXEL(op->clamp.limit[i]);
651  }
652  break;
653  case SWS_OP_SWAP_BYTES:
654  uop.uop = SWS_UOP_SWAP_BYTES;
655  uop.type = pixel_type_to_int(op->type);
656  break;
657  default:
658  return AVERROR(ENOTSUP);
659  }
660 
662  return ff_sws_uop_list_append(uops, &uop);
663 }
664 
667 {
668  SwsComps input = ops->comps_src;
669  for (int i = 0; i < ops->num_ops; i++) {
670  const SwsOp *op = &ops->ops[i];
671  const int pixel_size = ff_sws_pixel_type_size(op->type);
672  if (pixel_size > uops->pixel_size_max)
673  uops->pixel_size_max = pixel_size;
674 
675  int ret = translate_op(ctx, uops, flags, op, &input);
676  if (ret < 0)
677  return ret;
678  input = ops->ops[i].comps;
679  }
680 
681  return ff_sws_uop_list_optimize(ctx, flags, uops);
682 }
SWS_OP_READ
@ SWS_OP_READ
Definition: ops.h:39
SwsUOpList::pixel_size_max
int pixel_size_max
Definition: uops.h:299
SWS_UOP_RW_SHUFFLE
@ SWS_UOP_RW_SHUFFLE
Definition: uops.h:147
factor
static const int factor[16]
Definition: vf_pp7.c:98
ff_sws_rw_op_planes
int ff_sws_rw_op_planes(const SwsOp *op)
Return the number of planes involved in a read/write operation.
Definition: ops.c:132
SWS_UOP_SCALE
@ SWS_UOP_SCALE
Definition: uops.h:164
SWS_OP_SWIZZLE
@ SWS_OP_SWIZZLE
Definition: ops.h:42
Q2PIXEL
#define Q2PIXEL(val)
Definition: uops.c:66
av_bprint_is_complete
static int av_bprint_is_complete(const AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition: bprint.h:218
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
SwsUOpParams::move
SwsMoveUOp move
Definition: uops.h:250
SWS_OP_LSHIFT
@ SWS_OP_LSHIFT
Definition: ops.h:47
SWS_OP_UNPACK
@ SWS_OP_UNPACK
Definition: ops.h:45
SWS_RW_PLANAR
@ SWS_RW_PLANAR
Note: 1-component reads are either SWS_RW_PLANAR or SWS_RW_PACKED, depending on the underlying interp...
Definition: ops.h:100
SwsOpList::comps_src
SwsComps comps_src
Source component metadata associated with pixel values from each corresponding component (in plane/me...
Definition: ops.h:284
SWS_UOP_RSHIFT
@ SWS_UOP_RSHIFT
Definition: uops.h:173
SWS_PIXEL_NONE
@ SWS_PIXEL_NONE
Definition: uops.h:39
SWS_OP_CLEAR
@ SWS_OP_CLEAR
Definition: ops.h:51
SwsClearUOp::zero
SwsCompMask zero
Definition: uops.h:218
matrix
Definition: vc1dsp.c:43
planes
static const struct @605 planes[]
ff_sws_comp_mask_q4
SwsCompMask ff_sws_comp_mask_q4(const AVRational64 q[4])
Definition: ops.c:99
ops.h
ff_sws_pixel_type_is_int
static av_const bool ff_sws_pixel_type_is_int(SwsPixelType type)
Definition: uops.h:62
u
#define u(width, name, range_min, range_max)
Definition: cbs_apv.c:68
SWS_OP_DITHER
@ SWS_OP_DITHER
Definition: ops.h:59
SWS_BITEXACT
@ SWS_BITEXACT
Definition: swscale.h:178
uops_list.h
av_dynarray2_add
void * av_dynarray2_add(void **tab_ptr, int *nb_ptr, size_t elem_size, const uint8_t *elem_data)
Add an element of size elem_size to a dynamic array.
Definition: mem.c:341
b
#define b
Definition: input.c:43
SWS_UOP_MOVE_MAX
#define SWS_UOP_MOVE_MAX
Definition: uops.h:204
SWS_UOP_LINEAR_FMA
@ SWS_UOP_LINEAR_FMA
Definition: uops.h:176
SWS_UOP_MAX
@ SWS_UOP_MAX
Definition: uops.h:167
translate_rw_op
static int translate_rw_op(SwsContext *ctx, SwsUOpList *ops, SwsUOpFlags flags, const SwsOp *op)
Definition: uops.c:294
ff_sws_uop_cmp
int ff_sws_uop_cmp(const SwsUOp *a, const SwsUOp *b)
Copyright (C) 2026 Niklas Haas.
Definition: uops.c:31
max
#define max(a, b)
Definition: cuda_runtime.h:33
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
SWS_COMP_MASK
#define SWS_COMP_MASK(X, Y, Z, W)
Definition: uops.h:101
SWS_UOP_LSHIFT
@ SWS_UOP_LSHIFT
Definition: uops.h:172
SwsLinearUOp::one
uint32_t one
Definition: uops.h:222
SWS_UOP_TYPE_NB
@ SWS_UOP_TYPE_NB
Definition: uops.h:180
SWS_UOP_NAME_MAX
#define SWS_UOP_NAME_MAX
Generate a unique name for a SwsUOp.
Definition: uops.h:289
check_filter_fma
static bool check_filter_fma(SwsContext *ctx, SwsUOpFlags flags, const SwsOp *op)
Definition: uops.c:276
ff_sws_comp_mask_needed
SwsCompMask ff_sws_comp_mask_needed(const SwsOp *op)
Definition: ops.c:122
SwsMoveUOp::num_moves
int num_moves
Definition: uops.h:205
bit
#define bit(string, value)
Definition: cbs_mpeg2.c:56
SwsMoveUOp
Definition: uops.h:202
SWS_COMP_TEST
#define SWS_COMP_TEST(mask, X)
Definition: uops.h:97
av_bprint_init_for_buffer
void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size)
Init a print buffer using a pre-existing buffer.
Definition: bprint.c:85
SWS_UOP_TO_U16
@ SWS_UOP_TO_U16
Definition: uops.h:159
SwsOpList::num_ops
int num_ops
Definition: ops.h:267
SWS_UOP_PACK
@ SWS_UOP_PACK
Definition: uops.h:171
SwsShiftUOp::amount
uint8_t amount
Definition: uops.h:199
SWS_UOP_PERMUTE
@ SWS_UOP_PERMUTE
Definition: uops.h:150
SwsUOpParams::pack
SwsPackUOp pack
Definition: uops.h:251
SWS_UOP_EXPAND_BIT
@ SWS_UOP_EXPAND_BIT
Definition: uops.h:155
UOP_NAME
#define UOP_NAME(OP, ABBR)
val
static double val(void *priv, double ch)
Definition: aeval.c:77
type
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf type
Definition: writing_filters.txt:86
SwsUOpParams
Definition: uops.h:246
SWS_COMP_ELEMS
#define SWS_COMP_ELEMS(N)
Definition: uops.h:99
SwsFilterUOp::type
SwsPixelType type
Definition: uops.h:195
refstruct.h
SWS_FILTER_SCALE
@ SWS_FILTER_SCALE
14-bit coefficients are picked to fit comfortably within int16_t for efficient SIMD processing (e....
Definition: filters.h:40
av_refstruct_allocz
static void * av_refstruct_allocz(size_t size)
Equivalent to av_refstruct_alloc_ext(size, 0, NULL, NULL)
Definition: refstruct.h:105
SWS_UOP_COPY
@ SWS_UOP_COPY
Definition: uops.h:151
SWS_UOP_INVALID
@ SWS_UOP_INVALID
Definition: uops.h:129
SWS_RW_PACKED
@ SWS_RW_PACKED
Definition: ops.h:101
SWS_OP_SCALE
@ SWS_OP_SCALE
Definition: ops.h:55
avassert.h
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
SWS_UOP_WRITE_NIBBLE
@ SWS_UOP_WRITE_NIBBLE
Definition: uops.h:143
SWS_UOP_READ_PALETTE
@ SWS_UOP_READ_PALETTE
Definition: uops.h:139
SWS_OP_NEEDED
#define SWS_OP_NEEDED(op, idx)
Definition: ops.h:237
SwsUOp::kernel
SwsFilterWeights * kernel
Definition: uops.h:266
float
float
Definition: af_crystalizer.c:122
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:504
SwsShuffleUOp::write_size
uint8_t write_size
Definition: uops.h:186
dither
static const uint16_t dither[8][8]
Definition: vf_gradfun.c:46
SwsUOp::uop
SwsUOpType uop
Definition: uops.h:260
UOPS_LIST
#define UOPS_LIST(ENTRY)
This file is part of FFmpeg.
Definition: uops_list.h:23
AVFormatContext::flags
int flags
Flags modifying the (de)muxer behaviour.
Definition: avformat.h:1484
SWS_UOP_WRITE_PLANAR
@ SWS_UOP_WRITE_PLANAR
Definition: uops.h:141
op
static int op(uint8_t **dst, const uint8_t *dst_end, GetByteContext *gb, int pixel, int count, int *x, int width, int linesize)
Perform decode operation.
Definition: anm.c:76
bits
uint8_t bits
Definition: vp3data.h:128
SWS_UOP_TO_F32
@ SWS_UOP_TO_F32
Definition: uops.h:161
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:42
SWS_UOP_MIN
@ SWS_UOP_MIN
Definition: uops.h:166
SWS_OP_MIN
@ SWS_OP_MIN
Definition: ops.h:53
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
SwsCompMask
uint8_t SwsCompMask
Bit-mask of components.
Definition: uops.h:87
SWS_UOP_READ_PACKED
@ SWS_UOP_READ_PACKED
Definition: uops.h:136
SWS_OP_LINEAR
@ SWS_OP_LINEAR
Definition: ops.h:58
count_idx
static int count_idx(const int *arr, size_t size, int val)
Definition: uops.c:347
SWS_OP_FILTER_H
@ SWS_OP_FILTER_H
Definition: ops.h:62
SwsPixel::f32
float f32
Definition: uops.h:83
av_mallocz
#define av_mallocz(s)
Definition: tableprint_vlc.h:31
uop_names
static const struct @594 uop_names[SWS_UOP_TYPE_NB]
ff_sws_pixel_type_size
static av_const int ff_sws_pixel_type_size(SwsPixelType type)
Definition: uops.h:49
SWS_OP_PACK
@ SWS_OP_PACK
Definition: ops.h:46
result
and forward the result(frame or status change) to the corresponding input. If nothing is possible
NULL
#define NULL
Definition: coverity.c:32
SwsUOp::mat4
SwsPixel mat4[4][5]
Definition: uops.h:270
SWS_PIXEL_TYPE_NB
@ SWS_PIXEL_TYPE_NB
Definition: uops.h:44
SwsUOpParams::shift
SwsShiftUOp shift
Definition: uops.h:249
av_unreachable
#define av_unreachable(msg)
Asserts that are used as compiler optimization hints depending upon ASSERT_LEVEL and NBDEBUG.
Definition: avassert.h:116
SwsShuffleUOp::read_size
uint8_t read_size
Definition: uops.h:185
SwsMoveUOp::dst
int8_t dst[SWS_UOP_MOVE_MAX]
Definition: uops.h:208
SwsClearUOp::one
SwsCompMask one
Definition: uops.h:217
SWS_OP_FILTER_V
@ SWS_OP_FILTER_V
Definition: ops.h:63
SWS_UOP_READ_NIBBLE
@ SWS_UOP_READ_NIBBLE
Definition: uops.h:137
SWS_UOP_ADD
@ SWS_UOP_ADD
Definition: uops.h:165
translate_dither_op
static int translate_dither_op(SwsUOpList *ops, const SwsOp *op)
Definition: uops.c:452
SwsPixelType
SwsPixelType
Definition: uops.h:38
pixel_is_1s
static bool pixel_is_1s(SwsPixelType type, SwsPixel val)
Definition: uops.c:68
index
int index
Definition: gxfenc.c:90
SwsUOp::par
SwsUOpParams par
Definition: uops.h:262
SWS_UOP_TO_U32
@ SWS_UOP_TO_U32
Definition: uops.h:160
ff_sws_uop_list_remove_at
void ff_sws_uop_list_remove_at(SwsUOpList *uops, int index, int count)
Definition: uops.c:215
exact_prod
static bool exact_prod(SwsPixelType type, SwsPixel coef, const SwsComps *comps, int idx)
Definition: uops.c:254
SwsUOp
Definition: uops.h:257
SWS_UOP_WRITE_BIT
@ SWS_UOP_WRITE_BIT
Definition: uops.h:144
uop_uninit
static void uop_uninit(SwsUOp *uop)
Definition: uops.c:167
SWS_UOP_READ_PLANAR_FV_FMA
@ SWS_UOP_READ_PLANAR_FV_FMA
Definition: uops.h:135
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
SwsLinearUOp::zero
uint32_t zero
Definition: uops.h:223
SwsUOp::mask
SwsCompMask mask
Definition: uops.h:261
SwsDitherUOp::size_log2
uint8_t size_log2
Definition: uops.h:237
size
int size
Definition: twinvq_data.h:10344
SWS_OP_RSHIFT
@ SWS_OP_RSHIFT
Definition: ops.h:48
AVRational64
64-bit Rational number (pair of numerator and denominator).
Definition: rational64.h:52
SWS_OP_WRITE
@ SWS_OP_WRITE
Definition: ops.h:40
SWS_UOP_UNPACK
@ SWS_UOP_UNPACK
Definition: uops.h:170
SWS_COMP
#define SWS_COMP(X)
Definition: uops.h:96
SWS_PIXEL_U32
@ SWS_PIXEL_U32
Definition: uops.h:42
av_refstruct_ref
void * av_refstruct_ref(void *obj)
Create a new reference to an object managed via this API, i.e.
Definition: refstruct.c:140
SwsShuffleUOp::clear_value
uint8_t clear_value
Definition: uops.h:184
a
The reader does not expect b to be semantically here and if the code is changed by maybe adding a a division or other the signedness will almost certainly be mistaken To avoid this confusion a new type was SUINT is the C unsigned type but it holds a signed int to use the same example SUINT a
Definition: undefined.txt:41
SWS_MASK_ROW
#define SWS_MASK_ROW(I)
Definition: uops.h:231
SwsPixel
Definition: uops.h:77
SwsOp::comps
SwsComps comps
Metadata about the operation's input/output components.
Definition: ops.h:234
input
and forward the test the status of outputs and forward it to the corresponding return FFERROR_NOT_READY If the filters stores internally one or a few frame for some input
Definition: filter_design.txt:172
ff_sws_uop_list_alloc
SwsUOpList * ff_sws_uop_list_alloc(void)
Definition: uops.c:197
av_refstruct_unref
void av_refstruct_unref(void *objp)
Decrement the reference count of the underlying object and automatically free the object if there are...
Definition: refstruct.c:120
ff_sws_uop_list_optimize
int ff_sws_uop_list_optimize(SwsContext *ctx, SwsUOpFlags flags, SwsUOpList *uops)
Called internally by ff_sws_ops_translate().
Definition: ops_optimizer.c:1016
av_assert2
#define av_assert2(cond)
assert() equivalent, that does lie in speed critical code.
Definition: avassert.h:68
SWS_UOP_TO_U8
@ SWS_UOP_TO_U8
Definition: uops.h:158
exact_product_f32
static bool exact_product_f32(float a, float b)
Definition: uops.c:247
SWS_UOP_READ_PLANAR
@ SWS_UOP_READ_PLANAR
Definition: uops.h:132
SwsOpList::ops
SwsOp * ops
Definition: ops.h:266
SWS_PIXEL_U8
@ SWS_PIXEL_U8
Definition: uops.h:40
av_assert1
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:58
needed
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is needed
Definition: filter_design.txt:212
SWS_UOP_SWAP_BYTES
@ SWS_UOP_SWAP_BYTES
Definition: uops.h:154
SwsUOp::scalar
SwsPixel scalar
Definition: uops.h:268
SWS_UOP_LINEAR
@ SWS_UOP_LINEAR
Definition: uops.h:175
SwsOp
Definition: ops.h:210
SwsUOpParams::lin
SwsLinearUOp lin
Definition: uops.h:253
SwsUOpList::planes_out
SwsCompMask planes_out
Definition: uops.h:298
SwsPackUOp::pattern
uint8_t pattern[4]
Definition: uops.h:213
abbr
char abbr[32]
Definition: uops.c:43
SwsUOp::type
SwsPixelType type
Definition: uops.h:259
AVRational64::den
int64_t den
Denominator.
Definition: rational64.h:54
pixel_type_to_int
static SwsPixelType pixel_type_to_int(const SwsPixelType type)
Definition: uops.c:234
ff_sws_ops_translate
int ff_sws_ops_translate(SwsContext *ctx, const SwsOpList *ops, SwsUOpFlags flags, SwsUOpList *uops)
Translate a list of operations down to micro-ops, which can be further optimized and then directly ex...
Definition: uops.c:665
ret
ret
Definition: filter_design.txt:187
is_expand_bit
static bool is_expand_bit(SwsPixelType type, AVRational64 factor)
Definition: uops.c:543
SwsComps::min
AVRational64 min[4]
Definition: ops.h:87
SwsUOpList::num_ops
int num_ops
Definition: uops.h:294
SWS_OP_MAX
@ SWS_OP_MAX
Definition: ops.h:54
ff_sws_uop_list_free
void ff_sws_uop_list_free(SwsUOpList **p_ops)
Definition: uops.c:183
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:122
SWS_RW_PALETTE
@ SWS_RW_PALETTE
Definition: ops.h:102
SwsUOp::ptr
SwsPixel * ptr
Definition: uops.h:267
SwsComps
Definition: ops.h:82
ff_sws_pixel_type_name
const char * ff_sws_pixel_type_name(SwsPixelType type)
Definition: ops.c:56
SwsLinearUOp::exact
uint32_t exact
Definition: uops.h:226
SWS_OP_SWAP_BYTES
@ SWS_OP_SWAP_BYTES
Definition: ops.h:41
ff_sws_uop_name
void ff_sws_uop_name(const SwsUOp *op, char buf[SWS_UOP_NAME_MAX])
Definition: uops.c:81
SwsDitherUOp::y_offset
uint8_t y_offset[4]
Definition: uops.h:236
px
#define px
Definition: uops_tmpl.c:54
SwsUOpList
Definition: uops.h:292
SwsUOp::vec4
SwsPixel vec4[4]
Definition: uops.h:269
SwsUOpList::planes_in
SwsCompMask planes_in
Definition: uops.h:297
ff_sws_uop_list_append
int ff_sws_uop_list_append(SwsUOpList *uops, SwsUOp *uop)
Definition: uops.c:202
SWS_UOP_DITHER
@ SWS_UOP_DITHER
Definition: uops.h:177
SWS_UOP_WRITE_PACKED
@ SWS_UOP_WRITE_PACKED
Definition: uops.h:142
SwsDitherUOp
Definition: uops.h:235
SwsUOpParams::dither
SwsDitherUOp dither
Definition: uops.h:254
mem.h
SWS_PIXEL_F32
@ SWS_PIXEL_F32
Definition: uops.h:43
SWS_UOP_READ_PLANAR_FV
@ SWS_UOP_READ_PLANAR_FV
Definition: uops.h:134
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
uops.h
SWS_UOP_EXPAND_QUAD
@ SWS_UOP_EXPAND_QUAD
Definition: uops.h:157
SwsUOpFlags
uint32_t SwsUOpFlags
Definition: uops.h:121
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
AVRational64::num
int64_t num
Numerator.
Definition: rational64.h:53
SwsComps::max
AVRational64 max[4]
Definition: ops.h:87
SWS_OP_CONVERT
@ SWS_OP_CONVERT
Definition: ops.h:52
SWS_UOP_READ_PLANAR_FH
@ SWS_UOP_READ_PLANAR_FH
Definition: uops.h:133
SwsMoveUOp::src
int8_t src[SWS_UOP_MOVE_MAX]
Definition: uops.h:209
SwsUOpParams::filter
SwsFilterUOp filter
Definition: uops.h:248
translate_linear_op
static int translate_linear_op(SwsContext *ctx, SwsUOpList *ops, SwsUOpFlags flags, const SwsOp *op, const SwsComps *input)
Definition: uops.c:500
SWS_UOP_FLAG_FMA
@ SWS_UOP_FLAG_FMA
Definition: uops.h:124
ff_sws_dither_height
int ff_sws_dither_height(const SwsDitherUOp *dither)
Computes (1 << size_log2) + MAX(y_offset).
Definition: uops.c:226
translate_op
static int translate_op(SwsContext *ctx, SwsUOpList *uops, SwsUOpFlags flags, const SwsOp *op, const SwsComps *input)
Definition: uops.c:561
av_bprint_chars
void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
Append char c n times to a print buffer.
Definition: bprint.c:130
SWS_UOP_READ_BIT
@ SWS_UOP_READ_BIT
Definition: uops.h:138
stride
#define stride
Definition: h264pred_template.c:536
SWS_UOP_CLEAR
@ SWS_UOP_CLEAR
Definition: uops.h:174
SwsOpList
Helper struct for representing a list of operations.
Definition: ops.h:265
SwsContext
Main external API structure.
Definition: swscale.h:227
SWS_PIXEL_U16
@ SWS_PIXEL_U16
Definition: uops.h:41
SWS_MASK
#define SWS_MASK(I, J)
Definition: uops.h:229
SwsUOp::data
union SwsUOp::@596 data
translate_swizzle
static int translate_swizzle(SwsUOpList *ops, const SwsOp *op)
Definition: uops.c:358
SwsUOpParams::clear
SwsClearUOp clear
Definition: uops.h:252
SwsUOpList::ops
SwsUOp * ops
Definition: uops.h:293
src
#define src
Definition: vp8dsp.c:248
SWS_UOP_EXPAND_PAIR
@ SWS_UOP_EXPAND_PAIR
Definition: uops.h:156
pixel_from_q64
static SwsPixel pixel_from_q64(SwsPixelType type, AVRational64 val)
Definition: uops.c:50
SwsUOpParams::shuffle
SwsShuffleUOp shuffle
Definition: uops.h:247
ff_sws_comp_mask_str
#define ff_sws_comp_mask_str(mask)
Definition: uops.h:109
min
float min
Definition: vorbis_enc_data.h:429